Compare commits

..

310 Commits

Author SHA1 Message Date
Christian Buttner
5ab5fdf954 Address/memory/thread sanitizer. 2024-08-21 16:01:45 +02:00
Christoffer Lerno
b46463563e Add path test windows and escape in double quote. 2024-08-21 10:37:50 +02:00
Christoffer Lerno
33ce8e8a75 Add path test windows. 2024-08-21 10:15:45 +02:00
Chuck Benedict
05ab0707fc Add RISC-V block asm support 2024-08-20 22:42:38 +02:00
Christoffer Lerno
d32861193b DynamicArenaAllocator would not correctly free. 2024-08-20 22:42:01 +02:00
Christoffer Lerno
fb4a231703 Add $member.get(value) to replace value.$eval($member.nameof) 2024-08-20 14:24:12 +02:00
Christoffer Lerno
0963ab4cc0 Update readme. 2024-08-19 23:52:39 +02:00
Christoffer Lerno
a248511d7b Added ElasticArray 2024-08-19 23:20:14 +02:00
Christoffer Lerno
79a1639f8a Fix aligned alloc for Win32 targets. 2024-08-19 15:25:00 +02:00
Christoffer Lerno
476a6424ee insert_at incorrectly prevented inserts at the end of a list. 2024-08-19 11:44:06 +02:00
Christoffer Lerno
6de17b9ae9 Fix use of deprecated function. Fix bug when compile time subtracting a distinct type. Fix test/benchmark debug info use. 2024-08-19 09:36:45 +02:00
Christoffer Lerno
cb7116f08b New linker options handling 2024-08-19 01:28:57 +02:00
Christoffer Lerno
15a4e23b22 Benchmark / test no longer suppresses debug info. #1364 2024-08-18 22:29:41 +02:00
Christoffer Lerno
2b0857baf9 Add connection reset error. 2024-08-18 20:01:54 +02:00
Christoffer Lerno
20b0bf43ad Fix of dstring. 2024-08-18 09:50:54 +02:00
Christoffer Lerno
17d6f03bae New hashmap type, Map 2024-08-18 00:37:24 +02:00
Owen Shepherd
4edaf603c9 fix: Guard against uninitialized hashmap in key removal
Removing non-present keys is a supported operation on HashMaps,
and most other operations are well-defined on uninitialized
HashMaps.

Currently, removing any key on an uninitialized HashMap will
result in an 'Array index out of bounds' error.

This change guards against such a case.
2024-08-17 02:35:04 +02:00
Christoffer Lerno
74b8da1e15 Avoid any constants that have the "untyped list" type but isn't a CONST_UNTYPED_LIST. 2024-08-16 21:49:21 +02:00
Christoffer Lerno
16cb756d3f Bug converting untyped list #1360 2024-08-16 18:33:30 +02:00
Christoffer Lerno
f1efdf3d98 Incorrect zero analysis on foo["test"] = {} #1360 2024-08-16 16:50:58 +02:00
Christoffer Lerno
edfea639cf - Introduce $vaarg[...] syntax and deprecate the old $vaarg(...).
- Similar change to `$vasplat`: `$vasplat` and `$vasplat[1..]`.
2024-08-16 09:28:28 +02:00
Christoffer Lerno
9fd9280132 Fix incorrect parsing of $exec. 2024-08-16 00:07:09 +02:00
Christoffer Lerno
d0bb69516a Missing check on optional left hand side for s.x. #1360 2024-08-15 21:15:59 +02:00
Christoffer Lerno
dc44254ba1 Debug info with recursive canonical type usage could cause segfault. 2024-08-15 20:22:56 +02:00
Christian Buttner
85c682f7e6 Escape arguments to platform linker/compiler. (#1358)
* Escape arguments to platform linker/compiler.
2024-08-15 15:01:53 +02:00
Christoffer Lerno
2a69f93605 Issues with wincrt linking. 2024-08-15 13:33:08 +02:00
Christoffer Lerno
ad4950130c Remove use of tappend in rmtree on windows. 2024-08-15 00:40:01 +02:00
Christoffer Lerno
3ccb4b9ec3 $exec may now provide a stdin parameter. Deprecated path.append, path.tappend, getcwd, tgetcwd, path.absolute, ls. Deprecated env::get_config_dir, replaced by env::new_get_config_dir. Added path.has_extension, path.new_append, path.temp_append, new_cwd, temp_cwd, path.new_absolute, new_ls, temp_ls. Added dstring.replace Updated win escapes for exec. 2024-08-15 00:31:47 +02:00
Christoffer Lerno
6bc486400c Add globals to -P output. 2024-08-13 22:50:45 +02:00
Christoffer Lerno
9228dbb8b8 Fix ordering issues with $include / $exec by adding a pass #1302. 2024-08-13 22:19:53 +02:00
Christoffer Lerno
e68b453218 Do not bundle output with docker. 2024-08-13 15:23:56 +02:00
Christoffer Lerno
1dd2b0ec19 Add a few newlines to maybe keep some compilers happy. 2024-08-13 14:33:33 +02:00
Tom Clesius
e7e9d3b8c7 Adapt Docker script and Dockerfile (#1347)
Adapt Docker script and Dockerfile
2024-08-13 13:37:16 +02:00
kostyavechkanov
800ad9e898 Feature/add-target (#1) (#1350)
Feature/add target (#1) project add-target command
2024-08-13 13:34:53 +02:00
Christoffer Lerno
ddecf2d5f0 Correctly show macOS version settings for project.json 2024-08-13 10:25:15 +02:00
Mikhail Shimanov
09da17dab7 Update README.md
Duplicate package removed
2024-08-12 23:26:38 +02:00
Christoffer Lerno
1678e2a939 Assert not properly traced #1354. Update interface fix. 2024-08-12 21:01:04 +02:00
Christoffer Lerno
9aab962ebc Interface resolution when part of generics #1348. 2024-08-12 10:25:53 +02:00
Christoffer Lerno
baf6e71a80 Fix interface lazy resolution errors. Fix i128 change in LLVM. 2024-08-12 01:25:30 +02:00
Christoffer Lerno
412fa4b12f Use PIE/PIC on Linux 2024-08-11 23:00:38 +02:00
Christoffer Lerno
3cae557b88 Int128 alignment fixed on x64 Linux. 2024-08-11 22:48:21 +02:00
Christoffer Lerno
f7c39ae4a9 Recursively follow interfaces when looking up method. 2024-08-11 21:16:02 +02:00
Christoffer Lerno
6d93ce9d33 Update to libc::setjmp on Win32, to do no stack unwinding. 2024-08-11 18:22:14 +02:00
Sergwest
031cbae0d6 added the necessary library to build on void linux in the README.md 2024-08-11 17:07:48 +02:00
Christoffer Lerno
5fbee47c2b Update version information, 2024-08-11 17:03:21 +02:00
Christoffer Lerno
2cd25a489a Fix of global state init. 2024-08-11 16:55:40 +02:00
Christoffer Lerno
e67586b8b0 Fixes to library loading and test sources. 2024-08-11 16:46:53 +02:00
Christoffer Lerno
7d643942b4 Fix issues when checking methods and interfaces hasn't been resolved yet. 2024-08-11 16:16:16 +02:00
Christoffer Lerno
2257a7f4ec Comment out decl size. 2024-08-11 15:14:52 +02:00
Christoffer Lerno
f8ca173fd8 Refactoring a bit. 2024-08-11 15:05:36 +02:00
Christoffer Lerno
b08e6743be When resolving inherited interfaces, the interface type wasn't always resolved. 2024-08-11 10:19:20 +02:00
Christoffer Lerno
a97e4fe42d Add temp allocator scribble. Make bufferstream safer. 2024-08-11 01:17:25 +02:00
Christoffer Lerno
2706495668 Add temp allocator scribble. Make bufferstream safer. 2024-08-11 01:17:03 +02:00
Christoffer Lerno
224c3f4123 Printable values passed to the Formatter as pointers, will print as if passed by value. Pointers are rendered with "0x" prefix when passed to '%s'. 2024-08-11 00:27:06 +02:00
Christoffer Lerno
f2911be116 Assertion when has_tagof is accidentally called on fn type #1343 2024-08-10 21:59:41 +02:00
Christoffer Lerno
05c5eaed48 Add deprecation notice for $and, $or, $concat, $append. 2024-08-10 21:25:13 +02:00
Christoffer Lerno
8541e9535e Fix print when a tag is not found. 2024-08-10 21:15:19 +02:00
Christoffer Lerno
811cb2b95c Add string methods to json, and fix issue in dstring when the formatter uses temp. Remove unnecessary use of temp allocator in to_format for json. 2024-08-10 19:13:58 +02:00
Christoffer Lerno
05421223be Add --silence-deprecation 2024-08-10 09:54:59 +02:00
Christoffer Lerno
808a6b82f3 Add simple UTF16 detection. 2024-08-10 02:50:42 +02:00
Christian Buttner
30af7f1ca6 Add c-include-dirs project/manifest setting. (#1338)
Set the include directories to be used when compiling C sources.
2024-08-10 01:51:59 +02:00
Christoffer Lerno
274e5280cb Rename muldiv and update tests for LLVM 20 2024-08-09 23:56:26 +02:00
Samuel Goad
f85c4cd79f Update string_iterator.c3 to include extra convenience methods (#1327)
Update string_iterator.c3 to include extra convenience methods

Added peek: returns the next character without incrementing current
Added has_next: checks if the iterator has another element
Added get: gets the current element (the same one that was returned with the previous call to next).
2024-08-09 23:10:46 +02:00
Lexi
696d39b922 Move safe_mul_div macro and make it generic on integer types (#1334)
Move safe_mul_div macro and make it generic on integer types
2024-08-09 22:54:26 +02:00
Christoffer Lerno
d997445284 The compiler now skips UTF8 BOM. 2024-08-09 22:39:24 +02:00
Christoffer Lerno
f3e5268083 % analysis was incorrect for int vectors. 2024-08-09 15:23:40 +02:00
Christoffer Lerno
44db4a21fc Add @tag and .tagof .has_tagof. Allow bitstructs to have attributes. 2024-08-09 15:03:44 +02:00
Christoffer Lerno
c8a113384c Better precision with Clock on Win32 2024-08-08 23:03:04 +02:00
Lexi Allen
07e7bc0a94 Fix win32 native_clock() by converting native performance counter value to nanoseconds using previously gotten frequency value 2024-08-08 21:14:58 +02:00
Prithviraj Renjella Rajendra Prasad
7c8acbe485 Implemented arg passing to clean-run and run commands (#1328)
* added clean-run and run commands to list of commands that pass args

* Updated compiler usage message to reflect that commands run and clean-run accept args
2024-08-08 16:24:00 +02:00
Christoffer Lerno
65c2126202 Removing tb codegen info, because it's sure to have code-rotted by now. 2024-08-08 12:55:40 +02:00
Christoffer Lerno
0ef0f62b69 Only destroy temp allocators on env::LIBC. 2024-08-08 12:48:01 +02:00
Christoffer Lerno
921422a189 Fix Vec2.angle 2024-08-08 01:48:39 +02:00
Christoffer Lerno
56b771a7ad Support destroying temp allocators, and destroy temp allocators on exit. 2024-08-07 16:24:26 +02:00
Christoffer Lerno
d2988e6a88 With single module, name the .o file after what -o provides. #1306 2024-08-07 01:35:09 +02:00
Velikiy Kirill
16510d2400 Update tcp.c3 to finally (i guess) fix Windows Sockets (#1324)
Update tcp.c3
2024-08-06 21:20:59 +02:00
Christoffer Lerno
6f790598ef Update manifest.json template. #1321 2024-08-06 17:42:23 +02:00
Christoffer Lerno
63f0c7b2fe 'wincrt' in manifest.json should now be respected #1322 2024-08-06 17:37:43 +02:00
Christoffer Lerno
800f7970a7 Fixes to the socket functions. Improved output when pointer is out of range. Better error when casting to a distinct fails. 2024-08-06 17:08:03 +02:00
Velikiy Kirill
b7381fc075 Adding win32_WSACleanup() 2024-08-06 15:59:36 +02:00
Christoffer Lerno
b1785606cc LLVM codegen for constants in enums could fail. 2024-08-06 00:28:03 +02:00
Christoffer Lerno
387d7d5508 Fix defer on 19/20 2024-08-05 23:15:55 +02:00
Christoffer Lerno
6adacf8892 Fix module name regression. 2024-08-05 23:04:29 +02:00
Christoffer Lerno
f7d6f93f1b Refactoring -> ensure built in aliases have a valid unit. 2024-08-05 22:48:18 +02:00
Dodzey
9daa173ab7 Add methodsof to type info (#1303)
Add `methodsof` to type info for struct, union and bitstruct
2024-08-05 21:58:13 +02:00
Lexi
e748f72447 Project view command (#1314)
Add parsing for the project command and view subcommand. Add basic implementation of c3c project view. Move get_valid_integer into common build.

Co-authored-by: Christoffer Lerno <christoffer.lerno@gmail.com>
2024-08-05 21:45:15 +02:00
Christoffer Lerno
14358417c8 Refactoring -> ensure built in aliases have a valid unit. 2024-08-05 21:44:02 +02:00
Oloruntobi1
8aa3461bf6 [DOCS] updated readme with issue 1086 link 2024-08-05 19:55:04 +02:00
Christoffer Lerno
04c37a98b5 Formatting. 2024-08-05 19:53:39 +02:00
rexim
4850f1e94b Ignore EINTR return from waitpid
Apparently it is a thing that can happen and for example musl just
ignores such situations and tries to wait again.

dd1e63c363/src/process/system.c (L39)
2024-08-05 19:53:05 +02:00
Christoffer Lerno
60945ffe58 Fix of tests. 2024-08-05 19:52:34 +02:00
Christoffer Lerno
746016996c Variable in if-try / if-catch cannot be a reused variable name. 2024-08-05 18:43:04 +02:00
Christoffer Lerno
67a2734777 Issue where a if (catch e = ...) in a defer would be incorrectly copied. Causing codegen error. 2024-08-05 15:20:50 +02:00
Christoffer Lerno
b208fc7cf5 Add unreachable. 2024-08-04 23:40:28 +02:00
Christoffer Lerno
2748cf99b3 - Fix issue where a compile time parameter is followed by "...".
- Fix issue with some conversions to untyped list.
- Experimental change: add `+++` `&&&` `|||` as replacement for `$concat`, `$and` and `$or`.
2024-08-04 23:16:25 +02:00
Christoffer Lerno
b49b60ab5f Fix compiler timings. 2024-08-04 11:21:23 +02:00
Christoffer Lerno
620c67b04e Bug in List add_array when reserving memory. 2024-08-04 01:34:45 +02:00
Alexey Kutepov
a5b5f315d1 Implement passing arguments to program via compile-run (#1296) 2024-08-03 19:47:52 +02:00
Christoffer Lerno
43ea05aad2 Remove $expand. 2024-08-03 12:47:19 +02:00
Halen84
0ec1c80221 Fix typo in parse_global.c
In parse_def_ident()
2024-08-03 03:50:55 +02:00
Christoffer Lerno
d91c289bf6 Distinct inline can now be called if it is aliasing a function pointer. 2024-08-03 03:08:38 +02:00
Christoffer Lerno
74b9971494 Add wincrt setting to libraries. 2024-08-02 20:15:40 +02:00
Christoffer Lerno
f8f116109a Use back-off strategy when allocating virtual memory. 2024-08-02 15:45:16 +02:00
Christoffer Lerno
8498cb6258 Add @const attribute for macros, for better error messages with constant macros #1293 2024-08-02 15:01:02 +02:00
Yhya Ibrahim
7a72f44f64 Add --run-once option to delete the output file after running it (#1295)
Add `run-once` option to delete the output file after running it
2024-08-02 12:47:36 +02:00
Christoffer Lerno
a90e3c440b Distinct inline would not implement protocol if the inlined implemented it. This closes #1292 2024-08-02 11:37:38 +02:00
Christoffer Lerno
1aab8b87ec Add experimental @noalias attribute. 2024-08-01 22:57:26 +02:00
Christoffer Lerno
ebf071ac51 Fix incorrect override of optimization levels when using projects. 2024-08-01 21:40:51 +02:00
Christoffer Lerno
3d0fc33441 && doesn't work correctly with lambdas #1279. 2024-08-01 21:16:23 +02:00
Christoffer Lerno
c50df85976 Fix docs to match the update in supporting LLVM 17+ only. 2024-08-01 20:04:54 +02:00
Christoffer Lerno
db9fc20acf Wrapper RTTI now follows LLVM RTTI. 2024-08-01 19:58:52 +02:00
Christoffer Lerno
10058cf271 - Distinct func type would not accept direct function address assign. #1287 2024-08-01 16:26:50 +02:00
Christoffer Lerno
310dadef45 No type_lowering in the frontend. 2024-08-01 13:26:47 +02:00
Christoffer Lerno
3159f036a2 Update lowering for function pointers. 2024-08-01 11:37:40 +02:00
Christoffer Lerno
c3e426c82a Assertion with duplicate function pointer signatures #1286 2024-08-01 01:52:26 +02:00
Christoffer Lerno
b83d388523 Incorrect justify formatting of integers. 2024-08-01 01:20:42 +02:00
Christoffer Lerno
d8820259d2 Enable LLVM 19 2024-08-01 00:21:14 +02:00
Christoffer Lerno
354d78e893 Temporarily disable LLVM for Linux 2024-08-01 00:12:01 +02:00
Christoffer Lerno
7f00f35f4b $expand macro, to expand a string into code. opt project setting now properly documented. 2024-08-01 00:07:16 +02:00
Yhya Ibrahim
8c33b073c2 Fix a warning/error where C compilers can not predict that a variable is initialized 2024-07-31 18:05:26 +02:00
Christoffer Lerno
d6490c9bab Make it possible to set max limit for memory pages. 2024-07-31 18:03:52 +02:00
PalsFreniers
b0e104bfd0 Adding Termios library as std::libc::termios (posix libc functions) (#1272)
Adding libc's termios to lib/std/libc
2024-07-31 14:45:04 +02:00
Chuck Benedict
563e677b08 Add Riscv Example (#1268)
Add Riscv example. Risc-V CI. Install baremetal toolchain. Prevent imported crt file from messing up linker search.
2024-07-31 14:43:47 +02:00
Ikko Eltociear Ashimine
7664d0568e Update bytewriter.c3
minor fix
2024-07-31 01:37:12 +02:00
Christoffer Lerno
e1a13e433f Experimental xtensa support 2024-07-31 01:36:28 +02:00
Dodzey
d212f7d946 Remove extra item 2024-07-30 17:52:14 +02:00
Christoffer Lerno
8d6dabf65c Struct members declared in a single line declaration were not sharing attributes. #1266 2024-07-30 02:45:50 +02:00
Dmitry Atamanov
a4c5b85db8 Remove extra space. 2024-07-29 15:16:14 +02:00
Christoffer Lerno
e66001c182 Using winmain would call the wrong definition #1265. 2024-07-29 15:04:32 +02:00
Christoffer Lerno
08c7b35731 Improve the error message when typing fn void Foo(). 2024-07-28 21:08:47 +02:00
Christoffer Lerno
35cb36fcea Fix incorrect linker selection. 2024-07-28 17:55:59 +02:00
Christoffer Lerno
bf8ca989d6 Add --show-backtrace option to disable backtrace for even smaller binary. 2024-07-28 01:10:59 +02:00
Christoffer Lerno
4976ebcef4 Permit foreach values to be optional. Update matching algorithm. 2024-07-27 21:53:44 +02:00
Christoffer Lerno
51661f5c55 c3c init-lib does not create the directory with the .c3l suffix #1253 2024-07-27 11:52:56 +02:00
Christoffer Lerno
3cbb10392c Don't generate .o files on compile and compile-run if there is no main. 2024-07-27 05:00:27 +02:00
Christoffer Lerno
168ce752d1 Package Linux binaries in a folder called "c3" and not "linux" 2024-07-27 02:49:09 +02:00
Christoffer Lerno
8fcf9bc6bf Give some symbol name suggestions when the path is matched. 2024-07-27 01:21:02 +02:00
Christoffer Lerno
56f43f55f3 Add WASM test. 2024-07-26 21:39:45 +02:00
Christoffer Lerno
9386ac026d dbghelp.lib was linked even on nolibc on Windows. 2024-07-26 20:54:57 +02:00
Christoffer Lerno
e1565ccdc5 Regression: Invalid is_random implementation due to changes in 0.6. 2024-07-26 20:49:17 +02:00
Christoffer Lerno
34993a20fd Fix broken WASM std library code. 2024-07-26 19:20:58 +02:00
Christoffer Lerno
ea0124433a Remove "EXPR_GROUP" to simplify the code somewhat. 2024-07-26 14:34:08 +02:00
Christoffer Lerno
73b15c691d Deprecate *-add settings, use without -add. Updated CI. 2024-07-26 03:23:30 +02:00
Christoffer Lerno
623dd9f3b3 Added "weak" type aliases def Foo = my_foo::Foo @weak; 2024-07-26 01:13:48 +02:00
Christoffer Lerno
379637f214 Scalar -> vector not implicit in call or assign. 2024-07-24 14:00:09 +02:00
Christian Buttner
26ca8f7777 Add type property is_substruct. 2024-07-24 14:00:09 +02:00
Christoffer Lerno
237f7e7f1a Updated stats. 2024-07-24 14:00:09 +02:00
Christoffer Lerno
34fc9851bf Update wrapper 2024-07-24 14:00:09 +02:00
Christoffer Lerno
abdaca08fe Add new optimizer runner. 2024-07-24 14:00:09 +02:00
Christoffer Lerno
bdc9f339c9 The msvc_sdk script failed to work properly on windows when run in folders with spaces 2024-07-24 13:18:26 +02:00
Christoffer Lerno
3188d4d858 Reference parameter doesn't work with vector subscript #1250. 2024-07-23 21:20:40 +02:00
Christoffer Lerno
1bb76b1a49 Unsplat with named parameters was accidentally disallowed. 2024-07-22 11:44:34 +02:00
Christoffer Lerno
9584efd84c Update AVX support. 2024-07-22 02:34:33 +02:00
Christoffer Lerno
c84bc8a8f3 Add convenience function. 2024-07-21 00:43:16 +02:00
Christian Buttner
edc55a2afd Small fixes to stdlib. (#1247)
Small fixes to stdlib. Match the signature of `NativeConditionVariable.wait_timeout` and `NativeMutex.lock_timeout` of thread_win32.c3 to `ConditionVariable.wait_timeout` and `TimedMutex.lock_timeout` to avoid casting errors. Add `time::us`.
2024-07-20 19:19:16 +02:00
Christoffer Lerno
480325177c Remove accidental debug code. 2024-07-20 18:40:25 +02:00
Christoffer Lerno
03cfa42eb6 Duplicate symbols with static variable declared in macro #1248. Improved error message when trying user foreach with an untyped list. 2024-07-20 03:39:33 +02:00
Christoffer Lerno
b25c573ae3 Indexing into a constant array / struct now works at compile time. Constants defined by indexing into another constant could fail codegen. Stdlib nolibc code bugs fixed. 2024-07-20 01:20:03 +02:00
Christoffer Lerno
7f5757d66b Add .dot to integer vectors. 2024-07-19 11:34:05 +02:00
Christoffer Lerno
a3a275c3d5 Updated linux build 2024-07-19 11:10:59 +02:00
Christoffer Lerno
557f007b12 Spelling 2024-07-19 10:38:52 +02:00
Christoffer Lerno
542406c16f Exclude 18 for linux for now. 2024-07-19 01:13:49 +02:00
Christoffer Lerno
1fa870411f Separate LLVM18 compile for Linux in CI 2024-07-19 00:36:04 +02:00
Christoffer Lerno
c096487eea Test if this fixes LLVM 18 compilation. 2024-07-19 00:32:27 +02:00
Christoffer Lerno
97a8e0cdd4 Retain backwards compatibility with old manifest.json. 2024-07-19 00:23:54 +02:00
Christoffer Lerno
eb20a5c051 mainfest.json is now checked for incorrect keys. Added --list-manifest-properties to list the available properties in manifest.json. 2024-07-19 00:03:05 +02:00
Christoffer Lerno
5c6acf89da Added docs to io.c3 2024-07-18 20:44:36 +02:00
Christoffer Lerno
9dfe7ddbde Add wrapper methods, use LLVM-transforms directly. 2024-07-18 20:44:36 +02:00
Christian Buttner
8285720180 Add tests and improvements for @nopadding and @compact. 2024-07-17 17:00:36 +02:00
Christoffer Lerno
a4a1a42842 Update llvm build to use on windows. 2024-07-17 16:55:55 +02:00
Christoffer Lerno
3c3217ab2b Fix PIE. 2024-07-16 14:58:48 +02:00
Alex Anderson
17ee3887dd Use usz and fix out of bounds access in branchless loop 2024-07-16 13:22:11 +02:00
Alex Anderson
db75da65db Make countingsort.c3's recursion stage branchless
Tracks the three potential cases for each fallback, item counts ranging from [2,32], [33,128], [128, ...] and uses a loop specifically for each fallback.
2024-07-15 22:25:59 +02:00
Christoffer Lerno
cf95257c81 Fix test (again). 2024-07-15 17:30:42 +02:00
Christoffer Lerno
b40036c203 Fix test. 2024-07-15 17:04:06 +02:00
Christian Buttner
b18661a8b0 Make stdlib mem::allocator more complete. (#1238)
Make stdlib mem::allocator more complete. Fill in some gaps and docstrings. List.to_new_array. Handle overalignment smoothly in list.
2024-07-15 16:35:40 +02:00
Christoffer Lerno
bc0d52142a Added pull request #1189: Fix os::native_is_{file,dir} bug. Add tests. 2024-07-15 03:02:54 +02:00
Christoffer Lerno
24041ed80d Macro $case statements now pick the first match and does not evaluate the rest. Added countingsort tests #1234. 2024-07-15 02:01:26 +02:00
Christoffer Lerno
1a03e6b22e Prevent implicit array casts to pointers with higher alignment. #1237 2024-07-14 23:44:05 +02:00
Christoffer Lerno
68fb916195 Fix when memcmp is defined. 2024-07-14 16:51:38 +02:00
Christoffer Lerno
dfb8a1b8cb Improved bool and float array comparisons. 2024-07-14 14:16:17 +02:00
Christoffer Lerno
6c38409c57 Array comparison now uses built-in memcmp on LLVM to enable optimizations. 2024-07-14 01:35:19 +02:00
Christoffer Lerno
27fd7a9088 - Fix problem where a $$FUNC would return "<GLOBAL>" when evaluated for a static in a function #1236. 2024-07-13 19:57:04 +02:00
Christoffer Lerno
0e62423e06 Bitstruct in struct fix. 2024-07-13 01:54:45 +02:00
Christoffer Lerno
3f45ed14b9 Compare @compact structs. 2024-07-12 23:54:07 +02:00
Christoffer Lerno
ca4b782912 MemberIndex -> ArrayIndex 2024-07-12 18:27:05 +02:00
Christian Buttner
1976a11154 @nopadding and @compact attributes (#1235)
Add `@nopadding` attribute. `@compact`
2024-07-12 18:25:09 +02:00
Christoffer Lerno
e7d8f64a49 Compile c files to separate directories. Add compressed library to example test project. 2024-07-10 13:35:01 +02:00
Christoffer Lerno
5cf1f13328 Private function called from nested macro not visible to linker #1232 2024-07-09 22:01:39 +02:00
Christoffer Lerno
fba706f10b Updated sorting code. 2024-07-09 01:04:11 +02:00
Alex Anderson
c50630989e draft: add countingsort.c3 (#1230)
Draft countingsort.c3
2024-07-08 21:08:57 +02:00
Christoffer Lerno
3832be94d0 Added sort helper function. 2024-07-08 21:02:49 +02:00
Alex Anderson
900c1152d3 add insertion sort (#1225) 2024-07-08 18:53:47 +02:00
Christoffer Lerno
4ea50a8a85 Update version. 2024-07-08 17:39:31 +02:00
Christoffer Lerno
0132fd4101 Bad error message when using a generic method without generic parameters #1228 2024-07-08 17:32:39 +02:00
Christoffer Lerno
0e90ce3b8a Prevent accidental delete of lib folder when building. 2024-07-08 14:05:09 +02:00
Christoffer Lerno
9368ebfbd3 Allow using $defined(&a[1]) to check if the operation is supported. 2024-07-08 01:42:34 +02:00
Christoffer Lerno
8381dbbd8f Fix incorrect INLINE on const init function. 2024-07-07 23:29:57 +02:00
Christoffer Lerno
343ccaa2ef Support c-file compilation in libraries. 2024-07-07 11:21:31 +02:00
Christoffer Lerno
3f62775f4b Support c-file compilation in libraries. 2024-07-07 02:04:37 +02:00
Christoffer Lerno
c3ecad96b7 Update CI, add example. 2024-07-05 16:53:49 +02:00
Christoffer Lerno
2ffb0cf5f7 Fix ABI lowering for 128 bit vectors on Linux. 2024-07-05 16:07:17 +02:00
Christoffer Lerno
ef716f3a69 Pull requests to dev also have a test action. 2024-07-05 15:17:23 +02:00
Christoffer Lerno
cc935862b7 Build using LLVM 18 2024-07-05 02:06:37 +02:00
Christoffer Lerno
85a535dd0c $typeof(*x) should be valid when x is an [out] parameter #1226 2024-07-04 16:50:35 +02:00
Christoffer Lerno
ab626fe3eb Update avoid warning in FetchContent 2024-07-04 12:07:01 +02:00
Christoffer Lerno
05011df13a Update flags to mac compile 2024-07-04 02:36:17 +02:00
Christoffer Lerno
fcdb25c426 Update some comments and variable names. 2024-07-04 02:15:08 +02:00
Christian Buttner
cc9ca35e04 Add $debugtrap builtin. (#1220)
Add `$breakpoint` builtin.
2024-07-04 00:50:29 +02:00
Christoffer Lerno
4a50de8318 Use LLVM 18 by default. Update MSVC to LLVM 18.1.8. 2024-07-04 00:48:35 +02:00
Christian Buttner
12051e7544 Fix $$unaligned_store arg check and add test. (#1224)
Fix `$$unaligned_store` arg check and add test.
2024-07-04 00:44:32 +02:00
Christoffer Lerno
210508fe4f Updated test. 2024-07-03 15:59:46 +02:00
Christoffer Lerno
ba5b045351 Fix Type->$Type in allocator #1223 2024-07-03 15:57:17 +02:00
Christoffer Lerno
9a19eeacb3 Added further tests to #1219 2024-07-03 15:14:50 +02:00
Christian Buttner
10ed03d6bf Extend win32 stdlib API. 2024-07-03 11:11:34 +02:00
Christoffer Lerno
3be1bf4384 Added test and updated releasenotes for formatter changes. 2024-07-02 23:28:23 +02:00
Christian Buttner
3396b20661 Fix formatter crash for null ZString, print "(null)" for null pointers. 2024-07-02 23:24:18 +02:00
Christoffer Lerno
c9e1140189 Reorganizing the Windows OS files. 2024-07-02 17:37:45 +02:00
Christoffer Lerno
416cd30b42 Wrong size for structs containing overaligned structs #1219 2024-07-02 15:17:41 +02:00
Christoffer Lerno
d66a07cc55 Add defer catch test. 2024-07-02 13:57:48 +02:00
Christoffer Lerno
ce17dbe240 Bug fix for rethrow + defer catch. More types and functions for win32 2024-07-02 02:48:48 +02:00
Christoffer Lerno
326fc501e2 Simplified @is_comparer 2024-07-02 00:36:05 +02:00
Christoffer Lerno
91ad3ee0a2 Fix regression for math::log 2024-07-01 16:52:39 +02:00
Christoffer Lerno
2993c422c1 Fix to scalar -> vector conversions. 2024-07-01 15:03:40 +02:00
Christian Buttner
6f8cdde7e4 Added a --no-headers option. 2024-07-01 13:38:58 +02:00
Christoffer Lerno
f521a0dd77 FOREACH_BEGIN / VECEACH replaced by FOREACH / FOREACH_IDX 2024-07-01 13:31:41 +02:00
Christian Buttner
12fdb58da6 Implicitly cast distinct inline to index. (#1218)
Implicitly cast distinct inline to index.
2024-07-01 13:16:39 +02:00
Christoffer Lerno
09876cefde @unaligned_store and @unaligned_load 2024-06-30 01:05:57 +02:00
Christoffer Lerno
d1e2ea7635 Require MSVC 17.7 or higher. 2024-06-29 20:47:25 +02:00
Christoffer Lerno
7b131f2a45 Print MSVC version 2024-06-29 20:35:23 +02:00
Christoffer Lerno
f3d5e3d4c2 Set minimum LLVM version for compilation. 2024-06-29 20:30:37 +02:00
Christoffer Lerno
492f83f5e2 Bit negating const zero flags would give an incorrect result. #1213 2024-06-28 16:43:57 +02:00
Christoffer Lerno
7dcd1618d8 Fixes to header gen. 2024-06-28 11:28:05 +02:00
Christoffer Lerno
e2a39aa12e Updated mangling code. 2024-06-28 00:57:14 +02:00
Christoffer Lerno
043833be7b Fixes to casts. 2024-06-27 19:32:45 +02:00
Christoffer Lerno
ad394c19d5 Remove asserts from header gen. 2024-06-27 17:21:08 +02:00
Christoffer Lerno
05592183b1 Fixed distinct comparison behaviour. 2024-06-27 15:06:23 +02:00
Christoffer Lerno
079cbb8f68 Updated module mangling, restrict module names. 2024-06-27 13:37:37 +02:00
Christoffer Lerno
3bddde20ab Fixes to distinct inline conversions. 2024-06-26 21:48:10 +02:00
Christoffer Lerno
0a8a63bc15 Fix to headergen. Updated module name store. 2024-06-26 11:43:14 +02:00
Christoffer Lerno
fd2491446a Update mangling. 2024-06-24 21:55:49 +02:00
Christoffer Lerno
26f3fe37f4 Fix of built in aliases for headers. 2024-06-24 17:23:59 +02:00
Christoffer Lerno
4cff80ecea Header exports implicit. 2024-06-24 15:04:44 +02:00
Christian Buttner
83fe94d497 Fix posix NativeConditionVariable.wait_timeout. (#1211)
Fix posix NativeConditionVariable.wait_timeout. TimeSpec::ns may not exceed one second.
2024-06-24 11:52:21 +02:00
Christoffer Lerno
616bde2c4d Further header updates. 2024-06-24 11:34:23 +02:00
Christian Buttner
0b971c2bd0 Fix off-by-one errors for stdlib unicode conversions. 2024-06-23 23:46:19 +02:00
Christoffer Lerno
201b1b7fbc - Bitstructs, unions and flexible arrays now correctly emitted in headers.
- Require `@export` functions to have `@export` types.
2024-06-23 23:39:58 +02:00
Christoffer Lerno
b0b976ee52 Fix JSON and compile issue. 2024-06-23 17:40:56 +02:00
Christoffer Lerno
7020569f45 Cleanup. 2024-06-23 16:36:04 +02:00
Christoffer Lerno
e153c76719 Bit negate now properly does type promotion. 2024-06-23 16:13:37 +02:00
Christoffer Lerno
e7f9c11a14 "panic-msg" setting to suppress panic message output. 2024-06-23 10:42:03 +02:00
Christoffer Lerno
f2e5c5e9b9 - Fix bug with @jump miscompile
- Remove "panic" text from unreachable() when safe mode is turned off.
2024-06-22 23:20:23 +02:00
Christoffer Lerno
e02f73417c Trailing body arguments may now be &ref, #hash, $const and $Type arguments. 2024-06-22 22:04:20 +02:00
Christian Buttner
41db9c43e5 Allow omitting = true for designated initializers of bitstruct bools. 2024-06-22 15:57:41 +02:00
Christoffer Lerno
0dc2f0e923 Make function pointers comparable with null again. 2024-06-22 15:38:19 +02:00
Christoffer Lerno
5940d5ddad Removal of unused code. 2024-06-21 23:24:05 +02:00
Christoffer Lerno
684850dda1 Fixing flexible array resolution. 2024-06-21 18:36:39 +02:00
Christoffer Lerno
e8e615f4db Remove superfluous code and flags for type resolution. 2024-06-21 17:45:33 +02:00
Christoffer Lerno
559b060b6b Fix bug in header gen. 2024-06-21 12:31:50 +02:00
Christoffer Lerno
581262d736 Try LLVM 19 support. 2024-06-21 11:44:27 +02:00
Christoffer Lerno
8878a49a1d Introduction of TYPE_FUNC_PTR / TYPE_FUNC_RAW. Fixed rules for function pointers. 2024-06-21 10:46:28 +02:00
Christoffer Lerno
3a7bc4d253 Return the typekind "FUNC" for a function pointer. 2024-06-20 20:47:24 +02:00
Christoffer Lerno
316982fb8f Added test and removed todo. 2024-06-19 01:17:43 +02:00
Christoffer Lerno
cfaea34053 Some additional cleanup. 2024-06-19 00:57:38 +02:00
Christoffer Lerno
8fd1d895d6 Cleanup ct_call parsing. 2024-06-19 00:28:24 +02:00
Christoffer Lerno
b592ecf6f5 Fixed crash on certain recursive function definitions #1209. 2024-06-18 22:33:10 +02:00
Christoffer Lerno
65a8826158 Fix of missing copy of parameterized custom attributes. 2024-06-17 22:05:32 +02:00
Christoffer Lerno
c9fab898cc Improved error notes when call expressions have errors. 2024-06-16 23:33:37 +02:00
Christoffer Lerno
819049d596 @str_hash, @str_upper, @str_lower, @str_find compile time macros. 2024-06-16 21:16:03 +02:00
Christoffer Lerno
147dee6ec7 Addition of $append and $concat functions. Added $$str_hash builtin. Fix to the macho runtime. 2024-06-16 01:57:05 +02:00
Christoffer Lerno
b0b885d506 Prevent Mach-O from removing @init and @dynamic in a more reliable way #1200. 2024-06-15 15:58:12 +02:00
Christoffer Lerno
c94610f8a9 Error with unsigned compare in @ensure when early returning 0 #1207. Added remove_first_item remove_last_item and remove_item as aliases for the match functions. 2024-06-14 17:29:46 +02:00
Christoffer Lerno
21fa006850 Merge 0.5.6 changes into 0.6.0 2024-06-12 11:39:52 +02:00
Christoffer Lerno
e293c435af 0.6.0: init_new/init_temp removed. LinkedList API rewritten. List "pop" and "remove" function now return Optionals. RingBuffer API rewritten. Allocator interface changed. Deprecated Allocator, DString and mem functions removed. "identity" functions are now constants for Matrix and Complex numbers. @default implementations for interfaces removed. any* => any, same for interfaces. Emit local/private globals as "private" in LLVM, following C "static". Updated enum syntax. Add support [rgba] properties in vectors. Improved checks of aliased "void". Subarray -> slice. Fix of llvm codegen enum check. Improved alignment handling. Add --output-dir #1155. Removed List/Object append. GenericList renamed AnyList. Remove unused "unwrap". Fixes to cond. Optimize output in dead branches. Better checking of operator methods. Disallow any from implementing dynamic methods. Check for operator mismatch. Remove unnecessary bitfield. Remove numbering in --list* commands Old style enum declaration for params/type, but now the type is optional. Add note on #1086. Allow making distinct types out of "void", "typeid", "anyfault" and faults. Remove system linker build options. "Try" expressions must be simple expressions. Add optimized build to Mac tests. Register int. assert(false) only allowed in unused branches or in tests. Compile time failed asserts is a compile time error. Remove current_block_is_target. Bug when assigning an optional from an optional. Remove unused emit_zstring. Simplify phi code. Remove unnecessary unreachable blocks and remove unnecessary current_block NULL assignments. Proper handling of '.' and Win32 '//server' paths. Add "no discard" to expression blocks with a return value. Detect "unsigned >= 0" as errors. Fix issue with distinct void as a member #1147. Improve callstack debug information #1184. Fix issue with absolute output-dir paths. Lambdas were not type checked thoroughly #1185. Fix compilation warning #1187. Request jump table using @jump for switches. Path normalization - fix possible null terminator out of bounds. Improved error messages on inlined macros.
Upgrade of mingw in CI. Fix problems using reflection on interface types #1203. Improved debug information on defer. $foreach doesn't create an implicit syntactic scope.
Error if `@if` depends on `@if`. Updated Linux stacktrace. Fix of default argument stacktrace. Allow linking libraries directly by file path. Improve inlining warning messages. Added `index_of_char_from`. Compiler crash using enum nameof from different module #1205. Removed unused fields in find_msvc. Use vswhere to find msvc. Update tests for LLVM 19
2024-06-12 10:14:26 +02:00
Christoffer Lerno
321c5ec756 Update mingw version and funding. 2024-05-27 12:03:45 +02:00
Christoffer Lerno
f04d93f9aa Fix flaw in bitstruct check. 2024-05-20 22:20:33 +02:00
Christoffer Lerno
9436efe554 Compiler crash on designated initializer for structs with bitstruct. 2024-05-20 14:42:09 +02:00
Christoffer Lerno
3acbf708d3 Fix location on foreach debug output. 2024-05-19 23:27:57 +02:00
Christoffer Lerno
92979984ea Fix mutex and wait signatures for Win32. 2024-05-18 22:24:45 +02:00
Christoffer Lerno
a16d41a1e1 Do not elide memory storage on variable for debug. 2024-05-17 19:51:35 +02:00
Christoffer Lerno
ff8b78fc99 Correct debug info on parameters without storage. 2024-05-17 16:26:12 +02:00
Christoffer Lerno
97c9bd7ce0 Assertion failed when casting argument to enum #1196 2024-05-16 16:07:55 +02:00
Christoffer Lerno
c40c93340d Compile time fmod evaluates to 0 #1195 2024-05-16 14:33:11 +02:00
Christoffer Lerno
094c105464 Union is not properly zero-initialized #1194 2024-05-16 11:28:04 +02:00
Christoffer Lerno
e36c696624 Update CI. 2024-05-15 21:49:18 +02:00
Christoffer Lerno
555a4ab4c5 Casting a slice address to its pointer type should not compile #1193. 2024-05-15 21:36:53 +02:00
Christoffer Lerno
7d8cc8776d Duplicate emit of expressions on negation would incorrectly compile negated macros. 2024-05-14 23:30:39 +02:00
Christoffer Lerno
960646ac8a Patch test. 2024-05-09 09:53:48 +02:00
Christoffer Lerno
ed9f15becf Foreach uses non-wrapping add/dec. 2024-05-08 23:05:12 +02:00
Christoffer Lerno
b09aa74f2f Generic modules parameterized with constants would sometimes get the wrong parameterized module name causing conversion errors #1192. 2024-05-04 23:34:37 +02:00
Christoffer Lerno
60805fd11d Bounds checking on length for foo[1:2] slicing #1191 2024-04-28 18:55:26 +02:00
Christoffer Lerno
89ecd4b33d Default to AVX on x64. 2024-04-26 19:29:32 +02:00
Christoffer Lerno
237f142a87 Default CPU actually defaults to a value instead of picking the native CPU. 2024-04-26 19:03:25 +02:00
Christoffer Lerno
a21647a1aa Do not default to native vector capability. 2024-04-26 18:45:14 +02:00
Christoffer Lerno
e9afe4ee25 Update CI script for mac. 2024-04-26 18:09:03 +02:00
Christoffer Lerno
acd067582a Update CI script. 2024-04-26 18:07:16 +02:00
Christoffer Lerno
82227e8901 Incorrect cast of bitstructs #1186 2024-04-26 17:39:30 +02:00
Christoffer Lerno
8b6735a6aa Allow recursive function definitions as long as they are pointers #1182. Add 'zstr' variants for string::new_format / string::tformat. 2024-04-16 19:42:32 +02:00
Christoffer Lerno
9ed8831500 Updated link 2024-04-14 23:07:48 +02:00
Christoffer Lerno
e7d726cc2c Fixup of scratch buffer code. 2024-04-09 14:27:52 +02:00
Christoffer Lerno
11a3dd26c8 Update mingw version. 2024-04-09 14:10:54 +02:00
Christoffer Lerno
04738586b9 Fix bug in scratch_buffer_printf. 2024-04-09 13:26:08 +02:00
cpiernikowski
18b4fce1ca Change return type of next_bool(random) from void to bool 2024-03-28 09:44:47 +01:00
Christoffer Lerno
3b9babe745 0.5.6 Add grammar for defer (catch err). 2024-03-26 09:36:45 +01:00
Brennan Cottrell
a4a85b7bbf Added print-input command line argument (#1175)
* added print-input command line argument
2024-03-26 09:33:47 +01:00
Christoffer Lerno
e8f0275d8e 0.5.6 Add defer (catch err) feature. 2024-03-25 11:35:16 +01:00
Christoffer Lerno
3251f58d46 Change version for MinGW 2024-03-25 09:40:52 +01:00
David
204fb211ac Fix x86_64 ABI small issue (#1174)
* Fix x86_64 ABI small issue Update tests for fix.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2024-03-25 09:39:26 +01:00
Christoffer Lerno
6cade814e1 Update includes for FreeBSD. 2024-03-22 09:11:18 +01:00
Christoffer Lerno
eb2fbabbb1 Update version to 0.5.6 2024-03-19 16:15:13 +01:00
498 changed files with 28413 additions and 19178 deletions

2
.github/FUNDING.yml vendored
View File

@@ -3,7 +3,7 @@
github: [c3lang]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
ko_fi: c3lang
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username

View File

@@ -2,13 +2,16 @@ name: CI
on:
push:
branches: [ master, dev, ci_testing ]
branches: [ master, dev, ci_testing, experiments ]
pull_request:
branches: [ master ]
branches: [ master, dev ]
env:
LLVM_RELEASE_VERSION: 16
LLVM_RELEASE_VERSION_WINDOWS: 18
LLVM_RELEASE_VERSION_MAC: 18
LLVM_RELEASE_VERSION_LINUX: 17
LLVM_RELEASE_VERSION_UBUNTU20: 17
LLVM_DEV_VERSION: 20
jobs:
build-msvc:
@@ -33,28 +36,31 @@ jobs:
- name: Compile and run some examples
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\hello_world_many.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\time.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\fannkuch-redux.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run -L C:\ --print-linking examples\hello_world_many.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run --print-linking examples\time.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run --print-linking examples\fannkuch-redux.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\ls.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\process.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --test -g -O0 --threads 1 --target macos-x64 examples\constants.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\args.c3 -- foo -bar "baz baz"
..\build\${{ matrix.build_type }}\c3c.exe compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples\constants.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run msvc_stack.c3
- name: Build testproject
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log --emit-llvm run hello_world_win32
dir build\llvm_ir
..\..\build\${{ matrix.build_type }}\c3c.exe clean
dir build\llvm_ir
- name: Build testproject lib
run: |
cd resources/testproject
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log build hello_world_win32_lib
- name: Vendor-fetch
@@ -64,7 +70,7 @@ jobs:
- name: Try raylib
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_arkanoid.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_snake.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_tetris.c3
@@ -88,7 +94,9 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: c3-windows-${{ matrix.build_type }}
path: build\${{ matrix.build_type }}\c3c.exe
path: |
build\${{ matrix.build_type }}\c3c.exe
build\${{ matrix.build_type }}\c3c_rt
build-msys2-mingw:
runs-on: windows-latest
@@ -112,8 +120,8 @@ jobs:
install: git binutils mingw-w64-x86_64-clang mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python
- shell: msys2 {0}
run: |
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-18.1.1-3-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-18.1.1-3-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-18.1.8-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-18.1.8-1-any.pkg.tar.zst
- name: CMake
run: |
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
@@ -122,12 +130,13 @@ jobs:
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
../build/c3c compile --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
../build/c3c compile-run --print-linking examples/hello_world_many.c3
../build/c3c compile-run --print-linking examples/time.c3
../build/c3c compile-run --print-linking examples/fannkuch-redux.c3
../build/c3c compile-run --print-linking examples/contextfree/boolerr.c3
../build/c3c compile-run --print-linking examples/load_world.c3
../build/c3c compile-run --print-linking examples/args.c3 -- foo -bar "baz baz"
../build/c3c compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
- name: Build testproject
run: |
@@ -141,7 +150,7 @@ jobs:
- name: Build testproject lib
run: |
cd resources/testproject
../../build/c3c build hello_world_lib --debug-log
../../build/c3c build hello_world_lib --cc cc --debug-log
- name: run compiler tests
run: |
@@ -183,7 +192,8 @@ jobs:
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
../build/c3c compile --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
../build/c3c compile --no-entry --test -g -O0 --threads 1 --target macos-x64 examples/constants.c3
- name: Build testproject
run: |
cd resources/testproject
@@ -206,7 +216,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [15, 16, 17, 18, 19]
llvm_version: [17, 18, 19, 20]
steps:
- uses: actions/checkout@v4
@@ -217,25 +227,29 @@ jobs:
- name: Install Clang ${{matrix.llvm_version}}
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
if [[ "${{matrix.llvm_version}}" < 16 ]]; then
sudo apt remove libllvm15
fi
if [[ "${{matrix.llvm_version}}" < 19 ]]; then
if [[ "${{matrix.llvm_version}}" < 18 ]]; then
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
sudo apt-get update
sudo apt-get install -y -t llvm-toolchain-focal-${{matrix.llvm_version}} libpolly-${{matrix.llvm_version}}-dev \
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev libmlir-${{matrix.llvm_version}} \
libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
else
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
sudo apt-get install -y -t llvm-toolchain-focal libpolly-${{matrix.llvm_version}}-dev \
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev libmlir-${{matrix.llvm_version}} \
libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
if [[ "${{matrix.llvm_version}}" < "${{env.LLVM_DEV_VERSION}}" ]]; then
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
sudo apt-get update
sudo apt-get install -y -t llvm-toolchain-focal-${{matrix.llvm_version}} libpolly-${{matrix.llvm_version}}-dev \
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
else
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
sudo apt-get install -y -t llvm-toolchain-focal libpolly-${{matrix.llvm_version}}-dev \
clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev \
lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
fi
fi
- name: CMake
if: matrix.llvm_version != 18
if: matrix.llvm_version < 18 || matrix.llvm_version == env.LLVM_DEV_VERSION
run: |
cmake -B build \
-G Ninja \
@@ -249,7 +263,7 @@ jobs:
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
cmake --build build
- name: CMake18
if: matrix.llvm_version == 18
if: matrix.llvm_version >= 18 && matrix.llvm_version != env.LLVM_DEV_VERSION
run: |
cmake -B build \
-G Ninja \
@@ -260,12 +274,12 @@ jobs:
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DC3_LLVM_VERSION=18.1
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
cmake --build build
- name: Compile and run some examples
run: |
cd resources
cd resources
../build/c3c compile examples/base64.c3
../build/c3c compile examples/binarydigits.c3
../build/c3c compile examples/brainfk.c3
@@ -273,9 +287,9 @@ jobs:
../build/c3c compile examples/fasta.c3
../build/c3c compile examples/gameoflife.c3
../build/c3c compile examples/hash.c3
../build/c3c compile examples/levenshtein.c3
../build/c3c compile-only examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile examples/map.c3
../build/c3c compile-only examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/nbodies.c3
@@ -284,8 +298,8 @@ jobs:
../build/c3c compile examples/contextfree/boolerr.c3
../build/c3c compile examples/contextfree/dynscope.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
@@ -293,8 +307,9 @@ jobs:
../build/c3c compile-run examples/load_world.c3
../build/c3c compile-run examples/process.c3
../build/c3c compile-run examples/ls.c3
../build/c3c compile-run --system-linker=no linux_stack.c3
../build/c3c compile-run --linker=builtin linux_stack.c3
../build/c3c compile-run linux_stack.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
- name: Compile run unit tests
run: |
@@ -306,10 +321,31 @@ jobs:
cd resources/testproject
../../build/c3c run --debug-log
- name: Test WASM
run: |
cd resources/testfragments
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
- name: Install QEMU and Risc-V toolchain
run: |
sudo apt-get install opensbi qemu-system-misc u-boot-qemu gcc-riscv64-unknown-elf
- name: Compile and run Baremetal Risc-V Example
run: |
cd resources/examples/embedded/riscv-qemu
make C3C_PATH=../../../../build/ run
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --system-linker=no
../../build/c3c run --debug-log --linker=builtin
- name: Init a library & a project
run: |
./build/c3c init-lib mylib
ls mylib.c3l
./build/c3c init myproject
ls myproject
- name: run compiler tests
run: |
@@ -317,16 +353,16 @@ jobs:
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == 16
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
run: |
mkdir linux
cp -r lib linux
cp msvc_build_libraries.py linux
cp build/c3c linux
tar czf c3-linux-${{matrix.build_type}}.tar.gz linux
mkdir c3
cp -r lib c3
cp msvc_build_libraries.py c3
cp build/c3c c3
tar czf c3-linux-${{matrix.build_type}}.tar.gz c3
- name: upload artifacts
if: matrix.llvm_version == 16
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
uses: actions/upload-artifact@v3
with:
name: c3-linux-${{matrix.build_type}}
@@ -339,8 +375,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [16]
llvm_version: [17, 18, 19]
steps:
- uses: actions/checkout@v4
- name: Install common deps
@@ -350,16 +385,17 @@ jobs:
- name: Install Clang ${{matrix.llvm_version}}
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
if [[ "${{matrix.llvm_version}}" < 17 ]]; then
if [[ "${{matrix.llvm_version}}" < "${{env.LLVM_DEV_VERSION}}" ]]; then
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
else
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
fi
sudo apt-get update
sudo apt-get update
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
- name: CMake
- name: CMake Old
if: matrix.llvm_version < 18 || matrix.llvm_version == env.LLVM_DEV_VERSION
run: |
cmake -B build \
-G Ninja \
@@ -372,13 +408,26 @@ jobs:
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
cmake --build build
- name: CMake
if: matrix.llvm_version >= 18 && matrix.llvm_version != env.LLVM_DEV_VERSION
run: |
cmake -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
cmake --build build
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile examples/gameoflife.c3
../build/c3c compile examples/levenshtein.c3
../build/c3c compile examples/map.c3
../build/c3c compile-only examples/levenshtein.c3
../build/c3c compile-only examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/spectralnorm.c3
@@ -388,8 +437,8 @@ jobs:
../build/c3c compile-run examples/nbodies.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/contextfree/dynscope.c3
../build/c3c compile-run examples/contextfree/multi.c3
../build/c3c compile-run examples/contextfree/cleanup.c3
../build/c3c compile-run examples/contextfree/multi.c3
../build/c3c compile-run examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
@@ -400,8 +449,9 @@ jobs:
../build/c3c compile-run examples/factorial_macro.c3
../build/c3c compile-run examples/fasta.c3
../build/c3c compile-run examples/process.c3
../build/c3c compile-run --system-linker=no linux_stack.c3
../build/c3c compile-run --linker=builtin linux_stack.c3
../build/c3c compile-run linux_stack.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
- name: Compile run unit tests
run: |
@@ -416,7 +466,7 @@ jobs:
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --system-linker=no
../../build/c3c run --debug-log --linker=builtin
- name: run compiler tests
run: |
@@ -424,20 +474,108 @@ jobs:
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == 16
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
run: |
mkdir linux
cp -r lib linux
cp msvc_build_libraries.py linux
cp build/c3c linux
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz linux
mkdir c3
cp -r lib c3
cp msvc_build_libraries.py c3
cp build/c3c c3
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz c3
- name: upload artifacts
if: matrix.llvm_version == 16
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
uses: actions/upload-artifact@v3
with:
name: c3-ubuntu-20-${{matrix.build_type}}
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
build-with-docker:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ubuntu_version: [20.04, 22.04]
build_type: [Release, Debug]
llvm_version: [17, 18, 19, 20]
steps:
- uses: actions/checkout@v4
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Make script executable
run: chmod +x ./build-with-docker.sh
- name: Run build
run: |
LLVM_VERSION=${{ matrix.llvm_version }} UBUNTU_VERSION=${{ matrix.ubuntu_version }} CMAKE_BUILD_TYPE=${{ matrix.build_type }} ./build-with-docker.sh
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile examples/base64.c3
../build/c3c compile examples/binarydigits.c3
../build/c3c compile examples/brainfk.c3
../build/c3c compile examples/factorial_macro.c3
../build/c3c compile examples/fasta.c3
../build/c3c compile examples/gameoflife.c3
../build/c3c compile examples/hash.c3
../build/c3c compile-only examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile-only examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/nbodies.c3
../build/c3c compile examples/spectralnorm.c3
../build/c3c compile examples/swap.c3
../build/c3c compile examples/contextfree/boolerr.c3
../build/c3c compile examples/contextfree/dynscope.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
../build/c3c compile-run examples/process.c3
../build/c3c compile-run examples/ls.c3
../build/c3c compile-run --linker=builtin linux_stack.c3
../build/c3c compile-run linux_stack.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
- name: Test WASM
run: |
cd resources/testfragments
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --linker=builtin
- name: Init a library & a project
run: |
./build/c3c init-lib mylib
ls mylib.c3l
./build/c3c init myproject
ls myproject
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c test_suite/
build-mac:
runs-on: macos-latest
@@ -446,20 +584,26 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [15, 16, 17]
llvm_version: [17, 18]
steps:
- uses: actions/checkout@v4
- name: Download LLVM
run: |
brew install llvm@${{ matrix.llvm_version }} ninja curl
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
echo "/opt/homebrew/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
- name: CMake
if: matrix.llvm_version < 18
run: |
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: CMake18
if: matrix.llvm_version >= 18
run: |
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}}.1 -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Vendor-fetch
run: |
@@ -474,6 +618,8 @@ jobs:
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/process.c3
../build/c3c compile-run examples/load_world.c3
../build/c3c compile-run -O5 examples/load_world.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
- name: Compile run unit tests
run: |
@@ -488,7 +634,7 @@ jobs:
- name: Build testproject direct linker
run: |
cd resources/testproject
../../build/c3c run --debug-log --system-linker=no
../../build/c3c run --debug-log --linker=builtin
- name: Build testproject lib
run: |
@@ -501,7 +647,7 @@ jobs:
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
run: |
mkdir macos
cp -r lib macos
@@ -510,7 +656,7 @@ jobs:
zip -r c3-macos-${{matrix.build_type}}.zip macos
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
uses: actions/upload-artifact@v3
with:
name: c3-macos-${{matrix.build_type}}

3
.gitignore vendored
View File

@@ -67,3 +67,6 @@ out/
/cmake-build-debug/
/cmake-build-release/
# Emacs files
TAGS

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.15)
cmake_minimum_required(VERSION 3.20)
# Grab the version
file(READ "src/version.h" ver)
@@ -10,6 +10,11 @@ endif()
project(c3c VERSION ${CMAKE_MATCH_1})
message("C3C version: ${CMAKE_PROJECT_VERSION}")
# Avoid warning for FetchContent
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
cmake_policy(SET CMP0135 NEW)
endif()
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
if (MSVC)
set(CMAKE_INSTALL_LIBDIR "c:\\c3c\\lib")
@@ -32,6 +37,7 @@ set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
if(MSVC)
message(STATUS "MSVC version ${MSVC_VERSION}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa")
@@ -75,7 +81,7 @@ if (NOT WIN32)
find_package(CURL)
endif()
if (NOT C3_LLVM_VERSION STREQUAL "auto")
if (${C3_LLVM_VERSION} VERSION_LESS 15 OR ${C3_LLVM_VERSION} VERSION_GREATER 19)
if (${C3_LLVM_VERSION} VERSION_LESS 17 OR ${C3_LLVM_VERSION} VERSION_GREATER 20)
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
endif()
endif()
@@ -97,25 +103,27 @@ endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "16")
set(C3_LLVM_VERSION "18")
endif()
FetchContent_Declare(
LLVM_Windows
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt.7z
URL https://github.com/c3lang/win-llvm/releases/download/llvm_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt.7z
)
FetchContent_Declare(
LLVM_Windows_debug
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt-dbg.7z
URL https://github.com/c3lang/win-llvm/releases/download/llvm_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows_debug)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_debug_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
set(llvm_dir ${llvm_windows_debug_SOURCE_DIR})
else()
message("Loading Windows LLVM libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
set(llvm_dir ${llvm_windows_SOURCE_DIR})
endif()
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
find_package(LLVM REQUIRED CONFIG)
find_package(LLD REQUIRED CONFIG)
else()
@@ -130,12 +138,19 @@ message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
if (NOT LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL 15.0)
message(FATAL_ERROR "LLVM version 15.0 or later is required.")
endif()
if(LLVM_ENABLE_RTTI)
message(STATUS "LLVM was built with RTTI")
else()
message(STATUS "LLVM was not built with RTTI")
endif()
string(REPLACE "." ";" VERSION_LIST ${LLVM_PACKAGE_VERSION})
list(GET VERSION_LIST 0 LLVM_MAJOR_VERSION)
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
add_definitions(${LLVM_DEFINITIONS})
@@ -195,33 +210,35 @@ else()
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
endif()
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL 16)
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
else()
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
endif()
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
if (APPLE)
set(lld_libs ${lld_libs} xar)
endif ()
find_file(RT_ASAN_DYNAMIC NAMES libclang_rt.asan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
find_file(RT_TSAN_DYNAMIC NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
find_file(RT_UBSAN_DYNAMIC NAMES libclang_rt.ubsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
find_file(RT_LSAN_DYNAMIC NAMES libclang_rt.lsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIRS}/clang/${LLVM_MAJOR_VERSION}/lib/darwin")
set(sanitizer_runtime_libraries
${RT_ASAN_DYNAMIC}
${RT_TSAN_DYNAMIC}
# Unused
# ${RT_UBSAN_DYNAMIC}
# ${RT_LSAN_DYNAMIC}
)
endif()
message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Found lld libs ${lld_libs}")
@@ -234,11 +251,15 @@ add_executable(c3c
src/build/builder.c
src/build/build_options.c
src/build/project_creation.c
src/build/project_manipulation.c
src/build/libraries.c
src/compiler/ast.c
src/compiler/bigint.c
src/compiler/codegen_general.c
src/compiler/compiler.c
src/compiler/compiler.h
src/compiler/subprocess.c
src/compiler/subprocess.h
src/compiler/context.c
src/compiler/copying.c
src/compiler/diagnostics.c
@@ -246,7 +267,6 @@ add_executable(c3c
src/compiler/headers.c
src/compiler/json_output.c
src/compiler/lexer.c
src/compiler/libraries.c
src/compiler/linker.c
src/compiler/llvm_codegen.c
src/compiler/abi/c_abi_aarch64.c
@@ -315,7 +335,8 @@ add_executable(c3c
src/compiler/expr.c
src/utils/time.c
src/utils/http.c
src/compiler/sema_liveness.c)
src/compiler/sema_liveness.c
src/build/common_build.c)
if (C3_USE_TB)
@@ -356,11 +377,12 @@ endif()
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/src/")
"${CMAKE_SOURCE_DIR}/src/"
"${CMAKE_SOURCE_DIR}/wrapper/include/")
target_include_directories(c3c_wrappers PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/src/")
"${CMAKE_SOURCE_DIR}/wrapper/include/")
target_include_directories(miniz PUBLIC
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
@@ -388,6 +410,9 @@ if(MSVC)
message("Adding MSVC options")
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
if (NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PUBLIC /GR-)
endif()
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(c3c PUBLIC /MTd)
@@ -404,16 +429,41 @@ if(MSVC)
target_compile_options(tilde-backend PUBLIC /MT)
endif()
endif()
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
set(sanitizer_runtime_libraries
${clang_lib_dir}/clang_rt.asan-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
else()
message(STATUS "using gcc/clang warning switches")
target_link_options(c3c PRIVATE -pthread)
if (NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
endif()
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
endif()
install(TARGETS c3c DESTINATION bin)
install(DIRECTORY lib/ DESTINATION lib/c3)
if (DEFINED sanitizer_runtime_libraries)
add_custom_command(TARGET c3c POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
COMMAND "${CMAKE_COMMAND}" -E make_directory $<TARGET_FILE_DIR:c3c>/c3c_rt
COMMAND "${CMAKE_COMMAND}" -E copy ${sanitizer_runtime_libraries} $<TARGET_FILE_DIR:c3c>/c3c_rt
VERBATIM
COMMENT "Copying sanitizer runtime libraries to output directory")
if (APPLE)
# Change LC_ID_DYLIB to be rpath-based instead of having an absolute path
add_custom_command(TARGET c3c POST_BUILD
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
VERBATIM)
endif()
install(DIRECTORY $<TARGET_FILE_DIR:c3c>/c3c_rt/ DESTINATION bin/c3c_rt)
endif()
feature_summary(WHAT ALL)

View File

@@ -32,7 +32,7 @@ whole new language.
### Example code
The following code shows [generic modules](http://www.c3-lang.org/generics/) (more examples can be found at http://www.c3-lang.org/examples/).
The following code shows [generic modules](https://c3-lang.org/references/docs/generics/) (more examples can be found at https://c3-lang.org/references/docs/examples/).
```c++
module stack (<Type>);
@@ -122,7 +122,7 @@ fn void main()
- No mandatory header files
- New semantic macro system
- Module based name spacing
- Subarrays (slices)
- Slices
- Compile time reflection
- Enhanced compile time execution
- Generics based on generic modules
@@ -137,9 +137,9 @@ fn void main()
### Current status
The current stable version of the compiler is **version 0.5**.
The current stable version of the compiler is **version 0.6.1**.
The upcoming 0.6 release will focus on expanding the standard library.
The upcoming 0.6.x releases will focus on expanding the standard library.
Follow the issues [here](https://github.com/c3lang/c3c/issues).
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
@@ -212,6 +212,8 @@ More platforms will be supported in the future.
3. Unzip executable and standard lib.
4. Run `./c3c`.
(*Note that there is a known issue with debug symbol generation on MacOS 13, see [issue #1086](https://github.com/c3lang/c3c/issues/1086))
#### Installing on Arch Linux
There is an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git).
@@ -251,7 +253,7 @@ A `c3c` executable will be found under `bin/`.
#### Installing on OS X using Homebrew
2. Install CMake: `brew install cmake`
3. Install LLVM 15: `brew install llvm`
3. Install LLVM 17+: `brew install llvm`
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
5. Enter the C3C directory `cd c3c`.
6. Create a build directory `mkdir build`
@@ -306,7 +308,7 @@ You can try it out by running some sample code: `c3c.exe compile ../resources/ex
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
2. Install CMake: `sudo apt install cmake`
3. Install LLVM 15 (or greater: C3C supports LLVM 15-17): `sudo apt-get install clang-15 zlib1g zlib1g-dev libllvm15 llvm-15 llvm-15-dev llvm-15-runtime liblld-15-dev liblld-15`
3. Install LLVM 17+ (or greater: C3C supports LLVM 17+): `sudo apt-get install clang-17 zlib1g zlib1g-dev libllvm17 llvm-17 llvm-17-dev llvm-17-runtime liblld-17-dev liblld-17`
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
5. Enter the C3C directory `cd c3c`.
6. Create a build directory `mkdir build`
@@ -321,7 +323,7 @@ You can try it out by running some sample code: `./c3c compile ../resources/exam
#### Compiling on Void Linux
1. As root, ensure that all project dependencies are installed: `xbps-install git cmake llvm15 lld-devel libcurl-devel ncurses-devel zlib-devel libzstd-devel libxml2-devel`
1. As root, ensure that all project dependencies are installed: `xbps-install git cmake llvm17 lld17-devel libcurl-devel ncurses-devel zlib-devel libzstd-devel libxml2-devel`
2. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
- If you only need the latest commit, you may want to make a shallow clone instead: `git clone https://github.com/c3lang/c3c.git --depth=1`
3. Enter the directory: `cd c3c`
@@ -337,7 +339,7 @@ For a sytem-wide installation, run the following as root: `cmake --install .`
#### Compiling on other Linux / Unix variants
1. Install CMake.
2. Install or compile LLVM and LLD *libraries* (version 15+ or higher)
2. Install or compile LLVM and LLD *libraries* (version 17+ or higher)
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory `cd c3c`.
5. Create a build directory `mkdir build`

View File

@@ -1,43 +1,44 @@
#!/bin/bash
## build-with-docker.sh
## @author gdm85
## @modified by Kenta
##
## Script to build c3c for Ubuntu 22
##
#
read -p "Select Build Type: Debug/Release: " config
: ${DOCKER:=docker}
: ${IMAGE:="c3c-builder"}
: ${CMAKE_BUILD_TYPE:=Release}
: ${LLVM_VERSION:=18}
: ${UBUNTU_VERSION:="22.04"}
: ${CMAKE_VERSION:="3.20.0"}
set -e
cd docker || exit 1 # Exit if the 'docker' directory doesn't exist
DOCKER=docker
DOCKER_RUN=""
IMAGE="c3c-builder"
if type podman 2>/dev/null >/dev/null; then
DOCKER=podman
DOCKER_RUN="--userns=keep-id"
IMAGE="localhost/$IMAGE"
$DOCKER build \
--build-arg LLVM_VERSION=$LLVM_VERSION \
--build-arg CMAKE_VERSION=$CMAKE_VERSION \
--build-arg UBUNTU_VERSION=$UBUNTU_VERSION \
-t $IMAGE .
if [ $? -ne 0 ]; then
echo "Docker image build failed. Exiting."
exit 1
fi
if [ $config == "Debug" ]; then
CMAKE_BUILD_TYPE=Debug
else
CMAKE_BUILD_TYPE="$config"
fi
UBUNTU_VERSION="22.10"
LLVM_VERSION="15"
IMAGE="$IMAGE:22"
cd docker && $DOCKER build -t $IMAGE\
--build-arg DEPS="llvm-$LLVM_VERSION-dev liblld-$LLVM_VERSION-dev clang-$LLVM_VERSION libllvm$LLVM_VERSION llvm-$LLVM_VERSION-runtime" \
--build-arg UBUNTU_VERSION="$UBUNTU_VERSION" .
cd ..
rm -rf build bin
mkdir -p build bin
exec $DOCKER run -ti --rm --tmpfs=/tmp $DOCKER_RUN -v "$PWD":/home/c3c/source -w /home/c3c/source $IMAGE bash -c \
"cd build && cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DC3_LLVM_VERSION=$LLVM_VERSION .. && cmake --build . && mv c3c lib ../bin/"
chmod -R 777 build bin
exec $DOCKER run -i --rm \
-v "$PWD":/home/c3c/source \
-w /home/c3c/source $IMAGE bash -c \
"cmake -S . -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
-DCMAKE_C_COMPILER=clang-$LLVM_VERSION \
-DCMAKE_CXX_COMPILER=clang++-$LLVM_VERSION \
-DCMAKE_LINKER=lld-$LLVM_VERSION \
-DCMAKE_OBJCOPY=llvm-objcopy-$LLVM_VERSION \
-DCMAKE_STRIP=llvm-strip-$LLVM_VERSION \
-DCMAKE_DLLTOOL=llvm-dlltool-$LLVM_VERSION \
-DC3_LLVM_VERSION=auto && \
cmake --build build && \
cp -r build/c3c build/lib bin"

View File

@@ -1,16 +1,49 @@
ARG UBUNTU_VERSION=22.04
FROM ubuntu:${UBUNTU_VERSION}
ARG UBUNTU_VERSION
FROM ubuntu:$UBUNTU_VERSION
ARG LLVM_VERSION=18
ENV LLVM_DEV_VERSION=20
ARG DEPS
ARG CMAKE_VERSION=3.20
RUN export DEBIAN_FRONTEND=noninteractive && export TERM=xterm && apt-get update && apt-get install -y build-essential cmake zlib1g zlib1g-dev \
$DEPS && \
rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y wget gnupg software-properties-common zlib1g zlib1g-dev python3 ninja-build curl g++ && \
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-$CMAKE_VERSION-linux-x86_64.sh && \
mkdir -p /opt/cmake && \
sh cmake-${CMAKE_VERSION}-linux-x86_64.sh --prefix=/opt/cmake --skip-license && \
rm cmake-${CMAKE_VERSION}-linux-x86_64.sh && \
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
ARG GID=1000
ARG UID=1000
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \
if [ "${LLVM_VERSION}" -lt 18 ]; then \
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
apt-get update && \
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
libpolly-${LLVM_VERSION}-dev \
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev libmlir-${LLVM_VERSION} \
libmlir-${LLVM_VERSION}-dev mlir-${LLVM_VERSION}-tools; \
elif [ "${LLVM_VERSION}" -lt "${LLVM_DEV_VERSION}" ]; then \
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
apt-get update && \
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
libpolly-${LLVM_VERSION}-dev \
clang-${LLVM_VERSION} clang++-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
else \
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" && \
apt-get update && \
apt-get install -y -t llvm-toolchain-focal \
libpolly-${LLVM_VERSION}-dev \
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
fi && \
rm -rf /var/lib/apt/lists/*
RUN groupadd -o --gid=$GID c3c && useradd --gid=$GID --uid=$GID --create-home --shell /bin/bash c3c
RUN groupadd -g 1337 c3c && \
useradd -m -u 1337 -g c3c c3c
# Add cmake to PATH for user c3c
USER c3c
ENV PATH="/opt/cmake/bin:${PATH}"
WORKDIR /home/c3c

View File

@@ -124,7 +124,7 @@ macro @atomic_exec(#func, data, value, ordering) @local
case RELEASE: return #func(data, value, RELEASE);
case ACQUIRE_RELEASE: return #func(data, value, ACQUIRE_RELEASE);
case SEQ_CONSISTENT: return #func(data, value, SEQ_CONSISTENT);
default: assert(false, "Ordering may not be non-atomic or unordered.");
default: unreachable("Ordering may not be non-atomic or unordered.");
}
}

View File

@@ -9,7 +9,7 @@ macro @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, $succe
case AtomicOrdering.RELAXED.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.RELAXED.ordinal, $alignment);
case AtomicOrdering.ACQUIRE.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.ACQUIRE.ordinal, $alignment);
case AtomicOrdering.SEQ_CONSISTENT.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.SEQ_CONSISTENT.ordinal, $alignment);
default: assert(false, "Unrecognized failure ordering");
default: unreachable("Unrecognized failure ordering");
}
return 0;
}
@@ -23,12 +23,12 @@ macro @__atomic_compare_exchange_ordering_success(ptr, expected, desired, succes
case AtomicOrdering.RELEASE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.RELEASE.ordinal, failure, $alignment);
case AtomicOrdering.ACQUIRE_RELEASE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.ACQUIRE_RELEASE.ordinal, failure, $alignment);
case AtomicOrdering.SEQ_CONSISTENT.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.SEQ_CONSISTENT.ordinal, failure, $alignment);
default: assert(false, "Unrecognized success ordering");
default: unreachable("Unrecognized success ordering");
}
return 0;
}
fn CInt __atomic_compare_exchange(CInt size, any* ptr, any* expected, any* desired, CInt success, CInt failure) @extern("__atomic_compare_exchange") @export
fn CInt __atomic_compare_exchange(CInt size, any ptr, any expected, any desired, CInt success, CInt failure) @extern("__atomic_compare_exchange") @export
{
switch (size)
{
@@ -57,7 +57,7 @@ fn CInt __atomic_compare_exchange(CInt size, any* ptr, any* expected, any* desir
nextcase;
$endif
default:
assert(false, "Unsuported size (%d) for atomic_compare_exchange", size);
unreachable("Unsuported size (%d) for atomic_compare_exchange", size);
}
return 0;
}

View File

@@ -1,18 +1,18 @@
// Copyright (c) 2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::generic_list;
module std::collections::anylist;
import std::io,std::math;
def GenericPredicate = fn bool(any* value);
def GenericTest = fn bool(any* type, any* context);
def AnyPredicate = fn bool(any value);
def AnyTest = fn bool(any type, any context);
struct GenericList (Printable)
struct AnyList (Printable)
{
usz size;
usz capacity;
Allocator* allocator;
any** entries;
Allocator allocator;
any* entries;
}
@@ -20,14 +20,14 @@ struct GenericList (Printable)
* @param initial_capacity "The initial capacity to reserve"
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn GenericList* GenericList.new_init(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap())
fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap())
{
self.allocator = allocator;
self.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
self.entries = allocator::alloc_array(allocator, any*, initial_capacity);
self.entries = allocator::alloc_array(allocator, any, initial_capacity);
}
else
{
@@ -37,26 +37,17 @@ fn GenericList* GenericList.new_init(&self, usz initial_capacity = 16, Allocator
return self;
}
/**
* @param initial_capacity "The initial capacity to reserve"
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn GenericList* GenericList.init_new(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(initial_capacity, allocator) @inline;
}
/**
* Initialize the list using the temp allocator.
*
* @param initial_capacity "The initial capacity to reserve"
**/
fn GenericList* GenericList.temp_init(&self, usz initial_capacity = 16)
fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16)
{
return self.new_init(initial_capacity, allocator::temp()) @inline;
}
fn usz! GenericList.to_format(&self, Formatter* formatter) @dynamic
fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
@@ -76,12 +67,12 @@ fn usz! GenericList.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String GenericList.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String AnyList.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}
fn String GenericList.to_tstring(&self)
fn String AnyList.to_tstring(&self)
{
return string::tformat("%s", *self);
}
@@ -89,13 +80,13 @@ fn String GenericList.to_tstring(&self)
/**
* Push an element on the list by cloning it.
**/
macro void GenericList.push(&self, element)
macro void AnyList.push(&self, element)
{
if (!self.allocator) self.allocator = allocator::heap();
self.append_internal(allocator::clone(self.allocator, element));
}
fn void GenericList.append_internal(&self, any* element) @local
fn void AnyList.append_internal(&self, any element) @local
{
self.ensure_capacity();
self.entries[self.size++] = element;
@@ -104,7 +95,7 @@ fn void GenericList.append_internal(&self, any* element) @local
/**
* Free a retained element removed using *_retained.
**/
fn void GenericList.free_element(&self, any* element) @inline
fn void AnyList.free_element(&self, any element) @inline
{
allocator::free(self.allocator, element.ptr);
}
@@ -115,7 +106,7 @@ fn void GenericList.free_element(&self, any* element) @inline
*
* @return! CastResult.TYPE_MISMATCH, IteratorResult.NO_MORE_ELEMENT
**/
macro GenericList.pop(&self, $Type)
macro AnyList.pop(&self, $Type)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
@@ -126,7 +117,7 @@ macro GenericList.pop(&self, $Type)
* Pop the last value and allocate the copy using the given allocator.
* @return! IteratorResult.NO_MORE_ELEMENT
**/
fn any*! GenericList.new_pop(&self, Allocator* allocator = allocator::heap())
fn any! AnyList.new_pop(&self, Allocator allocator = allocator::heap())
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
@@ -137,19 +128,19 @@ fn any*! GenericList.new_pop(&self, Allocator* allocator = allocator::heap())
* Pop the last value and allocate the copy using the temp allocator
* @return! IteratorResult.NO_MORE_ELEMENT
**/
fn any*! GenericList.temp_pop(&self) => self.new_pop(allocator::temp());
fn any! AnyList.temp_pop(&self) => self.new_pop(allocator::temp());
/**
* Pop the last value. It must later be released using list.free_element()
* @return! IteratorResult.NO_MORE_ELEMENT
**/
fn any*! GenericList.pop_retained(&self)
fn any! AnyList.pop_retained(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
fn void GenericList.clear(&self)
fn void AnyList.clear(&self)
{
for (usz i = 0; i < self.size; i++)
{
@@ -161,7 +152,7 @@ fn void GenericList.clear(&self)
/**
* Same as pop() but pops the first value instead.
**/
macro GenericList.pop_first(&self, $Type)
macro AnyList.pop_first(&self, $Type)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.remove_at(0);
@@ -171,7 +162,7 @@ macro GenericList.pop_first(&self, $Type)
/**
* Same as pop_retained() but pops the first value instead.
**/
fn any*! GenericList.pop_first_retained(&self)
fn any! AnyList.pop_first_retained(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.remove_at(0);
@@ -181,7 +172,7 @@ fn any*! GenericList.pop_first_retained(&self)
/**
* Same as new_pop() but pops the first value instead.
**/
fn any*! GenericList.new_pop_first(&self, Allocator* allocator = allocator::heap())
fn any! AnyList.new_pop_first(&self, Allocator allocator = allocator::heap())
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
@@ -192,19 +183,19 @@ fn any*! GenericList.new_pop_first(&self, Allocator* allocator = allocator::heap
/**
* Same as temp_pop() but pops the first value instead.
**/
fn any*! GenericList.temp_pop_first(&self) => self.new_pop_first(allocator::temp());
fn any! AnyList.temp_pop_first(&self) => self.new_pop_first(allocator::temp());
/**
* @require index < self.size
**/
fn void GenericList.remove_at(&self, usz index)
fn void AnyList.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.free_element(self.entries[index]);
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
fn void GenericList.add_all(&self, GenericList* other_list)
fn void AnyList.add_all(&self, AnyList* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
@@ -217,7 +208,7 @@ fn void GenericList.add_all(&self, GenericList* other_list)
/**
* Reverse the elements in a list.
**/
fn void GenericList.reverse(&self)
fn void AnyList.reverse(&self)
{
if (self.size < 2) return;
usz half = self.size / 2U;
@@ -228,7 +219,7 @@ fn void GenericList.reverse(&self)
}
}
fn any*[] GenericList.array_view(&self)
fn any[] AnyList.array_view(&self)
{
return self.entries[:self.size];
}
@@ -236,7 +227,7 @@ fn any*[] GenericList.array_view(&self)
/**
* Push an element to the front of the list.
**/
macro void GenericList.push_front(&self, type)
macro void AnyList.push_front(&self, type)
{
self.insert_at(0, type);
}
@@ -244,16 +235,16 @@ macro void GenericList.push_front(&self, type)
/**
* @require index < self.size
**/
macro void GenericList.insert_at(&self, usz index, type) @local
macro void AnyList.insert_at(&self, usz index, type) @local
{
any* value = allocator::copy(self.allocator, type);
any value = allocator::copy(self.allocator, type);
self.insert_at_internal(self, index, value);
}
/**
* @require index < self.size
**/
fn void GenericList.insert_at_internal(&self, usz index, any* value) @local
fn void AnyList.insert_at_internal(&self, usz index, any value) @local
{
self.ensure_capacity();
for (usz i = self.size; i > index; i--)
@@ -268,7 +259,7 @@ fn void GenericList.insert_at_internal(&self, usz index, any* value) @local
/**
* @require self.size > 0
**/
fn void GenericList.remove_last(&self)
fn void AnyList.remove_last(&self)
{
self.free_element(self.entries[--self.size]);
}
@@ -276,37 +267,37 @@ fn void GenericList.remove_last(&self)
/**
* @require self.size > 0
**/
fn void GenericList.remove_first(&self)
fn void AnyList.remove_first(&self)
{
self.remove_at(0);
}
macro GenericList.first(&self, $Type)
macro AnyList.first(&self, $Type)
{
return *anycast(self.first_any(), $Type);
}
fn any*! GenericList.first_any(&self) @inline
fn any! AnyList.first_any(&self) @inline
{
return self.size ? self.entries[0] : IteratorResult.NO_MORE_ELEMENT?;
}
macro GenericList.last(&self, $Type)
macro AnyList.last(&self, $Type)
{
return *anycast(self.last_any(), $Type);
}
fn any*! GenericList.last_any(&self) @inline
fn any! AnyList.last_any(&self) @inline
{
return self.size ? self.entries[self.size - 1] : IteratorResult.NO_MORE_ELEMENT?;
}
fn bool GenericList.is_empty(&self) @inline
fn bool AnyList.is_empty(&self) @inline
{
return !self.size;
}
fn usz GenericList.len(&self) @operator(len) @inline
fn usz AnyList.len(&self) @operator(len) @inline
{
return self.size;
}
@@ -314,7 +305,7 @@ fn usz GenericList.len(&self) @operator(len) @inline
/**
* @require index < self.size "Index out of range"
**/
macro GenericList.get(&self, usz index, $Type)
macro AnyList.get(&self, usz index, $Type)
{
return *anycast(self.entries[index], $Type);
}
@@ -322,12 +313,12 @@ macro GenericList.get(&self, usz index, $Type)
/**
* @require index < self.size "Index out of range"
**/
fn any* GenericList.get_any(&self, usz index) @inline
fn any AnyList.get_any(&self, usz index) @inline
{
return self.entries[index];
}
fn void GenericList.free(&self)
fn void AnyList.free(&self)
{
if (!self.allocator) return;
self.clear();
@@ -336,9 +327,9 @@ fn void GenericList.free(&self)
self.entries = null;
}
fn void GenericList.swap(&self, usz i, usz j)
fn void AnyList.swap(&self, usz i, usz j)
{
any* temp = self.entries[i];
any temp = self.entries[i];
self.entries[i] = self.entries[j];
self.entries[j] = temp;
}
@@ -347,7 +338,7 @@ fn void GenericList.swap(&self, usz i, usz j)
* @param filter "The function to determine if it should be removed or not"
* @return "the number of deleted elements"
**/
fn usz GenericList.remove_if(&self, GenericPredicate filter)
fn usz AnyList.remove_if(&self, AnyPredicate filter)
{
return self._remove_if(filter, false);
}
@@ -356,12 +347,12 @@ fn usz GenericList.remove_if(&self, GenericPredicate filter)
* @param selection "The function to determine if it should be kept or not"
* @return "the number of deleted elements"
**/
fn usz GenericList.retain_if(&self, GenericPredicate selection)
fn usz AnyList.retain_if(&self, AnyPredicate selection)
{
return self._remove_if(selection, true);
}
macro usz GenericList._remove_if(&self, GenericPredicate filter, bool $invert) @local
macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
@@ -387,17 +378,17 @@ macro usz GenericList._remove_if(&self, GenericPredicate filter, bool $invert) @
return size - self.size;
}
fn usz GenericList.remove_using_test(&self, GenericTest filter, any* context)
fn usz AnyList.remove_using_test(&self, AnyTest filter, any context)
{
return self._remove_using_test(filter, false, context);
}
fn usz GenericList.retain_using_test(&self, GenericTest filter, any* context)
fn usz AnyList.retain_using_test(&self, AnyTest filter, any context)
{
return self._remove_using_test(filter, true, context);
}
macro usz GenericList._remove_using_test(&self, GenericTest filter, bool $invert, ctx) @local
macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
@@ -426,17 +417,17 @@ macro usz GenericList._remove_using_test(&self, GenericTest filter, bool $invert
/**
* Reserve at least min_capacity
**/
fn void GenericList.reserve(&self, usz min_capacity)
fn void AnyList.reserve(&self, usz min_capacity)
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, any*.sizeof * min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
self.capacity = min_capacity;
}
macro any* GenericList.@item_at(&self, usz index) @operator([])
macro any AnyList.@item_at(&self, usz index) @operator([])
{
return self.entries[index];
}
@@ -444,7 +435,7 @@ macro any* GenericList.@item_at(&self, usz index) @operator([])
/**
* @require index <= self.size "Index out of range"
**/
macro void GenericList.set(&self, usz index, value)
macro void AnyList.set(&self, usz index, value)
{
if (index == self.size)
{
@@ -455,7 +446,7 @@ macro void GenericList.set(&self, usz index, value)
self.entries[index] = allocator::copy(self.allocator, value);
}
fn void GenericList.ensure_capacity(&self, usz added = 1) @inline @private
fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;

View File

@@ -86,31 +86,17 @@ struct GrowableBitSet
* @param initial_capacity
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator* allocator = allocator::heap())
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator allocator = allocator::heap())
{
self.data.new_init(initial_capacity, allocator);
return self;
}
/**
* @param initial_capacity
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn GrowableBitSet* GrowableBitSet.init_new(&self, usz initial_capacity = 1, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(initial_capacity, allocator) @inline;
}
fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1)
{
return self.new_init(initial_capacity, allocator::temp()) @inline;
}
fn GrowableBitSet* GrowableBitSet.init_temp(&self, usz initial_capacity = 1) @deprecated("Replaced by temp_init")
{
return self.temp_init(initial_capacity);
}
fn void GrowableBitSet.free(&self)
{
self.data.free();

View File

@@ -0,0 +1,434 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
/**
* @require MAX_SIZE >= 1 `The size must be at least 1 element big.`
**/
module std::collections::elastic_array(<Type, MAX_SIZE>);
import std::io, std::math, std::collections::list_common;
def ElementPredicate = fn bool(Type *type);
def ElementTest = fn bool(Type *type, any context);
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
const ELEMENT_IS_POINTER = Type.kindof == POINTER;
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
struct ElasticArray (Printable)
{
usz size;
Type[MAX_SIZE] entries;
}
fn usz! ElasticArray.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
case 0:
return formatter.print("[]")!;
case 1:
return formatter.printf("[%s]", self.entries[0])!;
default:
usz n = formatter.print("[")!;
foreach (i, element : self.entries[:self.size])
{
if (i != 0) formatter.print(", ")!;
n += formatter.printf("%s", element)!;
}
n += formatter.print("]")!;
return n;
}
}
fn String ElasticArray.to_string(&self, Allocator allocator) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}
fn String ElasticArray.to_tstring(&self)
{
return string::tformat("%s", *self);
}
fn void! ElasticArray.push_try(&self, Type element) @inline
{
if (self.size == MAX_SIZE) return AllocationFailure.OUT_OF_MEMORY?;
self.entries[self.size++] = element;
}
/**
* @require self.size < MAX_SIZE `Tried to exceed the max size`
**/
fn void ElasticArray.push(&self, Type element) @inline
{
self.entries[self.size++] = element;
}
fn Type! ElasticArray.pop(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
fn void ElasticArray.clear(&self)
{
self.size = 0;
}
/**
* @require self.size > 0
**/
fn Type! ElasticArray.pop_first(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
/**
* @require index < self.size
**/
fn void ElasticArray.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
/**
* @require other_list.size + self.size <= MAX_SIZE
**/
fn void ElasticArray.add_all(&self, ElasticArray* other_list)
{
if (!other_list.size) return;
foreach (&value : other_list)
{
self.entries[self.size++] = *value;
}
}
/**
* Add as many elements as possible to the new array,
* returning the number of elements that didn't fit.
**/
fn usz ElasticArray.add_all_to_limit(&self, ElasticArray* other_list)
{
if (!other_list.size) return 0;
foreach (i, &value : other_list)
{
if (self.size == MAX_SIZE) return other_list.size - i;
self.entries[self.size++] = *value;
}
return 0;
}
/**
* Add as many values from this array as possible, returning the
* number of elements that didn't fit.
*
* @param [in] array
**/
fn usz ElasticArray.add_array_to_limit(&self, Type[] array)
{
if (!array.len) return 0;
foreach (i, &value : array)
{
if (self.size == MAX_SIZE) return array.len - i;
self.entries[self.size++] = *value;
}
return 0;
}
/**
* Add the values of an array to this list.
*
* @param [in] array
* @require array.len + self.size <= MAX_SIZE `Size would exceed max.`
* @ensure self.size >= array.len
**/
fn void ElasticArray.add_array(&self, Type[] array)
{
if (!array.len) return;
foreach (&value : array)
{
self.entries[self.size++] = *value;
}
}
/**
* IMPORTANT The returned array must be freed using free_aligned.
**/
fn Type[] ElasticArray.to_new_aligned_array(&self, Allocator allocator = allocator::heap())
{
return list_common::list_to_new_aligned_array(Type, self, allocator);
}
/**
* @require !type_is_overaligned() : "This function is not available on overaligned types"
**/
macro Type[] ElasticArray.to_new_array(&self, Allocator allocator = allocator::heap())
{
return list_common::list_to_new_array(Type, self, allocator);
}
fn Type[] ElasticArray.to_tarray(&self)
{
$if type_is_overaligned():
return self.to_new_aligned_array(allocator::temp());
$else
return self.to_new_array(allocator::temp());
$endif;
}
/**
* Reverse the elements in a list.
**/
fn void ElasticArray.reverse(&self)
{
list_common::list_reverse(self);
}
fn Type[] ElasticArray.array_view(&self)
{
return self.entries[:self.size];
}
/**
* @require self.size < MAX_SIZE `List would exceed max size`
**/
fn void ElasticArray.push_front(&self, Type type) @inline
{
self.insert_at(0, type);
}
/**
* @require self.size < MAX_SIZE `List would exceed max size`
**/
fn void! ElasticArray.push_front_try(&self, Type type) @inline
{
return self.insert_at_try(0, type);
}
/**
* @require index <= self.size
**/
fn void! ElasticArray.insert_at_try(&self, usz index, Type value)
{
if (self.size == MAX_SIZE) return AllocationFailure.OUT_OF_MEMORY?;
self.insert_at(index, value);
}
/**
* @require self.size < MAX_SIZE `List would exceed max size`
* @require index <= self.size
**/
fn void ElasticArray.insert_at(&self, usz index, Type type)
{
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.entries[index] = type;
}
/**
* @require index < self.size
**/
fn void ElasticArray.set_at(&self, usz index, Type type)
{
self.entries[index] = type;
}
fn void! ElasticArray.remove_last(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
self.size--;
}
fn void! ElasticArray.remove_first(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
self.remove_at(0);
}
fn Type! ElasticArray.first(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[0];
}
fn Type! ElasticArray.last(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[self.size - 1];
}
fn bool ElasticArray.is_empty(&self) @inline
{
return !self.size;
}
fn usz ElasticArray.byte_size(&self) @inline
{
return Type.sizeof * self.size;
}
fn usz ElasticArray.len(&self) @operator(len) @inline
{
return self.size;
}
fn Type ElasticArray.get(&self, usz index) @inline
{
return self.entries[index];
}
fn void ElasticArray.swap(&self, usz i, usz j)
{
@swap(self.entries[i], self.entries[j]);
}
/**
* @param filter "The function to determine if it should be removed or not"
* @return "the number of deleted elements"
**/
fn usz ElasticArray.remove_if(&self, ElementPredicate filter)
{
return list_common::list_remove_if(self, filter, false);
}
/**
* @param selection "The function to determine if it should be kept or not"
* @return "the number of deleted elements"
**/
fn usz ElasticArray.retain_if(&self, ElementPredicate selection)
{
return list_common::list_remove_if(self, selection, true);
}
fn usz ElasticArray.remove_using_test(&self, ElementTest filter, any context)
{
return list_common::list_remove_using_test(self, filter, false, context);
}
fn usz ElasticArray.retain_using_test(&self, ElementTest filter, any context)
{
return list_common::list_remove_using_test(self, filter, true, context);
}
macro Type ElasticArray.@item_at(&self, usz index) @operator([])
{
return self.entries[index];
}
fn Type* ElasticArray.get_ref(&self, usz index) @operator(&[]) @inline
{
return &self.entries[index];
}
fn void ElasticArray.set(&self, usz index, Type value) @operator([]=)
{
self.entries[index] = value;
}
// Functions for equatable types
fn usz! ElasticArray.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
{
foreach (i, v : self)
{
if (equals(v, type)) return i;
}
return SearchResult.MISSING?;
}
fn usz! ElasticArray.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
{
foreach_r (i, v : self)
{
if (equals(v, type)) return i;
}
return SearchResult.MISSING?;
}
fn bool ElasticArray.equals(&self, ElasticArray other_list) @if(ELEMENT_IS_EQUATABLE)
{
if (self.size != other_list.size) return false;
foreach (i, v : self)
{
if (!equals(v, other_list.entries[i])) return false;
}
return true;
}
/**
* Check for presence of a value in a list.
*
* @param [&in] self "the list to find elements in"
* @param value "The value to search for"
* @return "True if the value is found, false otherwise"
**/
fn bool ElasticArray.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
foreach (i, v : self)
{
if (equals(v, value)) return true;
}
return false;
}
/**
* @param [&inout] self "The list to remove elements from"
* @param value "The value to remove"
* @return "true if the value was found"
**/
fn bool ElasticArray.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return @ok(self.remove_at(self.rindex_of(value)));
}
/**
* @param [&inout] self "The list to remove elements from"
* @param value "The value to remove"
* @return "true if the value was found"
**/
fn bool ElasticArray.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return @ok(self.remove_at(self.index_of(value)));
}
/**
* @param [&inout] self "The list to remove elements from"
* @param value "The value to remove"
* @return "the number of deleted elements."
**/
fn usz ElasticArray.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return list_common::list_remove_item(self, value);
}
fn void ElasticArray.remove_all_from(&self, ElasticArray* other_list) @if(ELEMENT_IS_EQUATABLE)
{
if (!other_list.size) return;
foreach (v : other_list) self.remove_item(v);
}
/**
* @param [&in] self
* @return "The number non-null values in the list"
**/
fn usz ElasticArray.compact_count(&self) @if(ELEMENT_IS_POINTER)
{
usz vals = 0;
foreach (v : self) if (v) vals++;
return vals;
}
fn usz ElasticArray.compact(&self) @if(ELEMENT_IS_POINTER)
{
return list_common::list_compact(self);
}

View File

@@ -25,7 +25,7 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
return n;
}
fn String EnumMap.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String EnumMap.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}

View File

@@ -141,7 +141,7 @@ fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic
return n;
}
fn String EnumSet.to_new_string(&set, Allocator* allocator = allocator::heap()) @dynamic
fn String EnumSet.to_new_string(&set, Allocator allocator = allocator::heap()) @dynamic
{
return string::new_format("%s", *set, .allocator = allocator);
}

View File

@@ -0,0 +1,400 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::map(<Key, Value>);
import std::math;
struct HashMap
{
Entry*[] table;
Allocator allocator;
uint count; // Number of elements
uint threshold; // Resize limit
float load_factor;
}
/**
* @param [&inout] allocator "The allocator to use"
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !self.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
self.load_factor = load_factor;
self.threshold = (uint)(capacity * load_factor);
self.table = allocator::new_array(allocator, Entry*, capacity);
return self;
}
/**
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !self.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.new_init(capacity, load_factor, allocator::temp()) @inline;
}
/**
* Has this hash map been initialized yet?
*
* @param [&in] map "The hash map we are testing"
* @return "Returns true if it has been initialized, false otherwise"
**/
fn bool HashMap.is_initialized(&map)
{
return (bool)map.allocator;
}
/**
* @param [&inout] allocator "The allocator to use"
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map, Allocator allocator = allocator::heap())
{
self.new_init(other_map.table.len, other_map.load_factor, allocator);
self.put_all_for_create(other_map);
return self;
}
/**
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map)
{
return map.new_init_from_map(other_map, allocator::temp()) @inline;
}
fn bool HashMap.is_empty(&map) @inline
{
return !map.count;
}
fn usz HashMap.len(&map) @inline
{
return map.count;
}
fn Value*! HashMap.get_ref(&map, Key key)
{
if (!map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return SearchResult.MISSING?;
}
fn Entry*! HashMap.get_entry(&map, Key key)
{
if (!map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return SearchResult.MISSING?;
}
/**
* Get the value or update and
* @require $assignable(#expr, Value)
**/
macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
{
if (!map.count)
{
Value val = #expr;
map.set(key, val);
return val;
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e.value;
}
Value val = #expr;
map.add_entry(hash, key, val, index);
return val;
}
fn Value! HashMap.get(&map, Key key) @operator([])
{
return *map.get_ref(key) @inline;
}
fn bool HashMap.has_key(&map, Key key)
{
return @ok(map.get_ref(key));
}
fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
{
// If the map isn't initialized, use the defaults to initialize it.
if (!map.allocator)
{
map.new_init();
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return true;
}
}
map.add_entry(hash, key, value, index);
return false;
}
fn void! HashMap.remove(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
}
fn void HashMap.clear(&map)
{
if (!map.count) return;
foreach (Entry** &entry_ref : map.table)
{
Entry* entry = *entry_ref;
if (!entry) continue;
Entry *next = entry.next;
while (next)
{
Entry *to_delete = next;
next = next.next;
map.free_entry(to_delete);
}
map.free_entry(entry);
*entry_ref = null;
}
map.count = 0;
}
fn void HashMap.free(&map)
{
if (!map.allocator) return;
map.clear();
map.free_internal(map.table.ptr);
map.table = {};
}
fn Key[] HashMap.key_tlist(&map)
{
return map.key_new_list(allocator::temp()) @inline;
}
fn Key[] HashMap.key_new_list(&map, Allocator allocator = allocator::heap())
{
if (!map.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.key;
entry = entry.next;
}
}
return list;
}
macro HashMap.@each(map; @body(key, value))
{
map.@each_entry(; Entry* entry) {
@body(entry.key, entry.value);
};
}
macro HashMap.@each_entry(map; @body(entry))
{
if (map.count)
{
foreach (Entry* entry : map.table)
{
while (entry)
{
@body(entry);
entry = entry.next;
}
}
}
}
fn Value[] HashMap.value_tlist(&map)
{
return map.value_new_list(allocator::temp()) @inline;
}
fn Value[] HashMap.value_new_list(&map, Allocator allocator = allocator::heap())
{
if (!map.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.value;
entry = entry.next;
}
}
return list;
}
fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
{
if (!map.count) return false;
foreach (Entry* entry : map.table)
{
while (entry)
{
if (equals(v, entry.value)) return true;
entry = entry.next;
}
}
return false;
}
// --- private methods
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
}
}
fn void HashMap.resize(&map, uint new_capacity) @private
{
Entry*[] old_table = map.table;
uint old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
map.threshold = uint.max;
return;
}
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
map.transfer(new_table);
map.table = new_table;
map.free_internal(old_table.ptr);
map.threshold = (uint)(new_capacity * map.load_factor);
}
fn void HashMap.transfer(&map, Entry*[] new_table) @private
{
Entry*[] src = map.table;
uint new_capacity = new_table.len;
foreach (uint j, Entry *e : src)
{
if (!e) continue;
do
{
Entry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
fn void HashMap.put_all_for_create(&map, HashMap* other_map) @private
{
if (!other_map.count) return;
foreach (Entry *e : other_map.table)
{
if (!e) continue;
map.put_for_create(e.key, e.value);
}
}
fn void HashMap.put_for_create(&map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
for (Entry *e = map.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return;
}
}
map.create_entry(hash, key, value, i);
}
fn void HashMap.free_internal(&map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
Entry* prev = map.table[i];
Entry* e = prev;
while (e)
{
Entry *next = e.next;
if (e.hash == hash && equals(key, e.key))
{
map.count--;
if (prev == e)
{
map.table[i] = next;
}
else
{
prev.next = next;
}
map.free_entry(e);
return true;
}
prev = e;
e = next;
}
return false;
}
fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
map.count++;
}
fn void HashMap.free_entry(&self, Entry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
$endif
self.free_internal(entry);
}

View File

@@ -3,6 +3,8 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::linkedlist(<Type>);
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
struct Node @private
{
Node *next;
@@ -12,51 +14,28 @@ struct Node @private
struct LinkedList
{
Allocator *allocator;
Allocator allocator;
usz size;
Node *_first;
Node *_last;
}
fn void LinkedList.push(&self, Type value)
{
self.link_first(value);
}
fn void LinkedList.push_last(&self, Type value)
{
self.link_last(value);
}
/**
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
* @return "the initialized list"
**/
fn LinkedList* LinkedList.new_init(&self, Allocator* allocator = allocator::heap())
fn LinkedList* LinkedList.new_init(&self, Allocator allocator = allocator::heap())
{
*self = { .allocator = allocator };
return self;
}
/**
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
* @return "the initialized list"
**/
fn LinkedList* LinkedList.init_new(&self, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(allocator);
}
fn LinkedList* LinkedList.temp_init(&self)
{
return self.new_init(allocator::temp()) @inline;
}
fn LinkedList* LinkedList.init_temp(&self) @deprecated("Replaced by temp_init")
{
return self.temp_init() @inline;
}
/**
* @require self.allocator
**/
@@ -71,7 +50,7 @@ macro Node* LinkedList.alloc_node(&self) @private
return allocator::alloc(self.allocator, Node);
}
fn void LinkedList.link_first(&self, Type value) @private
fn void LinkedList.push_front(&self, Type value)
{
Node *first = self._first;
Node *new_node = self.alloc_node();
@@ -88,7 +67,7 @@ fn void LinkedList.link_first(&self, Type value) @private
self.size++;
}
fn void LinkedList.link_last(&self, Type value) @private
fn void LinkedList.push(&self, Type value)
{
Node *last = self._last;
Node *new_node = self.alloc_node();
@@ -172,7 +151,7 @@ fn void LinkedList.set(&self, usz index, Type element)
/**
* @require index < self.size
**/
fn void LinkedList.remove(&self, usz index)
fn void LinkedList.remove_at(&self, usz index)
{
self.unlink(self.node_at_index(index));
}
@@ -180,14 +159,14 @@ fn void LinkedList.remove(&self, usz index)
/**
* @require index <= self.size
**/
fn void LinkedList.insert(&self, usz index, Type element)
fn void LinkedList.insert_at(&self, usz index, Type element)
{
switch (index)
{
case 0:
self.push(element);
self.push_front(element);
case self.size:
self.push_last(element);
self.push(element);
default:
self.link_before(self.node_at_index(index), element);
}
@@ -232,7 +211,53 @@ fn void LinkedList.unlink_first(&self) @private
self.size--;
}
fn bool LinkedList.remove_value(&self, Type t)
fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
usz start = self.size;
Node* node = self._first;
while (node)
{
switch
{
case equals(node.value, t):
Node* next = node.next;
self.unlink(node);
node = next;
default:
node = node.next;
}
}
return start - self.size;
}
fn Type! LinkedList.pop(&self)
{
if (!self._last) return IteratorResult.NO_MORE_ELEMENT?;
defer self.unlink_last();
return self._last.value;
}
fn Type! LinkedList.pop_front(&self)
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
defer self.unlink_first();
return self._first.value;
}
fn void! LinkedList.remove_last(&self) @maydiscard
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
self.unlink_last();
}
fn void! LinkedList.remove_first(&self) @maydiscard
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
self.unlink_first();
}
fn bool LinkedList.remove_first_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
for (Node* node = self._first; node != null; node = node.next)
{
@@ -245,7 +270,7 @@ fn bool LinkedList.remove_value(&self, Type t)
return false;
}
fn bool LinkedList.remove_last_value(&self, Type t)
fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
for (Node* node = self._last; node != null; node = node.prev)
{
@@ -257,26 +282,6 @@ fn bool LinkedList.remove_last_value(&self, Type t)
}
return false;
}
fn Type! LinkedList.pop(&self)
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
defer self.unlink_first();
return self._first.value;
}
fn void! LinkedList.remove_last(&self)
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
self.unlink_last();
}
fn void! LinkedList.remove_first(&self)
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
self.unlink_first();
}
/**
* @require self._last
**/

View File

@@ -2,52 +2,37 @@
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::list(<Type>);
import std::io,std::math;
import std::io, std::math, std::collections::list_common;
def ElementPredicate = fn bool(Type *type);
def ElementTest = fn bool(Type *type, any* context);
def ElementTest = fn bool(Type *type, any context);
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
const ELEMENT_IS_POINTER = Type.kindof == POINTER;
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
struct List (Printable)
{
usz size;
usz capacity;
Allocator *allocator;
Allocator allocator;
Type *entries;
}
/**
* @param initial_capacity "The initial capacity to reserve"
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap())
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap())
{
self.allocator = allocator;
self.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
self.entries = allocator::malloc_aligned(allocator, Type.sizeof * initial_capacity, .alignment = Type[1].alignof)!!;
}
else
{
self.entries = null;
}
self.capacity = initial_capacity;
self.capacity = 0;
self.entries = null;
self.reserve(initial_capacity);
return self;
}
/**
* @param initial_capacity "The initial capacity to reserve"
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn List* List.init_new(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(initial_capacity, allocator) @inline;
}
/**
* Initialize the list using the temp allocator.
*
@@ -59,24 +44,40 @@ fn List* List.temp_init(&self, usz initial_capacity = 16)
}
/**
* Initialize the list using the temp allocator.
* Initialize a new list with an array.
*
* @param initial_capacity "The initial capacity to reserve"
* @param [in] values `The values to initialize the list with.`
* @require self.size == 0 "The List must be empty"
**/
fn List* List.init_temp(&self, usz initial_capacity = 16) @deprecated("Replaced by temp_init")
fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = allocator::heap())
{
return self.temp_init(initial_capacity) @inline;
self.new_init(values.len, allocator) @inline;
self.add_array(values) @inline;
return self;
}
/**
* Initialize a temporary list with an array.
*
* @param [in] values `The values to initialize the list with.`
* @require self.size == 0 "The List must be empty"
**/
fn void List.init_wrapping_array(&self, Type[] types, Allocator* allocator = allocator::heap())
fn List* List.temp_init_with_array(&self, Type[] values)
{
self.temp_init(values.len) @inline;
self.add_array(values) @inline;
return self;
}
/**
* @require self.capacity == 0 "The List must not be allocated"
**/
fn void List.init_wrapping_array(&self, Type[] types, Allocator allocator = allocator::heap())
{
self.allocator = allocator;
self.size = types.len;
self.capacity = types.len;
self.entries = types.ptr;
self.set_size(types.len);
}
fn usz! List.to_format(&self, Formatter* formatter) @dynamic
@@ -99,7 +100,7 @@ fn usz! List.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String List.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String List.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}
@@ -111,36 +112,30 @@ fn String List.to_tstring(&self)
fn void List.push(&self, Type element) @inline
{
self.append(element);
self.reserve(1);
self.entries[self.set_size(self.size + 1)] = element;
}
fn void List.append(&self, Type element)
fn Type! List.pop(&self)
{
self.ensure_capacity();
self.entries[self.size++] = element;
}
/**
* @require self.size > 0
**/
fn Type List.pop(&self)
{
return self.entries[--self.size];
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.set_size(self.size - 1);
return self.entries[self.size - 1];
}
fn void List.clear(&self)
{
self.size = 0;
self.set_size(0);
}
/**
* @require self.size > 0
**/
fn Type List.pop_first(&self)
fn Type! List.pop_first(&self)
{
Type value = self.entries[0];
self.remove_at(0);
return value;
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
/**
@@ -148,7 +143,8 @@ fn Type List.pop_first(&self)
**/
fn void List.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.set_size(self.size - 1);
if (!self.size || index == self.size) return;
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
@@ -156,24 +152,37 @@ fn void List.add_all(&self, List* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
usz index = self.set_size(self.size + other_list.size);
foreach (&value : other_list)
{
self.entries[self.size++] = *value;
self.entries[index++] = *value;
}
}
fn Type[] List.to_new_array(&self, Allocator* allocator = allocator::heap())
/**
* IMPORTANT The returned array must be freed using free_aligned.
**/
fn Type[] List.to_new_aligned_array(&self, Allocator allocator = allocator::heap())
{
if (!self.size) return Type[] {};
Type[] result = allocator::alloc_array(allocator, Type, self.size);
result[..] = self.entries[:self.size];
return result;
return list_common::list_to_new_aligned_array(Type, self, allocator);
}
/**
* @require !type_is_overaligned() : "This function is not available on overaligned types"
**/
macro Type[] List.to_new_array(&self, Allocator allocator = allocator::heap())
{
return list_common::list_to_new_array(Type, self, allocator);
}
fn Type[] List.to_tarray(&self)
{
$if type_is_overaligned():
return self.to_new_aligned_array(allocator::temp());
$else
return self.to_new_array(allocator::temp());
$endif;
}
/**
@@ -181,13 +190,7 @@ fn Type[] List.to_tarray(&self)
**/
fn void List.reverse(&self)
{
if (self.size < 2) return;
usz half = self.size / 2U;
usz end = self.size - 1;
for (usz i = 0; i < half; i++)
{
@swap(self.entries[i], self.entries[end - i]);
}
list_common::list_reverse(self);
}
fn Type[] List.array_view(&self)
@@ -195,14 +198,18 @@ fn Type[] List.array_view(&self)
return self.entries[:self.size];
}
/**
* Add the values of an array to this list.
*
* @param [in] array
* @ensure self.size >= array.len
**/
fn void List.add_array(&self, Type[] array)
{
if (!array.len) return;
self.reserve(array.len);
foreach (&value : array)
{
self.entries[self.size++] = *value;
}
usz index = self.set_size(self.size + array.len);
self.entries[index : array.len] = array[..];
}
fn void List.push_front(&self, Type type) @inline
@@ -211,16 +218,16 @@ fn void List.push_front(&self, Type type) @inline
}
/**
* @require index < self.size
* @require index <= self.size
**/
fn void List.insert_at(&self, usz index, Type type)
{
self.ensure_capacity();
self.reserve(1);
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.set_size(self.size + 1);
self.entries[index] = type;
}
@@ -232,30 +239,28 @@ fn void List.set_at(&self, usz index, Type type)
self.entries[index] = type;
}
/**
* @require self.size > 0
**/
fn void List.remove_last(&self)
fn void! List.remove_last(&self) @maydiscard
{
self.size--;
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
self.set_size(self.size - 1);
}
/**
* @require self.size > 0
**/
fn void List.remove_first(&self)
fn void! List.remove_first(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
self.remove_at(0);
}
fn Type* List.first(&self)
fn Type! List.first(&self)
{
return self.size ? &self.entries[0] : null;
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[0];
}
fn Type* List.last(&self)
fn Type! List.last(&self)
{
return self.size ? &self.entries[self.size - 1] : null;
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[self.size - 1];
}
fn bool List.is_empty(&self) @inline
@@ -280,8 +285,15 @@ fn Type List.get(&self, usz index) @inline
fn void List.free(&self)
{
if (!self.allocator) return;
allocator::free_aligned(self.allocator, self.entries);
if (!self.allocator || !self.capacity) return;
self.pre_free(); // Remove sanitizer annotation
$if type_is_overaligned():
allocator::free_aligned(self.allocator, self.entries);
$else
allocator::free(self.allocator, self.entries);
$endif;
self.capacity = 0;
self.size = 0;
self.entries = null;
@@ -298,7 +310,7 @@ fn void List.swap(&self, usz i, usz j)
**/
fn usz List.remove_if(&self, ElementPredicate filter)
{
return self._remove_if(filter, false);
return list_common::list_remove_if(self, filter, false);
}
/**
@@ -307,80 +319,46 @@ fn usz List.remove_if(&self, ElementPredicate filter)
**/
fn usz List.retain_if(&self, ElementPredicate selection)
{
return self._remove_if(selection, true);
return list_common::list_remove_if(self, selection, true);
}
macro usz List._remove_if(&self, ElementPredicate filter, bool $invert) @local
fn usz List.remove_using_test(&self, ElementTest filter, any context)
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && filter(&self.entries[i - 1])) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$endif
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return size - self.size;
return list_common::list_remove_using_test(self, filter, false, context);
}
fn usz List.remove_using_test(&self, ElementTest filter, any* context)
{
return self._remove_using_test(filter, false, context);
}
fn usz List.retain_using_test(&self, ElementTest filter, any* context)
{
return self._remove_using_test(filter, true, context);
}
macro usz List._remove_using_test(&self, ElementTest filter, bool $invert, ctx) @local
fn usz List.retain_using_test(&self, ElementTest filter, any context)
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$endif
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return size - self.size;
return list_common::list_remove_using_test(self, filter, true, context);
}
/**
* Reserve at least min_capacity
**/
fn void List.reserve(&self, usz min_capacity)
fn void List.ensure_capacity(&self, usz min_capacity) @local
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
self.pre_free(); // Remove sanitizer annotation
min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof) ?? null;
$if type_is_overaligned():
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof)!!;
$else
self.entries = allocator::realloc(self.allocator, self.entries, Type.sizeof * min_capacity);
$endif;
self.capacity = min_capacity;
self.post_alloc(); // Add sanitizer annotation
}
macro Type List.@item_at(&self, usz index) @operator([])
@@ -398,7 +376,7 @@ fn void List.set(&self, usz index, Type value) @operator([]=)
self.entries[index] = value;
}
fn void List.ensure_capacity(&self, usz added = 1) @inline @private
fn void List.reserve(&self, usz added)
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;
@@ -406,7 +384,40 @@ fn void List.ensure_capacity(&self, usz added = 1) @inline @private
assert(new_size < usz.max / 2U);
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
while (new_capacity < new_size) new_capacity *= 2U;
self.reserve(new_capacity);
self.ensure_capacity(new_capacity);
}
fn void List._update_size_change(&self,usz old_size, usz new_size)
{
if (old_size == new_size) return;
sanitizer::annotate_contiguous_container(self.entries,
&self.entries[self.capacity],
&self.entries[old_size],
&self.entries[new_size]);
}
/**
* @require new_size == 0 || self.capacity != 0
**/
fn usz List.set_size(&self, usz new_size) @inline @private
{
usz old_size = self.size;
self._update_size_change(old_size, new_size);
self.size = new_size;
return old_size;
}
macro void List.pre_free(&self) @private
{
if (!self.capacity) return;
self._update_size_change(self.size, self.capacity);
}
/**
* @require self.capacity
**/
macro void List.post_alloc(&self) @private
{
self._update_size_change(self.capacity, self.size);
}
// Functions for equatable types
@@ -456,31 +467,49 @@ fn bool List.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
return false;
}
/**
* @param [&inout] self "The list to remove elements from"
* @param value "The value to remove"
* @return "true if the value was found"
**/
fn bool List.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return @ok(self.remove_at(self.rindex_of(value)));
}
/**
* @param [&inout] self "The list to remove elements from"
* @param value "The value to remove"
* @return "true if the value was found"
**/
fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
return @ok(self.remove_at(self.index_of(value)));
}
/**
* @param [&inout] self "The list to remove elements from"
* @param value "The value to remove"
* @return "the number of deleted elements."
**/
fn usz List.remove(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
usz size = self.size;
for (usz i = size; i > 0; i--)
{
if (!equals(self.entries[i - 1], value)) continue;
for (usz j = i; j < size; j++)
{
self.entries[j - 1] = self.entries[j];
}
self.size--;
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return size - self.size;
return list_common::list_remove_item(self, value);
}
fn void List.remove_all(&self, List* other_list) @if(ELEMENT_IS_EQUATABLE)
fn void List.remove_all_from(&self, List* other_list) @if(ELEMENT_IS_EQUATABLE)
{
if (!other_list.size) return;
foreach (v : other_list) self.remove(v);
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
foreach (v : other_list) self.remove_item(v);
}
/**
@@ -496,15 +525,42 @@ fn usz List.compact_count(&self) @if(ELEMENT_IS_POINTER)
fn usz List.compact(&self) @if(ELEMENT_IS_POINTER)
{
usz size = self.size;
for (usz i = size; i > 0; i--)
{
if (self.entries[i - 1]) continue;
for (usz j = i; j < size; j++)
{
self.entries[j - 1] = self.entries[j];
}
self.size--;
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return size - self.size;
return list_common::list_compact(self);
}
// --> Deprecated
/**
* @param [&inout] self "The list to remove elements from"
* @param value "The value to remove"
* @return "true if the value was found"
**/
fn bool List.remove_last_match(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
{
return self.remove_last_item(value) @inline;
}
/**
* @param [&inout] self "The list to remove elements from"
* @param value "The value to remove"
* @return "true if the value was found"
**/
fn bool List.remove_first_match(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
{
return self.remove_first_item(value) @inline;
}
/**
* @param [&inout] self "The list to remove elements from"
* @param value "The value to remove"
* @return "the number of deleted elements."
**/
fn usz List.remove_all_matches(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
{
return self.remove_item(value) @inline;
}

View File

@@ -0,0 +1,112 @@
module std::collections::list_common;
/**
* IMPORTANT The returned array must be freed using free_aligned.
**/
macro list_to_new_aligned_array($Type, self, Allocator allocator)
{
if (!self.size) return $Type[] {};
$Type[] result = allocator::alloc_array_aligned(allocator, $Type, self.size);
result[..] = self.entries[:self.size];
return result;
}
macro list_to_new_array($Type, self, Allocator allocator)
{
if (!self.size) return $Type[] {};
$Type[] result = allocator::alloc_array(allocator, $Type, self.size);
result[..] = self.entries[:self.size];
return result;
}
macro void list_reverse(self)
{
if (self.size < 2) return;
usz half = self.size / 2U;
usz end = self.size - 1;
for (usz i = 0; i < half; i++)
{
@swap(self.entries[i], self.entries[end - i]);
}
}
macro usz list_remove_using_test(self, filter, bool $invert, ctx)
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$endif
}
return size - self.size;
}
macro usz list_compact(self)
{
usz size = self.size;
for (usz i = size; i > 0; i--)
{
if (self.entries[i - 1]) continue;
for (usz j = i; j < size; j++)
{
self.entries[j - 1] = self.entries[j];
}
self.size--;
}
return size - self.size;
}
macro usz list_remove_item(self, value)
{
usz size = self.size;
for (usz i = size; i > 0; i--)
{
if (!equals(self.entries[i - 1], value)) continue;
for (usz j = i; j < self.size; j++)
{
self.entries[j - 1] = self.entries[j];
}
self.size--;
}
return size - self.size;
}
macro usz list_remove_if(self, filter, bool $invert)
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && filter(&self.entries[i - 1])) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$endif
}
return size - self.size;
}

View File

@@ -10,128 +10,84 @@ const float DEFAULT_LOAD_FACTOR = 0.75;
const VALUE_IS_EQUATABLE = Value.is_eq;
const bool COPY_KEYS = types::implements_copy(Key);
struct HashMap
distinct Map = void*;
struct MapImpl
{
Entry*[] table;
Allocator* allocator;
Allocator allocator;
uint count; // Number of elements
uint threshold; // Resize limit
float load_factor;
}
/**
* @param [&inout] allocator "The allocator to use"
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !self.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = allocator::heap())
fn Map new(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
self.load_factor = load_factor;
self.threshold = (uint)(capacity * load_factor);
self.table = allocator::new_array(allocator, Entry*, capacity);
return self;
}
/**
* @param [&inout] allocator "The allocator to use"
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.init_new(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return map.new_init(capacity, load_factor, allocator);
MapImpl* map = allocator::alloc(allocator, MapImpl);
_init(map, capacity, load_factor, allocator);
return (Map)map;
}
/**
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !self.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
fn Map temp(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.new_init(capacity, load_factor, allocator::temp()) @inline;
}
/**
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.init_temp(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Replaced by temp_init")
{
return map.temp_init(capacity, load_factor) @inline;
}
/**
* Has this hash map been initialized yet?
*
* @param [&in] map "The hash map we are testing"
* @return "Returns true if it has been initialized, false otherwise"
**/
fn bool HashMap.is_initialized(&map)
{
return (bool)map.allocator;
}
/**
* @param [&inout] allocator "The allocator to use"
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map, Allocator* allocator = allocator::heap())
{
self.new_init(other_map.table.len, other_map.load_factor, allocator);
self.put_all_for_create(other_map);
return self;
}
/**
* @param [&inout] allocator "The allocator to use"
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.init_new_from_map(&self, HashMap* other_map, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init_from_map")
{
return self.new_init_from_map(other_map, allocator) @inline;
MapImpl* map = mem::temp_alloc(MapImpl);
_init(map, capacity, load_factor, allocator::temp());
return (Map)map;
}
/**
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map)
fn Map new_from_map(Map other_map, Allocator allocator = null)
{
return map.new_init_from_map(other_map, allocator::temp()) @inline;
MapImpl* other_map_impl = (MapImpl*)other_map;
if (!other_map_impl)
{
if (allocator) return new(.allocator = allocator);
return null;
}
MapImpl* map = (MapImpl*)new(other_map_impl.table.len, other_map_impl.load_factor, allocator ?: allocator::heap());
if (!other_map_impl.count) return (Map)map;
foreach (Entry *e : other_map_impl.table)
{
if (!e) continue;
map._put_for_create(e.key, e.value);
}
return (Map)map;
}
/**
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.init_temp_from_map(&map, HashMap* other_map) @deprecated("Replaced by temp_init_from_map")
fn Map temp_from_map(Map other_map)
{
return map.temp_init_from_map(other_map) @inline;
return new_from_map(other_map, allocator::temp());
}
fn bool HashMap.is_empty(&map) @inline
fn bool Map.is_empty(map) @inline
{
return !map.count;
return !map || !((MapImpl*)map).count;
}
fn usz HashMap.len(&map) @inline
fn usz Map.len(map) @inline
{
return map.count;
return map ? ((MapImpl*)map).count : 0;
}
fn Value*! HashMap.get_ref(&map, Key key)
fn Value*! Map.get_ref(self, Key key)
{
if (!map.count) return SearchResult.MISSING?;
MapImpl *map = (MapImpl*)self;
if (!map || !map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
@@ -140,11 +96,12 @@ fn Value*! HashMap.get_ref(&map, Key key)
return SearchResult.MISSING?;
}
fn Entry*! HashMap.get_entry(&map, Key key)
fn Entry*! Map.get_entry(map, Key key)
{
if (!map.count) return SearchResult.MISSING?;
MapImpl *map_impl = (MapImpl*)map;
if (!map_impl || !map_impl.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
for (Entry *e = map_impl.table[index_for(hash, map_impl.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
@@ -155,9 +112,10 @@ fn Entry*! HashMap.get_entry(&map, Key key)
* Get the value or update and
* @require $assignable(#expr, Value)
**/
macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
macro Value Map.@get_or_set(&self, Key key, Value #expr)
{
if (!map.count)
MapImpl *map = (MapImpl*)*self;
if (!map || !map.count)
{
Value val = #expr;
map.set(key, val);
@@ -174,23 +132,27 @@ macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
return val;
}
fn Value! HashMap.get(&map, Key key) @operator([])
fn Value! Map.get(map, Key key) @operator([])
{
return *map.get_ref(key) @inline;
}
fn bool HashMap.has_key(&map, Key key)
fn bool Map.has_key(map, Key key)
{
return @ok(map.get_ref(key));
}
fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
macro Value Map.set_value_return(&map, Key key, Value value) @operator([]=)
{
map.set(key, value);
return value;
}
fn bool Map.set(&self, Key key, Value value)
{
// If the map isn't initialized, use the defaults to initialize it.
if (!map.allocator)
{
map.new_init();
}
if (!*self) *self = new();
MapImpl* map = (MapImpl*)*self;
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
@@ -201,18 +163,19 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
return true;
}
}
map.add_entry(hash, key, value, index);
map._add_entry(hash, key, value, index);
return false;
}
fn void! HashMap.remove(&map, Key key) @maydiscard
fn void! Map.remove(map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
if (!map || !((MapImpl*)map)._remove_entry_for_key(key)) return SearchResult.MISSING?;
}
fn void HashMap.clear(&map)
fn void Map.clear(self)
{
if (!map.count) return;
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return;
foreach (Entry** &entry_ref : map.table)
{
Entry* entry = *entry_ref;
@@ -222,30 +185,33 @@ fn void HashMap.clear(&map)
{
Entry *to_delete = next;
next = next.next;
map.free_entry(to_delete);
map._free_entry(to_delete);
}
map.free_entry(entry);
map._free_entry(entry);
*entry_ref = null;
}
map.count = 0;
}
fn void HashMap.free(&map)
fn void Map.free(self)
{
if (!map.allocator) return;
map.clear();
map.free_internal(map.table.ptr);
if (!self) return;
MapImpl* map = (MapImpl*)self;
self.clear();
map._free_internal(map.table.ptr);
map.table = {};
allocator::free(map.allocator, map);
}
fn Key[] HashMap.key_tlist(&map)
fn Key[] Map.temp_keys_list(map)
{
return map.key_new_list(allocator::temp()) @inline;
return map.new_keys_list(allocator::temp()) @inline;
}
fn Key[] HashMap.key_new_list(&map, Allocator* allocator = allocator::heap())
fn Key[] Map.new_keys_list(self, Allocator allocator = allocator::heap())
{
if (!map.count) return {};
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, map.count);
usz index = 0;
@@ -260,36 +226,36 @@ fn Key[] HashMap.key_new_list(&map, Allocator* allocator = allocator::heap())
return list;
}
macro HashMap.@each(map; @body(key, value))
macro Map.@each(map; @body(key, value))
{
map.@each_entry(; Entry* entry) {
@body(entry.key, entry.value);
};
}
macro HashMap.@each_entry(map; @body(entry))
macro Map.@each_entry(self; @body(entry))
{
if (map.count)
MapImpl *map = (MapImpl*)self;
if (!map || !map.count) return;
foreach (Entry* entry : map.table)
{
foreach (Entry* entry : map.table)
while (entry)
{
while (entry)
{
@body(entry);
entry = entry.next;
}
@body(entry);
entry = entry.next;
}
}
}
fn Value[] HashMap.value_tlist(&map)
fn Value[] Map.temp_values_list(map)
{
return map.value_new_list(allocator::temp()) @inline;
return map.new_values_list(allocator::temp()) @inline;
}
fn Value[] HashMap.value_new_list(&map, Allocator* allocator = allocator::heap())
fn Value[] Map.new_values_list(self, Allocator allocator = allocator::heap())
{
if (!map.count) return {};
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
@@ -303,9 +269,10 @@ fn Value[] HashMap.value_new_list(&map, Allocator* allocator = allocator::heap()
return list;
}
fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
fn bool Map.has_value(self, Value v) @if(VALUE_IS_EQUATABLE)
{
if (!map.count) return false;
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return false;
foreach (Entry* entry : map.table)
{
while (entry)
@@ -319,7 +286,7 @@ fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
// --- private methods
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
fn void MapImpl._add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
@@ -328,11 +295,11 @@ fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_ind
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
map._resize(map.table.len * 2);
}
}
fn void HashMap.resize(&map, uint new_capacity) @private
fn void MapImpl._resize(&map, uint new_capacity) @private
{
Entry*[] old_table = map.table;
uint old_capacity = old_table.len;
@@ -342,9 +309,9 @@ fn void HashMap.resize(&map, uint new_capacity) @private
return;
}
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
map.transfer(new_table);
map._transfer(new_table);
map.table = new_table;
map.free_internal(old_table.ptr);
map._free_internal(old_table.ptr);
map.threshold = (uint)(new_capacity * map.load_factor);
}
@@ -359,7 +326,7 @@ macro uint index_for(uint hash, uint capacity) @private
return hash & (capacity - 1);
}
fn void HashMap.transfer(&map, Entry*[] new_table) @private
fn void MapImpl._transfer(&map, Entry*[] new_table) @private
{
Entry*[] src = map.table;
uint new_capacity = new_table.len;
@@ -378,17 +345,18 @@ fn void HashMap.transfer(&map, Entry*[] new_table) @private
}
}
fn void HashMap.put_all_for_create(&map, HashMap* other_map) @private
fn void _init(MapImpl* impl, uint capacity, float load_factor, Allocator allocator) @private
{
if (!other_map.count) return;
foreach (Entry *e : other_map.table)
{
if (!e) continue;
map.put_for_create(e.key, e.value);
}
capacity = math::next_power_of_2(capacity);
*impl = {
.allocator = allocator,
.load_factor = load_factor,
.threshold = (uint)(capacity * load_factor),
.table = allocator::new_array(allocator, Entry*, capacity)
};
}
fn void HashMap.put_for_create(&map, Key key, Value value) @private
fn void MapImpl._put_for_create(&map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
@@ -400,16 +368,17 @@ fn void HashMap.put_for_create(&map, Key key, Value value) @private
return;
}
}
map.create_entry(hash, key, value, i);
map._create_entry(hash, key, value, i);
}
fn void HashMap.free_internal(&map, void* ptr) @inline @private
fn void MapImpl._free_internal(&map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
fn bool MapImpl._remove_entry_for_key(&map, Key key) @private
{
if (!map.count) return false;
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
Entry* prev = map.table[i];
@@ -428,7 +397,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
{
prev.next = next;
}
map.free_entry(e);
map._free_entry(e);
return true;
}
prev = e;
@@ -437,7 +406,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
return false;
}
fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
fn void MapImpl._create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
$if COPY_KEYS:
@@ -448,12 +417,12 @@ fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_i
map.count++;
}
fn void HashMap.free_entry(&self, Entry *entry) @local
fn void MapImpl._free_entry(&self, Entry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
$endif
self.free_internal(entry);
self._free_internal(entry);
}
struct Entry
@@ -462,4 +431,4 @@ struct Entry
Key key;
Value value;
Entry* next;
}
}

View File

@@ -11,7 +11,7 @@ const Object NULL_OBJECT = { .type = void*.typeid };
struct Object (Printable)
{
typeid type;
Allocator* allocator;
Allocator allocator;
union
{
uint128 i;
@@ -48,9 +48,9 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
return n;
case ObjectInternalMap:
usz n = formatter.printf("{")!;
@pool()
@stack_mem(1024; Allocator mem)
{
foreach (i, key : self.map.key_tlist())
foreach (i, key : self.map.key_new_list(mem))
{
if (i > 0) n += formatter.printf(",")!;
n += formatter.printf(`"%s":`, key)!;
@@ -76,7 +76,7 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
}
}
fn Object* new_obj(Allocator* allocator)
fn Object* new_obj(Allocator allocator)
{
return allocator::new(allocator, Object, { .allocator = allocator, .type = void.typeid });
}
@@ -86,22 +86,22 @@ fn Object* new_null()
return &NULL_OBJECT;
}
fn Object* new_int(int128 i, Allocator* allocator)
fn Object* new_int(int128 i, Allocator allocator)
{
return allocator::new(allocator, Object, { .i = i, .allocator = allocator, .type = int128.typeid });
}
macro Object* new_enum(e, Allocator* allocator)
macro Object* new_enum(e, Allocator allocator)
{
return allocator::new(allocator, Object, { .i = (int128)e, .allocator = allocator, .type = @typeid(e) });
}
fn Object* new_float(double f, Allocator* allocator)
fn Object* new_float(double f, Allocator allocator)
{
return allocator::new(allocator, Object, { .f = f, .allocator = allocator, .type = double.typeid });
}
fn Object* new_string(String s, Allocator* allocator)
fn Object* new_string(String s, Allocator allocator)
{
return allocator::new(allocator, Object, { .s = s.copy(allocator), .allocator = allocator, .type = String.typeid });
}
@@ -190,7 +190,6 @@ fn void Object.set_object(&self, String key, Object* new_object) @private
macro Object* Object.object_from_value(&self, value) @private
{
var $Type = $typeof(value);
$switch
$case types::is_int($Type):
return new_int(value, self.allocator);
@@ -234,10 +233,10 @@ macro Object* Object.set_at(&self, usz index, String key, value)
* @require self.is_indexable()
* @ensure return != null
**/
macro Object* Object.append(&self, value)
macro Object* Object.push(&self, value)
{
Object* val = self.object_from_value(value);
self.append_object(val);
self.push_object(val);
return val;
}
@@ -268,10 +267,10 @@ fn usz Object.get_len(&self)
/**
* @require self.is_indexable()
**/
fn void Object.append_object(&self, Object* to_append)
fn void Object.push_object(&self, Object* to_append)
{
self.init_array_if_needed();
self.array.append(to_append);
self.array.push(to_append);
}
/**
@@ -282,11 +281,11 @@ fn void Object.set_object_at(&self, usz index, Object* to_set)
self.init_array_if_needed();
while (self.array.len() < index)
{
self.array.append(&NULL_OBJECT);
self.array.push(&NULL_OBJECT);
}
if (self.array.len() == index)
{
self.array.append(to_set);
self.array.push(to_set);
return;
}
self.array.get(index).free();

View File

@@ -36,12 +36,7 @@ struct PrivatePriorityQueue (Printable)
Heap heap;
}
fn void PrivatePriorityQueue.init_new(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @inline @deprecated("Replaced by new_init")
{
return self.new_init(initial_capacity, allocator);
}
fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @inline
fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @inline
{
self.heap.new_init(initial_capacity, allocator);
}
@@ -51,11 +46,6 @@ fn void PrivatePriorityQueue.temp_init(&self, usz initial_capacity = 16) @inline
self.heap.new_init(initial_capacity, allocator::temp()) @inline;
}
fn void PrivatePriorityQueue.init_temp(&self, usz initial_capacity = 16) @inline @deprecated("Replaced by temp_init")
{
return self.temp_init(initial_capacity) @inline;
}
fn void PrivatePriorityQueue.push(&self, Type element)
{
self.heap.push(element);
@@ -117,7 +107,7 @@ fn Type! PrivatePriorityQueue.pop(&self)
return self.heap.pop();
}
fn Type! PrivatePriorityQueue.peek(&self)
fn Type! PrivatePriorityQueue.first(&self)
{
if (!self.len()) return IteratorResult.NO_MORE_ELEMENT?;
return self.heap.get(0);
@@ -141,7 +131,7 @@ fn bool PrivatePriorityQueue.is_empty(&self)
/**
* @require index < self.len()
*/
fn Type PrivatePriorityQueue.peek_at(&self, usz index) @operator([])
fn Type PrivatePriorityQueue.get(&self, usz index) @operator([])
{
return self.heap[index];
}
@@ -151,7 +141,7 @@ fn usz! PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic
return self.heap.to_format(formatter);
}
fn String PrivatePriorityQueue.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String PrivatePriorityQueue.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return self.heap.to_new_string(allocator);
}

View File

@@ -29,7 +29,7 @@ fn Type Range.get(&self, usz index) @operator([])
return (Type)(self.start + (usz)index);
}
fn String Range.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String Range.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return string::new_format("[%s..%s]", self.start, self.end, .allocator = allocator);
}
@@ -66,7 +66,7 @@ fn usz! ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic
return formatter.printf("[%s..<%s]", self.start, self.end)!;
}
fn String ExclusiveRange.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String ExclusiveRange.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return string::new_format("[%s..<%s]", self.start, self.end, .allocator = allocator);
}

View File

@@ -12,7 +12,7 @@ fn void RingBuffer.init(&self) @inline
*self = {};
}
fn void RingBuffer.putc(&self, Type c)
fn void RingBuffer.push(&self, Type c)
{
if (self.written < SIZE)
{
@@ -26,7 +26,7 @@ fn void RingBuffer.putc(&self, Type c)
}
}
fn Type RingBuffer.getc(&self, usz index)
fn Type RingBuffer.get(&self, usz index) @operator([])
{
index %= SIZE;
usz avail = SIZE - self.head;
@@ -37,7 +37,7 @@ fn Type RingBuffer.getc(&self, usz index)
return self.buf[index - avail];
}
fn Type! RingBuffer.popc(&self)
fn Type! RingBuffer.pop(&self)
{
switch
{
@@ -52,7 +52,7 @@ fn Type! RingBuffer.popc(&self)
}
}
fn usz RingBuffer.get(&self, usz index, Type[] buffer)
fn usz RingBuffer.read(&self, usz index, Type[] buffer)
{
index %= SIZE;
if (self.written < SIZE)
@@ -87,7 +87,7 @@ fn usz RingBuffer.get(&self, usz index, Type[] buffer)
return n1 + n2;
}
fn void RingBuffer.push(&self, Type[] buffer)
fn void RingBuffer.write(&self, Type[] buffer)
{
usz i;
while (self.written < SIZE && i < buffer.len)

View File

@@ -52,7 +52,7 @@ fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require size > 0
**/
fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
fn void*! ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
usz total_len = self.data.len;
@@ -65,7 +65,7 @@ fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
self.used = end;
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
header.size = size;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
@@ -75,7 +75,7 @@ fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
* @require old_pointer != null
* @require size > 0
**/
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment, usz offset) @dynamic
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
@@ -100,7 +100,7 @@ fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = self.acquire(size, false, alignment, 0)!;
void* mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -6,7 +6,7 @@ import std::math;
struct DynamicArenaAllocator (Allocator)
{
Allocator* backing_allocator;
Allocator backing_allocator;
DynamicArenaPage* page;
DynamicArenaPage* unused_page;
usz page_size;
@@ -16,7 +16,7 @@ struct DynamicArenaAllocator (Allocator)
* @param [&inout] allocator
* @require page_size >= 128
**/
fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator* allocator)
fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator allocator)
{
self.page = null;
self.unused_page = null;
@@ -24,12 +24,14 @@ fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator* allocator)
self.backing_allocator = allocator;
}
import std::io;
fn void DynamicArenaAllocator.free(&self)
{
DynamicArenaPage* page = self.page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
allocator::free(self.backing_allocator, page.memory);
allocator::free(self.backing_allocator, page);
page = next_page;
}
@@ -37,6 +39,7 @@ fn void DynamicArenaAllocator.free(&self)
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
allocator::free(self.backing_allocator, page.memory);
allocator::free(self.backing_allocator, page);
page = next_page;
}
@@ -77,7 +80,7 @@ fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
* @require old_pointer != null `Resize doesn't handle null pointers`
* @require self.page `tried to realloc pointer on invalid allocator`
*/
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
DynamicArenaPage* current_page = self.page;
alignment = alignment_for_allocation(alignment);
@@ -102,7 +105,7 @@ fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz a
current_page.used += add_size;
return old_pointer;
}
void* new_mem = self.acquire(size, false, alignment, 0)!;
void* new_mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}
@@ -131,8 +134,8 @@ fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
{
// First, make sure that we can align it, extending the page size if needed.
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof, alignment));
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment));
assert(page_size > size + DynamicArenaChunk.sizeof);
// Grab the page without alignment (we do it ourselves)
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
DynamicArenaPage*! page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
@@ -158,7 +161,7 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @loca
* @require size > 0 `acquire expects size > 0`
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
fn void*! DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = self.page;
@@ -195,6 +198,6 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignme
chunk.size = size;
return mem;
|}!;
if (clear) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
return ptr;
}

View File

@@ -21,16 +21,16 @@ fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
self.free_list = null;
}
fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
fn void*! SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
if (clear)
if (init_type == ZERO)
{
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
}
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
}
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
return alignment > 0
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)

View File

@@ -12,9 +12,9 @@ module std::core::mem::allocator @if(env::POSIX);
import std::os;
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (clear)
if (init_type == ZERO)
{
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
@@ -43,19 +43,9 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz
}
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (!new_bytes)
{
self.release(old_ptr, alignment > 0);
return null;
}
if (!old_ptr)
{
return self.acquire(new_bytes, false, alignment, 0);
}
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
void* new_ptr;
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return AllocationFailure.OUT_OF_MEMORY?;
@@ -83,13 +73,13 @@ module std::core::mem::allocator @if(env::WIN32);
import std::os::win32;
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (clear)
if (init_type == ZERO)
{
if (alignment > 0)
{
return win32::_aligned_recalloc(null, bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
return win32::_aligned_recalloc(null, 1, bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
}
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
@@ -101,7 +91,7 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz
return data;
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (alignment)
{
@@ -123,9 +113,9 @@ fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX);
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (clear)
if (init_type == ZERO)
{
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
return data ?: AllocationFailure.OUT_OF_MEMORY?;
@@ -142,7 +132,7 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (alignment)
{

View File

@@ -2,7 +2,7 @@ module std::core::mem::allocator;
struct OnStackAllocator (Allocator)
{
Allocator* backing_allocator;
Allocator backing_allocator;
char[] data;
usz used;
OnStackAllocatorExtraChunk* chunk;
@@ -20,7 +20,7 @@ struct OnStackAllocatorExtraChunk @local
* @param [&inout] allocator
* Initialize a memory arena for use using the provided bytes.
**/
fn void OnStackAllocator.init(&self, char[] data, Allocator* allocator)
fn void OnStackAllocator.init(&self, char[] data, Allocator allocator)
{
self.data = data;
self.backing_allocator = allocator;
@@ -103,18 +103,18 @@ fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a
* @require old_pointer != null
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
**/
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
if (!allocation_in_stack_mem(self, old_pointer))
{
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer);
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, 0)!;
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment)!;
}
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = self.acquire(size, false, alignment, 0)!;
void* mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
@@ -123,7 +123,7 @@ fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignm
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require size > 0
**/
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
fn void*! OnStackAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
bool aligned = alignment > 0;
alignment = alignment_for_allocation(alignment);
@@ -132,7 +132,7 @@ fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, u
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof ;
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(mem - self.data.ptr) + size;
Allocator* backing_allocator = self.backing_allocator;
Allocator backing_allocator = self.backing_allocator;
if (end > total_len)
{
@@ -140,7 +140,7 @@ fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, u
defer catch allocator::free(backing_allocator, chunk);
defer try self.chunk = chunk;
*chunk = { .prev = self.chunk, .is_aligned = aligned };
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, 0)!;
return chunk.data = backing_allocator.acquire(size, init_type, aligned ? alignment : 0)!;
}
self.used = end;
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;

View File

@@ -9,7 +9,7 @@ struct TempAllocatorChunk @local
struct TempAllocator (Allocator)
{
Allocator* backing_allocator;
Allocator backing_allocator;
TempAllocatorPage* last_page;
usz used;
usz capacity;
@@ -35,7 +35,7 @@ macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED ==
/**
* @require size >= 16
**/
fn TempAllocator*! new_temp_allocator(usz size, Allocator* allocator)
fn TempAllocator*! new_temp_allocator(usz size, Allocator allocator)
{
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
temp.last_page = null;
@@ -45,9 +45,11 @@ fn TempAllocator*! new_temp_allocator(usz size, Allocator* allocator)
return temp;
}
fn TempAllocator*! new_temp(usz size, Allocator* allocator) @deprecated("Use new_temp_allocator")
fn void TempAllocator.destroy(&self)
{
return new_temp_allocator(size, allocator);
self.reset(0);
if (self.last_page) (void)self._free_page(self.last_page);
allocator::free(self.backing_allocator, self);
}
fn usz TempAllocator.mark(&self) @dynamic => self.used;
@@ -58,6 +60,7 @@ fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
if (old_pointer + old_size == &self.data[self.used])
{
self.used -= old_size;
asan::poison_memory_region(&self.data[self.used], old_size);
}
}
fn void TempAllocator.reset(&self, usz mark) @dynamic
@@ -70,6 +73,19 @@ fn void TempAllocator.reset(&self, usz mark) @dynamic
self._free_page(to_free)!!;
}
self.last_page = last_page;
$if env::COMPILER_SAFE_MODE || env::ADDRESS_SANITIZER:
if (!last_page)
{
usz cleaned = self.used - mark;
if (cleaned > 0)
{
$if env::COMPILER_SAFE_MODE:
self.data[mark : cleaned] = 0xAA;
$endif
asan::poison_memory_region(&self.data[mark], cleaned);
}
}
$endif
self.used = mark;
}
@@ -94,13 +110,13 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = self.acquire(size, size > page_size, alignment, 0)!;
void* data = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
self.backing_allocator.release(real_pointer, page.is_aligned());
return data;
}
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz deprecated) @dynamic
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
{
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
if (chunk.size == (usz)-1)
@@ -111,8 +127,7 @@ fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, us
return self._realloc_page(page, size, alignment);
}
// TODO optimize last allocation
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, 0)!;
TempAllocatorChunk* data = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
@@ -123,7 +138,7 @@ fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, us
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
**/
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
void* start_mem = &self.data;
@@ -136,13 +151,14 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
}
usz new_usage = (usz)(mem - start_mem) + size;
// Arena alignment, simple!
// Arena allocation, simple!
if (new_usage <= self.capacity)
{
asan::unpoison_memory_region(starting_ptr, new_usage - self.used);
TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof;
chunk_start.size = size;
self.used = new_usage;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
@@ -154,7 +170,7 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
{
// This is actually simpler, since it will create the offset for us.
usz total_alloc_size = mem::aligned_offset(TempAllocatorPage.sizeof + size, alignment);
if (clear)
if (init_type == ZERO)
{
mem = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
}
@@ -171,7 +187,7 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
// Here we might need to pad
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
usz total_alloc_size = padded_header_size + size;
void* alloc = self.backing_allocator.acquire(total_alloc_size, clear, 0, 0)!;
void* alloc = self.backing_allocator.acquire(total_alloc_size, init_type, 0)!;
// Find the page.
page = alloc + padded_header_size - TempAllocatorPage.sizeof;

View File

@@ -20,7 +20,7 @@ def AllocMap = HashMap(<uptr, Allocation>);
// is not compatible with allocators that uses mark()
struct TrackingAllocator (Allocator)
{
Allocator* inner_allocator;
Allocator inner_allocator;
AllocMap map;
usz mem_total;
usz allocs_total;
@@ -31,7 +31,7 @@ struct TrackingAllocator (Allocator)
*
* @param [&inout] allocator "The allocator to track"
**/
fn void TrackingAllocator.init(&self, Allocator* allocator)
fn void TrackingAllocator.init(&self, Allocator allocator)
{
*self = { .inner_allocator = allocator };
self.map.new_init(.allocator = allocator);
@@ -69,7 +69,7 @@ fn usz TrackingAllocator.total_allocated(&self) => self.mem_total;
**/
fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total;
fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator* allocator)
fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator)
{
return self.map.value_tlist();
}
@@ -79,9 +79,9 @@ fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator* allocator)
**/
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
fn void*! TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
void* data = self.inner_allocator.acquire(size, clear, alignment, 0)!;
void* data = self.inner_allocator.acquire(size, init_type, alignment)!;
self.allocs_total++;
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
@@ -91,9 +91,9 @@ fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment,
return data;
}
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
void* data = self.inner_allocator.resize(old_pointer, size, alignment, 0)!;
void* data = self.inner_allocator.resize(old_pointer, size, alignment)!;
self.map.remove((uptr)old_pointer);
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
@@ -107,7 +107,7 @@ fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dy
{
if (catch self.map.remove((uptr)old_pointer))
{
assert(false, "Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
unreachable("Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
}
self.inner_allocator.release(old_pointer, is_aligned);
}
@@ -119,7 +119,7 @@ fn void TrackingAllocator.clear(&self)
fn void TrackingAllocator.print_report(&self) => self.fprint_report(io::stdout())!!;
fn void! TrackingAllocator.fprint_report(&self, OutStream* out)
fn void! TrackingAllocator.fprint_report(&self, OutStream out)
{
usz total = 0;

View File

@@ -45,17 +45,17 @@ macro rindex_of(array, element)
}
/**
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them.
* Concatenate two arrays or slices, returning a slice containing the concatenation of them.
*
* @param [in] arr1
* @param [in] arr2
* @param [&inout] allocator "The allocator to use, default is the heap allocator"
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
* @require @typekind(arr2) == SUBARRAY || @typekind(arr2) == ARRAY
* @require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
* @require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
* @ensure result.len == arr1.len + arr2.len
**/
macro concat_new(arr1, arr2, Allocator* allocator = allocator::heap())
macro concat_new(arr1, arr2, Allocator allocator = allocator::heap())
{
var $Type = $typeof(arr1[0]);
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
@@ -71,13 +71,13 @@ macro concat_new(arr1, arr2, Allocator* allocator = allocator::heap())
}
/**
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them,
* Concatenate two arrays or slices, returning a slice containing the concatenation of them,
* allocated using the temp allocator.
*
* @param [in] arr1
* @param [in] arr2
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
* @require @typekind(arr2) == SUBARRAY || @typekind(arr2) == ARRAY
* @require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
* @require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
* @ensure result.len == arr1.len + arr2.len
**/

View File

@@ -88,7 +88,7 @@ bitstruct UInt128LE : uint128 @littleendian
}
/**
* @require is_array_or_sub_of_char(bytes) "argument must be an array, a pointer to an array or a subarray of char"
* @require is_array_or_slice_of_char(bytes) "argument must be an array, a pointer to an array or a slice of char"
* @require is_bitorder($Type) "type must be a bitorder integer"
**/
macro read(bytes, $Type)
@@ -104,7 +104,7 @@ macro read(bytes, $Type)
}
/**
* @require is_arrayptr_or_sub_of_char(bytes) "argument must be a pointer to an array or a subarray of char"
* @require is_arrayptr_or_slice_of_char(bytes) "argument must be a pointer to an array or a slice of char"
* @require is_bitorder($Type) "type must be a bitorder integer"
**/
macro write(x, bytes, $Type)
@@ -144,7 +144,7 @@ macro is_bitorder($Type)
$endswitch
}
macro bool is_array_or_sub_of_char(bytes)
macro bool is_array_or_slice_of_char(bytes)
{
$switch (@typekind(bytes))
$case POINTER:
@@ -154,7 +154,7 @@ macro bool is_array_or_sub_of_char(bytes)
return $Inner2.typeid == char.typeid;
$endif
$case ARRAY:
$case SUBARRAY:
$case SLICE:
var $Inner = $typefrom($typeof(bytes).inner);
return $Inner.typeid == char.typeid;
$default:
@@ -162,7 +162,7 @@ macro bool is_array_or_sub_of_char(bytes)
$endswitch
}
macro bool is_arrayptr_or_sub_of_char(bytes)
macro bool is_arrayptr_or_slice_of_char(bytes)
{
$switch (@typekind(bytes))
$case POINTER:
@@ -171,7 +171,7 @@ macro bool is_arrayptr_or_sub_of_char(bytes)
var $Inner2 = $typefrom($Inner.inner);
return $Inner2.typeid == char.typeid;
$endif
$case SUBARRAY:
$case SLICE:
var $Inner = $typefrom($typeof(bytes).inner);
return $Inner.typeid == char.typeid;
$default:

View File

@@ -7,26 +7,19 @@ import libc, std::hash, std::io, std::os::backtrace;
/**
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
**/
fault IteratorResult
{
NO_MORE_ELEMENT
}
fault IteratorResult { NO_MORE_ELEMENT }
/**
* Use `SearchResult` when trying to return a value from some collection but the element is missing.
**/
fault SearchResult
{
MISSING
}
fault SearchResult { MISSING }
/**
* Use `CastResult` when an attempt at conversion fails.
**/
fault CastResult
{
TYPE_MISMATCH
}
fault CastResult { TYPE_MISMATCH }
def VoidFn = fn void();
/**
* Stores a variable on the stack, then restores it at the end of the
@@ -61,7 +54,7 @@ macro void @swap(&a, &b) @builtin
* @ensure @typeis(return, $Type*)
* @return! CastResult.TYPE_MISMATCH
**/
macro anycast(any* v, $Type) @builtin
macro anycast(any v, $Type) @builtin
{
if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?;
return ($Type*)v.ptr;
@@ -83,17 +76,18 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIV
foreach (i, &trace : backtrace)
{
if (i < backtraces_to_ignore) continue;
String inline_suffix = trace.is_inline ? " [inline]" : "";
if (trace.is_unknown())
{
io::eprintn(" in ???");
io::eprintfn(" in ???%s", inline_suffix);
continue;
}
if (trace.has_file())
{
io::eprintfn(" in %s (%s:%d) [%s]", trace.function, trace.file, trace.line, trace.object_file);
io::eprintfn(" in %s (%s:%d) [%s]%s", trace.function, trace.file, trace.line, trace.object_file, inline_suffix);
continue;
}
io::eprintfn(" in %s (source unavailable) [%s]", trace.function, trace.object_file);
io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix);
}
return true;
};
@@ -126,7 +120,7 @@ PanicFn panic = &default_panic;
fn void panicf(String fmt, String file, String function, uint line, args...)
{
@stack_mem(512; Allocator* allocator)
@stack_mem(512; Allocator allocator)
{
DString s;
s.new_init(.allocator = allocator);
@@ -142,7 +136,9 @@ fn void panicf(String fmt, String file, String function, uint line, args...)
**/
macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
$if env::COMPILER_SAFE_MODE:
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
$endif;
$$unreachable();
}
@@ -152,10 +148,18 @@ macro void unreachable(String string = "Unreachable statement reached.", ...) @b
**/
macro void unsupported(String string = "Unsupported function invoked") @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
$$unreachable();
}
/**
* Unconditionally break into an attached debugger when reached.
**/
macro void breakpoint() @builtin
{
$$breakpoint();
}
macro any_make(void* ptr, typeid type) @builtin
{
return $$any_make(ptr, type);
@@ -289,12 +293,12 @@ macro @prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write =
macro swizzle(v, ...) @builtin
{
return $$swizzle(v, $vasplat());
return $$swizzle(v, $vasplat);
}
macro swizzle2(v, v2, ...) @builtin
{
return $$swizzle2(v, v2, $vasplat());
return $$swizzle2(v, v2, $vasplat);
}
macro anyfault @catch(#expr) @builtin
@@ -314,6 +318,11 @@ macro char[] @as_char_view(&value) @builtin
return ((char*)value)[:$sizeof(*value)];
}
macro isz @str_find(String $string, String $needle) @builtin => $$str_find($string, $needle);
macro String @str_upper(String $str) @builtin => $$str_upper($str);
macro String @str_lower(String $str) @builtin => $$str_lower($str);
macro uint @str_hash(String $str) @builtin => $$str_hash($str);
macro uint int.hash(int i) => i;
macro uint uint.hash(uint i) => i;
macro uint short.hash(short s) => s;
@@ -330,6 +339,11 @@ macro uint String.hash(String c) => (uint)fnv32a::encode(c);
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
macro uint void*.hash(void* ptr) => ((ulong)(uptr)ptr).hash();
distinct EmptySlot = void*;
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
macro @is_empty_macro_slot(#arg) @builtin => @typeis(#arg, EmptySlot);
macro @is_valid_macro_slot(#arg) @builtin => !@typeis(#arg, EmptySlot);
const MAX_FRAMEADDRESS = 128;
/**
* @require n >= 0
@@ -662,7 +676,7 @@ fn void install_signal_handler(CInt signal, SignalFunction func) @local
}
// Clean this up
fn void install_signal_handlers() @init(101) @local
fn void install_signal_handlers() @init(101) @local @if(env::BACKTRACE)
{
install_signal_handler(libc::SIGBUS, &sig_bus_error);
install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);

View File

@@ -4,7 +4,7 @@
module std::core::builtin;
/**
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
* @require types::@comparable_value(a) && types::@comparable_value(b)
**/
macro less(a, b) @builtin
{
@@ -19,7 +19,7 @@ macro less(a, b) @builtin
}
/**
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
* @require types::@comparable_value(a) && types::@comparable_value(b)
**/
macro less_eq(a, b) @builtin
{
@@ -34,7 +34,7 @@ macro less_eq(a, b) @builtin
}
/**
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
* @require types::@comparable_value(a) && types::@comparable_value(b)
**/
macro greater(a, b) @builtin
{
@@ -49,7 +49,7 @@ macro greater(a, b) @builtin
}
/**
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
* @require types::@comparable_value(a) && types::@comparable_value(b)
**/
macro int compare_to(a, b) @builtin
{
@@ -63,7 +63,7 @@ macro int compare_to(a, b) @builtin
$endswitch
}
/**
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
* @require types::@comparable_value(a) && types::@comparable_value(b)
**/
macro greater_eq(a, b) @builtin
{
@@ -78,7 +78,7 @@ macro greater_eq(a, b) @builtin
}
/**
* @require types::is_equatable_value(a) && types::is_equatable_value(b) `values must be equatable`
* @require types::@equatable_value(a) && types::@equatable_value(b) `values must be equatable`
**/
macro bool equals(a, b) @builtin
{
@@ -97,13 +97,13 @@ macro bool equals(a, b) @builtin
macro min(x, ...) @builtin
{
$if $vacount == 1:
return less(x, $vaarg(0)) ? x : $vaarg(0);
return less(x, $vaarg[0]) ? x : $vaarg[0];
$else
var result = x;
$for (var $i = 0; $i < $vacount; $i++)
if (less($vaarg($i), result))
if (less($vaarg[$i], result))
{
result = $vaarg($i);
result = $vaarg[$i];
}
$endfor
return result;
@@ -113,13 +113,13 @@ macro min(x, ...) @builtin
macro max(x, ...) @builtin
{
$if $vacount == 1:
return greater(x, $vaarg(0)) ? x : $vaarg(0);
return greater(x, $vaarg[0]) ? x : $vaarg[0];
$else
var result = x;
$for (var $i = 0; $i < $vacount; $i++)
if (greater($vaarg($i), result))
if (greater($vaarg[$i], result))
{
result = $vaarg($i);
result = $vaarg[$i];
}
$endfor
return result;

View File

@@ -108,14 +108,14 @@ fn usz char32_to_utf8_unsafe(Char32 c, char** output)
{
switch
{
case c < 0x7f:
case c <= 0x7f:
(*output)++[0] = (char)c;
return 1;
case c < 0x7ff:
case c <= 0x7ff:
(*output)++[0] = (char)(0xC0 | c >> 6);
(*output)++[0] = (char)(0x80 | (c & 0x3F));
return 2;
case c < 0xffff:
case c <= 0xffff:
(*output)++[0] = (char)(0xE0 | c >> 12);
(*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F));
(*output)++[0] = (char)(0x80 | (c & 0x3F));
@@ -210,11 +210,11 @@ fn usz utf8len_for_utf32(Char32[] utf32)
{
switch (true)
{
case uc < 0x7f:
case uc <= 0x7f:
len++;
case uc < 0x7ff:
case uc <= 0x7ff:
len += 2;
case uc < 0xffff:
case uc <= 0xffff:
len += 3;
default:
len += 4;
@@ -237,12 +237,12 @@ fn usz utf8len_for_utf16(Char16[] utf16)
Char16 c = utf16[i];
if (c & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
{
if (c < 0x7f)
if (c <= 0x7f)
{
len++;
continue;
}
if (c < 0x7ff)
if (c <= 0x7ff)
{
len += 2;
continue;

View File

@@ -8,7 +8,7 @@ const usz MIN_CAPACITY @private = 16;
/**
* @require !self.data() "String already initialized"
**/
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator* allocator = allocator::heap())
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator allocator = allocator::heap())
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
@@ -18,14 +18,6 @@ fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator* alloc
return *self = (DString)data;
}
/**
* @require !self.data() "String already initialized"
**/
fn DString DString.init_new(&self, usz capacity = MIN_CAPACITY, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(capacity, allocator) @inline;
}
/**
* @require !self.data() "String already initialized"
**/
@@ -35,22 +27,14 @@ fn DString DString.temp_init(&self, usz capacity = MIN_CAPACITY)
return *self;
}
/**
* @require !self.data() "String already initialized"
**/
fn DString DString.init_temp(&self, usz capacity = MIN_CAPACITY) @deprecated("Replaced by temp_init")
{
return self.temp_init(capacity) @inline;
}
fn DString new_with_capacity(usz capacity, Allocator* allocator = allocator::heap())
fn DString new_with_capacity(usz capacity, Allocator allocator = allocator::heap())
{
return DString{}.new_init(capacity, allocator);
}
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, allocator::temp()) @inline;
fn DString new(String c = "", Allocator* allocator = allocator::heap())
fn DString new(String c = "", Allocator allocator = allocator::heap())
{
usz len = c.len;
StringData* data = (StringData*)new_with_capacity(len, allocator);
@@ -64,7 +48,57 @@ fn DString new(String c = "", Allocator* allocator = allocator::heap())
fn DString temp_new(String s = "") => new(s, allocator::temp()) @inline;
fn DString DString.new_concat(self, DString b, Allocator* allocator = allocator::heap())
fn void DString.replace_char(self, char ch, char replacement)
{
StringData* data = self.data();
foreach (&c : data.chars[:data.len])
{
if (*c == ch) *c = replacement;
}
}
fn void DString.replace(&self, String needle, String replacement)
{
StringData* data = self.data();
usz needle_len = needle.len;
if (!data || data.len < needle_len) return;
usz replace_len = replacement.len;
if (needle_len == 1 && replace_len == 1)
{
self.replace_char(needle[0], replacement[0]);
return;
}
@pool(data.allocator) {
String str = self.tcopy_str();
self.clear();
usz len = str.len;
usz match = 0;
foreach (i, c : str)
{
if (c == needle[match])
{
match++;
if (match == needle_len)
{
self.append_chars(replacement);
match = 0;
continue;
}
continue;
}
if (match > 0)
{
self.append_chars(str[i - match:match]);
match = 0;
}
self.append_char(c);
}
if (match > 0) self.append_chars(str[^match:match]);
};
}
fn DString DString.new_concat(self, DString b, Allocator allocator = allocator::heap())
{
DString string;
string.new_init(self.len() + b.len(), allocator);
@@ -75,8 +109,6 @@ fn DString DString.new_concat(self, DString b, Allocator* allocator = allocator:
fn DString DString.temp_concat(self, DString b) => self.new_concat(b, allocator::temp());
fn DString DString.new_tconcat(self, DString b) @deprecated("Replaced by temp_concat") => self.new_concat(b, allocator::temp());
fn ZString DString.zstr_view(&self)
{
StringData* data = self.data();
@@ -166,7 +198,7 @@ fn void DString.append_char32(&self, Char32 c)
fn DString DString.tcopy(&self) => self.copy(allocator::temp());
fn DString DString.copy(self, Allocator* allocator = null)
fn DString DString.copy(self, Allocator allocator = null)
{
if (!self)
{
@@ -180,7 +212,7 @@ fn DString DString.copy(self, Allocator* allocator = null)
return new_string;
}
fn ZString DString.copy_zstr(self, Allocator* allocator = allocator::heap())
fn ZString DString.copy_zstr(self, Allocator allocator = allocator::heap())
{
usz str_len = self.len();
if (!str_len)
@@ -194,7 +226,7 @@ fn ZString DString.copy_zstr(self, Allocator* allocator = allocator::heap())
return (ZString)zstr;
}
fn String DString.copy_str(self, Allocator* allocator = allocator::heap())
fn String DString.copy_str(self, Allocator allocator = allocator::heap())
{
return (String)self.copy_zstr(allocator)[:self.len()];
}
@@ -258,7 +290,7 @@ fn void DString.append_chars(&self, String str)
data.len += other_len;
}
fn Char32[] DString.copy_utf32(&self, Allocator* allocator = allocator::heap())
fn Char32[] DString.copy_utf32(&self, Allocator allocator = allocator::heap())
{
return self.str_view().to_new_utf32(allocator) @inline!!;
}
@@ -389,21 +421,29 @@ fn void DString.insert_at(&self, usz index, String s)
fn usz! DString.appendf(&self, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, self);
return formatter.vprintf(format, args);
if (!self.data()) self.new_init(format.len + 20);
@pool(self.data().allocator)
{
Formatter formatter;
formatter.init(&out_string_append_fn, self);
return formatter.vprintf(format, args);
};
}
fn usz! DString.appendfn(&self, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, self);
usz len = formatter.vprintf(format, args)!;
self.append('\n');
return len + 1;
if (!self.data()) self.new_init(format.len + 20);
@pool(self.data().allocator)
{
Formatter formatter;
formatter.init(&out_string_append_fn, self);
usz len = formatter.vprintf(format, args)!;
self.append('\n');
return len + 1;
};
}
fn DString new_join(String[] s, String joiner, Allocator* allocator = allocator::heap())
fn DString new_join(String[] s, String joiner, Allocator allocator = allocator::heap())
{
if (!s.len) return (DString)null;
usz total_size = joiner.len * s.len;
@@ -450,7 +490,7 @@ fn void DString.reserve(&self, usz addition)
*self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity);
}
fn usz! DString.read_from_stream(&self, InStream* reader)
fn usz! DString.read_from_stream(&self, InStream reader)
{
if (&reader.available)
{
@@ -482,7 +522,7 @@ fn usz! DString.read_from_stream(&self, InStream* reader)
struct StringData @private
{
Allocator* allocator;
Allocator allocator;
usz len;
usz capacity;
char[*] chars;

View File

@@ -112,10 +112,13 @@ enum ArchType
WASM64, // WebAssembly with 64-bit pointers
RSCRIPT32, // 32-bit RenderScript
RSCRIPT64, // 64-bit RenderScript
XTENSA, // Xtensa
}
const OsType OS_TYPE = (OsType)$$OS_TYPE;
const ArchType ARCH_TYPE = (ArchType)$$ARCH_TYPE;
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
const bool NO_LIBC = !$$COMPILER_LIBC_AVAILABLE;
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)$$COMPILER_OPT_LEVEL;
@@ -123,8 +126,10 @@ const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED;
const REGISTER_SIZE = $$REGISTER_SIZE;
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;
const bool DEBUG_SYMBOLS = $$DEBUG_SYMBOLS;
const bool BACKTRACE = $$BACKTRACE;
const usz LLVM_VERSION = $$LLVM_VERSION;
const bool BENCHMARKING = $$BENCHMARKING;
const bool TESTING = $$TESTING;
@@ -143,8 +148,12 @@ const bool FREEBSD = LIBC && OS_TYPE == FREEBSD;
const bool NETBSD = LIBC && OS_TYPE == NETBSD;
const bool WASI = LIBC && OS_TYPE == WASI;
const bool WASM_NOLIBC @builtin = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
const bool ADDRESS_SANITIZER = $$ADDRESS_SANITIZER;
const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER;
const bool THREAD_SANITIZER = $$THREAD_SANITIZER;
const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER;
macro bool os_is_darwin()
macro bool os_is_darwin() @const
{
$switch (OS_TYPE)
$case IOS:
@@ -157,7 +166,7 @@ macro bool os_is_darwin()
$endswitch
}
macro bool os_is_posix()
macro bool os_is_posix() @const
{
$switch (OS_TYPE)
$case IOS:

View File

@@ -8,6 +8,10 @@ import std::math;
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
macro bool @constant_is_power_of_2($x) @const @private
{
return $x != 0 && ($x & ($x - 1)) == 0;
}
/**
* Load a vector from memory according to a mask assuming default alignment.
@@ -37,7 +41,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru)
* @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
* @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
* @require passthru.len == mask.len : "Mask and passthru must have the same length"
* @require math::is_power_of_2($alignment) : "The alignment must be a power of two"
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
*
* @return "A vector with the loaded values where the mask is true, passthru where the mask is false"
**/
@@ -80,7 +84,7 @@ macro gather(ptrvec, bool[<*>] mask, passthru)
* @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
* @require passthru.len == mask.len : "Mask and passthru must have the same length"
* @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
* @require math::is_power_of_2($alignment) : "The alignment must be a power of two"
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
*
* @return "A vector with the loaded values where the mask is true, passthru where the mask is false"
**/
@@ -115,7 +119,7 @@ macro masked_store(ptr, value, bool[<*>] mask)
* @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
* @require @typekind(value) == VECTOR : "Expected value to be a vector"
* @require value.len == mask.len : "Mask and value must have the same length"
* @require math::is_power_of_2($alignment) : "The alignment must be a power of two"
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
*
**/
macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment)
@@ -150,13 +154,39 @@ macro scatter(ptrvec, value, bool[<*>] mask)
* @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
* @require value.len == mask.len : "Mask and value must have the same length"
* @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
* @require math::is_power_of_2($alignment) : "The alignment must be a power of two"
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
**/
macro @scatter_aligned(ptrvec, value, bool[<*>] mask, usz $alignment)
{
return $$scatter(ptrvec, value, mask, $alignment);
}
/**
* @param [in] x "The variable or dereferenced pointer to load."
* @param $alignment "The alignment to assume for the load"
* @return "The value of x"
*
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
**/
macro @unaligned_load(&x, usz $alignment) @builtin
{
return $$unaligned_load(x, $alignment);
}
/**
* @param [out] x "The variable or dereferenced pointer to store to."
* @param value "The value to store."
* @param $alignment "The alignment to assume for the store"
* @return "The value of x"
*
* @require $assignable(value, $typeof(*x)) : "The value doesn't match the variable"
* @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
**/
macro @unaligned_store(&x, value, usz $alignment) @builtin
{
return $$unaligned_store(x, ($typeof(*x))value, $alignment);
}
macro @volatile_load(&x) @builtin
{
return $$volatile_load(x);
@@ -343,9 +373,9 @@ macro void set_inline(void* dst, char val, usz $len, usz $dst_align = 0, bool $i
$$memset_inline(dst, val, $len, $is_volatile, $dst_align);
}
/**
* @require values::@inner_kind(a) == TypeKind.SUBARRAY || values::@inner_kind(a) == TypeKind.POINTER
* @require values::@inner_kind(b) == TypeKind.SUBARRAY || values::@inner_kind(b) == TypeKind.POINTER
* @require values::@inner_kind(a) != TypeKind.SUBARRAY || len == -1
* @require values::@inner_kind(a) == TypeKind.SLICE || values::@inner_kind(a) == TypeKind.POINTER
* @require values::@inner_kind(b) == TypeKind.SLICE || values::@inner_kind(b) == TypeKind.POINTER
* @require values::@inner_kind(a) != TypeKind.SLICE || len == -1
* @require values::@inner_kind(a) != TypeKind.POINTER || len > -1
* @require values::@assign_to(a, b) && values::@assign_to(b, a)
**/
@@ -356,7 +386,7 @@ macro bool equals(a, b, isz len = -1, usz $align = 0)
$endif
void* x @noinit;
void* y @noinit;
$if values::@inner_kind(a) == TypeKind.SUBARRAY:
$if values::@inner_kind(a) == TypeKind.SLICE:
len = a.len;
if (len != b.len) return false;
x = a.ptr;
@@ -403,9 +433,9 @@ macro type_alloc_must_be_aligned($Type)
/**
* Run with a specific allocator inside of the macro body.
**/
macro void @scoped(Allocator* allocator; @body())
macro void @scoped(Allocator allocator; @body())
{
Allocator* old_allocator = allocator::thread_allocator;
Allocator old_allocator = allocator::thread_allocator;
allocator::thread_allocator = allocator;
defer allocator::thread_allocator = old_allocator;
@body();
@@ -415,7 +445,7 @@ macro void @report_heap_allocs_in_scope(;@body())
{
TrackingAllocator tracker;
tracker.init(allocator::thread_allocator);
Allocator* old_allocator = allocator::thread_allocator;
Allocator old_allocator = allocator::thread_allocator;
allocator::thread_allocator = &tracker;
defer
{
@@ -426,7 +456,7 @@ macro void @report_heap_allocs_in_scope(;@body())
@body();
}
macro void @stack_mem(usz $size; @body(Allocator* mem)) @builtin
macro void @stack_mem(usz $size; @body(Allocator mem)) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
@@ -498,19 +528,11 @@ macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
@body();
}
import libc;
macro TempAllocator* temp() @deprecated("Use allocator::temp()") => allocator::temp();
macro Allocator* current_allocator() @deprecated("Use allocator::heap()") => allocator::heap();
macro Allocator* heap() @deprecated("Use allocator::heap()") => allocator::heap();
module std::core::mem @if(WASM_NOLIBC);
import std::core::mem::allocator @public;
SimpleHeapAllocator wasm_allocator @private;
extern int __heap_base;
@@ -521,8 +543,8 @@ fn void initialize_wasm_mem() @init(1) @private
uptr start = (uptr)&__heap_base;
if (start > mem::DEFAULT_MEM_ALIGNMENT) allocator::wasm_memory.use = start;
wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x));
temp_base_allocator = &wasm_allocator;
allocator::thread_allocator = &wasm_allocator;
allocator::init_default_temp_allocators();
}
module std::core::mem;
@@ -552,15 +574,24 @@ fn void* malloc(usz size) @builtin @inline @nodiscard
return allocator::malloc(allocator::heap(), size);
}
fn void* tmalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
fn void* malloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
{
return allocator::malloc_aligned(allocator::heap(), size, alignment)!!;
}
fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
{
if (!size) return null;
return allocator::temp().acquire(size, false, alignment, 0)!!;
return allocator::temp().acquire(size, NO_ZERO, alignment)!!;
}
/**
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
**/
macro new($Type, ...) @nodiscard
@@ -569,7 +600,7 @@ macro new($Type, ...) @nodiscard
return ($Type*)calloc($Type.sizeof);
$else
$Type* val = malloc($Type.sizeof);
*val = $vaexpr(0);
*val = $vaexpr[0];
return val;
$endif
}
@@ -578,7 +609,7 @@ macro new($Type, ...) @nodiscard
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
**/
macro new_aligned($Type, ...) @nodiscard
{
@@ -586,7 +617,7 @@ macro new_aligned($Type, ...) @nodiscard
return ($Type*)calloc_aligned($Type.sizeof, $Type.alignof);
$else
$Type* val = malloc_aligned($Type.sizeof, $Type.alignof);
*val = $vaexpr(0);
*val = $vaexpr[0];
return val;
$endif
}
@@ -608,19 +639,9 @@ macro alloc_aligned($Type) @nodiscard
return ($Type*)malloc_aligned($Type.sizeof, $Type.alignof);
}
macro new_clear($Type) @deprecated("Use mem::new")
{
return new($Type);
}
macro new_temp($Type) @deprecated("Use mem::temp_alloc or mem::temp_new")
{
return tmalloc($Type.sizeof);
}
/**
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
**/
macro temp_new($Type, ...) @nodiscard
{
@@ -628,7 +649,7 @@ macro temp_new($Type, ...) @nodiscard
return ($Type*)tcalloc($Type.sizeof) @inline;
$else
$Type* val = tmalloc($Type.sizeof) @inline;
*val = $vaexpr(0);
*val = $vaexpr[0];
return val;
$endif
}
@@ -638,12 +659,6 @@ macro temp_alloc($Type) @nodiscard
return tmalloc($Type.sizeof);
}
macro new_temp_clear($Type) @deprecated("use mem::temp_new")
{
return tcalloc($Type.sizeof);
}
/**
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
@@ -676,12 +691,7 @@ macro alloc_array($Type, usz elements) @nodiscard
**/
macro alloc_array_aligned($Type, usz elements) @nodiscard
{
return allocator::alloc_array(allocator::heap(), $Type, elements);
}
macro talloc_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array")
{
return temp_alloc_array($Type, elements);
return allocator::alloc_array_aligned(allocator::heap(), $Type, elements);
}
macro temp_alloc_array($Type, usz elements) @nodiscard
@@ -689,40 +699,29 @@ macro temp_alloc_array($Type, usz elements) @nodiscard
return (($Type*)tmalloc($Type.sizeof * elements, $Type.alignof))[:elements];
}
macro temp_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array")
{
return temp_alloc_array($Type, elements);
}
macro temp_new_array($Type, usz elements) @nodiscard
{
return (($Type*)tcalloc($Type.sizeof * elements, $Type.alignof))[:elements];
}
macro new_zero_array($Type, usz elements) @deprecated("Use new_array")
{
return new_array($Type, elements);
}
macro temp_zero_array($Type, usz elements) @deprecated("Use temp_new_array")
{
return temp_new_array($Type, elements);
}
fn void* calloc(usz size) @builtin @inline @nodiscard
{
return allocator::calloc(allocator::heap(), size);
}
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
fn void* calloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
{
return allocator::calloc_aligned(allocator::heap(), size, alignment)!!;
}
fn void* tcalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
fn void* tcalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
{
if (!size) return null;
return allocator::temp().acquire(size, false, alignment, 0)!!;
return allocator::temp().acquire(size, ZERO, alignment)!!;
}
fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
@@ -749,6 +748,6 @@ fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMEN
{
if (!size) return null;
if (!ptr) return tmalloc(size, alignment);
return allocator::temp().resize(ptr, size, alignment, 0)!!;
return allocator::temp().resize(ptr, size, alignment)!!;
}

View File

@@ -10,6 +10,11 @@ struct TrackingEnv
uint line;
}
enum AllocInitType
{
NO_ZERO,
ZERO
}
interface Allocator
{
@@ -18,18 +23,16 @@ interface Allocator
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset == 0 `offset no longer supported`
* @require size > 0
**/
fn void*! acquire(usz size, bool clear, usz alignment, usz offset);
fn void*! acquire(usz size, AllocInitType init_type, usz alignment = 0);
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset == 0 `offset no longer supported`
* @require ptr != null
* @require new_size > 0
**/
fn void*! resize(void* ptr, usz new_size, usz alignment, usz offset);
fn void*! resize(void* ptr, usz new_size, usz alignment = 0);
/**
* @require ptr != null
**/
@@ -46,54 +49,54 @@ fault AllocationFailure
fn usz alignment_for_allocation(usz alignment) @inline @private
{
return alignment < mem::DEFAULT_MEM_ALIGNMENT ? alignment = mem::DEFAULT_MEM_ALIGNMENT : alignment;
return alignment < mem::DEFAULT_MEM_ALIGNMENT ? mem::DEFAULT_MEM_ALIGNMENT : alignment;
}
macro void* malloc(Allocator* allocator, usz size) @nodiscard
macro void* malloc(Allocator allocator, usz size) @nodiscard
{
return malloc_try(allocator, size)!!;
}
macro void*! malloc_try(Allocator* allocator, usz size) @nodiscard
macro void*! malloc_try(Allocator allocator, usz size) @nodiscard
{
if (!size) return null;
$if env::TESTING:
char* data = allocator.acquire(size, false, 0, 0)!;
char* data = allocator.acquire(size, NO_ZERO)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
return allocator.acquire(size, false, 0, 0);
return allocator.acquire(size, NO_ZERO);
$endif
}
macro void* calloc(Allocator* allocator, usz size) @nodiscard
macro void* calloc(Allocator allocator, usz size) @nodiscard
{
return calloc_try(allocator, size)!!;
}
macro void*! calloc_try(Allocator* allocator, usz size) @nodiscard
macro void*! calloc_try(Allocator allocator, usz size) @nodiscard
{
if (!size) return null;
return allocator.acquire(size, true, 0, 0);
return allocator.acquire(size, ZERO);
}
macro void* realloc(Allocator* allocator, void* ptr, usz new_size) @nodiscard
macro void* realloc(Allocator allocator, void* ptr, usz new_size) @nodiscard
{
return realloc_try(allocator, ptr, new_size)!!;
}
macro void*! realloc_try(Allocator* allocator, void* ptr, usz new_size) @nodiscard
macro void*! realloc_try(Allocator allocator, void* ptr, usz new_size) @nodiscard
{
if (!new_size)
{
free(allocator, ptr);
return null;
}
if (!ptr) return allocator.acquire(new_size, false, 0, 0);
return allocator.resize(ptr, new_size, 0, 0);
if (!ptr) return allocator.acquire(new_size, NO_ZERO);
return allocator.resize(ptr, new_size);
}
macro void free(Allocator* allocator, void* ptr)
macro void free(Allocator allocator, void* ptr)
{
if (!ptr) return;
$if env::TESTING:
@@ -102,25 +105,25 @@ macro void free(Allocator* allocator, void* ptr)
allocator.release(ptr, false);
}
macro void*! malloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
macro void*! malloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard
{
if (!size) return null;
$if env::TESTING:
char* data = allocator.acquire(size, false, alignment, offset)!;
char* data = allocator.acquire(size, NO_ZERO, alignment)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
return allocator.acquire(size, false, alignment, offset);
return allocator.acquire(size, NO_ZERO, alignment);
$endif
}
macro void*! calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
macro void*! calloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard
{
if (!size) return null;
return allocator.acquire(size, true, alignment, offset);
return allocator.acquire(size, ZERO, alignment);
}
macro void*! realloc_aligned(Allocator* allocator, void* ptr, usz new_size, usz alignment, usz offset = 0) @nodiscard
macro void*! realloc_aligned(Allocator allocator, void* ptr, usz new_size, usz alignment) @nodiscard
{
if (!new_size)
{
@@ -131,104 +134,164 @@ macro void*! realloc_aligned(Allocator* allocator, void* ptr, usz new_size, usz
{
return malloc_aligned(allocator, new_size, alignment);
}
return allocator.resize(ptr, new_size, alignment, offset);
return allocator.resize(ptr, new_size, alignment);
}
macro void free_aligned(Allocator* allocator, void* ptr)
macro void free_aligned(Allocator allocator, void* ptr)
{
if (!ptr) return;
$if env::TESTING:
((char*)ptr)[0] = 0xBA;
$endif
allocator.release(ptr, true);
allocator.release(ptr, .aligned = true);
}
/**
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
**/
macro new(Allocator* allocator, $Type, ...) @nodiscard
macro new(Allocator allocator, $Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc(allocator, $Type.sizeof);
$else
$Type* val = malloc(allocator, $Type.sizeof);
*val = $vaexpr(0);
*val = $vaexpr[0];
return val;
$endif
}
/**
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
**/
macro new_try(Allocator* allocator, $Type, ...) @nodiscard
macro new_try(Allocator allocator, $Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc_try(allocator, $Type.sizeof);
$else
$Type* val = malloc_try(allocator, $Type.sizeof)!;
*val = $vaexpr(0);
*val = $vaexpr[0];
return val;
$endif
}
macro new_with_padding(Allocator* allocator, $Type, usz padding) @nodiscard
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
* @require $vacount < 2 : "Too many arguments."
* @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
**/
macro new_aligned($Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc_aligned(allocator, $Type.sizeof, $Type.alignof);
$else
$Type* val = malloc_aligned(allocator, $Type.sizeof, $Type.alignof);
*val = $vaexpr[0];
return val;
$endif
}
/**
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT
**/
macro new_with_padding(Allocator allocator, $Type, usz padding) @nodiscard
{
return ($Type*)calloc_try(allocator, $Type.sizeof + padding);
}
macro alloc(Allocator* allocator, $Type) @nodiscard
/**
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
**/
macro alloc(Allocator allocator, $Type) @nodiscard
{
return ($Type*)malloc(allocator, $Type.sizeof);
}
macro alloc_try(Allocator* allocator, $Type) @nodiscard
/**
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
**/
macro alloc_try(Allocator allocator, $Type) @nodiscard
{
return ($Type*)malloc_try(allocator, $Type.sizeof);
}
macro alloc_with_padding(Allocator* allocator, $Type, usz padding) @nodiscard
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
macro alloc_aligned(Allocator allocator, $Type) @nodiscard
{
return ($Type*)malloc_aligned(allocator, $Type.sizeof, $Type.alignof);
}
/**
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT
**/
macro alloc_with_padding(Allocator allocator, $Type, usz padding) @nodiscard
{
return ($Type*)malloc_try(allocator, $Type.sizeof + padding);
}
macro new_array(Allocator* allocator, $Type, usz elements) @nodiscard
/**
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
**/
macro new_array(Allocator allocator, $Type, usz elements) @nodiscard
{
return new_array_try(allocator, $Type, elements)!!;
}
macro new_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
/**
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
**/
macro new_array_try(Allocator allocator, $Type, usz elements) @nodiscard
{
return (($Type*)calloc_try(allocator, $Type.sizeof * elements))[:elements];
}
macro new_array_aligned(Allocator* allocator, $Type, usz elements) @nodiscard
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
macro new_array_aligned(Allocator allocator, $Type, usz elements) @nodiscard
{
return ((Type*)calloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
return (($Type*)calloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
}
macro alloc_array(Allocator* allocator, $Type, usz elements) @nodiscard
/**
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
**/
macro alloc_array(Allocator allocator, $Type, usz elements) @nodiscard
{
return alloc_array_try(allocator, $Type, elements)!!;
}
macro alloc_array_aligned(Allocator* allocator, $Type, usz elements) @nodiscard
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
macro alloc_array_aligned(Allocator allocator, $Type, usz elements) @nodiscard
{
return ((Type*)malloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
return (($Type*)malloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
}
macro alloc_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
/**
* @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
**/
macro alloc_array_try(Allocator allocator, $Type, usz elements) @nodiscard
{
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
}
macro clone(Allocator* allocator, value) @nodiscard
macro clone(Allocator allocator, value) @nodiscard
{
return new(allocator, $typeof(value), value);
}
fn any* clone_any(Allocator* allocator, any* value) @nodiscard
fn any clone_any(Allocator allocator, any value) @nodiscard
{
usz size = value.type.sizeof;
void* data = malloc(allocator, size);
@@ -236,106 +299,6 @@ fn any* clone_any(Allocator* allocator, any* value) @nodiscard
return any_make(data, value.type);
}
// Allocator "functions"
macro void*! Allocator.alloc_checked(&self, usz size) @deprecated("Use allocator::malloc_try")
{
return malloc_try(self, size);
}
macro void*! Allocator.calloc_checked(&self, usz size) @deprecated("Use allocator::calloc_try")
{
return calloc_try(self, size);
}
macro void*! Allocator.realloc_checked(&self, void* ptr, usz new_size) @deprecated("Use allocator::realloc_try")
{
return realloc_try(ptr, new_size);
}
macro Allocator.new_array(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::alloc_array")
{
return (($Type*)self.alloc_checked($Type.sizeof * size + end_padding))[:size]!!;
}
macro Allocator.new_array_checked(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::alloc_array_try")
{
return (($Type*)self.alloc_checked($Type.sizeof * size + end_padding))[:size];
}
macro Allocator.new_zero_array(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::new_array")
{
return (($Type*)self.calloc_checked($Type.sizeof * size + end_padding))[:size]!!;
}
macro Allocator.new_zero_array_checked(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::new_array_try")
{
return (($Type*)self.calloc_checked($Type.sizeof * size + end_padding))[:size];
}
macro Allocator.new(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::alloc")
{
return ($Type*)self.alloc_checked($Type.sizeof + end_padding)!!;
}
macro Allocator.new_checked(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::alloc_try")
{
return ($Type*)self.alloc_checked($Type.sizeof + end_padding);
}
macro Allocator.new_clear(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::new")
{
return ($Type*)self.calloc_checked($Type.sizeof + end_padding)!!;
}
macro Allocator.new_clear_checked(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::new_try")
{
return ($Type*)self.calloc_checked($Type.sizeof + end_padding);
}
macro Allocator.clone(&self, value) @deprecated("Use allocator::clone")
{
var x = self.alloc($typeof(value));
*x = value;
return x;
}
fn void* Allocator.alloc(&self, usz size) @nodiscard @deprecated("Use allocator::malloc")
{
return malloc(self, size);
}
fn void* Allocator.calloc(&self, usz size) @nodiscard @deprecated("Use allocator::calloc")
{
return calloc(self, size);
}
fn void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard @deprecated("Use allocator::realloc")
{
return realloc(self, ptr, new_size);
}
fn void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::malloc_aligned")
{
return malloc_aligned(self, size, alignment, 0);
}
fn void*! Allocator.calloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::calloc_aligned")
{
return calloc_aligned(self, size, alignment, 0);
}
fn void*! Allocator.realloc_aligned(&self, void* ptr, usz new_size, usz alignment = 0, usz offset = 0) @deprecated("Use allocator::realloc_aligned")
{
return realloc_aligned(self, ptr, new_size, alignment, 0);
}
fn void Allocator.free(&self, void* ptr) @deprecated("Use allocator::free")
{
free(self, ptr);
}
fn void Allocator.free_aligned(&self, void* ptr) @deprecated("Use allocator::free_aligned")
{
free_aligned(self, ptr);
}
/**
* @require bytes > 0
@@ -395,12 +358,12 @@ macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
// All allocators
tlocal Allocator* thread_allocator @private = &allocator::LIBC_ALLOCATOR;
tlocal Allocator thread_allocator @private = &allocator::LIBC_ALLOCATOR;
tlocal TempAllocator* thread_temp_allocator @private = null;
tlocal TempAllocator*[2] temp_allocator_pair @private;
Allocator* temp_base_allocator @private = &allocator::LIBC_ALLOCATOR;
Allocator temp_base_allocator @private = &allocator::LIBC_ALLOCATOR;
macro TempAllocator* create_default_sized_temp_allocator(Allocator* allocator) @local
macro TempAllocator* create_default_sized_temp_allocator(Allocator allocator) @local
{
$switch (env::MEMORY_ENV)
$case NORMAL:
@@ -414,7 +377,7 @@ macro TempAllocator* create_default_sized_temp_allocator(Allocator* allocator) @
$endswitch
}
macro Allocator* heap() => thread_allocator;
macro Allocator heap() => thread_allocator;
macro TempAllocator* temp()
{
@@ -432,7 +395,24 @@ fn void init_default_temp_allocators() @private
thread_temp_allocator = temp_allocator_pair[0];
}
fn TempAllocator *temp_allocator_next() @private
fn void destroy_temp_allocators_after_exit() @finalizer(65535) @local @if(env::LIBC)
{
destroy_temp_allocators();
}
/**
* Call this to destroy any memory used by the temp allocators. This will invalidate all temp memory.
**/
fn void destroy_temp_allocators()
{
if (!thread_temp_allocator) return;
temp_allocator_pair[0].destroy();
temp_allocator_pair[1].destroy();
temp_allocator_pair[..] = null;
thread_temp_allocator = null;
}
fn TempAllocator* temp_allocator_next() @private
{
if (!thread_temp_allocator)
{
@@ -441,4 +421,4 @@ fn TempAllocator *temp_allocator_next() @private
}
usz index = thread_temp_allocator == temp_allocator_pair[0] ? 1 : 0;
return thread_temp_allocator = temp_allocator_pair[index];
}
}

View File

@@ -175,7 +175,7 @@ fn void runtime_startup() @public @export("__c3_runtime_startup")
}
assert(runtime_state == RUN_CTORS);
runtime_state = READ_DYLIB;
ctor = null;
ctor_first = null;
}
fn void runtime_finalize() @public @export("__c3_runtime_finalize")

View File

@@ -84,19 +84,19 @@ macro void release_wargs(String[] list) @private
free(list.ptr);
}
macro int @win_to_err_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
macro int @win_to_err_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
if (catch #m()) return 1;
return 0;
}
macro int @win_to_int_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => #m();
macro int @win_to_void_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
macro int @win_to_int_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) => #m();
macro int @win_to_void_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
#m();
return 0;
}
macro int @win_to_err_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
macro int @win_to_err_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
@@ -104,14 +104,14 @@ macro int @win_to_err_main_args(#m, void* handle, Char16* cmd_line, int show_cmd
return 0;
}
macro int @win_to_int_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
macro int @win_to_int_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(args);
}
macro int @win_to_void_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
macro int @win_to_void_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
@@ -119,26 +119,26 @@ macro int @win_to_void_main_args(#m, void* handle, Char16* cmd_line, int show_cm
return 0;
}
macro int @win_to_err_main(#m, void* handle, Char16* cmd_line, int show_cmd)
macro int @win_to_err_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
if (catch #m(handle, args, show_cmd)) return 1;
if (catch #m(handle, prev_handle, args, show_cmd)) return 1;
return 0;
}
macro int @win_to_int_main(#m, void* handle, Char16* cmd_line, int show_cmd)
macro int @win_to_int_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(handle, args, show_cmd);
return #m(handle, prev_handle, args, show_cmd);
}
macro int @win_to_void_main(#m, void* handle, Char16* cmd_line, int show_cmd)
macro int @win_to_void_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
#m(handle, args, show_cmd);
#m(handle, prev_handle, args, show_cmd);
return 0;
}

View File

@@ -4,13 +4,13 @@
module std::core::runtime;
import libc, std::time, std::io, std::sort;
struct AnyStruct
struct AnyRaw
{
void* ptr;
typeid type;
}
struct SubArrayStruct
struct SliceRaw
{
void* ptr;
usz len;
@@ -24,7 +24,7 @@ struct BenchmarkUnit
BenchmarkFn func;
}
fn BenchmarkUnit[] benchmark_collection_create(Allocator* allocator = allocator::heap())
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
{
BenchmarkFn[] fns = $$BENCHMARK_FNS;
String[] names = $$BENCHMARK_NAMES;
@@ -142,7 +142,7 @@ struct TestUnit
TestFn func;
}
fn TestUnit[] test_collection_create(Allocator* allocator = allocator::heap())
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
{
TestFn[] fns = $$TEST_FNS;
String[] names = $$TEST_NAMES;

View File

@@ -0,0 +1,127 @@
// Add this to your code to suppress leak detection or set other default options
// fn ZString __asan_default_options() @export("__asan_default_options") @if(env::ADDRESS_SANITIZER)
// {
// return "detect_leaks=0";
// }
// Add this to break on error
// asan::set_error_report_callback(fn (ZString err)
// {
// breakpoint();
// });
module std::core::sanitizer::asan;
def ErrorCallback = fn void (ZString);
/**
* Marks a memory region ([addr, addr+size)) as unaddressable.
*
* This memory must be previously allocated by your program. Instrumented
* code is forbidden from accessing addresses in this region until it is
* unpoisoned. This function is not guaranteed to poison the entire region -
* it could poison only a subregion of [addr, addr+size) due to ASan
* alignment restrictions.
*
* NOTE This function is not thread-safe because no two threads can poison or
* unpoison memory in the same memory region simultaneously.
*
* @param addr "Start of memory region."
* @param size "Size of memory region."
**/
macro poison_memory_region(void* addr, usz size)
{
$if env::ADDRESS_SANITIZER:
__asan_poison_memory_region(addr, size);
$endif
}
/**
* Marks a memory region ([addr, addr+size)) as addressable.
*
* This memory must be previously allocated by your program. Accessing
* addresses in this region is allowed until this region is poisoned again.
* This function could unpoison a super-region of [addr, addr+size) due
* to ASan alignment restrictions.
*
* NOTE This function is not thread-safe because no two threads can
* poison or unpoison memory in the same memory region simultaneously.
*
* @param addr "Start of memory region."
* @param size "Size of memory region."
**/
macro unpoison_memory_region(void* addr, usz size)
{
$if env::ADDRESS_SANITIZER:
__asan_unpoison_memory_region(addr, size);
$endif
}
/**
* Checks if an address is poisoned.
* @return "True if 'addr' is poisoned (that is, 1-byte read/write access to this address would result in an error report from ASan). Otherwise returns false."
* @param addr "Address to check."
**/
macro bool address_is_poisoned(void* addr)
{
$if env::ADDRESS_SANITIZER:
return (bool)__asan_address_is_poisoned(addr);
$else
return false;
$endif
}
/**
* Checks if a region is poisoned.
*
* If at least one byte in [beg, beg+size) is poisoned, returns the
* address of the first such byte. Otherwise returns 0.
*
* @param beg "Start of memory region."
* @param size "Start of memory region."
* @return "Address of first poisoned byte."
**/
macro void* region_is_poisoned(void* beg, usz size)
{
$if env::ADDRESS_SANITIZER:
return __asan_region_is_poisoned(addr);
$else
return null;
$endif
}
/**
* Sets the callback function to be called during ASan error reporting.
**/
fn void set_error_report_callback(ErrorCallback callback)
{
$if env::ADDRESS_SANITIZER:
__asan_set_error_report_callback(callback);
$endif
}
module std::core::sanitizer::asan @if(env::ADDRESS_SANITIZER);
extern fn void __asan_poison_memory_region(void* addr, usz size);
extern fn void __asan_unpoison_memory_region(void* addr, usz size);
extern fn CInt __asan_address_is_poisoned(void* addr);
extern fn void* __asan_region_is_poisoned(void* beg, usz size);
extern fn void __asan_describe_address(void* addr);
extern fn CInt __asan_report_present();
extern fn void* __asan_get_report_pc();
extern fn void* __asan_get_report_bp();
extern fn void* __asan_get_report_sp();
extern fn void* __asan_get_report_address();
extern fn CInt __asan_get_report_access_type();
extern fn usz __asan_get_report_access_size();
extern fn ZString __asan_get_report_description();
extern fn ZString __asan_locate_address(void* addr, char* name, usz name_size, void** region_address, usz* region_size);
extern fn usz __asan_get_alloc_stack(void* addr, void** trace, usz size, CInt* thread_id);
extern fn usz __asan_get_free_stack(void* addr, void** trace, usz size, CInt* thread_id);
extern fn void __asan_get_shadow_mapping(usz* shadow_scale, usz* shadow_offset);
extern fn void __asan_set_error_report_callback(ErrorCallback callback);
extern fn void __asan_print_accumulated_stats();
extern fn void* __asan_get_current_fake_stack();
extern fn void* __asan_addr_is_in_fake_stack(void* fake_stack, void* addr, void** beg, void** end);
extern fn void __asan_handle_no_return();
extern fn CInt __asan_update_allocation_context(void* addr);

View File

@@ -0,0 +1,80 @@
module std::core::sanitizer;
macro void annotate_contiguous_container(void* beg, void* end, void* old_mid, void* new_mid)
{
$if env::ADDRESS_SANITIZER:
__sanitizer_annotate_contiguous_container(beg, end, old_mid, new_mid);
$endif
}
macro void annotate_double_ended_contiguous_container(void* storage_beg, void* storage_end, void* old_container_beg, void* old_container_end, void* new_container_beg, void* new_container_end)
{
$if env::ADDRESS_SANITIZER:
__sanitizer_annotate_double_ended_contiguous_container(storage_beg, storage_end, old_container_beg, old_container_end, new_container_beg, new_container_end);
$endif
}
macro void print_stack_trace()
{
$if env::ADDRESS_SANITIZER:
__sanitizer_print_stack_trace();
$endif
}
fn void set_death_callback(VoidFn callback)
{
$if env::ANY_SANITIZER:
__sanitizer_set_death_callback(callback);
$endif
}
module std::core::sanitizer @if (env::ANY_SANITIZER);
struct __Sanitizer_sandbox_arguments
{
CInt coverage_sandboxed;
iptr coverage_fd;
CUInt coverage_max_block_size;
}
extern fn void __sanitizer_set_report_path(ZString path);
extern fn void __sanitizer_set_report_fd(void* fd);
extern fn ZString __sanitizer_get_report_path();
extern fn void __sanitizer_sandbox_on_notify(__Sanitizer_sandbox_arguments* args);
extern fn void __sanitizer_report_error_summary(ZString error_summary);
extern fn ushort __sanitizer_unaligned_load16(void* p);
extern fn uint __sanitizer_unaligned_load32(void* p);
extern fn ulong __sanitizer_unaligned_load64(void* p);
extern fn void __sanitizer_unaligned_store16(void* p, ushort x);
extern fn void __sanitizer_unaligned_store32(void* p, uint x);
extern fn void __sanitizer_unaligned_store64(void* p, ulong x);
extern fn CInt __sanitizer_acquire_crash_state();
extern fn void __sanitizer_annotate_contiguous_container(void* beg, void* end, void* old_mid, void* new_mid);
extern fn void __sanitizer_annotate_double_ended_contiguous_container(void* storage_beg, void* storage_end,
void* old_container_beg, void* old_container_end,
void* new_container_beg, void* new_container_end);
extern fn CInt __sanitizer_verify_contiguous_container(void* beg, void* mid, void* end);
extern fn CInt __sanitizer_verify_double_ended_contiguous_container(
void* storage_beg, void* container_beg,
void* container_end, void* storage_end);
extern fn void* __sanitizer_contiguous_container_find_bad_address(void* beg, void* mid, void* end);
extern fn void* __sanitizer_double_ended_contiguous_container_find_bad_address(
void* storage_beg, void* container_beg,
void* container_end, void* storage_end);
extern fn void __sanitizer_print_stack_trace();
extern fn void __sanitizer_symbolize_pc(void* pc, ZString fmt, char* out_buf, usz out_buf_size);
extern fn void __sanitizer_symbolize_global(void* data_ptr, ZString fmt, char* out_buf, usz out_buf_size);
extern fn void __sanitizer_set_death_callback(VoidFn callback);
extern fn void __sanitizer_weak_hook_memcmp(void* called_pc, void* s1, void* s2, usz n, CInt result);
extern fn void __sanitizer_weak_hook_strncmp(void* called_pc, ZString s1, ZString s2, usz n, CInt result);
extern fn void __sanitizer_weak_hook_strncasecmp(void* called_pc, ZString s1, ZString s2, usz n, CInt result);
extern fn void __sanitizer_weak_hook_strcmp(void* called_pc, ZString s1, ZString s2, CInt result);
extern fn void __sanitizer_weak_hook_strcasecmp(void* called_pc, ZString s1, ZString s2, CInt result);
extern fn void __sanitizer_weak_hook_strstr(void* called_pc, ZString s1, ZString s2, char* result);
extern fn void __sanitizer_weak_hook_strcasestr(void* called_pc, ZString s1, ZString s2, char* result);
extern fn void __sanitizer_weak_hook_memmem(void* called_pc, void* s1, usz len1, void* s2, usz len2, void* result);
extern fn void __sanitizer_print_memory_profile(usz top_percent, usz max_number_of_contexts);
extern fn void __sanitizer_start_switch_fiber(void** fake_stack_save, void* bottom, usz size);
extern fn void __sanitizer_finish_switch_fiber(void* fake_stack_save, void** bottom_old, usz* size_old);
extern fn CInt __sanitizer_get_module_and_offset_for_pc(void* pc, char* module_path, usz module_path_len, void** pc_offset);

View File

@@ -0,0 +1,39 @@
module std::core::sanitizer::tsan;
distinct MutexFlags = inline CUInt;
const MutexFlags MUTEX_LINKER_INIT = 1 << 0;
const MutexFlags MUTEX_WRITE_REENTRANT = 1 << 1;
const MutexFlags MUTEX_READ_REENTRANT = 1 << 2;
const MutexFlags MUTEX_NOT_STATIC = 1 << 8;
const MutexFlags MUTEX_READ_LOCK = 1 << 3;
const MutexFlags MUTEX_TRY_LOCK = 1 << 4;
const MutexFlags MUTEX_TRY_LOCK_FAILED = 1 << 5;
const MutexFlags MUTEX_RECURSIVE_LOCK = 1 << 6;
const MutexFlags MUTEX_RECURSIVE_UNLOCK = 1 << 7;
const MutexFlags MUTEX_TRY_READ_LOCK = MUTEX_READ_LOCK | MUTEX_TRY_LOCK;
const MutexFlags MUTEX_TRY_READ_LOCK_FAILED = MUTEX_TRY_READ_LOCK | MUTEX_TRY_LOCK_FAILED;
macro void mutex_create(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_create(addr, flags); $endif }
macro void mutex_destroy(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_destroy(addr, flags); $endif }
macro void mutex_pre_lock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_lock(addr, flags); $endif }
macro void mutex_post_lock(void* addr, MutexFlags flags, CInt recursion) { $if env::THREAD_SANITIZER: __tsan_mutex_post_lock(addr, flags, recursion); $endif }
macro CInt mutex_pre_unlock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: return __tsan_mutex_pre_unlock(addr, flags); $else return 0; $endif }
macro void mutex_post_unlock(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_unlock(addr, flags); $endif }
macro void mutex_pre_signal(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_signal(addr, flags); $endif }
macro void mutex_post_signal(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_signal(addr, flags); $endif }
macro void mutex_pre_divert(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_pre_divert(addr, flags); $endif }
macro void mutex_post_divert(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_post_divert(addr, flags); $endif }
module std::core::sanitizer::tsan @if(env::THREAD_SANITIZER) @private;
extern fn void __tsan_mutex_create(void* addr, CUInt flags);
extern fn void __tsan_mutex_destroy(void* addr, CUInt flags);
extern fn void __tsan_mutex_pre_lock(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_lock(void* addr, CUInt flags, CInt recursion);
extern fn CInt __tsan_mutex_pre_unlock(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_unlock(void* addr, CUInt flags);
extern fn void __tsan_mutex_pre_signal(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_signal(void* addr, CUInt flags);
extern fn void __tsan_mutex_pre_divert(void* addr, CUInt flags);
extern fn void __tsan_mutex_post_divert(void* addr, CUInt flags);

View File

@@ -1,6 +1,7 @@
module std::core::string;
import std::ascii;
distinct String @if(!$defined(String)) = inline char[];
distinct ZString = inline char*;
distinct WString = inline Char16*;
def Char32 = uint;
@@ -31,31 +32,77 @@ fault NumberConversion
FLOAT_OUT_OF_RANGE,
}
/**
* Return a temporary String created using the formatting function.
*
* @param [in] fmt `The formatting string`
**/
macro String tformat(String fmt, ...)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat());
str.appendf(fmt, $vasplat);
return str.str_view();
}
macro String new_format(String fmt, ..., Allocator* allocator = allocator::heap())
/**
* Return a temporary ZString created using the formatting function.
*
* @param [in] fmt `The formatting string`
**/
macro ZString tformat_zstr(String fmt, ...)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat);
return str.zstr_view();
}
/**
* Return a new String created using the formatting function.
*
* @param [in] fmt `The formatting string`
* @param [inout] allocator `The allocator to use`
**/
macro String new_format(String fmt, ..., Allocator allocator = allocator::heap())
{
@pool(allocator)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat());
str.appendf(fmt, $vasplat);
return str.copy_str(allocator);
};
}
/**
* Return a new ZString created using the formatting function.
*
* @param [in] fmt `The formatting string`
* @param [inout] allocator `The allocator to use`
**/
macro ZString new_format_zstr(String fmt, ..., Allocator allocator = allocator::heap())
{
@pool(allocator)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat);
return str.copy_zstr(allocator);
};
}
/**
* Check if a character is in a set.
*
* @param c `the character to check`
* @param [in] set `The formatting string`
* @pure
* @return `True if a character is in the set`
**/
macro bool char_in_set(char c, String set)
{
foreach (ch : set) if (ch == c) return true;
return false;
}
fn String join_new(String[] s, String joiner, Allocator* allocator = allocator::heap())
fn String join_new(String[] s, String joiner, Allocator allocator = allocator::heap())
{
if (!s)
{
@@ -81,8 +128,12 @@ fn String join_new(String[] s, String joiner, Allocator* allocator = allocator::
}
/**
* @param [in] string
* @param [in] to_trim
* Remove characters from the front and end of a string.
*
* @param [in] string `The string to trim`
* @param [in] to_trim `The set of characters to trim, defaults to whitespace`
* @pure
* @return `a substring of the string passed in`
**/
fn String String.trim(string, String to_trim = "\t\n\r ")
{
@@ -96,8 +147,12 @@ fn String String.trim(string, String to_trim = "\t\n\r ")
}
/**
* Check if the String starts with the needle.
*
* @param [in] string
* @param [in] needle
* @pure
* @return `'true' if the string starts with the needle`
**/
fn bool String.starts_with(string, String needle)
{
@@ -107,8 +162,12 @@ fn bool String.starts_with(string, String needle)
}
/**
* Check if the String ends with the needle.
*
* @param [in] string
* @param [in] needle
* @pure
* @return `'true' if the string ends with the needle`
**/
fn bool String.ends_with(string, String needle)
{
@@ -122,6 +181,8 @@ fn bool String.ends_with(string, String needle)
*
* @param [in] string
* @param [in] needle
* @pure
* @return `the substring with the prefix removed`
**/
fn String String.strip(string, String needle)
{
@@ -134,6 +195,8 @@ fn String String.strip(string, String needle)
*
* @param [in] string
* @param [in] needle
* @pure
* @return `the substring with the suffix removed`
**/
fn String String.strip_end(string, String needle)
{
@@ -153,7 +216,7 @@ fn String String.strip_end(string, String needle)
* @require needle.len > 0 "The needle must be at least 1 character long"
* @ensure return.len > 0
**/
fn String[] String.split(s, String needle, usz max = 0, Allocator* allocator = allocator::heap())
fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = allocator::heap())
{
usz capacity = 16;
usz i = 0;
@@ -196,6 +259,14 @@ fn String[] String.tsplit(s, String needle, usz max = 0)
return s.split(needle, max, allocator::temp()) @inline;
}
/**
* Check if a substring is found in the string.
* @param [in] s
* @param [in] needle "The string to look for."
* @pure
* @return "true if the string contains the substring, false otherwise"
**/
fn bool String.contains(s, String needle)
{
return @ok(s.index_of(needle));
@@ -205,6 +276,7 @@ fn bool String.contains(s, String needle)
* Find the index of the first incidence of a string.
*
* @param [in] s
* @param needle "The character to look for"
* @pure
* @ensure return < s.len
* @return "the index of the needle"
@@ -220,9 +292,32 @@ fn usz! String.index_of_char(s, char needle)
}
/**
* Find the index of the first incidence of a string.
* Find the index of the first incidence of a character.
*
* @param [in] s
* @param needle "The character to look for"
* @param start_index "The index to start with, may exceed max index."
* @pure
* @ensure return < s.len
* @return "the index of the needle"
* @return! SearchResult.MISSING "if the needle cannot be found starting from the start_index"
**/
fn usz! String.index_of_char_from(s, char needle, usz start_index)
{
usz len = s.len;
if (len <= start_index) return SearchResult.MISSING?;
for (usz i = start_index; i < len; i++)
{
if (s[i] == needle) return i;
}
return SearchResult.MISSING?;
}
/**
* Find the index of the first incidence of a character starting from the end.
*
* @param [in] s
* @param needle "the character to find"
* @pure
* @ensure return < s.len
* @return "the index of the needle"
@@ -312,7 +407,7 @@ fn usz ZString.len(str)
}
fn ZString String.zstr_copy(s, Allocator* allocator = allocator::heap())
fn ZString String.zstr_copy(s, Allocator allocator = allocator::heap())
{
usz len = s.len;
char* str = allocator::malloc(allocator, len + 1);
@@ -321,7 +416,7 @@ fn ZString String.zstr_copy(s, Allocator* allocator = allocator::heap())
return (ZString)str;
}
fn String String.concat(s1, String s2, Allocator* allocator = allocator::heap())
fn String String.concat(s1, String s2, Allocator allocator = allocator::heap())
{
usz full_len = s1.len + s2.len;
char* str = allocator::malloc(allocator, full_len + 1);
@@ -337,7 +432,7 @@ fn String String.tconcat(s1, String s2) => s1.concat(s2, allocator::temp());
fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline;
fn String String.copy(s, Allocator* allocator = allocator::heap())
fn String String.copy(s, Allocator allocator = allocator::heap())
{
usz len = s.len;
char* str = allocator::malloc(allocator, len + 1);
@@ -346,7 +441,7 @@ fn String String.copy(s, Allocator* allocator = allocator::heap())
return (String)str[:len];
}
fn void String.free(&s, Allocator* allocator = allocator::heap())
fn void String.free(&s, Allocator allocator = allocator::heap())
{
if (!s.len) return;
allocator::free(allocator, s.ptr);
@@ -355,7 +450,7 @@ fn void String.free(&s, Allocator* allocator = allocator::heap())
fn String String.tcopy(s) => s.copy(allocator::temp()) @inline;
fn String ZString.copy(z, Allocator* allocator = allocator::temp())
fn String ZString.copy(z, Allocator allocator = allocator::temp())
{
return z.str_view().copy(allocator) @inline;
}
@@ -371,7 +466,7 @@ fn String ZString.tcopy(z)
* @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
* @return! AllocationFailure "If allocation of the string fails"
**/
fn Char16[]! String.to_new_utf16(s, Allocator* allocator = allocator::heap())
fn Char16[]! String.to_new_utf16(s, Allocator allocator = allocator::heap())
{
usz len16 = conv::utf16len_for_utf8(s);
Char16* data = allocator::alloc_array_try(allocator, Char16, len16 + 1)!;
@@ -391,7 +486,7 @@ fn Char16[]! String.to_temp_utf16(s)
return s.to_new_utf16(allocator::temp());
}
fn WString! String.to_new_wstring(s, Allocator* allocator = allocator::heap())
fn WString! String.to_new_wstring(s, Allocator allocator = allocator::heap())
{
return (WString)s.to_new_utf16(allocator).ptr;
}
@@ -401,7 +496,7 @@ fn WString! String.to_temp_wstring(s)
return (WString)s.to_temp_utf16().ptr;
}
fn Char32[]! String.to_new_utf32(s, Allocator* allocator = allocator::heap())
fn Char32[]! String.to_new_utf32(s, Allocator allocator = allocator::heap())
{
usz codepoints = conv::utf8_codepoints(s);
Char32* data = allocator::alloc_array_try(allocator, Char32, codepoints + 1)!;
@@ -415,29 +510,49 @@ fn Char32[]! String.to_temp_utf32(s)
return s.to_new_utf32(allocator::temp());
}
/**
* Convert a string to ASCII lower case.
*
* @param [inout] s
* @pure
**/
fn void String.convert_ascii_to_lower(s)
{
foreach (&c : s) if (c.is_upper()) *c += 'a' - 'A';
foreach (&c : s) if (c.is_upper() @pure) *c += 'a' - 'A';
}
fn String String.new_ascii_to_lower(s, Allocator* allocator = allocator::heap())
fn String String.new_ascii_to_lower(s, Allocator allocator = allocator::heap())
{
String copy = s.copy(allocator);
copy.convert_ascii_to_lower();
return copy;
}
fn String String.temp_ascii_to_lower(s, Allocator* allocator = allocator::heap())
fn String String.temp_ascii_to_lower(s, Allocator allocator = allocator::heap())
{
return s.new_ascii_to_lower(allocator::temp());
}
/**
* Convert a string to ASCII upper case.
*
* @param [inout] s
* @pure
**/
fn void String.convert_ascii_to_upper(s)
{
foreach (&c : s) if (c.is_lower()) *c -= 'a' - 'A';
foreach (&c : s) if (c.is_lower() @pure) *c -= 'a' - 'A';
}
fn String String.new_ascii_to_upper(s, Allocator* allocator = allocator::heap())
/**
* Returns a string converted to ASCII upper case.
*
* @param [in] s
* @param [inout] allocator
*
* @return `a new String converted to ASCII upper case.`
**/
fn String String.new_ascii_to_upper(s, Allocator allocator = allocator::heap())
{
String copy = s.copy(allocator);
copy.convert_ascii_to_upper();
@@ -449,12 +564,16 @@ fn StringIterator String.iterator(s)
return { s, 0 };
}
/**
* @param [in] s
* @return `a temporary String converted to ASCII upper case.`
**/
fn String String.temp_ascii_to_upper(s)
{
return s.new_ascii_to_upper(allocator::temp());
}
fn String! new_from_utf32(Char32[] utf32, Allocator* allocator = allocator::heap())
fn String! new_from_utf32(Char32[] utf32, Allocator allocator = allocator::heap())
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = allocator::malloc_try(allocator, len + 1)!;
@@ -464,7 +583,7 @@ fn String! new_from_utf32(Char32[] utf32, Allocator* allocator = allocator::heap
return (String)data[:len];
}
fn String! new_from_utf16(Char16[] utf16, Allocator* allocator = allocator::heap())
fn String! new_from_utf16(Char16[] utf16, Allocator allocator = allocator::heap())
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = allocator::malloc_try(allocator, len + 1)!;
@@ -474,7 +593,7 @@ fn String! new_from_utf16(Char16[] utf16, Allocator* allocator = allocator::heap
return (String)data[:len];
}
fn String! new_from_wstring(WString wstring, Allocator* allocator = allocator::heap())
fn String! new_from_wstring(WString wstring, Allocator allocator = allocator::heap())
{
usz utf16_len;
while (wstring[utf16_len] != 0) utf16_len++;

View File

@@ -13,11 +13,37 @@ fn void StringIterator.reset(&self)
fn Char32! StringIterator.next(&self)
{
usz len = self.utf8.len;
usz current = self.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
usz read = (len - current < 4 ? len - current : 4);
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
self.current += read;
return res;
usz len = self.utf8.len;
usz current = self.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
usz read = (len - current < 4 ? len - current : 4);
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
self.current += read;
return res;
}
fn Char32! StringIterator.peek(&self)
{
usz len = self.utf8.len;
usz current = self.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
usz read = (len - current < 4 ? len - current : 4);
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
return res;
}
fn bool StringIterator.has_next(&self)
{
return self.current < self.utf8.len;
}
fn Char32! StringIterator.get(&self)
{
usz len = self.utf8.len;
usz current = self.current;
usz read = (len - current < 4 ? len - current : 4);
usz index = current > read ? current - read : 0;
if (index >= len) return IteratorResult.NO_MORE_ELEMENT?;
Char32 res = conv::utf8_to_char32(&self.utf8[index], &read)!;
return res;
}

View File

@@ -11,7 +11,7 @@ fault ConversionResult
/**
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
**/
macro any_to_int(any* v, $Type)
macro any_to_int(any v, $Type)
{
typeid any_type = v.type;
TypeKind kind = any_type.kindof;
@@ -108,10 +108,10 @@ fn bool TypeKind.is_int(kind) @inline
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
}
macro bool is_subarray_convertable($Type)
macro bool is_slice_convertable($Type)
{
$switch ($Type.kindof)
$case SUBARRAY:
$case SLICE:
return true;
$case POINTER:
return $Type.inner.kindof == TypeKind.ARRAY;
@@ -120,10 +120,20 @@ macro bool is_subarray_convertable($Type)
$endswitch
}
macro bool is_bool($Type) => $Type.kindof == TypeKind.BOOL;
macro bool is_int($Type) => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
macro bool is_bool($Type) @const => $Type.kindof == TypeKind.BOOL;
macro bool is_int($Type) @const => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
macro bool is_intlike($Type)
macro bool is_indexable($Type) @const
{
return $defined($Type{}[0]);
}
macro bool is_ref_indexable($Type) @const
{
return $defined(&$Type{}[0]);
}
macro bool is_intlike($Type) @const
{
$switch ($Type.kindof)
$case SIGNED_INT:
@@ -136,7 +146,7 @@ macro bool is_intlike($Type)
$endswitch
}
macro bool is_underlying_int($Type)
macro bool is_underlying_int($Type) @const
{
$switch ($Type.kindof)
$case SIGNED_INT:
@@ -149,9 +159,9 @@ macro bool is_underlying_int($Type)
$endswitch
}
macro bool is_float($Type) => $Type.kindof == TypeKind.FLOAT;
macro bool is_float($Type) @const => $Type.kindof == TypeKind.FLOAT;
macro bool is_floatlike($Type)
macro bool is_floatlike($Type) @const
{
$switch ($Type.kindof)
$case FLOAT:
@@ -163,12 +173,12 @@ macro bool is_floatlike($Type)
$endswitch
}
macro bool is_vector($Type)
macro bool is_vector($Type) @const
{
return $Type.kindof == TypeKind.VECTOR;
}
macro TypeKind inner_kind($Type)
macro TypeKind inner_kind($Type) @const
{
$if $Type.kindof == TypeKind.DISTINCT:
return inner_kind($typefrom($Type.inner));
@@ -177,26 +187,26 @@ macro TypeKind inner_kind($Type)
$endif
}
macro bool is_same($TypeA, $TypeB)
macro bool is_same($TypeA, $TypeB) @const
{
return $TypeA.typeid == $TypeB.typeid;
}
macro bool @has_same(#a, #b, ...)
macro bool @has_same(#a, #b, ...) @const
{
var $type_a = @typeid(#a);
$if $type_a != @typeid(#b):
return false;
$endif
$for (var $i = 0; $i < $vacount; $i++)
$if @typeid($vaexpr($i)) != $type_a:
$if @typeid($vaexpr[$i]) != $type_a:
return false;
$endif
$endfor
return true;
}
macro bool may_load_atomic($Type)
macro bool may_load_atomic($Type) @const
{
$switch ($Type.kindof)
$case SIGNED_INT:
@@ -211,7 +221,7 @@ macro bool may_load_atomic($Type)
$endswitch
}
macro lower_to_atomic_compatible_type($Type)
macro lower_to_atomic_compatible_type($Type) @const
{
$switch ($Type.kindof)
$case SIGNED_INT:
@@ -237,10 +247,10 @@ macro lower_to_atomic_compatible_type($Type)
$endswitch
}
macro bool is_promotable_to_floatlike($Type) => types::is_floatlike($Type) || types::is_int($Type);
macro bool is_promotable_to_float($Type) => types::is_float($Type) || types::is_int($Type);
macro bool is_promotable_to_floatlike($Type) @const => types::is_floatlike($Type) || types::is_int($Type);
macro bool is_promotable_to_float($Type) @const => types::is_float($Type) || types::is_int($Type);
macro bool is_same_vector_type($Type1, $Type2)
macro bool is_same_vector_type($Type1, $Type2) @const
{
$if $Type1.kindof != TypeKind.VECTOR:
return $Type2.kindof != TypeKind.VECTOR;
@@ -249,7 +259,7 @@ macro bool is_same_vector_type($Type1, $Type2)
$endif
}
macro bool is_equatable_type($Type)
macro bool is_equatable_type($Type) @const
{
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
return true;
@@ -261,17 +271,31 @@ macro bool is_equatable_type($Type)
/**
* Checks if a type implements the copy protocol.
**/
macro bool implements_copy($Type)
macro bool implements_copy($Type) @const
{
return $defined($Type.copy) && $defined($Type.free);
}
macro bool is_equatable_value(value)
macro bool is_equatable_value(value) @deprecated
{
return is_equatable_type($typeof(value));
}
macro bool is_comparable_value(value)
macro bool @equatable_value(#value) @const
{
return is_equatable_type($typeof(#value));
}
macro bool @comparable_value(#value) @const
{
$if $defined(#value.less) || $defined(#value.compare_to):
return true;
$else
return $typeof(#value).is_ordered;
$endif
}
macro bool is_comparable_value(value) @deprecated
{
$if $defined(value.less) || $defined(value.compare_to):
return true;
@@ -298,10 +322,11 @@ enum TypeKind : char
FUNC,
OPTIONAL,
ARRAY,
SUBARRAY,
SLICE,
VECTOR,
DISTINCT,
POINTER,
INTERFACE,
}
struct TypeEnum

View File

@@ -1,21 +1,21 @@
module std::core::values;
macro typeid @typeid(#value) @builtin => $typeof(#value).typeid;
macro TypeKind @typekind(#value) @builtin => $typeof(#value).kindof;
macro bool @typeis(#value, $Type) @builtin => $typeof(#value).typeid == $Type.typeid;
macro typeid @typeid(#value) @const @builtin => $typeof(#value).typeid;
macro TypeKind @typekind(#value) @const @builtin => $typeof(#value).kindof;
macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $Type.typeid;
/**
* Return true if two values have the same type before any conversions.
**/
macro bool @is_same_type(#value1, #value2) => $typeof(#value1).typeid == $typeof(#value2).typeid;
macro bool @is_bool(#value) => types::is_bool($typeof(#value));
macro bool @is_int(#value) => types::is_int($typeof(#value));
macro bool @is_floatlike(#value) => types::is_floatlike($typeof(#value));
macro bool @is_float(#value) => types::is_float($typeof(#value));
macro bool @is_promotable_to_floatlike(#value) => types::is_promotable_to_floatlike($typeof(#value));
macro bool @is_promotable_to_float(#value) => types::is_promotable_to_float($typeof(#value));
macro bool @is_vector(#value) => types::is_vector($typeof(#value));
macro bool @is_same_vector_type(#value1, #value2) => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
macro bool @assign_to(#value1, #value2) => $assignable(#value1, $typeof(#value2));
macro bool @is_same_type(#value1, #value2) @const => $typeof(#value1).typeid == $typeof(#value2).typeid;
macro bool @is_bool(#value) @const => types::is_bool($typeof(#value));
macro bool @is_int(#value) @const => types::is_int($typeof(#value));
macro bool @is_floatlike(#value) @const => types::is_floatlike($typeof(#value));
macro bool @is_float(#value) @const => types::is_float($typeof(#value));
macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to_floatlike($typeof(#value));
macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_float($typeof(#value));
macro bool @is_vector(#value) @const => types::is_vector($typeof(#value));
macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2));
macro promote_int(x)
{
@@ -26,5 +26,21 @@ macro promote_int(x)
$endif
}
macro TypeKind @inner_kind(#value) => types::inner_kind($typeof(#value));
macro promote_int_same(x, y)
{
$if @is_int(x):
$switch
$case @is_vector(y) &&& $typeof(y).inner == float.typeid:
return (float)x;
$case $typeof(y).typeid == float.typeid:
return (float)x;
$default:
return (double)x;
$endswitch
$else
return x;
$endif
}
macro TypeKind @inner_kind(#value) @const => types::inner_kind($typeof(#value));

View File

@@ -28,6 +28,18 @@ fn void Rc4.init(&self, char[] key)
self.j = 0;
}
/**
* Run a single pass of en/decryption using a particular key.
* @param [in] key
* @param [inout] data
**/
fn void crypt(char[] key, char[] data)
{
Rc4 rc4;
rc4.init(key);
rc4.crypt(data, data);
}
/**
* Encrypt or decrypt a sequence of bytes.
*

View File

@@ -3,22 +3,22 @@ import std::io;
struct CsvReader
{
InStream* stream;
InStream stream;
String separator;
}
fn void CsvReader.init(&self, InStream* stream, String separator = ",")
fn void CsvReader.init(&self, InStream stream, String separator = ",")
{
self.stream = stream;
self.separator = separator;
}
fn String[]! CsvReader.read_new_row(self, Allocator* allocator = allocator::heap())
fn String[]! CsvReader.read_new_row(self, Allocator allocator = allocator::heap())
{
return self.read_new_row_with_allocator(allocator::temp()) @inline;
}
fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator* allocator = allocator::heap())
fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator allocator = allocator::heap())
{
@pool(allocator)
{
@@ -45,7 +45,7 @@ macro CsvReader.@each_row(self, int rows = int.max; @body(String[] row))
String sep = self.separator;
while (rows--)
{
@stack_mem(512; Allocator* mem)
@stack_mem(512; Allocator mem)
{
String[] parts;
@pool()

View File

@@ -15,11 +15,29 @@ fault JsonParsingError
INVALID_NUMBER,
}
fn Object*! parse(InStream* s, Allocator* allocator = allocator::heap())
fn Object*! parse_string(String s, Allocator allocator = allocator::heap())
{
return parse(ByteReader{}.init(s), allocator);
}
fn Object*! temp_parse_string(String s)
{
return parse(ByteReader{}.init(s), allocator::temp());
}
fn Object*! parse(InStream s, Allocator allocator = allocator::heap())
{
JsonContext context = { .last_string = dstring::new_with_capacity(64, allocator), .stream = s, .allocator = allocator };
defer context.last_string.free();
return parse_any(&context);
@pool(allocator)
{
return parse_any(&context);
};
}
fn Object*! temp_parse(InStream s)
{
return parse(s, allocator::temp());
}
// -- Implementation follows --
@@ -44,8 +62,8 @@ enum JsonTokenType @local
struct JsonContext @local
{
uint line;
InStream* stream;
Allocator* allocator;
InStream stream;
Allocator allocator;
JsonTokenType token;
DString last_string;
double last_number;
@@ -170,7 +188,7 @@ fn Object*! parse_array(JsonContext* context) @local
while (token != JsonTokenType.RBRACKET)
{
Object* element = parse_from_token(context, token)!;
list.append(element);
list.push(element);
token = advance(context)!;
if (token == JsonTokenType.COMMA)
{

View File

@@ -2,12 +2,12 @@ module std::io;
struct BitReader
{
InStream* reader;
InStream reader;
uint bits;
uint len;
}
fn void BitReader.init(&self, InStream* byte_reader)
fn void BitReader.init(&self, InStream byte_reader)
{
*self = { .reader = byte_reader };
}
@@ -40,12 +40,12 @@ fn char! BitReader.read_bits(&self, uint nbits)
struct BitWriter
{
OutStream* writer;
OutStream writer;
uint bits;
uint len;
}
fn void BitWriter.init(&self, OutStream* byte_writer)
fn void BitWriter.init(&self, OutStream byte_writer)
{
*self = { .writer = byte_writer };
}

View File

@@ -161,7 +161,7 @@ fn char[]! load_buffer(String filename, char[] buffer)
}
fn char[]! load_new(String filename, Allocator* allocator = allocator::heap())
fn char[]! load_new(String filename, Allocator allocator = allocator::heap())
{
File file = open(filename, "rb")!;
defer (void)file.close();

View File

@@ -6,7 +6,7 @@ const int PRINTF_NTOA_BUFFER_SIZE = 256;
interface Printable
{
fn String to_new_string(Allocator *allocator) @optional;
fn String to_new_string(Allocator allocator) @optional;
fn usz! to_format(Formatter* formatter) @optional;
}
@@ -72,7 +72,7 @@ fn usz! Formatter.out(&self, char c) @private
return 1;
}
fn usz! Formatter.print_with_function(&self, Printable* arg)
fn usz! Formatter.print_with_function(&self, Printable arg)
{
if (&arg.to_format)
{
@@ -85,6 +85,7 @@ fn usz! Formatter.print_with_function(&self, Printable* arg)
self.width = old_width;
self.prec = old_prec;
}
if (!arg) return self.out_substr("(null)");
return arg.to_format(self);
}
if (&arg.to_new_string)
@@ -98,7 +99,8 @@ fn usz! Formatter.print_with_function(&self, Printable* arg)
self.width = old_width;
self.prec = old_prec;
}
@stack_mem(1024; Allocator* mem)
if (!arg) return self.out_substr("(null)");
@stack_mem(1024; Allocator mem)
{
return self.out_substr(arg.to_new_string(mem));
};
@@ -107,7 +109,7 @@ fn usz! Formatter.print_with_function(&self, Printable* arg)
}
fn usz! Formatter.out_str(&self, any* arg) @private
fn usz! Formatter.out_str(&self, any arg) @private
{
switch (arg.type.kindof)
{
@@ -119,7 +121,7 @@ fn usz! Formatter.out_str(&self, any* arg) @private
case FAULT:
return self.out_substr((*(anyfault*)arg.ptr).nameof);
case ANY:
return self.out_str(*(any**)arg);
return self.out_str(*(any*)arg);
case OPTIONAL:
unreachable();
case SIGNED_INT:
@@ -149,18 +151,9 @@ fn usz! Formatter.out_str(&self, any* arg) @private
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
default:
}
usz! n = self.print_with_function((Printable*)arg);
if (catch err = n)
{
case SearchResult.MISSING:
break;
default:
return err?;
}
else
{
return n;
}
usz! n = self.print_with_function((Printable)arg);
if (try n) return n;
if (@catch(n) != SearchResult.MISSING) n!;
switch (arg.type.kindof)
{
case ENUM:
@@ -178,14 +171,23 @@ fn usz! Formatter.out_str(&self, any* arg) @private
case DISTINCT:
if (arg.type == ZString.typeid)
{
return self.out_substr(((ZString*)arg).str_view());
return self.out_substr(*(ZString*)arg ? ((ZString*)arg).str_view() : "(null)");
}
if (arg.type == DString.typeid)
{
return self.out_substr(((DString*)arg).str_view());
return self.out_substr(*(DString*)arg ? ((DString*)arg).str_view() : "(null)");
}
return self.out_str(arg.as_inner());
case POINTER:
typeid inner = arg.type.inner;
void** pointer = arg.ptr;
if (arg.type.inner != void.typeid)
{
any deref = any_make(*pointer, inner);
n = self.print_with_function((Printable)deref);
if (try n) return n;
if (@catch(n) != SearchResult.MISSING) n!;
}
PrintFlags flags = self.flags;
uint width = self.width;
defer
@@ -193,8 +195,8 @@ fn usz! Formatter.out_str(&self, any* arg) @private
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
self.out_substr("0x")!;
return self.ntoa_any(arg, 16);
case ARRAY:
// this is SomeType[*] so grab the "SomeType"
@@ -246,7 +248,7 @@ fn usz! Formatter.out_str(&self, any* arg) @private
}
len += self.out_substr(">]")!;
return len;
case SUBARRAY:
case SLICE:
// this is SomeType[] so grab the "SomeType"
typeid inner = arg.type.inner;
if (inner == char.typeid)
@@ -291,7 +293,7 @@ fn void! out_null_fn(void* data @unused, char c @unused) @private
fn usz! Formatter.vprintf(&self, String format, any*[] anys)
fn usz! Formatter.vprintf(&self, String format, any[] anys)
{
if (!self.out_fn)
{
@@ -358,7 +360,7 @@ fn usz! Formatter.vprintf(&self, String format, any*[] anys)
// evaluate specifier
uint base = 0;
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
any* current = anys[variant_index++];
any current = anys[variant_index++];
switch (c)
{
case 'd':

View File

@@ -10,7 +10,7 @@ fn usz! Formatter.adjust(&self, usz len) @local
return self.pad(' ', self.width, len);
}
fn uint128! int_from_any(any* arg, bool *is_neg) @private
fn uint128! int_from_any(any arg, bool *is_neg) @private
{
switch (arg.type.kindof)
{
@@ -63,14 +63,11 @@ fn uint128! int_from_any(any* arg, bool *is_neg) @private
}
}
fn FloatType! float_from_any(any* arg) @private
fn FloatType! float_from_any(any arg) @private
{
$if env::F128_SUPPORT:
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
$endif
$if env::F16_SUPPORT:
if (arg.type == float16.typeid) return *((float16*)arg.ptr);
$endif
if (arg.type.kindof == TypeKind.DISTINCT)
{
return float_from_any(arg.as_inner());
@@ -588,14 +585,14 @@ fn usz! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
}
fn usz! Formatter.ntoa_any(&self, any* arg, uint base) @private
fn usz! Formatter.ntoa_any(&self, any arg, uint base) @private
{
bool is_neg;
uint128 val = int_from_any(arg, &is_neg)!!;
return self.ntoa(val, is_neg, base) @inline;
}
fn usz! Formatter.out_char(&self, any* arg) @private
fn usz! Formatter.out_char(&self, any arg) @private
{
usz len = 1;
uint l = 1;
@@ -631,15 +628,15 @@ fn usz! Formatter.out_reverse(&self, char[] buf) @private
usz buffer_start_idx = self.idx;
usz len = buf.len;
// pad spaces up to given width
if (!self.flags.zeropad)
if (!self.flags.zeropad && !self.flags.left)
{
n += self.adjust(len)!;
n += self.pad(' ', self.width, len)!;
}
// reverse string
while (len) n += self.out(buf[--len])!;
// append pad spaces up to given width
n += self.adjust(self.idx - buffer_start_idx)!;
n += self.adjust(n)!;
return n;
}
@@ -649,21 +646,21 @@ fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline @private
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT?;
}
fn any*! next_any(any** args_ptr, usz args_len, usz* arg_index_ptr) @inline @private
fn any! next_any(any* args_ptr, usz args_len, usz* arg_index_ptr) @inline @private
{
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG?;
return args_ptr[(*arg_index_ptr)++];
}
fn int! printf_parse_format_field(
any** args_ptr, usz args_len, usz* args_index_ptr,
any* args_ptr, usz args_len, usz* args_index_ptr,
char* format_ptr, usz format_len, usz* index_ptr) @inline @private
{
char c = format_ptr[*index_ptr];
if (c.is_digit()) return simple_atoi(format_ptr, format_len, index_ptr);
if (c != '*') return 0;
printf_advance_format(format_len, index_ptr)!;
any* val = next_any(args_ptr, args_len, args_index_ptr)!;
any val = next_any(args_ptr, args_len, args_index_ptr)!;
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG?;
uint! intval = types::any_to_int(val, int);
return intval ?? FormattingFault.INVALID_WIDTH_ARG?;

View File

@@ -46,12 +46,18 @@ fault IoError
/**
* @param stream
* @require @is_instream(stream)
* Read from a stream (default is stdin) to the next "\n"
* or to the end of the stream, whatever comes first.
* "\r" will be filtered from the String.
*
* @param stream `The stream to read from.`
* @require @is_instream(stream) `The stream must implement InStream.`
* @param [inout] allocator `the allocator to use.`
* @return `The string containing the data read.`
**/
macro String! readline(stream = io::stdin(), Allocator* allocator = allocator::heap())
macro String! readline(stream = io::stdin(), Allocator allocator = allocator::heap())
{
bool $is_stream = @typeid(stream) == InStream*.typeid;
bool $is_stream = @typeis(stream, InStream);
$if $is_stream:
$typeof(&stream.read_byte) func = &stream.read_byte;
char val = func((void*)stream)!;
@@ -84,38 +90,67 @@ macro String! readline(stream = io::stdin(), Allocator* allocator = allocator::h
};
}
macro String! treadline(stream = io::stdin()) => readline(stream, allocator::temp()) @inline;
/**
* Reads a string, see `readline`, except the it is allocated
* on the temporary allocator and does not need to be freed.
*
* @param stream `The stream to read from.`
* @require @is_instream(stream) `The stream must implement InStream.`
* @return `The temporary string containing the data read.`
**/
macro String! treadline(stream = io::stdin())
{
return readline(stream, allocator::temp()) @inline;
}
/**
* @require @is_outstream(out) "The output must implement OutStream"
* Print a value to a stream.
*
* @param out `the stream to print to`
* @param x `the value to print`
* @require @is_outstream(out) `The output must implement OutStream.`
* @return `the number of bytes printed.`
*/
macro usz! fprint(out, x)
{
var $Type = $typeof(x);
$switch ($Type)
$case String:
return out.write(x);
$case ZString:
return out.write(x.str_view());
$case DString:
return out.write(x.str_view());
$default:
$if $assignable(x, String):
return out.write((String)x);
$else
return fprintf(out, "%s", x);
$endif
$case String: return out.write(x);
$case ZString: return out.write(x.str_view());
$case DString: return out.write(x.str_view());
$default:
$if $assignable(x, String):
return out.write((String)x);
$else
return fprintf(out, "%s", x);
$endif
$endswitch
}
fn usz! fprintf(OutStream* out, String format, args...)
/**
* Prints using a 'printf'-style formatting string.
* See `printf` for details on formatting.
*
* @param [inout] out `The OutStream to print to`
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! fprintf(OutStream out, String format, args...)
{
Formatter formatter;
formatter.init(&out_putstream_fn, &out);
return formatter.vprintf(format, args);
}
fn usz! fprintfn(OutStream* out, String format, args...) @maydiscard
/**
* Prints using a 'printf'-style formatting string,
* appending '\n' at the end. See `printf`.
*
* @param [inout] out `The OutStream to print to`
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! fprintfn(OutStream out, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putstream_fn, &out);
@@ -133,7 +168,7 @@ macro usz! fprintn(out, x = "")
usz len = fprint(out, x)!;
out.write_byte('\n')!;
$switch
$case @typeid(out) == OutStream*.typeid:
$case @typeid(out) == OutStream.typeid:
if (&out.flush) out.flush()!;
$case $defined(out.flush):
out.flush()!;
@@ -141,21 +176,37 @@ macro usz! fprintn(out, x = "")
return len + 1;
}
/**
* Print any value to stdout.
**/
macro void print(x)
{
(void)fprint(io::stdout(), x);
}
/**
* Print any value to stdout, appending an '\n after.
*
* @param x "The value to print"
**/
macro void printn(x = "")
{
(void)fprintn(io::stdout(), x);
}
/**
* Print any value to stderr.
**/
macro void eprint(x)
{
(void)fprint(io::stderr(), x);
}
/**
* Print any value to stderr, appending an '\n after.
*
* @param x "The value to print"
**/
macro void eprintn(x)
{
(void)fprintn(io::stderr(), x);
@@ -164,7 +215,7 @@ macro void eprintn(x)
fn void! out_putstream_fn(void* data, char c) @private
{
OutStream** stream = data;
OutStream* stream = data;
return (*stream).write_byte(c);
}
@@ -173,6 +224,20 @@ fn void! out_putchar_fn(void* data @unused, char c) @private
libc::putchar(c);
}
/**
* Prints using a 'printf'-style formatting string.
* To print integer numbers, use "%d" or "%x"/"%X,
* the latter gives the hexadecimal representation.
*
* All types can be printed using "%s" which gives
* the default representation of the value.
*
* To create a custom output for a type, implement
* the Printable interface.
*
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! printf(String format, args...) @maydiscard
{
Formatter formatter;
@@ -180,6 +245,13 @@ fn usz! printf(String format, args...) @maydiscard
return formatter.vprintf(format, args);
}
/**
* Prints using a 'printf'-style formatting string,
* appending '\n' at the end. See `printf`.
*
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! printfn(String format, args...) @maydiscard
{
Formatter formatter;
@@ -190,19 +262,33 @@ fn usz! printfn(String format, args...) @maydiscard
return len + 1;
}
/**
* Prints using a 'printf'-style formatting string
* to stderr.
*
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! eprintf(String format, args...) @maydiscard
{
Formatter formatter;
OutStream* stream = stderr();
OutStream stream = stderr();
formatter.init(&out_putstream_fn, &stream);
return formatter.vprintf(format, args);
}
/**
* Prints using a 'printf'-style formatting string,
* to stderr appending '\n' at the end. See `printf`.
*
* @param [in] format `The printf-style format string`
* @return `the number of characters printed`
**/
fn usz! eprintfn(String format, args...) @maydiscard
{
Formatter formatter;
OutStream* stream = stderr();
OutStream stream = stderr();
formatter.init(&out_putstream_fn, &stream);
usz len = formatter.vprintf(format, args)! + 1;
stderr().write_byte('\n')!;
@@ -210,6 +296,14 @@ fn usz! eprintfn(String format, args...) @maydiscard
return len;
}
/**
* Prints using a 'printf'-style formatting string,
* to a string buffer. See `printf`.
*
* @param [inout] buffer `The buffer to print to`
* @param [in] format `The printf-style format string`
* @return `a slice formed from the "buffer" with the resulting length.`
**/
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
{
Formatter formatter;
@@ -219,6 +313,7 @@ fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
return buffer[:data.written];
}
// Used to print to a buffer.
fn void! out_buffer_fn(void *data, char c) @private
{
BufferData *buffer_data = data;
@@ -226,22 +321,30 @@ fn void! out_buffer_fn(void *data, char c) @private
buffer_data.buffer[buffer_data.written++] = c;
}
// Used for buffer printing
struct BufferData @private
{
char[] buffer;
usz written;
}
// Only available with LIBC
module std::io @if (env::LIBC);
import libc;
/**
* Libc `putchar`, prints a single character to stdout.
**/
fn void putchar(char c) @inline
{
libc::putchar(c);
}
/**
* Get standard out.
*
* @return `stdout as a File`
**/
fn File* stdout()
{
static File file;
@@ -249,6 +352,11 @@ fn File* stdout()
return &file;
}
/**
* Get standard err.
*
* @return `stderr as a File`
**/
fn File* stderr()
{
static File file;
@@ -256,6 +364,11 @@ fn File* stderr()
return &file;
}
/**
* Get standard in.
*
* @return `stdin as a File`
**/
fn File* stdin()
{
static File file;
@@ -271,7 +384,7 @@ File stderr_file;
fn void putchar(char c) @inline
{
(void)stdout_file.putc(c);
(void)stdout_file.write_byte(c);
}
fn File* stdout()

View File

@@ -95,7 +95,7 @@ fn bool native_is_file(String path)
$case env::DARWIN:
$case env::LINUX:
Stat stat;
return @ok(native_stat(&stat, path)) && stat.st_mode & libc::S_IFREG;
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFREG);
$default:
File! f = file::open(path, "r");
defer (void)f.close();
@@ -107,7 +107,7 @@ fn bool native_is_dir(String path)
{
$if env::DARWIN || env::LINUX:
Stat stat;
return @ok(native_stat(&stat, path)) && stat.st_mode & libc::S_IFDIR;
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFDIR);
$else
return native_file_or_dir_exists(path) && !native_is_file(path);
$endif

View File

@@ -1,7 +1,7 @@
module std::io::os;
import libc, std::os;
macro String! getcwd(Allocator* allocator = allocator::heap())
macro String! getcwd(Allocator allocator = allocator::heap())
{
$switch
$case env::WIN32:

View File

@@ -1,7 +1,7 @@
module std::io::os @if(env::POSIX);
import std::io, std::os;
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* allocator)
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
{
PathList list;
list.new_init(.allocator = allocator);
@@ -16,7 +16,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
if (entry.d_type == posix::DT_LNK && no_symlinks) continue;
if (entry.d_type == posix::DT_DIR && no_dirs) continue;
Path path = path::new(name, allocator)!!;
list.append(path);
list.push(path);
}
return list;
}
@@ -24,7 +24,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
module std::io::os @if(env::WIN32);
import std::time, std::os, std::io;
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* allocator)
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
{
PathList list;
list.new_init(.allocator = allocator);
@@ -43,7 +43,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
{
String filename = string::temp_from_wstring((WString)&find_data.cFileName)!;
if (filename == ".." || filename == ".") continue;
list.append(path::new(filename, allocator)!);
list.push(path::new(filename, allocator)!);
};
} while (win32::findNextFileW(find, &find_data));
return list;

View File

@@ -16,7 +16,7 @@ fn void! native_rmtree(Path dir)
{
String name = ((ZString)&entry.name).str_view();
if (!name || name == "." || name == "..") continue;
Path new_path = dir.tappend(name)!;
Path new_path = dir.temp_append(name)!;
if (entry.d_type == posix::DT_DIR)
{
native_rmtree(new_path)!;
@@ -49,7 +49,7 @@ fn void! native_rmtree(Path path)
{
String filename = string::new_from_wstring((WString)&find_data.cFileName, allocator::temp())!;
if (filename == "." || filename == "..") continue;
Path file_path = path.tappend(filename)!;
Path file_path = path.temp_append(filename)!;
if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)
{
native_rmtree(file_path)!;

View File

@@ -1,7 +1,7 @@
module std::io::os @if(env::LIBC);
import std::io::path, std::os;
fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(!env::WIN32)
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(!env::WIN32)
{
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
{
@@ -11,7 +11,7 @@ fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(!en
return path::new("/tmp", allocator);
}
fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(env::WIN32)
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(env::WIN32)
{
@pool(allocator)
{
@@ -24,8 +24,9 @@ fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(env
}
module std::io::os @if(env::NO_LIBC);
import std::io::path;
macro Path! native_temp_directory(Allocator* allocator = allocator::heap())
macro Path! native_temp_directory(Allocator allocator = allocator::heap())
{
return IoError.UNSUPPORTED_OPERATION?;
}

View File

@@ -1,5 +1,6 @@
module std::io::path;
import std::collections::list, std::io::os;
import std::os::win32;
const PathEnv DEFAULT_PATH_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX;
const char PREFERRED_SEPARATOR_WIN32 = '\\';
@@ -14,7 +15,9 @@ fault PathResult
NO_PARENT,
}
struct Path (Printable)
def Path = PathImp;
struct PathImp (Printable)
{
String path_string;
PathEnv env;
@@ -26,7 +29,15 @@ enum PathEnv
POSIX
}
fn Path! getcwd(Allocator* allocator = allocator::heap())
fn Path! new_cwd(Allocator allocator = allocator::heap())
{
@pool(allocator)
{
return new(os::getcwd(allocator::temp()), allocator);
};
}
fn Path! getcwd(Allocator allocator = allocator::heap()) @deprecated("Use new_cwd()")
{
@pool(allocator)
{
@@ -38,9 +49,10 @@ fn bool is_dir(Path path) => os::native_is_dir(path.str_view());
fn bool is_file(Path path) => os::native_is_file(path.str_view());
fn usz! file_size(Path path) => os::native_file_size(path.str_view());
fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
fn Path! tgetcwd() => getcwd(allocator::temp()) @inline;
fn Path! temp_cwd() => new_cwd(allocator::temp()) @inline;
fn Path! tgetcwd() @deprecated("Use temp_cwd()") => new_cwd(allocator::temp()) @inline;
fn void! chdir(Path path) => os::native_chdir(path) @inline;
fn Path! temp_directory(Allocator* allocator = allocator::heap()) => os::native_temp_directory(allocator);
fn Path! temp_directory(Allocator allocator = allocator::heap()) => os::native_temp_directory(allocator);
fn void! delete(Path path) => os::native_remove(path.str_view()) @inline;
macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV)
@@ -58,7 +70,17 @@ macro bool is_win32_separator(char c)
return c == '/' || c == '\\';
}
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator* allocator = allocator::heap())
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap()) @deprecated("use new_ls")
{
return new_ls(dir, no_dirs, no_symlinks, mask, allocator);
}
fn PathList! temp_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "")
{
return new_ls(dir, no_dirs, no_symlinks, mask, allocator::temp()) @inline;
}
fn PathList! new_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap())
{
$if $defined(os::native_ls):
return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator);
@@ -105,7 +127,7 @@ fn void! rmtree(Path path)
$endif
}
fn Path! new(String path, Allocator* allocator = allocator::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
fn Path! new(String path, Allocator allocator = allocator::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
{
return { normalize(path.copy(allocator), path_env), path_env };
}
@@ -115,7 +137,7 @@ fn Path! temp_new(String path, PathEnv path_env = DEFAULT_PATH_ENV)
return new(path, allocator::temp(), path_env);
}
fn Path! new_win32_wstring(WString path, Allocator* allocator = allocator::heap())
fn Path! new_win32_wstring(WString path, Allocator allocator = allocator::heap())
{
@pool(allocator)
{
@@ -123,12 +145,12 @@ fn Path! new_win32_wstring(WString path, Allocator* allocator = allocator::heap(
};
}
fn Path! new_windows(String path, Allocator* allocator = allocator::heap())
fn Path! new_windows(String path, Allocator allocator = allocator::heap())
{
return new(path, allocator, WIN32);
}
fn Path! new_posix(String path, Allocator* allocator = allocator::heap())
fn Path! new_posix(String path, Allocator allocator = allocator::heap())
{
return new(path, allocator, POSIX);
}
@@ -138,12 +160,17 @@ fn bool Path.equals(self, Path p2)
return self.env == p2.env && self.path_string == p2.path_string;
}
fn Path! Path.append(self, String filename, Allocator allocator = allocator::heap()) @deprecated("Use path.new_append(...)")
{
return self.new_append(filename, allocator) @inline;
}
/**
* Append the string to the current path.
*
* @param [in] filename
**/
fn Path! Path.append(self, String filename, Allocator* allocator = allocator::heap())
fn Path! Path.new_append(self, String filename, Allocator allocator = allocator::heap())
{
if (!self.path_string.len) return new(filename, allocator, self.env)!;
assert(!is_separator(self.path_string[^1], self.env));
@@ -158,7 +185,9 @@ fn Path! Path.append(self, String filename, Allocator* allocator = allocator::he
};
}
fn Path! Path.tappend(self, String filename) => self.append(filename, allocator::temp());
fn Path! Path.temp_append(self, String filename) => self.new_append(filename, allocator::temp());
fn Path! Path.tappend(self, String filename) @deprecated("Use path.temp_append(...)") => self.new_append(filename, allocator::temp());
fn usz Path.start_of_base_name(self) @local
{
@@ -166,7 +195,19 @@ fn usz Path.start_of_base_name(self) @local
if (!path_str.len) return 0;
if (self.env == PathEnv.WIN32)
{
return path_str.rindex_of_char('\\') + 1 ?? volume_name_len(path_str, self.env)!!;
if (try index = path_str.rindex_of_char('\\'))
{
// c:\ style path, we're done!
if (path_str[0] != '\\') return index + 1;
// Handle \\server\foo
// Find the \ before "foo"
usz last_index = 2 + path_str[2..].index_of_char('\\')!!;
// If they don't match, we're done
assert(last_index <= index, "Invalid normalized, path %d vs %s in %s", last_index, index, path_str);
if (last_index != index) return index + 1;
// Otherwise just default to the volume length.
}
return volume_name_len(path_str, self.env)!!;
}
return path_str.rindex_of_char('/') + 1 ?? 0;
}
@@ -176,28 +217,44 @@ fn bool! Path.is_absolute(self)
String path_str = self.str_view();
if (!path_str.len) return false;
usz path_start = volume_name_len(path_str, self.env)!;
if (path_start > 0 && path_str[0] == '\\') return true;
return path_start < path_str.len && is_separator(path_str[path_start], self.env);
}
fn Path! Path.absolute(self, Allocator* allocator = allocator::heap())
fn Path! Path.absolute(self, Allocator allocator = allocator::heap()) @deprecated("Use path.new_absolute()")
{
return self.new_absolute(allocator) @inline;
}
/**
* @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
**/
fn Path! Path.new_absolute(self, Allocator allocator = allocator::heap())
{
String path_str = self.str_view();
if (!path_str.len) path_str = ".";
if (!path_str.len) return PathResult.INVALID_PATH?;
if (self.is_absolute()!) return new(path_str, allocator, self.env);
if (path_str == ".")
{
@pool(allocator)
{
String cwd = os::getcwd(allocator::temp())!;
return new(cwd, allocator, self.env);
};
}
$if DEFAULT_PATH_ENV == WIN32:
@pool(allocator)
{
const usz BUFFER_LEN = 4096;
WString buffer = (WString)mem::temp_alloc_array(Char16, BUFFER_LEN);
buffer = win32::_wfullpath(buffer, path_str.to_temp_wstring()!, BUFFER_LEN);
if (!buffer) return PathResult.INVALID_PATH?;
return { string::new_from_wstring(buffer, allocator), WIN32 };
};
$else
String cwd = os::getcwd(allocator::temp())!;
return new(cwd, allocator, self.env);
}
switch (self.env)
{
case WIN32:
usz path_start = volume_name_len(path_str, self.env)!;
if (path_start > 0) return self;
case POSIX:
if (path_str[0] == PREFERRED_SEPARATOR_POSIX) return self;
}
String cwd = os::getcwd(allocator::temp())!;
return Path{ cwd, self.env }.append(path_str, allocator)!;
return Path { cwd, self.env }.new_append(path_str, allocator)!;
$endif
}
fn String Path.basename(self)
@@ -208,16 +265,40 @@ fn String Path.basename(self)
return path_str[basename_start..];
}
fn String Path.dirname(self)
{
usz basename_start = self.start_of_base_name();
String path_str = self.path_string;
if (basename_start == 0) return "";
if (basename_start == 0) return ".";
usz start = volume_name_len(path_str, self.env)!!;
if (basename_start <= start + 1) return path_str[:basename_start];
if (basename_start <= start + 1)
{
if (self.env == WIN32 && basename_start > start && path_str[0..1] == `\\`)
{
return path_str[:basename_start - 1];
}
return path_str[:basename_start];
}
return path_str[:basename_start - 1];
}
/**
* Test if the path has the given extension, so given the path /foo/bar.c3
* this would be true matching the extension "c3"
*
* @param [in] extension `The extension name (not including the leading '.')`
* @require extension.len > 0 : `The extension cannot be empty`
* @return `true if the extension matches`
**/
fn bool Path.has_extension(self, String extension)
{
String basename = self.basename();
if (basename.len <= extension.len) return false;
if (basename[^extension.len + 1] != '.') return false;
return basename[^extension.len..] == extension;
}
fn String! Path.extension(self)
{
String basename = self.basename();
@@ -248,13 +329,20 @@ fn usz! volume_name_len(String path, PathEnv path_env) @local
while (count < len && path[count] == '\\') count++;
// Not 2 => folded paths
if (count != 2) return 0;
// Check that we have a name followed by '/'
// Check that we have a name followed by '\'
isz base_found = 0;
for (usz i = 2; i < len; i++)
{
char c = path[i];
if (is_win32_separator(c)) return i;
if (is_win32_separator(c))
{
if (base_found) return i;
base_found = i;
continue;
}
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?;
}
if (base_found > 0 && base_found + 1 < len) return len;
return PathResult.INVALID_PATH?;
case 'A'..'Z':
case 'a'..'z':
@@ -281,6 +369,10 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
{
if (!path_str.len) return "";
usz path_start = volume_name_len(path_str, path_env)!;
if (path_start > 0 && path_env == PathEnv.WIN32)
{
for (usz i = 0; i < path_start; i++) if (path_str[i] == '/') path_str[i] = '\\';
}
usz path_len = path_str.len;
if (path_start == path_len) return path_str;
char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
@@ -384,7 +476,9 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
len++;
}
if (len > path_start + 1 && is_separator(path_str[len - 1], path_env)) len--;
path_str.ptr[len] = 0;
if (path_str.len > len) path_str.ptr[len] = 0;
// Empty path after normalization -> "."
if (!len) return ".";
return path_str[:len];
}
@@ -395,6 +489,7 @@ fn String Path.root_directory(self)
String path_str = self.str_view();
usz len = path_str.len;
if (!len) return "";
if (path_str == ".") return ".";
if (self.env == PathEnv.WIN32)
{
usz root_len = volume_name_len(path_str, self.env)!!;
@@ -417,18 +512,19 @@ def PathWalker = fn bool! (Path, bool is_dir, void*);
/*
* Walk the path recursively. PathWalker is run on every file and
* directory found. Return true to abort the walk.
* @require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
*/
fn bool! Path.walk(self, PathWalker w, void* data)
{
const PATH_MAX = 512;
@stack_mem(PATH_MAX; Allocator* allocator)
@stack_mem(PATH_MAX; Allocator allocator)
{
Path abs = self.absolute(allocator)!;
PathList files = ls(abs, .allocator = allocator)!;
Path abs = self.new_absolute(allocator)!;
PathList files = new_ls(abs, .allocator = allocator)!;
foreach (f : files)
{
if (f.str_view() == "." || f.str_view() == "..") continue;
f = abs.append(f.str_view(), allocator)!;
f = abs.new_append(f.str_view(), allocator)!;
bool is_directory = is_dir(f);
if (w(f, is_directory, data)!) return true;
if (is_directory && f.walk(w, data)!) return true;
@@ -448,6 +544,11 @@ fn bool Path.has_suffix(self, String str)
return self.str_view().ends_with(str);
}
fn void Path.free_with_allocator(self, Allocator allocator)
{
allocator::free(allocator, self.path_string.ptr);
}
fn void Path.free(self)
{
free(self.path_string.ptr);
@@ -459,7 +560,7 @@ fn usz! Path.to_format(&self, Formatter* formatter) @dynamic
return formatter.print(self.str_view());
}
fn String Path.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String Path.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return self.str_view().copy(allocator);
}

View File

@@ -9,7 +9,7 @@ interface InStream
fn usz! available() @optional;
fn usz! read(char[] buffer);
fn char! read_byte();
fn usz! write_to(OutStream* out) @optional;
fn usz! write_to(OutStream out) @optional;
fn void! pushback_byte() @optional;
}
@@ -21,10 +21,10 @@ interface OutStream
fn void! flush() @optional;
fn usz! write(char[] bytes);
fn void! write_byte(char c);
fn usz! read_to(InStream* in) @optional;
fn usz! read_to(InStream in) @optional;
}
fn usz! available(InStream* s)
fn usz! available(InStream s)
{
if (&s.available) return s.available();
if (&s.seek)
@@ -39,19 +39,19 @@ fn usz! available(InStream* s)
macro bool @is_instream(#expr)
{
return $assignable(#expr, InStream*);
return $assignable(#expr, InStream);
}
macro bool @is_outstream(#expr)
{
return $assignable(#expr, OutStream*);
return $assignable(#expr, OutStream);
}
/**
* @param [&out] ref
* @require @is_instream(stream)
**/
macro usz! read_any(stream, any* ref)
macro usz! read_any(stream, any ref)
{
return read_all(stream, ((char*)ref)[:ref.type.sizeof]);
}
@@ -61,7 +61,7 @@ macro usz! read_any(stream, any* ref)
* @require @is_outstream(stream)
* @ensure return == ref.type.sizeof
*/
macro usz! write_any(stream, any* ref)
macro usz! write_any(stream, any ref)
{
return write_all(stream, ((char*)ref)[:ref.type.sizeof]);
}
@@ -134,29 +134,23 @@ macro void! @pushback_using_seek(&s)
s.seek(-1, CURSOR)!;
}
fn usz! copy_to(InStream* in, OutStream* dst, char[] buffer = {})
fn usz! copy_to(InStream in, OutStream dst, char[] buffer = {})
{
if (buffer.len) return copy_through_buffer(in, dst, buffer);
if (&in.write_to) return in.write_to(dst);
if (&dst.read_to) return dst.read_to(in);
$switch (env::MEMORY_ENV)
$case NORMAL:
@pool()
{
return copy_through_buffer(in, dst, mem::temp_alloc_array(char, 4096));
};
return copy_through_buffer(in, dst, &&char[4096]{});
$case SMALL:
@pool()
{
return copy_through_buffer(in, dst, mem::temp_alloc_array(char, 1024));
};
return copy_through_buffer(in, dst, &&char[1024]{});
$case TINY:
$case NONE:
return copy_through_buffer(in, dst, &&(char[256]{}));
$endswitch
}
macro usz! copy_through_buffer(InStream *in, OutStream* dst, char[] buffer) @local
macro usz! copy_through_buffer(InStream in, OutStream dst, char[] buffer) @local
{
usz total_copied;
while (true)

View File

@@ -2,7 +2,7 @@ module std::io;
struct ReadBuffer (InStream)
{
InStream* wrapped_stream;
InStream wrapped_stream;
char[] bytes;
usz read_idx;
usz write_idx;
@@ -14,7 +14,7 @@ struct ReadBuffer (InStream)
* @require bytes.len > 0
* @require self.bytes.len == 0 "Init may not run on already initialized data"
**/
fn ReadBuffer* ReadBuffer.init(&self, InStream* wrapped_stream, char[] bytes)
fn ReadBuffer* ReadBuffer.init(&self, InStream wrapped_stream, char[] bytes)
{
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
return self;
@@ -63,7 +63,7 @@ fn void! ReadBuffer.refill(&self) @local @inline
struct WriteBuffer (OutStream)
{
OutStream* wrapped_stream;
OutStream wrapped_stream;
char[] bytes;
usz index;
}
@@ -74,7 +74,7 @@ struct WriteBuffer (OutStream)
* @require bytes.len > 0 "Non-empty buffer required"
* @require self.bytes.len == 0 "Init may not run on already initialized data"
**/
fn WriteBuffer* WriteBuffer.init(&self, OutStream* wrapped_stream, char[] bytes)
fn WriteBuffer* WriteBuffer.init(&self, OutStream wrapped_stream, char[] bytes)
{
*self = { .wrapped_stream = wrapped_stream, .bytes = bytes };
return self;

View File

@@ -3,7 +3,7 @@ import std::math;
struct ByteBuffer (InStream, OutStream)
{
Allocator* allocator;
Allocator allocator;
usz max_read;
char[] bytes;
usz read_idx;
@@ -16,17 +16,7 @@ struct ByteBuffer (InStream, OutStream)
* max_read defines how many bytes might be kept before its internal buffer is shrinked.
* @require self.bytes.len == 0 "Buffer already initialized."
**/
fn ByteBuffer*! ByteBuffer.init_new(&self, usz max_read, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(max_read, initial_capacity, allocator) @inline;
}
/**
* ByteBuffer provides a streamable read/write buffer.
* max_read defines how many bytes might be kept before its internal buffer is shrinked.
* @require self.bytes.len == 0 "Buffer already initialized."
**/
fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator* allocator = allocator::heap())
fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator allocator = allocator::heap())
{
*self = { .allocator = allocator, .max_read = max_read };
initial_capacity = max(initial_capacity, 16);
@@ -34,11 +24,6 @@ fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity =
return self;
}
fn ByteBuffer*! ByteBuffer.init_temp(&self, usz max_read, usz initial_capacity = 16) @deprecated("Replaced by temp_init")
{
return self.temp_init(max_read, initial_capacity) @inline;
}
fn ByteBuffer*! ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16)
{
return self.new_init(max_read, initial_capacity, allocator::temp());

View File

@@ -53,7 +53,7 @@ fn usz! ByteReader.seek(&self, isz offset, Seek seek) @dynamic
return new_index;
}
fn usz! ByteReader.write_to(&self, OutStream* writer) @dynamic
fn usz! ByteReader.write_to(&self, OutStream writer) @dynamic
{
if (self.index >= self.bytes.len) return 0;
usz written = writer.write(self.bytes[self.index..])!;

View File

@@ -5,16 +5,16 @@ struct ByteWriter (OutStream)
{
char[] bytes;
usz index;
Allocator* allocator;
Allocator allocator;
}
/**
* @param [&inout] self
* @param [&inout] allocator
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
* @require self.bytes.len == 0 "Init may not run on already initialized data"
* @ensure (bool)allocator, self.index == 0
**/
fn ByteWriter* ByteWriter.new_init(&self, Allocator* allocator = allocator::heap())
fn ByteWriter* ByteWriter.new_init(&self, Allocator allocator = allocator::heap())
{
*self = { .bytes = {}, .allocator = allocator };
return self;
@@ -22,18 +22,7 @@ fn ByteWriter* ByteWriter.new_init(&self, Allocator* allocator = allocator::heap
/**
* @param [&inout] self
* @param [&inout] allocator
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure (bool)allocator, self.index == 0
**/
fn ByteWriter* ByteWriter.init_new(&self, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(allocator) @inline;
}
/**
* @param [&inout] self
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
* @require self.bytes.len == 0 "Init may not run on already initialized data"
* @ensure self.index == 0
**/
fn ByteWriter* ByteWriter.temp_init(&self)
@@ -41,16 +30,6 @@ fn ByteWriter* ByteWriter.temp_init(&self)
return self.new_init(allocator::temp()) @inline;
}
/**
* @param [&inout] self
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure self.index == 0
**/
fn ByteWriter* ByteWriter.init_temp(&self) @deprecated("Replaced by temp_init")
{
return self.temp_init() @inline;
}
fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)
{
*self = { .bytes = data, .allocator = null };
@@ -97,7 +76,7 @@ fn void! ByteWriter.write_byte(&self, char c) @dynamic
* @param [&inout] self
* @param reader
**/
fn usz! ByteWriter.read_from(&self, InStream* reader) @dynamic
fn usz! ByteWriter.read_from(&self, InStream reader) @dynamic
{
usz start_index = self.index;
if (&reader.available)

View File

@@ -2,7 +2,7 @@ module std::io;
struct LimitReader (InStream)
{
InStream* wrapped_stream;
InStream wrapped_stream;
usz limit;
}
@@ -10,7 +10,7 @@ struct LimitReader (InStream)
* @param [&inout] wrapped_stream "The stream to read from"
* @param limit "The max limit to read"
**/
fn LimitReader* LimitReader.init(&self, InStream* wrapped_stream, usz limit)
fn LimitReader* LimitReader.init(&self, InStream wrapped_stream, usz limit)
{
*self = { .wrapped_stream = wrapped_stream, .limit = limit };
return self;

View File

@@ -2,7 +2,7 @@ module std::io;
struct Scanner (InStream)
{
InStream* wrapped_stream;
InStream wrapped_stream;
char[] buf;
usz pattern_idx;
usz read_idx;
@@ -16,7 +16,7 @@ struct Scanner (InStream)
* @param [&in] stream "The stream to read data from."
* @require buffer.len > 0 "Non-empty buffer required."
**/
fn void Scanner.init(&self, InStream* stream, char[] buffer)
fn void Scanner.init(&self, InStream stream, char[] buffer)
{
*self = { .wrapped_stream = stream, .buf = buffer };
}

View File

@@ -183,8 +183,8 @@ extern fn isz write(Fd fd, void* buffer, usz count) @if(!env::WIN32);
extern fn CFile fmemopen(void* ptr, usz size, ZString mode);
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
extern fn int timespec_get(TimeSpec* ts, int base);
extern fn int nanosleep(TimeSpec* req, TimeSpec* remaining);
extern fn CInt timespec_get(TimeSpec* ts, CInt base);
extern fn CInt nanosleep(TimeSpec* req, TimeSpec* remaining);
extern fn ZString ctime(Time_t *timer);
extern fn Time_t time(Time_t *timer);
@@ -360,6 +360,7 @@ const int EOF = -1;
const int FOPEN_MAX = 20;
const int FILENAME_MAX = 1024;
macro bool libc_S_ISTYPE(value, mask) @builtin => (value & S_IFMT) == mask;
const S_IFMT = 0o170000; // type of file mask
const S_IFIFO = 0o010000; // named pipe (fifo)
const S_IFCHR = 0o020000; // character special

View File

@@ -1,5 +1,7 @@
module libc @if(env::POSIX);
extern fn isz recv(Fd socket, void *buffer, usz length, CInt flags);
extern fn isz send(Fd socket, void *buffer, usz length, CInt flags);
extern fn void* dlopen(ZString path, int flags);
extern fn CInt dlclose(void*);
@@ -53,3 +55,139 @@ struct Stack_t
extern fn CInt sigaltstack(Stack_t* ss, Stack_t* old_ss);
extern fn CInt sigaction(CInt signum, Sigaction *action, Sigaction *oldaction);
module libc::termios @if(env::LIBC &&& env::POSIX);
distinct Cc = char;
distinct Speed = CUInt;
distinct Tcflags = CUInt;
const Tcflags TCOOFF = 0;
const Tcflags TCOON = 1;
const Tcflags TCIOFF = 2;
const Tcflags TCION = 3;
const Tcflags TCIFLUSH = 0;
const Tcflags TCOFLUSH = 1;
const Tcflags TCIOFLUSH = 2;
const Tcflags TCSANOW = 0;
const Tcflags TCSADRAIN = 1;
const Tcflags TCSAFLUSH = 2;
const Speed B0 = 0000000;
const Speed B50 = 0000001;
const Speed B75 = 0000002;
const Speed B110 = 0000003;
const Speed B134 = 0000004;
const Speed B150 = 0000005;
const Speed B200 = 0000006;
const Speed B300 = 0000007;
const Speed B600 = 0000010;
const Speed B1200 = 0000011;
const Speed B1800 = 0000012;
const Speed B2400 = 0000013;
const Speed B4800 = 0000014;
const Speed B9600 = 0000015;
const Speed B19200 = 0000016;
const Speed B38400 = 0000017;
const Speed B57600 = 0010001;
const Speed B115200 = 0010002;
const Speed B230400 = 0010003;
const Speed B460800 = 0010004;
const Speed B500000 = 0010005;
const Speed B576000 = 0010006;
const Speed B921600 = 0010007;
const Speed B1000000 = 0010010;
const Speed B1152000 = 0010011;
const Speed B1500000 = 0010012;
const Speed B2000000 = 0010013;
const Speed B2500000 = 0010014;
const Speed B3000000 = 0010015;
const Speed B3500000 = 0010016;
const Speed B4000000 = 0010017;
const Speed MAX_BAUD = B4000000;
const CInt VINTR = 0;
const CInt VQUIT = 1;
const CInt VERASE = 2;
const CInt VKILL = 3;
const CInt VEOF = 4;
const CInt VTIME = 5;
const CInt VMIN = 6;
const CInt VSWTC = 7;
const CInt VSTART = 8;
const CInt VSTOP = 9;
const CInt VSUSP = 10;
const CInt VEOL = 11;
const CInt VREPRINT = 12;
const CInt VDISCARD = 13;
const CInt VWERASE = 14;
const CInt VLNEXT = 15;
const CInt VEOL2 = 16;
const CInt ISIG = 0000001;
const CInt ICANON = 0000002;
const CInt ECHO = 0000010;
const CInt ECHOE = 0000020;
const CInt ECHOK = 0000040;
const CInt ECHONL = 0000100;
const CInt NOFLSH = 0000200;
const CInt TOSTOP = 0000400;
const CInt IEXTEN = 0100000;
const CInt CSIZE = 0000060;
const CInt CS5 = 0000000;
const CInt CS6 = 0000020;
const CInt CS7 = 0000040;
const CInt CS8 = 0000060;
const CInt CSTOPB = 0000100;
const CInt CREAD = 0000200;
const CInt PARENB = 0000400;
const CInt PARODD = 0001000;
const CInt HUPCL = 0002000;
const CInt CLOCAL = 0004000;
const CInt OPOST = 0000001;
const CInt OLCUC = 0000002;
const CInt ONLCR = 0000004;
const CInt OCRNL = 0000010;
const CInt ONOCR = 0000020;
const CInt ONLRET = 0000040;
const CInt OFILL = 0000100;
const CInt OFDEL = 0000200;
const CInt VTDLY = 0040000;
const CInt VT0 = 0000000;
const CInt VT1 = 0040000;
const CInt IGNBRK = 0000001;
const CInt BRKINT = 0000002;
const CInt IGNPAR = 0000004;
const CInt PARMRK = 0000010;
const CInt INPCK = 0000020;
const CInt ISTRIP = 0000040;
const CInt INLCR = 0000100;
const CInt IGNCR = 0000200;
const CInt ICRNL = 0000400;
const CInt IUCLC = 0001000;
const CInt IXON = 0002000;
const CInt IXANY = 0004000;
const CInt IXOFF = 0010000;
const CInt IMAXBEL = 0020000;
const CInt IUTF8 = 0040000;
extern fn CInt tcgetattr(Fd fd, Termios* self);
extern fn CInt tcsetattr(Fd fd, CInt optional_actions, Termios* self);
extern fn CInt tcsendbreak(Fd fd, CInt duration);
extern fn CInt tcdrain(Fd fd);
extern fn CInt tcflush(Fd fd, CInt queue_selector);
extern fn CInt tcflow(Fd fd, CInt action);
extern fn Speed cfgetospeed(Termios* self);
extern fn Speed cfgetispeed(Termios* self);
extern fn CInt cfsetospeed(Termios* self, Speed speed);
extern fn CInt cfsetispeed(Termios* self, Speed speed);
const CInt NCCS = 32;
struct Termios {
Tcflags c_iflag;
Tcflags c_oflag;
Tcflags c_cflag;
Tcflags c_lflag;
Cc c_line;
Cc[NCCS] c_cc;
Speed c_ispeed;
Speed c_ospeed;
}

View File

@@ -1,5 +1,5 @@
module libc @if(env::WIN32);
import std::os::win32;
extern fn CFile __acrt_iob_func(CInt c);
extern fn CInt _close(Fd fd); def close = _close;
@@ -15,11 +15,13 @@ extern fn Time_t _mkgmtime64(Tm* timeptr); def timegm = _mkgmtime64;
extern fn Time_t _mktime64(Tm *timeptr); def mktime = _mktime64;
extern fn usz _msize(void* ptr);
extern fn CInt _read(Fd fd, void* buffer, CUInt buffer_size);
extern fn CInt _setjmp(void* frameptr, JmpBuf* buffer) @if(env::WIN32);
extern fn CInt _setjmp(JmpBuf* buffer, void* frameptr);
extern fn CFile _wfopen(WString, WString);
extern fn CFile _wfreopen(WString, WString, CFile);
extern fn CInt _write(Fd fd, void* buffer, CUInt count);
extern fn CInt _wremove(WString);
extern fn int recv(Win32_SOCKET s, void* buf, int len, int flags);
extern fn int send(Win32_SOCKET s, void* buf, int len, int flags);
struct SystemInfo
{
@@ -45,7 +47,7 @@ extern fn CInt get_system_info(SystemInfo*) @extern("GetSystemInfo");
// Aliases to simplify libc use
macro Tm* localtime_r(Time_t* timer, Tm* buf) => _localtime64_s(buf, timer);
macro CInt setjmp(JmpBuf* buffer) => _setjmp($$frameaddress(0), buffer);
macro CInt setjmp(JmpBuf* buffer) => _setjmp(buffer, null);
macro Tm* gmtime_r(Time_t* timer, Tm* buf) => _gmtime64_s(buf, timer);
macro isz read(Fd fd, void* buffer, usz buffer_size) => _read(fd, buffer, (CUInt)buffer_size);
macro isz write(Fd fd, void* buffer, usz count) => _write(fd, buffer, (CUInt)count);

122
lib/std/libc/termios.c3 Normal file
View File

@@ -0,0 +1,122 @@
module libc::termios @if(env::LIBC &&& env::POSIX);
fn int sendBreak(Fd fd, int duration) => tcsendbreak(fd, duration);
fn int drain(Fd fd) => tcdrain(fd);
fn int flush(Fd fd, int queue_selector) => tcflush(fd, queue_selector);
fn int flow(Fd fd, int action) => tcflow(fd, action);
fn Speed Termios.getOSpeed(Termios* self) => cfgetospeed(self);
fn Speed Termios.getISpeed(Termios* self) => cfgetispeed(self);
fn int Termios.setOSpeed(Termios* self, Speed speed) => cfsetospeed(self, speed);
fn int Termios.setISpeed(Termios* self, Speed speed) => cfsetispeed(self, speed);
fn int Termios.getAttr(Termios* self, Fd fd) => tcgetattr(fd, self);
fn int Termios.setAttr(Termios* self, Fd fd, int optional_actions) => tcsetattr(fd, optional_actions, self);
module libc::termios @if(!env::LIBC ||| !env::POSIX);
distinct Cc = char;
distinct Speed = CUInt;
distinct Tcflags = CUInt;
struct Termios {
void* dummy;
}
fn CInt tcgetattr(Fd fd, Termios* self)
{
unreachable("tcgetattr unavailable");
}
fn CInt tcsetattr(Fd fd, CInt optional_actions, Termios* self)
{
unreachable("tcsetattr unavailable");
}
fn CInt tcsendbreak(Fd fd, CInt duration)
{
unreachable("tcsendbreak unavailable");
}
fn CInt tcdrain(Fd fd)
{
unreachable("tcdrain unavailable");
}
fn CInt tcflush(Fd fd, CInt queue_selector)
{
unreachable("tcflush unavailable");
}
fn CInt tcflow(Fd fd, CInt action)
{
unreachable("tcflow unavailable");
}
fn Speed cfgetospeed(Termios* self)
{
unreachable("cfgetospeed unavailable");
}
fn Speed cfgetispeed(Termios* self)
{
unreachable("cfgetispeed unavailable");
}
fn CInt cfsetospeed(Termios* self, Speed speed)
{
unreachable("cfsetospeed unavailable");
}
fn CInt cfsetispeed(Termios* self, Speed speed)
{
unreachable("cfsetispeed unavailable");
}
fn int sendBreak(Fd fd, int duration)
{
unreachable("sendBreak unavailable");
}
fn int drain(Fd fd)
{
unreachable("drain unavailable");
}
fn int flush(Fd fd, int queue_selector)
{
unreachable("flush unavailable");
}
fn int flow(Fd fd, int action)
{
unreachable("flow unavailable");
}
fn Speed Termios.getOSpeed(Termios* self)
{
unreachable("Termios.getOSpeed unavailable");
}
fn Speed Termios.getISpeed(Termios* self)
{
unreachable("Termios.getISpeed unavailable");
}
fn int Termios.setOSpeed(Termios* self, Speed speed)
{
unreachable("Termios.setOSpeed unavailable");
}
fn int Termios.setISpeed(Termios* self, Speed speed)
{
unreachable("Termios.setISpeed unavailable");
}
fn int Termios.getAttr(Termios* self, Fd fd)
{
unreachable("Termios.getAttr unavailable");
}
fn int Termios.setAttr(Termios* self, Fd fd, int optional_actions)
{
unreachable("Termios.setAttr unavailable");
}

View File

@@ -92,15 +92,13 @@ fault MatrixError
def Complexf = Complex(<float>);
def Complex = Complex(<double>);
def complexf_identity = complex::identity(<float>);
def complex_identity = complex::identity(<double>);
def COMPLEX_IDENTITY = complex::IDENTITY(<double>);
def COMPLEXF_IDENTITY = complex::IDENTITY(<float>);
def Quaternionf = Quaternion(<float>);
def Quaternion = Quaternion(<double>);
def QUATERNION_IDENTITY = quaternion::IDENTITY(<double>);
def QUATERNIONF_IDENTITY = quaternion::IDENTITY(<float>);
def quaternion_identity = quaternion::identity(<double>);
def quaternionf_identity = quaternion::identity(<float>);
def Matrix2f = Matrix2x2(<float>);
def Matrix2 = Matrix2x2(<double>);
@@ -161,7 +159,7 @@ macro atan2(x, y)
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
* @require $and(@typekind(y) == ARRAY || @typekind(y) == VECTOR, y.len == 2)
* @require (@typekind(y) == ARRAY || @typekind(y) == VECTOR) &&& y.len == 2
* @require $assignable(x, $typeof(y[0]))
**/
macro sincos(x, y)
@@ -268,7 +266,7 @@ macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))up
* @require values::@is_promotable_to_floatlike(mag) `The input must be a number value or float vector`
* @require values::@assign_to(sgn, values::promote_int(mag))
**/
macro copysign(mag, sgn) => $$copysign(values::promote_int(mag), ($typeof(values::promote_int(mag)))sgn);
macro copysign(mag, sgn) => $$copysign(values::promote_int_same(mag, sgn), ($typeof(values::promote_int_same(mag, sgn)))sgn);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
@@ -341,7 +339,10 @@ macro ln(x) => $$log(values::promote_int(x));
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
* @require values::@is_promotable_to_floatlike(base) `The base must be a number or a float vector`
**/
macro log(x, base) => $$log(values::promote_int(x)) / $$log(values::promote_int(base));
macro log(x, base)
{
return $$log(values::promote_int_same(x, base)) / $$log(values::promote_int_same(base, x));
}
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
@@ -364,7 +365,7 @@ macro max(x, y, ...)
$else
var m = $$max(x, y);
$for (var $i = 0; $i < $vacount; $i++)
m = $$max(m, $vaarg($i));
m = $$max(m, $vaarg[$i]);
$endfor
return m;
$endif
@@ -381,7 +382,7 @@ macro min(x, y, ...)
$else
var m = $$min(x, y);
$for (var $i = 0; $i < $vacount; $i++)
m = $$min(m, $vaarg($i));
m = $$min(m, $vaarg[$i]);
$endfor
return m;
$endif
@@ -405,7 +406,7 @@ macro nearbyint(x) => $$nearbyint(x);
macro pow(x, exp)
{
$if types::is_floatlike($typeof(exp)):
return $$pow(values::promote_int(x), ($typeof(values::promote_int(x)))exp);
return $$pow(values::promote_int_same(x, exp), ($typeof(values::promote_int_same(x, exp)))exp);
$else
return $$pow_int(values::promote_int(x), exp);
$endif
@@ -692,6 +693,7 @@ macro ichar ichar[<*>].or(ichar[<*>] x) => $$reduce_or(x);
macro ichar ichar[<*>].xor(ichar[<*>] x) => $$reduce_xor(x);
macro ichar ichar[<*>].max(ichar[<*>] x) => $$reduce_max(x);
macro ichar ichar[<*>].min(ichar[<*>] x) => $$reduce_min(x);
macro ichar ichar[<*>].dot(ichar[<*>] x, ichar[<*>] y) => (x * y).sum();
macro bool[<*>] short[<*>].comp_lt(short[<*>] x, short[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] short[<*>].comp_le(short[<*>] x, short[<*>] y) => $$veccomple(x, y);
@@ -707,6 +709,7 @@ macro short short[<*>].or(short[<*>] x) => $$reduce_or(x);
macro short short[<*>].xor(short[<*>] x) => $$reduce_xor(x);
macro short short[<*>].max(short[<*>] x) => $$reduce_max(x);
macro short short[<*>].min(short[<*>] x) => $$reduce_min(x);
macro short short[<*>].dot(short[<*>] x, short[<*>] y) => (x * y).sum();
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) => $$veccomple(x, y);
@@ -722,6 +725,7 @@ macro int int[<*>].or(int[<*>] x) => $$reduce_or(x);
macro int int[<*>].xor(int[<*>] x) => $$reduce_xor(x);
macro int int[<*>].max(int[<*>] x) => $$reduce_max(x);
macro int int[<*>].min(int[<*>] x) => $$reduce_min(x);
macro int int[<*>].dot(int[<*>] x, int[<*>] y) => (x * y).sum();
macro bool[<*>] long[<*>].comp_lt(long[<*>] x, long[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] long[<*>].comp_le(long[<*>] x, long[<*>] y) => $$veccomple(x, y);
@@ -736,6 +740,7 @@ macro long long[<*>].or(long[<*>] x) => $$reduce_or(x);
macro long long[<*>].xor(long[<*>] x) => $$reduce_xor(x);
macro long long[<*>].max(long[<*>] x) => $$reduce_max(x);
macro long long[<*>].min(long[<*>] x) => $$reduce_min(x);
macro long long[<*>].dot(long[<*>] x, long[<*>] y) => (x * y).sum();
macro bool[<*>] int128[<*>].comp_lt(int128[<*>] x, int128[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] int128[<*>].comp_le(int128[<*>] x, int128[<*>] y) => $$veccomple(x, y);
@@ -750,6 +755,7 @@ macro int128 int128[<*>].or(int128[<*>] x) => $$reduce_or(x);
macro int128 int128[<*>].xor(int128[<*>] x) => $$reduce_xor(x);
macro int128 int128[<*>].max(int128[<*>] x) => $$reduce_max(x);
macro int128 int128[<*>].min(int128[<*>] x) => $$reduce_min(x);
macro int128 int128[<*>].dot(int128[<*>] x, int128[<*>] y) => (x * y).sum();
macro bool[<*>] bool[<*>].comp_lt(bool[<*>] x, bool[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] bool[<*>].comp_le(bool[<*>] x, bool[<*>] y) => $$veccomple(x, y);
@@ -780,6 +786,7 @@ macro char char[<*>].or(char[<*>] x) => $$reduce_or(x);
macro char char[<*>].xor(char[<*>] x) => $$reduce_xor(x);
macro char char[<*>].max(char[<*>] x) => $$reduce_max(x);
macro char char[<*>].min(char[<*>] x) => $$reduce_min(x);
macro char char[<*>].dot(char[<*>] x, char[<*>] y) => (x * y).sum();
macro bool[<*>] ushort[<*>].comp_lt(ushort[<*>] x, ushort[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ushort[<*>].comp_le(ushort[<*>] x, ushort[<*>] y) => $$veccomple(x, y);
@@ -795,6 +802,7 @@ macro ushort ushort[<*>].or(ushort[<*>] x) => $$reduce_or(x);
macro ushort ushort[<*>].xor(ushort[<*>] x) => $$reduce_xor(x);
macro ushort ushort[<*>].max(ushort[<*>] x) => $$reduce_max(x);
macro ushort ushort[<*>].min(ushort[<*>] x) => $$reduce_min(x);
macro ushort ushort[<*>].dot(ushort[<*>] x, ushort[<*>] y) => (x * y).sum();
macro bool[<*>] uint[<*>].comp_lt(uint[<*>] x, uint[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] uint[<*>].comp_le(uint[<*>] x, uint[<*>] y) => $$veccomple(x, y);
@@ -810,6 +818,7 @@ macro uint uint[<*>].or(uint[<*>] x) => $$reduce_or(x);
macro uint uint[<*>].xor(uint[<*>] x) => $$reduce_xor(x);
macro uint uint[<*>].max(uint[<*>] x) => $$reduce_max(x);
macro uint uint[<*>].min(uint[<*>] x) => $$reduce_min(x);
macro uint uint[<*>].dot(uint[<*>] x, uint[<*>] y) => (x * y).sum();
macro bool[<*>] ulong[<*>].comp_lt(ulong[<*>] x, ulong[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ulong[<*>].comp_le(ulong[<*>] x, ulong[<*>] y) => $$veccomple(x, y);
@@ -825,6 +834,7 @@ macro ulong ulong[<*>].or(ulong[<*>] x) => $$reduce_or(x);
macro ulong ulong[<*>].xor(ulong[<*>] x) => $$reduce_xor(x);
macro ulong ulong[<*>].max(ulong[<*>] x) => $$reduce_max(x);
macro ulong ulong[<*>].min(ulong[<*>] x) => $$reduce_min(x);
macro ulong ulong[<*>].dot(ulong[<*>] x, ulong[<*>] y) => (x * y).sum();
macro bool[<*>] uint128[<*>].comp_lt(uint128[<*>] x, uint128[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] uint128[<*>].comp_le(uint128[<*>] x, uint128[<*>] y) => $$veccomple(x, y);
@@ -840,6 +850,7 @@ macro uint128 uint128[<*>].or(uint128[<*>] x) => $$reduce_or(x);
macro uint128 uint128[<*>].xor(uint128[<*>] x) => $$reduce_xor(x);
macro uint128 uint128[<*>].max(uint128[<*>] x) => $$reduce_max(x);
macro uint128 uint128[<*>].min(uint128[<*>] x) => $$reduce_min(x);
macro uint128 uint128[<*>].dot(uint128[<*>] x, uint128[<*>] y) => (x * y).sum();
macro char char.sat_add(char x, char y) => $$sat_add(x, y);
macro char char.sat_sub(char x, char y) => $$sat_sub(x, y);
@@ -1085,3 +1096,70 @@ macro overflow_mul_helper(x, y) @local
if ($$overflow_mul(x, y, &res)) return MathError.OVERFLOW?;
return res;
}
macro mul_div_helper(val, mul, div) @private
{
var $Type = $typeof(val);
return ($Type)(($Type)mul * (val / ($Type)div) + ($Type)mul * (val % ($Type)div) / ($Type)div);
}
macro char char.muldiv(self, char mul, char div) => mul_div_helper(self, mul, div);
macro ichar ichar.muldiv(self, ichar mul, ichar div) => mul_div_helper(self, mul, div);
macro short short.muldiv(self, short mul, short div) => mul_div_helper(self, mul, div);
macro ushort ushort.muldiv(self, ushort mul, ushort div) => mul_div_helper(self, mul, div);
macro int int.muldiv(self, int mul, int div) => mul_div_helper(self, mul, div);
macro uint uint.muldiv(self, uint mul, uint div) => mul_div_helper(self, mul, div);
macro long long.muldiv(self, long mul, long div) => mul_div_helper(self, mul, div);
macro ulong ulong.muldiv(self, ulong mul, ulong div) => mul_div_helper(self, mul, div);
macro bool @is_same_vector_or_scalar(#vector_value, #vector_or_scalar) @private
{
return (values::@is_vector(#vector_or_scalar) &&& values::@is_same_vector_type(#vector_value, #vector_or_scalar)) ||| values::@is_int(#vector_or_scalar);
}
/**
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
*/
macro char[<*>] char[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
/**
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
*/
macro ichar[<*>] ichar[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
/**
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
*/
macro short[<*>] short[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
/**
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
*/
macro ushort[<*>] ushort[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
/**
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
*/
macro int[<*>] int[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
/**
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
*/
macro uint[<*>] uint[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
/**
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
*/
macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
/**
* @require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
* @require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
*/
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);

View File

@@ -10,7 +10,7 @@ union Complex
}
macro Complex identity() => { 1, 0 };
const Complex IDENTITY = { 1, 0 };
macro Complex Complex.add(self, Complex b) => Complex { .v = self.v + b.v };
macro Complex Complex.add_each(self, Real b) => Complex { .v = self.v + b };
macro Complex Complex.sub(self, Complex b) => Complex { .v = self.v - b.v };

View File

@@ -60,7 +60,7 @@ fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
w = x + r;
if (big)
{
s = 1 - 2 * odd;
s = (double)(1 - 2 * odd);
v = s - 2.0 * (x + (r - w*w/(w + s)));
return sign ? -v : v;
}

View File

@@ -63,7 +63,7 @@ fn int __rem_pio2f(float x, double *y)
if (ix >= 0x7f800000)
{
// x is inf or NaN */
*y = x-x;
*y = x - (double)x;
return 0;
}
/* scale x into [2^23, 2^24-1] */

View File

@@ -11,7 +11,6 @@ union Quaternion
const Quaternion IDENTITY = { 0, 0, 0, 1 };
macro Quaternion identity() @deprecated("Replaced with QUATERNION_IDENTITY constant") => { 0, 0, 0, 1 };
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => Quaternion { .v = a.v + b.v };
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => Quaternion { .v = a.v + b };
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => Quaternion { .v = a.v - b.v };

View File

@@ -1,18 +1,15 @@
/**
* Randoms:
* General usage -
* 1. Create a Random (see std/math/random for various alternatives), or pick DefaultRandom
* 2. Define it `DefaultRandom my_random;`
* 3. Seed it: `random::seed(&my_random, some_seed);` or `rand::seed_entropy(&my_random);`
* 4. Use it with the functions in random: `float random_float = random::next_float(&my_random);`
*
* For just a simple integer between 0 and n (not including n), you can use `rand(n)`.
**/
module std::math::random;
interface Random
{
fn void set_seed(char[] input);
fn char next_byte();
fn ushort next_short();
fn uint next_int();
fn ulong next_long();
fn uint128 next_int128();
fn void next_bytes(char[] buffer);
}
macro bool is_random(random) => $assignable(random, Random*);
/**
* @require is_random(random)
**/
@@ -21,12 +18,19 @@ macro void seed(random, seed)
random.set_seed(@as_char_view(seed));
}
/**
* Seed the random with some best effort entropy.
*
* @require is_random(random)
**/
macro void seed_entropy(random)
{
random.set_seed(&&entropy());
}
/**
* Get the next value between 0 and max (not including max).
*
* @require is_random(random)
**/
macro int next(random, int max)
@@ -34,6 +38,11 @@ macro int next(random, int max)
return (int)(next_double(random) * max);
}
def DefaultRandom = Sfc64Random;
/**
* Get a default random value between 0 and max (not including max)
**/
fn int rand(int max) @builtin
{
tlocal Sfc64Random default_random;
@@ -45,15 +54,20 @@ fn int rand(int max) @builtin
}
return next(&default_random, max);
}
/**
* Get 'true' or 'false'
*
* @require is_random(random)
**/
macro void next_bool(random)
macro bool next_bool(random)
{
return random.next_byte() & 1;
return (bool)(random.next_byte() & 1);
}
/**
* Get a float between 0 and 1.0, not including 1.0.
*
* @require is_random(random)
**/
macro float next_float(random)
@@ -63,6 +77,8 @@ macro float next_float(random)
}
/**
* Get a double between 0 and 1.0, not including 1.0.
*
* @require is_random(random)
**/
macro double next_double(random)
@@ -71,6 +87,9 @@ macro double next_double(random)
return val * 0x1.0p-53;
}
// True if the value is a Random.
macro bool is_random(random) => $assignable(random, Random);
macro uint128 @long_to_int128(#function) => (uint128)#function << 64 + #function;
macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function;
macro uint @short_to_int(#function) => (uint)#function << 16 + #function;
@@ -94,4 +113,17 @@ macro @random_value_to_bytes(#function, char[] bytes)
bytes = bytes[$byte_size..];
}
unreachable();
}
}
// This is the interface to implement for Random implementations, usually
// it is not invoked directly.
interface Random
{
fn void set_seed(char[] input);
fn char next_byte();
fn ushort next_short();
fn uint next_int();
fn ulong next_long();
fn uint128 next_int128();
fn void next_bytes(char[] buffer);
}

View File

@@ -25,11 +25,11 @@ macro Vec4.distance_sq(self, Vec4 v2) => (self - v2).length_sq();
macro Vec2f.transform(self, Matrix4f mat) => transform2(self, mat);
macro Vec2f.rotate(self, float angle) => rotate(self, angle);
macro Vec2f.angle(self, Vec2f v2) => math::atan2(v2[1], v2[0]) - math::atan2(self[1], v2[0]);
macro Vec2f.angle(self, Vec2f v2) => math::atan2(v2.y, v2.x) - math::atan2(self.y, self.x);
macro Vec2.transform(self, Matrix4 mat) => transform2(self, mat);
macro Vec2.rotate(self, double angle) => rotate(self, angle);
macro Vec2.angle(self, Vec2 v2) => math::atan2(v2[1], v2[0]) - math::atan2(self[1], v2[0]);
macro Vec2.angle(self, Vec2 v2) => math::atan2(v2.y, v2.x) - math::atan2(self.y, self.x);
macro Vec2f.clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max);
macro Vec3f.clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max);
@@ -190,7 +190,7 @@ macro rotate_axis_angle(v, axis, angle) @private
var w = axis * math::sin(angle);
var wv = w.cross(v);
var wwv = w.cross(wv);
wv *= math::cos(angle) * 2;
wv *= math::cos(($typeof(v))angle) * 2;
wwv *= 2;
return v + wv + wwv;

View File

@@ -4,9 +4,9 @@ import std::ascii;
enum IpProtocol : char (AIFamily ai_family)
{
UNSPECIFIED (os::AF_UNSPEC),
IPV4 (os::AF_INET),
IPV6 (os::AF_INET6),
UNSPECIFIED = os::AF_UNSPEC,
IPV4 = os::AF_INET,
IPV6 = os::AF_INET6,
}
struct InetAddress (Printable)
@@ -56,7 +56,7 @@ fn usz! InetAddress.to_format(InetAddress* addr, Formatter* formatter) @dynamic
return formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!;
}
fn String InetAddress.to_new_string(InetAddress* addr, Allocator* allocator = allocator::heap()) @dynamic
fn String InetAddress.to_new_string(InetAddress* addr, Allocator allocator = allocator::heap()) @dynamic
{
if (addr.is_ipv6)
{

View File

@@ -27,6 +27,7 @@ fault NetError
ALREADY_CONNECTED,
NETWORK_UNREACHABLE,
OPERATION_NOT_SUPPORTED_ON_SOCKET,
CONNECTION_RESET,
}
fn uint! ipv4toint(String s)
@@ -57,7 +58,7 @@ fn uint! ipv4toint(String s)
return out;
}
fn String! int_to_new_ipv4(uint val, Allocator* allocator = allocator::heap())
fn String! int_to_new_ipv4(uint val, Allocator allocator = allocator::heap())
{
char[3 * 4 + 3 + 1] buffer;
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)!;

View File

@@ -16,11 +16,11 @@ struct Posix_pollfd
def Posix_nfds_t = CUInt;
extern fn CInt connect(NativeSocket socket, SockAddrPtr address, Socklen_t address_len);
extern fn NativeSocket socket(AIFamily af, AISockType type, AIProtocol ip_protocol) @extern("socket");
extern fn int fcntl(NativeSocket socket, int cmd, ...) @extern("fcntl");
extern fn CInt bind(NativeSocket socket, SockAddrPtr address, Socklen_t address_len) @extern("bind");
extern fn CInt listen(NativeSocket socket, CInt backlog) @extern("listen");
extern fn NativeSocket accept(NativeSocket socket, SockAddrPtr address, Socklen_t* address_len) @extern("accept");
extern fn NativeSocket socket(AIFamily af, AISockType type, AIProtocol ip_protocol);
extern fn int fcntl(NativeSocket socket, int cmd, ...);
extern fn CInt bind(NativeSocket socket, SockAddrPtr address, Socklen_t address_len);
extern fn CInt listen(NativeSocket socket, CInt backlog);
extern fn NativeSocket accept(NativeSocket socket, SockAddrPtr address, Socklen_t* address_len);
extern fn CInt poll(Posix_pollfd* fds, Posix_nfds_t nfds, CInt timeout);
const CUShort POLLIN = 0x0001;
@@ -39,6 +39,7 @@ fn anyfault convert_error(Errno error)
case errno::EALREADY: return NetError.CONNECTION_ALREADY_IN_PROGRESS;
case errno::EBADF: return NetError.BAD_SOCKET_DESCRIPTOR;
case errno::ECONNREFUSED: return NetError.CONNECTION_REFUSED;
case errno::ECONNRESET: return NetError.CONNECTION_RESET;
case errno::EISCONN: return NetError.ALREADY_CONNECTED;
case errno::ENETUNREACH: return NetError.NETWORK_UNREACHABLE;
case errno::ENOTSOCK: return NetError.NOT_A_SOCKET;
@@ -55,6 +56,11 @@ fn anyfault socket_error()
return convert_error(libc::errno());
}
macro bool NativeSocket.is_valid(self)
{
return (iptr)self >= 0;
}
macro void! NativeSocket.close(self)
{
if (libc::close(self))

View File

@@ -12,7 +12,7 @@ const int FIONREAD = 1074030207;
const int FIONBIO = -2147195266;
const int FIOASYNC = -2147195267;
distinct NativeSocket = uptr;
distinct NativeSocket = inline Win32_SOCKET;
extern fn CInt ioctlsocket(NativeSocket, CLong cmd, CULong *argp);
extern fn WSAError closesocket(NativeSocket);
@@ -22,6 +22,11 @@ extern fn int bind(NativeSocket, SockAddrPtr address, Socklen_t address_len);
extern fn int listen(NativeSocket, int backlog);
extern fn NativeSocket accept(NativeSocket, SockAddrPtr address, Socklen_t* address_len);
macro bool NativeSocket.is_valid(self)
{
return self != (NativeSocket)(uptr)-1;
}
fn void! NativeSocket.set_non_blocking(self, bool non_blocking)
{
if (ioctlsocket(self, win32::FIONBIO, &&(CULong)non_blocking))
@@ -76,6 +81,7 @@ fn anyfault convert_error(WSAError error)
case wsa::ENOTSOCK: return NetError.NOT_A_SOCKET;
case wsa::EOPNOTSUPP: return NetError.OPERATION_NOT_SUPPORTED_ON_SOCKET;
case wsa::ETIMEDOUT: return NetError.CONNECTION_TIMED_OUT;
case wsa::ECONNRESET: return NetError.CONNECTION_RESET;
default: return IoError.GENERAL_ERROR;
}
}

View File

@@ -15,7 +15,7 @@ macro void @loop_over_ai(AddrInfo* ai; @body(NativeSocket fd, AddrInfo* ai))
while (ai)
{
NativeSocket sockfd = os::socket(ai.ai_family, ai.ai_socktype, ai.ai_protocol);
if (sockfd > 0)
if (sockfd.is_valid())
{
@body(sockfd, ai);
}
@@ -83,12 +83,12 @@ macro Socket new_socket(fd, ai)
enum SocketOption : char (CInt value)
{
REUSEADDR (os::SO_REUSEADDR),
REUSEPORT (os::SO_REUSEPORT) @if(!env::WIN32),
KEEPALIVE (os::SO_KEEPALIVE),
BROADCAST (os::SO_BROADCAST),
OOBINLINE (os::SO_OOBINLINE),
DONTROUTE (os::SO_DONTROUTE),
REUSEADDR = os::SO_REUSEADDR,
REUSEPORT @if(!env::WIN32) = os::SO_REUSEPORT,
KEEPALIVE = os::SO_KEEPALIVE,
BROADCAST = os::SO_BROADCAST,
OOBINLINE = os::SO_OOBINLINE,
DONTROUTE = os::SO_DONTROUTE,
}
fn bool! Socket.get_broadcast(&self) => self.get_option(BROADCAST);
@@ -120,8 +120,12 @@ fn bool! Socket.get_option(&self, SocketOption option)
fn usz! Socket.read(&self, char[] bytes) @dynamic
{
isz n = libc::read((Fd)self.sock, bytes.ptr, bytes.len);
if (n < 0) return NetError.READ_FAILED?;
$if env::WIN32:
isz n = libc::recv(self.sock, bytes.ptr, (int)bytes.len, 0);
$else
isz n = libc::recv(self.sock, bytes.ptr, bytes.len, 0);
$endif
if (n < 0) return os::socket_error()?;
return (usz)n;
}
@@ -129,8 +133,12 @@ fn char! Socket.read_byte(&self) @dynamic => io::@read_byte_using_read(self);
fn usz! Socket.write(&self, char[] bytes) @dynamic
{
isz n = libc::write((Fd)self.sock, bytes.ptr, bytes.len);
if (n < 0) return NetError.WRITE_FAILED?;
$if env::WIN32:
isz n = libc::send(self.sock, bytes.ptr, (int)bytes.len, 0);
$else
isz n = libc::send(self.sock, bytes.ptr, bytes.len, 0);
$endif
if (n < 0) return os::socket_error()?;
return (usz)n;
}

View File

@@ -43,8 +43,9 @@ fn TcpServerSocket! listen(String host, uint port, uint backlog, SocketOption...
fn TcpSocket! accept(TcpServerSocket* server_socket)
{
TcpSocket socket;
socket.ai_addrlen = socket.ai_addr_storage.len;
socket.sock = os::accept(server_socket.sock, (SockAddrPtr)&socket.ai_addr_storage, &socket.ai_addrlen);
if (socket.sock < 0) return NetError.ACCEPT_FAILED?;
if (!socket.sock.is_valid()) return NetError.ACCEPT_FAILED?;
return socket;
}

View File

@@ -10,7 +10,7 @@ fault BacktraceFault
RESOLUTION_FAILED,
}
const Backtrace BACKTRACE_UNKNOWN = { 0, "", "", "", 0, null };
const Backtrace BACKTRACE_UNKNOWN = { 0, "", "", "", 0, null, false };
struct Backtrace (Printable)
{
@@ -19,7 +19,8 @@ struct Backtrace (Printable)
String object_file;
String file;
uint line;
Allocator* allocator;
Allocator allocator;
bool is_inline;
}
@@ -35,15 +36,16 @@ fn bool Backtrace.is_unknown(&self)
fn usz! Backtrace.to_format(&self, Formatter* formatter) @dynamic
{
String inline_suffix = self.is_inline ? " [inline]" : "";
if (self.has_file())
{
return formatter.printf("%s (in %s) (%s:%d)", self.function, self.object_file, self.file, self.line);
return formatter.printf("%s (in %s) (%s:%d)%s", self.function, self.object_file, self.file, self.line, inline_suffix);
}
if (self.is_unknown())
{
return formatter.printf("??? (in unknown)");
return formatter.printf("??? (in unknown)%s", inline_suffix);
}
return formatter.printf("%s (in %s) (source unavailable)", self.function, self.object_file);
return formatter.printf("%s (in %s) (source unavailable)%s", self.function, self.object_file, inline_suffix);
}
fn void Backtrace.free(&self)
{
@@ -53,7 +55,7 @@ fn void Backtrace.free(&self)
allocator::free(self.allocator, self.file);
}
fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator* allocator)
fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator allocator)
{
if (!allocator)
{
@@ -95,7 +97,7 @@ def symbolize_backtrace = linux::symbolize_backtrace @if(env::LINUX);
def symbolize_backtrace = win32::symbolize_backtrace @if(env::WIN32);
def symbolize_backtrace = darwin::symbolize_backtrace @if(env::DARWIN);
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator) @if(!env::NATIVE_STACKTRACE)
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator) @if(!env::NATIVE_STACKTRACE)
{
return {};
}

View File

@@ -9,7 +9,7 @@ import std::io::path, libc, std::os;
* @require name.len > 0
* @return! SearchResult.MISSING
**/
fn String! get_var(String name, Allocator* allocator = allocator::heap())
fn String! get_var(String name, Allocator allocator = allocator::heap())
{
@pool(allocator)
{
@@ -72,7 +72,7 @@ fn bool set_var(String name, String value, bool overwrite = true)
/**
* Returns the current user's home directory.
**/
fn String! get_home_dir(Allocator* using = allocator::heap())
fn String! get_home_dir(Allocator using = allocator::heap())
{
String home;
$if !env::WIN32:
@@ -83,10 +83,15 @@ fn String! get_home_dir(Allocator* using = allocator::heap())
return get_var(home, using);
}
fn Path! get_config_dir(Allocator allocator = allocator::heap()) @deprecated("use new_get_config_dir()")
{
return new_get_config_dir(allocator) @inline;
}
/**
* Returns the current user's config directory.
**/
fn Path! get_config_dir(Allocator* allocator = allocator::heap())
fn Path! new_get_config_dir(Allocator allocator = allocator::heap())
{
@pool(allocator)
{
@@ -100,7 +105,7 @@ fn Path! get_config_dir(Allocator* allocator = allocator::heap())
String s = get_var_temp("XDG_CONFIG_HOME") ?? get_var_temp("HOME")!;
const DIR = ".config";
$endif
return path::temp_new(s).append(DIR, .allocator = allocator);
return path::temp_new(s).new_append(DIR, .allocator = allocator);
$endif
};
}
@@ -126,11 +131,16 @@ fn bool clear_var(String name)
};
}
fn String! executable_path(Allocator *allocator = allocator::heap())
fn String! executable_path(Allocator allocator = allocator::heap()) @deprecated("use new_executable_path()")
{
return new_executable_path(allocator) @inline;
}
fn String! new_executable_path(Allocator allocator = allocator::heap())
{
$if env::DARWIN:
return darwin::executable_path(allocator);
$else
return "<Unsupported>";
return SearchResult.MISSING?;
$endif
}

View File

@@ -128,17 +128,17 @@ fn ulong! elf_module_image_base(String path) @local
return 0;
}
fn Backtrace! backtrace_load_from_exec(void* addr, Allocator* allocator) @local
fn void! backtrace_add_from_exec(BacktraceList* list, void* addr, Allocator allocator) @local
{
char[] buf = mem::temp_alloc_array(char, 1024);
String exec_path = process::execute_stdout_to_buffer(buf, {"realpath", "-e", string::tformat("/proc/%d/exe", posix::getpid())})!;
String obj_name = exec_path.copy(allocator);
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::tformat("0x%x", addr)})!;
return backtrace_from_addr2line(addr, addr2line, obj_name, "???", allocator);
return backtrace_add_addr2line(list, addr, addr2line, obj_name, "???", allocator);
}
fn Backtrace! backtrace_load_from_dlinfo(void* addr, Linux_Dl_info* info, Allocator* allocator) @local
fn void! backtrace_add_from_dlinfo(BacktraceList* list, void* addr, Linux_Dl_info* info, Allocator allocator) @local
{
char[] buf = mem::temp_alloc_array(char, 1024);
@@ -146,30 +146,19 @@ fn Backtrace! backtrace_load_from_dlinfo(void* addr, Linux_Dl_info* info, Alloca
ZString obj_path = info.dli_fname;
String sname = info.dli_sname ? info.dli_sname.str_view() : "???";
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_addr - 1)})!;
return backtrace_from_addr2line(addr, addr2line, info.dli_fname.str_view(), sname, allocator);
return backtrace_add_addr2line(list, addr, addr2line, info.dli_fname.str_view(), sname, allocator);
}
fn Backtrace! backtrace_from_addr2line(void* addr, String addr2line, String obj_name, String func_name, Allocator* allocator) @local
fn Backtrace! backtrace_line_parse(String string, String obj_name, String func_name, bool is_inlined, Allocator allocator)
{
String[] parts = addr2line.tsplit(" at ");
if (parts.len != 2)
{
return {
.function = func_name.copy(allocator),
.object_file = obj_name.copy(allocator),
.offset = (uptr)addr,
.file = "".copy(allocator),
.line = 0,
.allocator = allocator
};
}
String[] parts = string.trim().tsplit(" at ");
if (parts.len != 2) return SearchResult.MISSING?;
uint line = 0;
uint line = 0;
String source = "";
if (!parts[1].contains("?") && parts[1].contains(":"))
{
usz index = parts[1].rindex_of_char(':')!;
source = parts[1][:index];
line = parts[1][index + 1..].to_uint()!;
}
@@ -179,25 +168,53 @@ fn Backtrace! backtrace_from_addr2line(void* addr, String addr2line, String obj_
.file = source.copy(allocator),
.line = line,
.allocator = allocator,
.is_inline = is_inlined
};
}
fn Backtrace! backtrace_load_element(void* addr, Allocator* allocator = allocator::heap()) @local
fn void! backtrace_add_addr2line(BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name, Allocator allocator) @local
{
if (!addr) return backtrace::BACKTRACE_UNKNOWN;
String[] inline_parts = addr2line.tsplit("(inlined by)");
usz last = inline_parts.len - 1;
foreach (i, part : inline_parts)
{
bool is_inline = i != last;
Backtrace! trace = backtrace_line_parse(part, obj_name, func_name, is_inline, allocator);
if (catch trace)
{
list.push({
.function = func_name.copy(allocator),
.object_file = obj_name.copy(allocator),
.offset = (uptr)addr,
.file = "".copy(allocator),
.line = 0,
.allocator = allocator,
.is_inline = is_inline
});
continue;
}
list.push(trace);
}
}
fn void! backtrace_add_element(BacktraceList *list, void* addr, Allocator allocator = allocator::heap()) @local
{
if (!addr)
{
list.push(backtrace::BACKTRACE_UNKNOWN);
return;
}
@pool(allocator) {
Linux_Dl_info info;
if (dladdr(addr, &info) == 0)
{
return backtrace_load_from_exec(addr, allocator);
return backtrace_add_from_exec(list, addr, allocator);
}
return backtrace_load_from_dlinfo(addr, &info, allocator);
return backtrace_add_from_dlinfo(list, addr, &info, allocator);
};
}
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
{
BacktraceList list;
list.new_init(backtrace.len, allocator);
@@ -213,8 +230,7 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
{
foreach (addr : backtrace)
{
Backtrace trace = backtrace_load_element(addr, allocator)!;
list.append(trace);
backtrace_add_element(&list, addr, allocator)!;
}
};
return list;

View File

@@ -68,7 +68,7 @@ struct Darwin_segment_command_64
}
fn String! executable_path(Allocator *allocator)
fn String! executable_path(Allocator allocator)
{
char[4096] path;
uint len = path.len;
@@ -80,7 +80,7 @@ fn uptr! load_address() @local
{
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
if (!cmd) return BacktraceFault.SEGMENT_NOT_FOUND?;
String path = env::executable_path(allocator::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
String path = env::new_executable_path(allocator::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
uint dyld_count = darwin::_dyld_image_count();
for (uint i = 0; i < dyld_count; i++)
{
@@ -93,7 +93,7 @@ fn uptr! load_address() @local
}
fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_address, Allocator* allocator = allocator::heap()) @local
fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_address, Allocator allocator = allocator::heap()) @local
{
@pool(allocator)
{
@@ -132,7 +132,7 @@ fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_a
};
}
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
{
void *load_addr = (void *)load_address()!;
BacktraceList list;
@@ -150,7 +150,7 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
String execpath = executable_path(allocator::temp())!;
foreach (addr : backtrace)
{
list.append(backtrace_load_element(execpath, addr, load_addr, allocator) ?? backtrace::BACKTRACE_UNKNOWN);
list.push(backtrace_load_element(execpath, addr, load_addr, allocator) ?? backtrace::BACKTRACE_UNKNOWN);
}
};
return list;

View File

@@ -24,7 +24,7 @@ macro Class! class_by_name(ZString c)
return cls ?: ObjcFailure.CLASS_NOT_FOUND?;
}
macro Class[] class_get_list(Allocator *allocator = allocator::heap())
macro Class[] class_get_list(Allocator allocator = allocator::heap())
{
int num_classes = macos_objc_getClassList(null, 0);
if (!num_classes) return {};

View File

@@ -6,7 +6,6 @@ enum Win32_GET_FILEEX_INFO_LEVELS
MAX,
}
struct Win32_FILE_ATTRIBUTE_DATA
{
Win32_DWORD dwFileAttributes;
@@ -17,7 +16,6 @@ struct Win32_FILE_ATTRIBUTE_DATA
Win32_DWORD nFileSizeLow;
}
const MAX_PATH = 260;
struct Win32_WIN32_FIND_DATAW
@@ -99,6 +97,8 @@ extern fn CFile _fdopen(int fd, ZString mode);
extern fn CInt _access(ZString path, CInt mode);
extern fn CInt _waccess(WString path, CInt mode);
extern fn WString _wfullpath(WString absPath, WString relPath, usz maxLength);
/*
extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extern("GetCurrentDirectoryW");
extern bool _win32_CreateSymbolicLinkW(WString symlink_file, WString target_file, ulong flags) @extern("CreateSymbolicLinkW");

6
lib/std/os/win32/gdi.c3 Normal file
View File

@@ -0,0 +1,6 @@
module std::os::win32 @if(env::WIN32);
extern fn Win32_HBRUSH createSolidBrush(Win32_COLORREF) @extern("CreateSolidBrush");
extern fn Win32_COLORREF setTextColor(Win32_HDC, Win32_COLORREF) @extern("SetTextColor");
extern fn CInt setBkMode(Win32_HDC, CInt) @extern("SetBkMode");
extern fn Win32_BOOL textOut(Win32_HDC, CInt, CInt, Win32_LPCWSTR, CInt) @extern("TextOutW");

View File

@@ -2,10 +2,10 @@ module std::os::win32 @if(env::WIN32);
extern fn void* _aligned_malloc(usz size, usz alignment);
extern fn void* _aligned_realloc(void* memblock, usz size, usz alignment);
extern fn void* _aligned_recalloc(void* memblock, usz size, usz alignment);
extern fn void* _aligned_recalloc(void* memblock, usz num, usz size, usz alignment);
extern fn void _aligned_free(void* memblock);
extern fn void _aligned_msize(void* memblock, usz alignment, usz offset);
extern fn void* _aligned_offset_malloc(usz size, usz alignment, usz offset);
extern fn void* _aligned_offset_realloc(void* memblock, usz size, usz alignment, usz offset);
extern fn void* _aligned_offset_recalloc(void* memblock, usz size, usz alignment, usz offset);
extern fn void* _aligned_offset_recalloc(void* memblock, usz num, usz size, usz alignment, usz offset);
extern fn usz _msize(void* memblock);

View File

@@ -48,20 +48,22 @@ const UNDNAME_COMPLETE = 0x0000;
extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection");
extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection");
extern fn Win32_HANDLE createMutex(void*, bool, void*) @extern("CreateMutexA");
extern fn Win32_HANDLE createMutex(void*, Win32_BOOL, void*) @extern("CreateMutexA");
extern fn Win32_BOOL releaseMutex(Win32_HANDLE) @extern("ReleaseMutex");
extern fn void enterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection");
extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection");
extern fn Win32_BOOL tryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection");
extern fn uint waitForSingleObject(Win32_HANDLE, uint milliseconds) @extern("WaitForSingleObject");
extern fn Win32_DWORD waitForSingleObject(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds) @extern("WaitForSingleObject");
extern fn Win32_DWORD waitForSingleObjectEx(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds, Win32_BOOL bAlertable) @extern("WaitForSingleObjectEx");
extern fn Win32_DWORD waitForMultipleObjects(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds) @extern("WaitForMultipleObjects");
extern fn Win32_DWORD waitForMultipleObjectsEx(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds, Win32_BOOL bAlertable) @extern("WaitForMultipleObjectsEx");
extern fn void sleep(uint ms) @extern("Sleep");
extern fn uint waitForMultipleObjects(uint count, Win32_HANDLE* handles, bool wait_all, uint ms) @extern("WaitForMultipleObjects");
extern fn Win32_BOOL resetEvent(Win32_HANDLE event) @extern("ResetEvent");
extern fn Win32_BOOL setEvent(Win32_HANDLE handle) @extern("SetEvent");
extern fn long interlockedCompareExchange(int* dest, int exchange, int comperand) @extern("InterlockedCompareExchange");
extern fn Win32_DWORD sleepEx(Win32_DWORD ms, Win32_BOOL alertable) @extern("SleepEx");
extern fn Win32_HANDLE createThread(void* attributes, usz stack, ThreadFn func, void* arg, uint flags, uint* thread_id) @extern("CreateThread");
extern fn Win32_BOOL getExitCodeThread(Win32_HANDLE handle, uint* exit_code) @extern("GetExitCodeThread");
extern fn Win32_HANDLE createThread(void* attributes, usz stack, ThreadFn func, Win32_LPVOID arg, Win32_DWORD flags, Win32_LPDWORD thread_id) @extern("CreateThread");
extern fn Win32_BOOL getExitCodeThread(Win32_HANDLE handle, Win32_LPDWORD exit_code) @extern("GetExitCodeThread");
extern fn Win32_BOOL getExitCodeProcess(Win32_HANDLE hProcess, Win32_LPDWORD lpExitCode) @extern("GetExitCodeProcess");
extern fn Win32_DWORD getThreadId(Win32_HANDLE) @extern("GetThreadId");
extern fn void exitThread(Win32_DWORD dwExitCode) @noreturn @extern("ExitThread");
@@ -152,7 +154,7 @@ struct Symbol
Win32_DWORD64 displacement;
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator allocator)
{
BacktraceList list;
list.new_init(backtrace.len, allocator);
@@ -161,12 +163,12 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
defer symCleanup(process);
foreach (addr : backtrace)
{
list.append(resolve_backtrace(addr, process, allocator) ?? backtrace::BACKTRACE_UNKNOWN);
list.push(resolve_backtrace(addr, process, allocator) ?? backtrace::BACKTRACE_UNKNOWN);
}
return list;
}
fn Backtrace! resolve_backtrace(void* addr, Win32_HANDLE process, Allocator* allocator)
fn Backtrace! resolve_backtrace(void* addr, Win32_HANDLE process, Allocator allocator)
{
Symbol symbol;
//Win32_DWORD image_type = load_modules()!;

View File

@@ -0,0 +1,33 @@
module std::os::win32 @if(env::WIN32);
struct Win32_RECT
{
Win32_LONG left;
Win32_LONG top;
Win32_LONG right;
Win32_LONG bottom;
}
struct Win32_POINT
{
Win32_LONG x;
Win32_LONG y;
}
struct Win32_SIZE
{
Win32_LONG cx;
Win32_LONG cy;
}
def Win32_PSIZE = Win32_SIZE*;
def Win32_NPSIZE = Win32_SIZE*;
def Win32_LPSIZE = Win32_SIZE*;
def Win32_PPOINT = Win32_POINT*;
def Win32_NPOINT = Win32_POINT*;
def Win32_LPOINT = Win32_POINT*;
def Win32_PRECT = Win32_RECT*;
def Win32_NPRECT = Win32_RECT*;
def Win32_LPRECT = Win32_RECT*;

150
lib/std/os/win32/winuser.c3 Normal file
View File

@@ -0,0 +1,150 @@
module std::os::win32 @if(env::WIN32);
def Win32_WNDPROC = fn Win32_LRESULT(Win32_HWND, Win32_UINT, Win32_WPARAM, Win32_LPARAM);
struct Win32_WNDCLASSEXW
{
Win32_UINT cbSize;
Win32_UINT style;
Win32_WNDPROC lpfnWndProc;
CInt cbClsExtra;
CInt cbWndExtra;
Win32_HINSTANCE hInstance;
Win32_HICON hIcon;
Win32_HCURSOR hCursor;
Win32_HBRUSH hbrBackground;
Win32_LPCWSTR lpszMenuName;
Win32_LPCWSTR lpszClassName;
Win32_HICON hIconSm;
}
struct Win32_MSG
{
Win32_HWND hwnd;
Win32_UINT message;
Win32_WPARAM wParam;
Win32_LPARAM lParam;
Win32_DWORD time;
Win32_POINT pt;
Win32_DWORD lPrivate;
}
struct Win32_PAINTSTRUCT
{
Win32_HDC hdc;
Win32_BOOL fErase;
Win32_RECT rcPaint;
Win32_BOOL fRestore;
Win32_BOOL fIncUpdate;
Win32_BYTE[32] rgbReserved;
}
def Win32_PWNDCLASSEXW = Win32_WNDCLASSEXW*;
def Win32_LPWNDCLASSEXW = Win32_WNDCLASSEXW*;
def Win32_NPWNDCLASSEXW = Win32_WNDCLASSEXW*;
def Win32_PPAINTSTRUCT = Win32_PAINTSTRUCT*;
def Win32_LPPAINTSTRUCT = Win32_PAINTSTRUCT*;
def Win32_NPPAINTSTRUCT = Win32_PAINTSTRUCT*;
def Win32_PMSG = Win32_MSG*;
def Win32_LPMSG = Win32_MSG*;
def Win32_NPMSG = Win32_MSG*;
def Win32_ATOM = ushort;
const Win32_DWORD WS_BORDER = 0x00800000L;
const Win32_DWORD WS_CAPTION = 0x00C00000L;
const Win32_DWORD WS_CHILD = 0x40000000L;
const Win32_DWORD WS_CHILDWINDOW = 0x40000000L;
const Win32_DWORD WS_CLIPCHILDREN = 0x02000000L;
const Win32_DWORD WS_CLIPSIBLINGS = 0x04000000L;
const Win32_DWORD WS_DISABLED = 0x08000000L;
const Win32_DWORD WS_DLGFRAME = 0x00400000L;
const Win32_DWORD WS_GROUP = 0x00020000L;
const Win32_DWORD WS_HSCROLL = 0x00100000L;
const Win32_DWORD WS_ICONIC = 0x20000000L;
const Win32_DWORD WS_MAXIMIZE = 0x01000000L;
const Win32_DWORD WS_MAXIMIZEBOX = 0x00010000L;
const Win32_DWORD WS_MINIMIZE = 0x20000000L;
const Win32_DWORD WS_MINIMIZEBOX = 0x00020000L;
const Win32_DWORD WS_OVERLAPPED = 0x00000000L;
const Win32_DWORD WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
const Win32_DWORD WS_POPUP = 0x80000000L;
const Win32_DWORD WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU;
const Win32_DWORD WS_SIZEBOX = 0x00040000L;
const Win32_DWORD WS_SYSMENU = 0x00080000L;
const Win32_DWORD WS_TABSTOP = 0x00010000L;
const Win32_DWORD WS_THICKFRAME = 0x00040000L;
const Win32_DWORD WS_TILED = 0x00000000L;
const Win32_DWORD WS_TILEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
const Win32_DWORD WS_VISIBLE = 0x10000000L;
const Win32_DWORD WS_VSCROLL = 0x00200000L;
const Win32_UINT MB_OK = 0x00000000;
const Win32_UINT MB_OKCANCEL = 0x00000001;
const Win32_UINT MB_ABORTRETRYIGNORE = 0x00000002;
const Win32_UINT MB_YESNOCANCEL = 0x00000003;
const Win32_UINT MB_YESNO = 0x00000004;
const Win32_UINT MB_RETRYCANCEL = 0x00000005;
const Win32_UINT MB_CANCELTRYCONTINUE = 0x00000006;
const Win32_UINT MB_ICONHAND = 0x00000010;
const Win32_UINT MB_ICONQUESTION = 0x00000020;
const Win32_UINT MB_ICONEXCLAMATION = 0x00000030;
const Win32_UINT MB_ICONASTERISK = 0x00000040;
const Win32_UINT MB_USERICON = 0x00000080;
const Win32_UINT MB_ICONWARNING = MB_ICONEXCLAMATION;
const Win32_UINT MB_ICONERROR = MB_ICONHAND;
const Win32_UINT MB_ICONINFORMATION = MB_ICONASTERISK;
const Win32_UINT MB_ICONSTOP = MB_ICONHAND;
const GWL_WNDPROC @if(env::ARCH_32_BIT) = -4;
const GWL_HINSTANCE @if(env::ARCH_32_BIT) = -6;
const GWL_HWNDPARENT @if(env::ARCH_32_BIT) = -8;
const GWL_STYLE = -16;
const GWL_EXSTYLE = -20;
const GWL_USERDATA @if(env::ARCH_32_BIT) = -21;
const GWL_ID = -12;
const GWLP_WNDPROC = -4;
const GWLP_HINSTANCE = -6;
const GWLP_HWNDPARENT = -8;
const GWLP_USERDATA = -21;
const GWLP_ID = -12;
extern fn Win32_HDC beginPaint(Win32_HWND, Win32_LPPAINTSTRUCT) @extern("BeginPaint");
extern fn Win32_LRESULT callWindowProcW(Win32_WNDPROC lpPrevWndFunc, Win32_HWND hWnd, Win32_UINT msg, Win32_WPARAM wParam, Win32_LPARAM lParam) @extern("CallWindowProcW");
extern fn Win32_HWND createWindowExW(Win32_DWORD, Win32_LPCWSTR, Win32_LPCWSTR, Win32_DWORD, CInt, CInt, CInt, CInt, Win32_HWND, Win32_HMENU, Win32_HINSTANCE, Win32_LPVOID) @extern("CreateWindowExW");
extern fn Win32_LRESULT defWindowProcW(Win32_HWND, Win32_UINT, Win32_WPARAM, Win32_LPARAM) @extern("DefWindowProcW");
extern fn Win32_BOOL dispatchMessage(Win32_MSG* lpMsg) @extern("DispatchMessageW");
extern fn Win32_BOOL endPaint(Win32_HWND, Win32_LPPAINTSTRUCT) @extern("EndPaint");
extern fn Win32_BOOL getMessageW(Win32_LPMSG, Win32_HWND, Win32_UINT, Win32_UINT) @extern("GetMessageW");
extern fn Win32_BOOL getUpdateRect(Win32_HWND hWnd, Win32_LPRECT lpRect, Win32_BOOL bErase) @extern("GetUpdateRect");
extern fn Win32_LONG_PTR getWindowLongPtrW(Win32_HWND hWnd, CInt nIndex) @extern("GetWindowLongPtrW");
extern fn Win32_LONG getWindowLongW(Win32_HWND hWnd, CInt nIndex) @extern("GetWindowLongW");
extern fn Win32_HCURSOR loadCursorW(Win32_HINSTANCE instance, Win32_LPCWSTR cursorName) @extern("LoadCursorW");
extern fn Win32_HICON loadIconW(Win32_HINSTANCE instance, Win32_LPCWSTR iconName) @extern("LoadIconW");
extern fn int messageBoxW(Win32_HWND hWnd, Win32_LPCWSTR lpText, Win32_LPCWSTR lpCaption, Win32_UINT uType) @extern("MessageBoxW");
extern fn void postQuitMessage(CInt) @extern("PostQuitMessage");
extern fn Win32_ATOM registerClassExW(Win32_WNDCLASSEXW*) @extern("RegisterClassExW");
extern fn Win32_LONG_PTR setWindowLongPtrW(Win32_HWND hWnd, CInt nIndex, Win32_LONG_PTR dwNewLong) @extern("SetWindowLongPtrW");
extern fn Win32_LONG setWindowLongW(Win32_HWND hWnd, CInt nIndex, Win32_LONG dwNewLong) @extern("SetWindowLongW");
extern fn Win32_BOOL showWindow(Win32_HWND, CInt) @extern("ShowWindow");
extern fn Win32_BOOL translateMessage(Win32_MSG* lpMsg) @extern("TranslateMessage");
extern fn Win32_BOOL updateWindow(Win32_HWND) @extern("UpdateWindow");
macro getWindowLongPtr(Win32_HWND hWnd, CInt nIndex)
{
$if env::ARCH_64_BIT:
return getWindowLongPtrW(hWnd, nIndex);
$else
return getWindowLongW(hWnd, nIndex);
$endif
}
macro setWindowLongPtr(Win32_HWND hWnd, CInt nIndex, dwNewLong)
{
$if env::ARCH_64_BIT:
return setWindowLongPtrW(hWnd, nIndex, dwNewLong);
$else
return setWindowLongW(hWnd, nIndex, dwNewLong);
$endif
}

View File

@@ -32,6 +32,8 @@ extern fn CInt win32_WSAPoll(Win32_LPWSAPOLLFD fdArray, Win32_ULONG fds, Win32_I
extern fn WSAError win32_WSAGetLastError() @extern("WSAGetLastError") @builtin;
extern fn void win32_WSASetLastError(WSAError error) @extern("WSASetLastError") @builtin;
extern fn CInt win32_WSAStartup(Win32_WORD, void*) @extern("WSAStartup") @builtin;
extern fn CInt win32_WSACleanup() @extern("WSACleanup") @builtin;
const int FIONBIO = -2147195266;
const int FIONREAD = 1074030207;
const int SIOCATMARK = 1074033415;

View File

@@ -3,17 +3,20 @@ module std::sort;
/**
* Perform a binary search over the sorted array and return the index
* in [0, array.len) where x would be inserted or cmp(i) is true and cmp(j) is true for j in [i, array.len).
* @require $defined(list[0]) && $defined(list.len) "The list must be indexable"
* @require $or(@typeid(cmp) == void*.typeid, @is_comparer(cmp, list)) "Expected a comparison function which compares values"
* @require @is_sortable(list) "The list must be sortable"
* @require @is_valid_cmp_fn(cmp, list, context) "Expected a comparison function which compares values"
* @require @is_valid_context(cmp, context) "Expected a valid context"
**/
macro usz binarysearch(list, x, cmp = null) @builtin
macro usz binarysearch(list, x, cmp = EMPTY_MACRO_SLOT, context = EMPTY_MACRO_SLOT) @builtin
{
usz i;
usz len = @len_from_list(list);
var $no_cmp = @is_empty_macro_slot(cmp);
var $has_context = @is_valid_macro_slot(context);
for (usz j = len; i < j;)
{
usz half = i + (j - i) / 2;
$if @typeid(cmp) == void*.typeid:
$if $no_cmp:
switch
{
case greater(list[half], x): j = half;
@@ -21,11 +24,20 @@ macro usz binarysearch(list, x, cmp = null) @builtin
default: return half;
}
$else
$switch
$case $typeof(cmp).params[0] == @typeid(list[0]):
$case $defined(cmp(list[0], list[0], context)):
int res = cmp(list[half], x, context);
$case $defined(cmp(list[0], list[0])):
assert(!$has_context);
int res = cmp(list[half], x);
$default:
$case $defined(cmp(&list[0], &list[0], context)):
int res = cmp(&list[half], &x, context);
$case $defined(cmp(&list[0], &list[0])):
assert(!$has_context);
int res = cmp(&list[half], &x);
$default:
assert(false, "Invalid comparison function");
$endswitch
switch
{

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