Compare commits

...

477 Commits

Author SHA1 Message Date
Christoffer Lerno
84e10cf635 0.6.3 Release version. 2024-10-03 10:11:31 +02:00
Christoffer Lerno
1b8f8c5f5a Compiler crash when compiling c code in a library without --obj-out #1503. 2024-10-03 00:56:01 +02:00
Fernando López Guevara
131a783e89 feat(hash): added test for sha256 2024-10-02 16:55:17 +02:00
chri-k
2233f24c8f Add variants of DString.insert_at to match .append (#1510) 2024-10-02 10:22:59 +02:00
Christoffer Lerno
607a625641 Updated bigint. 2024-10-02 01:13:34 +02:00
Christoffer Lerno
9b49d19224 DString reverse and an initial BigInt implementation (untested), 2024-10-01 22:51:48 +02:00
Christoffer Lerno
46ae4353e0 Assume XTensa in 21+ instead. 2024-10-01 16:06:23 +02:00
Christoffer Lerno
44fcba2e3a Remove LLVM 20 2024-10-01 15:58:23 +02:00
Christoffer Lerno
f434795ee5 Test enable 19 and 20 2024-10-01 15:51:44 +02:00
Christoffer Lerno
0ea423d022 Foreach over distinct iterable would ignore operator(len). 2024-10-01 13:00:54 +02:00
Fernando López Guevara
c9b9de2838 fix(string): remove allocator argument on temp_ascii_to_lower 2024-09-30 22:01:44 +02:00
Christoffer Lerno
5918d5120f Foreach over distinct pointer failed to be caught as error #1506. 2024-09-30 21:32:33 +02:00
DanyDollaro
0d73f2fffa Added mutex tests (#1501)
* Added mutex tests. Add errorcheck in safe mode for Posix threads. Make non-recursive locks fail when used recursively on Windows. Fix thread pool tests. Simple locking count.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2024-09-30 20:57:16 +02:00
Christoffer Lerno
c8018c5543 Added generic PBKDF2 implementation. 2024-09-30 00:07:49 +02:00
Christoffer Lerno
071bd0ebf2 Fix bug when reading zip manifest, that would not return a zero terminated string. #1490 2024-09-29 21:14:03 +02:00
Christoffer Lerno
a00fce516e Added test and releasenotes for #1498. 2024-09-29 11:23:40 +02:00
chri-k
94abb3bd0c Fix escape sequence handling in encoding::json 2024-09-29 11:22:15 +02:00
Christoffer Lerno
2e94ea1a0d Improved error messages on Foo a = foo { 1 }; #1496 2024-09-28 23:53:31 +02:00
Christoffer Lerno
3b009e0b50 Added generic HMAC. 2024-09-28 23:28:11 +02:00
Christoffer Lerno
cc130e04dd Added MD5 and crypto::safe_compare. 2024-09-28 22:16:25 +02:00
Christoffer Lerno
2eca868540 Add UUID generation. 2024-09-28 20:58:03 +02:00
Christoffer Lerno
eeba5f020a Bad error on parameterized type without parameter #1495. 2024-09-28 19:44:57 +02:00
Christoffer Lerno
ded8bce8e6 Change no recursive import to use attribute. 2024-09-28 13:48:43 +02:00
Christoffer Lerno
8e7efaae99 ThreadPool is now adhoc available. 2024-09-28 13:28:39 +02:00
Alex Ling
fe9e434020 Fix incorrect to_gmt_offset result 2024-09-28 13:23:48 +02:00
Christoffer Lerno
8a0b0f5cf5 Disable fixed_pool where no threads are available. 2024-09-28 03:50:22 +02:00
Christoffer Lerno
5df321816b Unintended commit reverse. 2024-09-28 01:42:06 +02:00
Christoffer Lerno
7ff645c423 Free if broadcast fails. 2024-09-28 01:33:12 +02:00
Christoffer Lerno
93f290d57c Added a simple fixed threadpool which allocates. 2024-09-28 01:25:08 +02:00
Rachad ADEKAMBI
2146a76795 fix typo#1492 (#1493)
* fix typo#1492 
* Fix missing update in sema_decls

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2024-09-28 00:22:15 +02:00
chopsticks-user
b071e24d7e Minimal GL and GLFW bindings to render a triangle 2024-09-27 13:12:32 +02:00
Christoffer Lerno
a99e4b602a Error when slicing a struct with an inline array #1488. 2024-09-27 13:10:48 +02:00
Alex Ling
4cdea865f0 TzDateTime enhancements #1473 2024-09-26 12:32:05 +02:00
Christoffer Lerno
bf9ae2f0d3 Bad error message aliasing an ident with a path. #1481. 2024-09-25 21:41:40 +02:00
alex_s168
da2f958614 add x86 APX features (#1482)
add apx features Update cpu_detect.c3
2024-09-25 21:11:01 +02:00
Christoffer Lerno
b47201fe61 Fix handling of default target with libc. 2024-09-25 21:09:33 +02:00
Christoffer Lerno
a8932910d9 wasm32 / wasm64 targets are use-libc=no by default. 2024-09-25 21:01:00 +02:00
Christoffer Lerno
413877b59d Allow ^ suffix for non-recursive imports #1480. 2024-09-25 15:23:21 +02:00
Christoffer Lerno
da47588502 Make methods be available in earlier stages of analysis. Add @adhoc attribute to allow types with ad hoc generic declarations. 2024-09-25 14:26:49 +02:00
Christoffer Lerno
6f7ffbeb3c Add rand_in_range random function. Fix methodsof to apply to more types. Prevent methodsof in the wrong stage. 2024-09-25 00:18:11 +02:00
Christoffer Lerno
a258f2084f Allow specifying an import module using @wasm #1305. 2024-09-24 21:31:48 +02:00
Christoffer Lerno
d067a31ce6 Fix entropy 2024-09-24 20:15:10 +02:00
Christoffer Lerno
c6e4eee789 Added rnd function. 2024-09-24 19:02:03 +02:00
Christoffer Lerno
07c49f832e Safer seed of rand with WASM. 2024-09-24 18:38:11 +02:00
Christoffer Lerno
01b087238a Const initializer refactoring. Improve error on "Foo![]" #1477 2024-09-24 18:04:39 +02:00
Christoffer Lerno
d4832812ef Fix regression. 2024-09-23 14:13:55 +02:00
Christoffer Lerno
9c098fd79f Always flatten cont initializer inner type. 2024-09-23 02:51:53 +02:00
Christoffer Lerno
029d5e9068 Const initializer further cleanup. 2024-09-23 00:29:43 +02:00
Christoffer Lerno
f30486adf9 Const initializer cleanup. 2024-09-22 22:31:33 +02:00
Brian Sinquin
e21c337d3d Project fetch missing libraries (#1469)
Project fetch missing libs
2024-09-21 23:49:26 +02:00
Christoffer Lerno
f66f324e0e Suppor slicing of bytes. 2024-09-21 22:56:27 +02:00
Christoffer Lerno
885acdac24 Support compile time slicing of untyped lists. 2024-09-21 20:20:56 +02:00
Christoffer Lerno
ccb04c317c Fix bug due to enum associated values not being checked for liveness. 2024-09-21 18:05:20 +02:00
Christoffer Lerno
abbedeec4f Allow the "self" parameter to be $/# for macro methods. Fix bug when passing a type as a compile time value. 2024-09-21 15:55:39 +02:00
Christoffer Lerno
cdae3ec936 Some refactoring of the bitstruct representation. Correctly represent inner types. #1471 2024-09-21 13:43:52 +02:00
Christoffer Lerno
d727696830 Segfault using ternary with no assignment #1468. 2024-09-21 00:57:36 +02:00
Christoffer Lerno
2a9078a3b4 Also depend on Ubuntu20 2024-09-20 23:14:19 +02:00
Christoffer Lerno
ac479c7e40 llvm issue with try when bool is combined #1467 2024-09-20 20:15:44 +02:00
Christoffer Lerno
cda6ffea1e Slicing constant strings at compile time works. 2024-09-20 19:34:00 +02:00
Caleb-o
0900f401c0 Add Enum constraint to enummap
Fix typo in enumset require
2024-09-20 19:17:09 +02:00
Christoffer Lerno
d5a96ed637 Disabling LLVM 19 again. 2024-09-20 19:16:33 +02:00
Christoffer Lerno
8d9eff5297 Re-enable LLVM 19 2024-09-20 16:22:04 +02:00
Christoffer Lerno
19c1511901 Support linking .o files in compilation command. #1417 2024-09-20 16:21:29 +02:00
Christoffer Lerno
8e37e54645 Add env::COMPILER_BUILD_HASH and env::COMPILER_BUILD_DATE 2024-09-20 12:29:23 +02:00
Christoffer Lerno
c25645eab1 Add .gitkeep files to project subfolders. 2024-09-20 10:47:13 +02:00
Chandler
b9f7711f21 Update Arch Linux install instructions
c3c was recently added to the official Arch 'extra' repo. Updated the README to reflect this.
2024-09-20 00:40:58 +02:00
Christoffer Lerno
9447913de6 Use arena with JSON parser. Slightly altered output for json printing. 2024-09-20 00:39:10 +02:00
Brian Sinquin
8a9834cac0 Vendor-fetch download to lib directory specified in project.json (#1422) (#1441)
Vendor-fetch download default destination set to dependency-search-path in project.json Add fetched libraries in the project configuration file dependency entry.
2024-09-19 23:44:05 +02:00
Christoffer Lerno
2fec1c83a4 Enum attributes would be overwritten by enum value attributes. 2024-09-19 23:02:06 +02:00
Christoffer Lerno
ff36380ddf Allow user-defined attributes to have typed parameters. Folding a constant array of structs at compile time would cause an assert. 2024-09-19 22:21:29 +02:00
Christoffer Lerno
f2cfa61a39 User defined attributes could not have more than 1 parameter due to bug. 2024-09-19 21:14:08 +02:00
Christoffer Lerno
41156cc45d Temp allocator overwrites data when doing reset on extra allocated pages. #1462 2024-09-19 20:50:14 +02:00
Christoffer Lerno
9f51bfcc10 Support casting bitstructs to bool. 2024-09-19 01:03:06 +02:00
Christoffer Lerno
9426e813be Add test. 2024-09-18 23:16:07 +02:00
Christoffer Lerno
20fd7aba9b Regression when passing types as #expr arguments. #1461 2024-09-18 22:16:26 +02:00
Christoffer Lerno
3bada4560e Correctly print interfaces. 2024-09-18 14:53:55 +02:00
Christoffer Lerno
9719abe99a Better slice error message, and enable slice on non-vaarg. 2024-09-18 10:55:18 +02:00
Christoffer Lerno
5540519e52 Cleanup. 2024-09-18 10:20:57 +02:00
Christoffer Lerno
0b94e73c0b Regression fix arguments needed when presenting error on a method. 2024-09-18 10:07:39 +02:00
Christoffer Lerno
5e2a06bfd6 Update shell argument escape. 2024-09-18 00:48:39 +02:00
Christoffer Lerno
ac95e411bc Make str_eq safe to use with NULL. 2024-09-17 23:48:36 +02:00
Christoffer Lerno
08219fc57e Fix reordering semantics in struct assignment. 2024-09-17 22:41:49 +02:00
Josh Ring
09643f3c8b fix install instructions on ubuntu's latest LTS (#1449)
* fix install instructions on ubuntu's latest LTS
* cleanup arch linux and fixed some typos
2024-09-17 01:38:50 +02:00
Christoffer Lerno
08a575fa82 Crash invoking a @body argument with the wrong number of parameters. 2024-09-17 00:33:38 +02:00
Christoffer Lerno
62887a6ce8 Temporarily disable building with LLVM 19 and 20 2024-09-17 00:28:53 +02:00
Christoffer Lerno
297a6c9348 Support C3C_LIB and C3C_CC environment variables. Enable compiling against 20 to see if it works. 2024-09-17 00:12:50 +02:00
Christoffer Lerno
1181edc48e Fix error message when not finding a particular function 2024-09-16 22:43:30 +02:00
Christoffer Lerno
81f1930349 Code cleanup. Correct deprecation notice on '$or'. Allow "self" param on macro method to be constant. 2024-09-16 10:40:34 +02:00
RealPacket
54a1819d46 chore: Remove overrides for C3 files (#1450) 2024-09-16 10:21:41 +02:00
Christoffer Lerno
1b5472cc94 Add paramsof. 2024-09-15 23:43:09 +02:00
Christoffer Lerno
06a083bafc Lambda / function type would accidentally be processed as a method. 2024-09-15 22:12:03 +02:00
Christoffer Lerno
9bb45cb6a3 Add missing concat. Fix error message location on not enough arguments. 2024-09-15 15:56:13 +02:00
Real-Packet
1bfe9c568e chore(ci): Update actions/*-artifact to v4 (simple find & replace)
Because v3 is being deprecated and uses Node 16 instead of Node 20.
v4 is also a drop in replacement so we can just find & replace the usages without any changes to easily upgrade to v4.
2024-09-15 02:11:00 +02:00
Christoffer Lerno
4f5e5fcdba Remove mem tap. 2024-09-15 02:10:19 +02:00
Christoffer Lerno
bca44d1c14 Bug with casting anyfault to error. 2024-09-15 00:55:22 +02:00
Christoffer Lerno
e6d1d66c8f Updated grammar script and fix concat op 2024-09-14 23:13:06 +02:00
Christoffer Lerno
c3a5f5c0f0 Build macos version with LLVM 17 2024-09-14 22:18:33 +02:00
Christoffer Lerno
c0875d7987 Fix macos release. 2024-09-14 22:15:16 +02:00
Christoffer Lerno
6b6ac2bcb3 Rename release files. 2024-09-14 20:19:12 +02:00
Christoffer Lerno
f78466452a Updated grammar.y 2024-09-14 19:24:21 +02:00
Christoffer Lerno
f51230e793 Fix bugs in "trap-on-wrap" #1434. 2024-09-14 16:19:40 +02:00
Christoffer Lerno
3ab201ce10 Use atexit to fix finalizers on Windows #1361. 2024-09-14 16:17:57 +02:00
Christoffer Lerno
45a94cfe86 Asserts are now correctly included and traced in when running tests. Removed accidental debug trace. 2024-09-14 13:48:27 +02:00
Christoffer Lerno
f16cc999bd Fix bug where inline index access to array in a struct would crash the compiler. 2024-09-14 12:58:37 +02:00
Christoffer Lerno
d39f25efd3 Support inline struct designated init as if inline was anonymous. 2024-09-13 20:31:21 +02:00
Christoffer Lerno
6b2ce6de6f Fix unreachable. 2024-09-13 15:32:26 +02:00
Christoffer Lerno
3f1738e0fe Unified constant handling. 2024-09-13 15:11:15 +02:00
Real-Packet
3ceaf2ab81 chore: migrate to softprops/action-gh-release
Hopefully this works. Had to generate a new GPG key because GPG requires a password for signing stuff with GPG and I forgot to use it to sign something so I could save it in the password manager.
Fixes c3lang/c3c#1437
2024-09-13 15:07:45 +02:00
wilsonk
4c7d61ae82 Bsd family fixes (#1435)
Some small fixes for the BSD's
Try fcntl for NetBSD
Fixes for stdin, etc. and setjmp/longjmp
2024-09-13 14:49:51 +02:00
Alexey Kutepov
d53dd57b84 Introduce os::native_fputc() abstraction layer for File.write_byte() (#1440)
Introduce os::native_fputc() abstraction layer for File.write_byte()
2024-09-13 12:25:41 +02:00
Christoffer Lerno
6ff5ac5592 Removed unused functions. 2024-09-12 15:28:19 +02:00
Christoffer Lerno
9ce1bbe3cd Update README.md
Fix link
2024-09-12 14:02:10 +02:00
Christoffer Lerno
65c48419d0 Minor refactorings. Added "Thank you" section to readme. Some fixes to hostinfo. 2024-09-12 13:47:49 +02:00
alex_s168
d376ee6671 ability to disable llvm at compile time (#1433)
ability to disable llvm at compile time
2024-09-12 13:36:00 +02:00
Christoffer Lerno
eaa419a48d Correct '.so' suffix on dynamic libraries on Linux. 2024-09-12 09:21:23 +02:00
Christoffer Lerno
1b6ec34c61 Refactor alignment code. Change deprecated function in test. 2024-09-12 08:30:01 +02:00
Christoffer Lerno
9f4da339c3 Support int[*] { 1, 2, 3 } expressions. 2024-09-12 00:11:09 +02:00
wilsonk
1b54a99f6a Add initial FreeBSD support (#1430)
Add initial FreeBSD support
2024-09-11 22:38:53 +02:00
ElaDeCode
2b0d2892af move macro matrix_look_at to matrix module 2024-09-11 14:58:42 +02:00
Fernando López Guevara
27f2d201ed fix: cast native thread 2024-09-11 10:12:33 +02:00
Christoffer Lerno
d6cf622e49 Make subscript use its own "index" type rather than reuse Range. 2024-09-10 22:11:19 +02:00
Christoffer Lerno
2092e2167e Add io::read_new_fully for reading to the end of a stream. Add io::wrap_bytes for reading bytes with io functions. 2024-09-10 13:21:07 +02:00
Christoffer Lerno
503032cbcf Update range checking. 2024-09-10 13:21:07 +02:00
Christoffer Lerno
6f90e13502 Fix regression for $include. 2024-09-10 12:15:45 +02:00
Christoffer Lerno
b22bd459dd Fix regression for splat. 2024-09-10 00:21:01 +02:00
Christoffer Lerno
f67147a405 Fix bug in new splat code, fixes #1423. 2024-09-09 22:12:30 +02:00
Christoffer Lerno
df4eb3d0f0 Allow var in lambdas in macros. Allow ad hoc generic declaration in lambdas and type definitions. Fix deprecation flag. 2024-09-09 21:46:06 +02:00
Christoffer Lerno
32cc4bcd03 Fix issues for compiling on 32-bit. 2024-09-09 00:55:50 +02:00
Christoffer Lerno
1502c6d660 Limit object filename lengths. #1415 2024-09-07 23:38:20 +02:00
Christoffer Lerno
d4fb5b747b Update QOI type names. 2024-09-07 16:10:15 +02:00
Hema2
7581651011 Add QOI to the standard library (#1409)
Add QOI to the standard library
2024-09-07 15:55:26 +02:00
Christoffer Lerno
4f54e273ab Asserts are retained regardless of optimization when running tests. 2024-09-07 15:40:32 +02:00
Christoffer Lerno
1cc1b83b6f format functions are now functions and work better with splat. 2024-09-07 14:34:30 +02:00
Christoffer Lerno
8e9199f453 Untyped splat. 2024-09-07 14:26:42 +02:00
Christoffer Lerno
223501eeca Support splat for varargs #1352. 2024-09-07 05:26:43 +02:00
Christoffer Lerno
7649738618 Improve lvalue handling in the compiler. #1357 2024-09-07 03:19:35 +02:00
Christoffer Lerno
78c60ae695 Increase stack size for msys. 2024-09-07 01:37:45 +02:00
Christoffer Lerno
f5f122d5a5 Reduce recursion depth. Improve error message. 2024-09-07 00:48:16 +02:00
Christoffer Lerno
4b27a33a10 Refactor vasplat. 2024-09-07 00:29:41 +02:00
id3nom
15aca2eb84 Add CMake option C3_ENABLE_CLANGD_LSP (#1414)
* CMake option C3_ENABLE_CLANGD_LSP

The CMake option enables the generation of compile_commands.json in
the build directory and the creation of a symlink in the root
directory targeting the new file,
this will allow the Clangd Language Server Protocol (LSP) to
function properly.

* Added .editorconfig

EditorConfig helps maintain consistent coding styles:
https://editorconfig.org/
2024-09-06 23:06:09 +02:00
Christoffer Lerno
1cb91c0ac9 Fold default args in non-debug. 2024-09-06 23:04:09 +02:00
Christoffer Lerno
840b3b3161 "optsize" did not work correctly in project.json. 2024-09-06 22:55:15 +02:00
Christoffer Lerno
a5cf3ce2f1 Update releasenotes. 2024-09-06 20:56:38 +02:00
Lars Nilsson
04c85eb9ce Adding hashmap and map initialization functions with data (#1402)
Adding hashmap and map initialization functions with data to populate them with
2024-09-06 20:55:42 +02:00
Christoffer Lerno
82364d2e3c Function vasplat refactoring. 2024-09-06 20:54:28 +02:00
Christoffer Lerno
3db7bf5dfd Crash when reading an empty 'manifest.json'. 2024-09-06 18:05:43 +02:00
Christoffer Lerno
de13023981 Converting a slice to a vector/array would copy too little data. 2024-09-06 15:36:43 +02:00
Christoffer Lerno
35b825c78a Function vasplat refactoring. 2024-09-06 11:44:48 +02:00
Christoffer Lerno
28428fcf30 Handle "splice splat" in the vararg slot as an expression. 2024-09-06 10:43:03 +02:00
Christoffer Lerno
1e570bf506 Rename vec_erase_ptr_at to vec_erase_at. 2024-09-06 00:41:07 +02:00
Christoffer Lerno
ad0e97ab7b Deprecated inline generic types, deprecated tuple / triple types. 2024-09-05 23:42:20 +02:00
Christoffer Lerno
ed5d338a39 Added new style named arguments. 2024-09-05 22:13:22 +02:00
Christoffer Lerno
e795745e43 lvalue refactoring. 2024-09-05 22:09:35 +02:00
Christoffer Lerno
5e4d790fc3 Fixing incorrectly solved generic module name collision bug. 2024-09-04 21:51:03 +02:00
Christoffer Lerno
7e47f4ed08 Generic methods were incorrectly registered as functions, leading to naming collisions. #1402 2024-09-04 15:13:29 +02:00
Christoffer Lerno
63fc77a861 Move of const to separate file and removal of old concat code. 2024-09-04 09:34:51 +02:00
Christoffer Lerno
59ff94c005 Issue where a lambda wasn't correctly registered as external. #1408 2024-09-03 23:25:47 +02:00
Christoffer Lerno
bbc199cda3 Some cleanup of asm and assert 2024-09-03 13:53:15 +02:00
Christoffer Lerno
df91ee3d2a Update version. 2024-09-03 11:48:43 +02:00
Christoffer Lerno
528fecef4d Create release. 2024-09-02 23:21:01 +02:00
Christoffer Lerno
d39f1a6af0 Fix to test. 2024-09-02 22:53:54 +02:00
Christoffer Lerno
4367ef11fa Further fixing storeload. 2024-09-02 22:37:50 +02:00
Ikko Eltociear Ashimine
b8d77d2490 chore: update linux.c3
Recieve -> Receive
2024-09-02 11:07:34 +02:00
Christoffer Lerno
2600c3116c Do not add the libc allocator if it isn't available. 2024-09-02 01:44:35 +02:00
Christoffer Lerno
2506c2579b Prevent loading / storing large structs with LLVM. 2024-09-02 01:16:48 +02:00
Lars Nilsson
fe7392a656 Adding check of HTTP response code so that Windows will remove up the opened file if the download was not successful 2024-09-01 21:44:09 +02:00
Christoffer Lerno
8b7a3f1835 Optimize the value after foo()!! 2024-09-01 21:14:30 +02:00
Christoffer Lerno
e6acc56c1f Bug where if try would work incorrectly in a macro. 2024-08-31 23:09:36 +02:00
Christoffer Lerno
d635cfb90f printf will now show errors in the output when there are errors. 2024-08-31 19:36:18 +02:00
Christoffer Lerno
6cb6113c57 - Memory leak in Object when not using temp allocators.
- Tracking allocator would double the allocations in the report.
2024-08-31 03:35:39 +02:00
Alexey Kutepov
6aea0f12cd Properly persist git hash on each build (#1391)
Properly persist git hash on each build

Rebuild `git_hash.h` only when `.git` folder changes

`add_custom_target()` always considers its target out-of-date which
leads to rebuilding of `git_hash.h` on every build (which is
ironically what we wanted) and consequently rebuilding of
build_options.c and relinking of c3c even when no changes are made,
which is mildly annoying.

We are replacing `add_custom_target()` with `add_custom_command()`
which depends on `.git`, so `git_hash.h` is only rebuilt if any git
commands are performed. Which is less annoying.

In case of no `.git` we simply do not depend on it which leads to
`git_hash.h` being rebuilt only once.
2024-08-30 15:01:08 +02:00
Christoffer Lerno
99ace59b45 Create a build library on --test. 2024-08-30 14:55:35 +02:00
Christoffer Lerno
31f9ed3e6b Methods can now properly be aliased using def #1393. 2024-08-30 12:50:39 +02:00
Christoffer Lerno
573c0881e9 Correctly use wincrt setting for in libraries. 2024-08-29 23:53:14 +02:00
Christoffer Lerno
7134b3ba35 Update Raylib examples to use Raylib5. 2024-08-29 23:34:31 +02:00
Christoffer Lerno
bc267e22bd Add fmod implementation for nolibc. 2024-08-29 20:04:59 +02:00
Christoffer Lerno
3d83316b03 Regression: backtrace accidentally turned off by default. 2024-08-29 19:15:50 +02:00
Christoffer Lerno
dfe80eb050 Improve the error message when the compilation does not produce any files #1390. 2024-08-28 11:16:39 +02:00
Christoffer Lerno
22151a0a03 Fix bug with defer (catch err) when used together with regular defer. 2024-08-28 10:41:59 +02:00
rexim
484a9acc6f Print Git Hash on --version 2024-08-27 04:41:39 +02:00
Christoffer Lerno
26acce246d Fixed int128 div/mod. Fix WASM memory init priority. 2024-08-27 04:31:14 +02:00
Christoffer Lerno
388578c209 Too restrictive compile time checks for @const. Fixes to wasm nolibc in the standard library. 2024-08-26 13:33:15 +02:00
Christoffer Lerno
b33cce385c Fix of bug in defer (catch err) with a direct return error. 2024-08-26 11:49:41 +02:00
Christoffer Lerno
4b2019cf20 Add "allocator-required" functions. 2024-08-25 21:53:54 +02:00
Christoffer Lerno
61246d713d Print linking in CI and fix win linking. 2024-08-25 21:10:24 +02:00
Christoffer Lerno
40455f5260 Print linking in CI 2024-08-25 20:30:21 +02:00
Christoffer Lerno
78ce03bd62 Prefer \ to concat windows paths. 2024-08-25 20:14:36 +02:00
Christoffer Lerno
61645d14fa Only output -pie to the linker for executables. Fix issue assembling paths using concat_file_arg 2024-08-25 19:54:38 +02:00
Christoffer Lerno
d465ba5356 --test will now provide the full filename and the column. 2024-08-25 18:26:44 +02:00
Christoffer Lerno
8fde7cd6f5 --path is now properly respected. 2024-08-25 18:15:33 +02:00
Itzerr
734e0f350a Fixed gencontext_begin_module using wrong reloc_model. (#1384)
Fixed gencontext_begin_module using wrong reloc_model.
2024-08-25 18:08:33 +02:00
Chuck Benedict
e1bbab3831 RISCV: Correct auipc imm; clarify signed imm error; add imm negative t… (#1378)
RISCV: Correct auipc imm; claify signed imm error; add imm negative tests. Allow fitted int asm imm const in uints; add rv regs
2024-08-25 11:19:30 +02:00
Christoffer Lerno
a870881fff Allow "project.json5" to be used. 2024-08-25 00:19:08 +02:00
Christoffer Lerno
1ed3eab010 Assigning a const zero to an aliased distinct caused an error. 2024-08-24 14:37:27 +02:00
theunixer
dd16ecf63a Essential dependency to build(llvm17-devel) on void linux was not mentioned. 2024-08-24 11:10:59 +02:00
Christoffer Lerno
d1a0ec5a35 Compiler didn't detect when a module name was used both as a generic and regular module. 2024-08-23 19:31:49 +02:00
Nikita Pivkin
cb790b4672 Remove unused parameters from check_col and check_row
Signed-off-by: Nikita Pivkin <nikita.pivkin@smartforce.io>
2024-08-23 19:03:03 +02:00
Christian Buttner
19d37ef641 Add types::is_signed, is_unsigned and inner_type. (#1365)
Add `types::is_signed`, `is_unsigned` and `inner_type`.
2024-08-23 19:01:05 +02:00
Christoffer Lerno
0722011385 Fix of compile arguments. 2024-08-23 16:47:19 +02:00
Christian Buttner
59ed118e66 Address/memory/thread sanitizer. 2024-08-23 16:06:22 +02:00
Christoffer Lerno
d54468d7ed Simplify some asm code and update releasenotes for RISCV 2024-08-23 10:23:17 +02:00
Christoffer Lerno
218f1a6ead Add support for vendor specific extensions in project.json and manifest.json. 2024-08-22 12:49:14 +02:00
Christoffer Lerno
abbd94e89b Add iOS and Android targets. 2024-08-22 00:31:03 +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
602 changed files with 38966 additions and 22194 deletions

28
.editorconfig Normal file
View File

@@ -0,0 +1,28 @@
# EditorConfig is awesome: https://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
[CMakeLists.txt]
indent_style = space
indent_size = 4
[*.{c,cc,h}]
indent_style = space
indent_size = 4
[*.{c3}]
indent_style = space
indent_size = 4
[*.{json,toml,yml,gyp}]
indent_style = space
indent_size = 2
[*.{py,pyi}]
indent_style = space
indent_size = 2

2
.gitattributes vendored
View File

@@ -1,4 +1,2 @@
$ cat .gitattributes
* text=auto
*.c3 linguist-language=C
*.c3t linguist-language=C

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: 17
LLVM_RELEASE_VERSION_LINUX: 17
LLVM_RELEASE_VERSION_UBUNTU20: 17
LLVM_DEV_VERSION: 20
jobs:
build-msvc:
@@ -33,41 +36,44 @@ 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
run: |
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib5
- name: Try raylib
- name: Try raylib5
run: |
cd resources
..\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
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib5
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_arkanoid.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_snake.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_tetris.c3
- name: run compiler tests
run: |
@@ -85,10 +91,12 @@ jobs:
dir msvc_sdk
- name: upload artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
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.5-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-18.1.5-1-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: |
@@ -136,12 +145,12 @@ jobs:
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib
./build/c3c vendor-fetch raylib5
- 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]
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 --target wasm32 -g0 --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,17 +353,17 @@ 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
uses: actions/upload-artifact@v3
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
uses: actions/upload-artifact@v4
with:
name: c3-linux-${{matrix.build_type}}
path: c3-linux-${{matrix.build_type}}.tar.gz
@@ -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
uses: actions/upload-artifact@v3
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
uses: actions/upload-artifact@v4
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]
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 --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,24 +584,30 @@ 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 "/opt/homebrew/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: |
./build/c3c vendor-fetch raylib
./build/c3c vendor-fetch raylib5
- name: Compile and run some examples
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,8 +656,8 @@ jobs:
zip -r c3-macos-${{matrix.build_type}}.zip macos
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
uses: actions/upload-artifact@v3
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
uses: actions/upload-artifact@v4
with:
name: c3-macos-${{matrix.build_type}}
path: c3-macos-${{matrix.build_type}}.zip
@@ -519,7 +665,7 @@ jobs:
release:
runs-on: ubuntu-latest
needs: [build-msvc, build-linux, build-mac]
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu20]
if: github.ref == 'refs/heads/master'
steps:
@@ -546,16 +692,22 @@ jobs:
sha: context.sha
})
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
- run: cp -r lib c3-windows-Release
- run: cp -r lib c3-windows-Debug
- run: cp msvc_build_libraries.py c3-windows-Release
- run: cp msvc_build_libraries.py c3-windows-Debug
- run: zip -r c3-windows-Release.zip c3-windows-Release
- run: zip -r c3-windows-Debug.zip c3-windows-Debug
- run: zip -r c3-windows.zip c3-windows-Release
- run: zip -r c3-windows-debug.zip c3-windows-Debug
- run: mv c3-linux-Release/c3-linux-Release.tar.gz c3-linux-Release/c3-linux.tar.gz
- run: mv c3-linux-Debug/c3-linux-Debug.tar.gz c3-linux-Debug/c3-linux-debug.tar.gz
- run: mv c3-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
- run: mv c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
- run: mv c3-macos-Release/c3-macos-Release.zip c3-macos-Release/c3-macos.zip
- run: mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-Debug/c3-macos-debug.zip
- id: create_release
uses: actions/create-release@v1
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -563,84 +715,12 @@ jobs:
release_name: latest
draft: false
prerelease: true
- name: upload windows
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-windows-Release.zip
asset_name: c3-windows.zip
asset_content_type: application/zip
- name: upload windows debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-windows-Debug.zip
asset_name: c3-windows-debug.zip
asset_content_type: application/zip
- name: upload linux
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-linux-Release/c3-linux-Release.tar.gz
asset_name: c3-linux.tar.gz
asset_content_type: application/gzip
- name: upload linux debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-linux-Debug/c3-linux-Debug.tar.gz
asset_name: c3-linux-debug.tar.gz
asset_content_type: application/gzip
- name: upload ubuntu 20
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz
asset_name: c3-ubuntu-20.tar.gz
asset_content_type: application/gzip
- name: upload ubuntu 20 debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz
asset_name: c3-ubuntu-20-debug.tar.gz
asset_content_type: application/gzip
- name: upload macos
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-macos-Release/c3-macos-Release.zip
asset_name: c3-macos.zip
asset_content_type: application/zip
- name: upload macos debug
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: c3-macos-Debug/c3-macos-Debug.zip
asset_name: c3-macos-debug.zip
asset_content_type: application/zip
files: |
c3-windows.zip
c3-windows-debug.zip
c3-linux-Release/c3-linux.tar.gz
c3-linux-Debug/c3-linux-debug.tar.gz
c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
c3-macos-Release/c3-macos.zip
c3-macos-Debug/c3-macos-debug.zip

8
.gitignore vendored
View File

@@ -67,3 +67,11 @@ out/
/cmake-build-debug/
/cmake-build-release/
# Emacs files
TAGS
# Clangd LSP files
/.cache/
/compile_commands.json

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")
@@ -57,6 +63,7 @@ set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
option(C3_USE_MIMALLOC "Use built-in mimalloc" OFF)
option(C3_USE_TB "Use TB" OFF)
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
option(C3_WITH_LLVM "Build with LLVM" ON)
set(C3_USE_MIMALLOC OFF)
if(C3_USE_MIMALLOC)
@@ -74,9 +81,11 @@ endif()
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)
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
if(C3_WITH_LLVM)
if (NOT C3_LLVM_VERSION STREQUAL "auto")
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()
endif()
@@ -95,110 +104,134 @@ if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
endif()
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "16")
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
# Clangd LSP support
option(C3_ENABLE_CLANGD_LSP "Enable/Disable output of compile commands during generation." OFF)
if(C3_ENABLE_CLANGD_LSP)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
execute_process(
COMMAND ${CMAKE_COMMAND} -E create_symlink
${CMAKE_BINARY_DIR}/compile_commands.json
${CMAKE_SOURCE_DIR}/compile_commands.json
)
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
)
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})
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})
endif()
find_package(LLVM REQUIRED CONFIG)
find_package(LLD REQUIRED CONFIG)
else()
if (NOT C3_LLVM_VERSION STREQUAL "auto")
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
else()
endif(C3_ENABLE_CLANGD_LSP)
if(C3_WITH_LLVM)
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "18")
endif()
FetchContent_Declare(
LLVM_Windows
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_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(llvm_dir ${llvm_windows_debug_SOURCE_DIR})
else()
message("Loading Windows LLVM libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows)
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()
if (NOT C3_LLVM_VERSION STREQUAL "auto")
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
else()
find_package(LLVM REQUIRED CONFIG)
endif()
endif()
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})
if(NOT C3_LINK_DYNAMIC)
set(LLVM_LINK_COMPONENTS
AllTargetsAsmParsers
AllTargetsCodeGens
AllTargetsDescs
AllTargetsDisassemblers
AllTargetsInfos
Analysis
AsmPrinter
BitReader
Core
DebugInfoPDB
InstCombine
IrReader
LibDriver
Linker
LTO
MC
MCDisassembler
native
nativecodegen
Object
Option
ScalarOpts
Support
Target
TransformUtils
WindowsManifest
WindowsDriver
)
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
else()
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(llvm_libs ${LLVM})
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
endif()
endif()
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
if(LLVM_ENABLE_RTTI)
message(STATUS "LLVM was built with RTTI")
else()
message(STATUS "LLVM was not built with RTTI")
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()
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
add_definitions(${LLVM_DEFINITIONS})
if(NOT C3_LINK_DYNAMIC)
set(LLVM_LINK_COMPONENTS
AllTargetsAsmParsers
AllTargetsCodeGens
AllTargetsDescs
AllTargetsDisassemblers
AllTargetsInfos
Analysis
AsmPrinter
BitReader
Core
DebugInfoPDB
InstCombine
IrReader
LibDriver
Linker
LTO
MC
MCDisassembler
native
nativecodegen
Object
Option
ScalarOpts
Support
Target
TransformUtils
WindowsManifest
WindowsDriver
)
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
else()
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(llvm_libs ${LLVM})
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
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)
if(C3_WITH_LLVM)
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}
@@ -208,37 +241,41 @@ if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL 16)
${LLD_ELF}
${LLD_MACHO}
)
else()
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
if (APPLE)
set(lld_libs ${lld_libs} xar)
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}")
endif()
if (APPLE)
set(lld_libs ${lld_libs} xar)
endif ()
message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Found lld libs ${lld_libs}")
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
add_library(miniz STATIC dependencies/miniz/miniz.c)
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,9 +283,7 @@ 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
src/compiler/abi/c_abi.c
src/compiler/abi/c_abi_riscv.c
@@ -256,14 +291,6 @@ add_executable(c3c
src/compiler/abi/c_abi_win64.c
src/compiler/abi/c_abi_x64.c
src/compiler/abi/c_abi_x86.c
src/compiler/llvm_codegen_debug_info.c
src/compiler/llvm_codegen_expr.c
src/compiler/llvm_codegen_function.c
src/compiler/llvm_codegen_instr.c
src/compiler/llvm_codegen_module.c
src/compiler/llvm_codegen_stmt.c
src/compiler/llvm_codegen_type.c
src/compiler/llvm_codegen_value.c
src/compiler/module.c
src/compiler/number.c
src/compiler/parse_expr.c
@@ -306,17 +333,59 @@ add_executable(c3c
src/utils/cpus.c
src/utils/unzipper.c
src/compiler/decltable.c
src/compiler/mac_support.c
src/compiler/llvm_codegen_storeload.c
src/compiler/mac_support.c
src/compiler/windows_support.c
src/compiler/codegen_asm.c
src/compiler/asm_target.c
src/compiler/llvm_codegen_builtins.c
src/compiler/asm_target.c
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
src/compiler/sema_const.c
${CMAKE_BINARY_DIR}/git_hash.h
)
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
# We are inside of a git repository so rebuilding the hash every time something changes.
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/git_hash.h
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/git_hash.cmake"
DEPENDS "${CMAKE_CURRENT_LIST_DIR}/.git")
else()
# We are NOT inside of a git repository. Building the has only once.
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/git_hash.h
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/git_hash.cmake")
endif()
if(C3_WITH_LLVM)
target_sources(c3c PRIVATE
src/compiler/llvm_codegen.c
src/compiler/llvm_codegen_debug_info.c
src/compiler/llvm_codegen_expr.c
src/compiler/llvm_codegen_function.c
src/compiler/llvm_codegen_instr.c
src/compiler/llvm_codegen_module.c
src/compiler/llvm_codegen_stmt.c
src/compiler/llvm_codegen_type.c
src/compiler/llvm_codegen_value.c
src/compiler/llvm_codegen_storeload.c
src/compiler/llvm_codegen_builtins.c)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
else()
target_sources(c3c PRIVATE src/utils/hostinfo.c)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
endif()
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/src/"
"${CMAKE_BINARY_DIR}")
target_include_directories(miniz PUBLIC
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
if (C3_USE_TB)
file(GLOB tilde-sources
@@ -350,23 +419,29 @@ if (C3_USE_TB)
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/tilde-backend/include/")
else()
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=0)
endif()
if(C3_WITH_LLVM)
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/src/")
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/include/")
target_include_directories(c3c_wrappers PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/include/")
target_include_directories(c3c_wrappers PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/src/")
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
target_include_directories(miniz PUBLIC
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
else()
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
target_link_libraries(c3c ${llvm_libs} miniz ${lld_libs})
endif()
if(C3_USE_MIMALLOC)
target_link_libraries(c3c mimalloc-static)
@@ -376,6 +451,11 @@ if (WIN32)
target_link_libraries(c3c Winhttp.lib)
endif()
if(MINGW)
message("Increase stack for msys")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,8388608")
endif ()
if (CURL_FOUND)
target_link_libraries(c3c ${CURL_LIBRARIES})
target_include_directories(c3c PRIVATE ${CURL_INCLUDES})
@@ -384,36 +464,73 @@ else()
target_compile_definitions(c3c PUBLIC CURL_FOUND=0)
endif()
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)
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
if(C3_WITH_LLVM)
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)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(c3c PUBLIC /MTd)
target_compile_options(c3c_wrappers PUBLIC /MTd)
if (C3_WITH_LLVM)
target_compile_options(c3c_wrappers PUBLIC /MTd)
endif()
target_compile_options(miniz PUBLIC /MTd)
if (C3_USE_TB)
target_compile_options(tilde-backend PUBLIC /MTd)
endif()
else()
target_compile_options(c3c PUBLIC /MT)
target_compile_options(c3c_wrappers PUBLIC /MT)
if (C3_WITH_LLVM)
target_compile_options(c3c_wrappers PUBLIC /MT)
endif()
target_compile_options(miniz PUBLIC /MT)
if (C3_USE_TB)
target_compile_options(tilde-backend PUBLIC /MT)
endif()
endif()
if(C3_WITH_LLVM)
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)
endif()
else()
message(STATUS "using gcc/clang warning switches")
target_link_options(c3c PRIVATE -pthread)
if (C3_WITH_LLVM AND 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 (C3_WITH_LLVM AND 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

@@ -10,7 +10,8 @@ Precompiled binaries for the following operating systems are available:
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
- MacOS x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-mac-with-precompiled-binaries).
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-mac-with-precompiled-binaries).
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
@@ -34,7 +35,7 @@ whole new language.
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++
```cpp
module stack (<Type>);
// Above: the parameterized type is applied to the entire module.
@@ -122,7 +123,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 +138,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.2**.
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)
@@ -205,17 +206,32 @@ More platforms will be supported in the future.
2. Unpack executable and standard lib.
3. Run `./c3c`.
#### Installing on Mac with precompiled binaries
#### Installing on Ubuntu with precompiled binaries
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20-debug.tar.gz))
2. Unpack executable and standard lib.
3. Run `./c3c`.
#### Installing on MacOS with precompiled binaries
1. Make sure you have XCode with command line tools installed.
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-macos-debug.zip))
3. Unzip executable and standard lib.
4. Run `./c3c`.
#### Installing on Arch Linux
There is an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git).
(*Note that there is a known issue with debug symbol generation on MacOS 13, see [issue #1086](https://github.com/c3lang/c3c/issues/1086))
Due to some issues with the LLVM packaged for Arch Linux, the AUR package will download and use LLVM 16 for Ubuntu-23.04 to compile the c3c compiler.
#### Installing on Arch Linux
Arch includes c3c in the official 'extra' repo. It can be easily installed the usual way:
```sh
sudo pacman -S c3c
# or paru -S c3c
# or yay -S c3c
# or aura -A c3c
```
There is also an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git).
You can use your AUR package manager:
```sh
@@ -251,7 +267,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`
@@ -302,17 +318,16 @@ You can try it out by running some sample code: `c3c.exe compile ../resources/ex
*Note that if you run into linking issues when building, make sure that you are using the latest version of VS17.*
#### Compiling on Ubuntu 20.10
#### Compiling on Ubuntu 24.04 LTS
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`
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`
7. Change directory to the build directory `cd build`
8. Set up CMake build: `cmake ..`
9. Build: `cmake --build .`
2. Install LLVM 18 `sudo apt-get install cmake git clang zlib1g zlib1g-dev libllvm18 llvm llvm-dev llvm-runtime liblld-dev liblld-18 libpolly-18-dev`
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`
6. Change directory to the build directory `cd build`
7. Set up CMake build: `cmake ..`
8. Build: `cmake --build .`
You should now have a `c3c` executable.
@@ -321,7 +336,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 llvm17-devel 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 +352,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`
@@ -367,3 +382,9 @@ Editor plugins can be found at https://github.com/c3lang/editor-plugins.
3. Run tests and see that they pass. (Recommended settings: `c3c compile-test -O0 test/unit`.
- in this example `test/unit/` is the relative path to the test directory, so adjust as required)
4. Make a pull request for the new tests.
## Thank yous
A huge "thank you" goes out to all contributors and sponsors.
A special thank you to sponsor [Caleb-o](https://github.com/Caleb-o) for going the extra mile.

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

14
git_hash.cmake Normal file
View File

@@ -0,0 +1,14 @@
find_package(Git QUIET)
set(GIT_HASH "unknown")
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/.git")
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse HEAD
OUTPUT_VARIABLE GIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
COMMAND_ERROR_IS_FATAL ANY)
endif()
message("Git Hash: ${GIT_HASH}")
file(WRITE ${CMAKE_BINARY_DIR}/git_hash.h "#pragma once\n#define GIT_HASH \"${GIT_HASH}\"\n")

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,33 +1,42 @@
// 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;
}
/**
* @param initial_capacity "The initial capacity to reserve"
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
* Use `init` for to use a custom 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 = null)
{
return self.init(allocator ?: allocator::heap(), initial_capacity) @inline;
}
/**
* @param [&inout] allocator "The allocator to use"
* @param initial_capacity "The initial capacity to reserve"
**/
fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
{
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 +46,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;
return self.init(allocator::temp(), initial_capacity) @inline;
}
fn usz! GenericList.to_format(&self, Formatter* formatter) @dynamic
fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
@@ -76,26 +76,29 @@ 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 = null) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String GenericList.to_tstring(&self)
fn String AnyList.to_string(&self, Allocator allocator) @dynamic
{
return string::tformat("%s", *self);
return string::format("%s", *self, allocator: allocator);
}
fn String AnyList.to_tstring(&self) => string::tformat("%s", *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 +107,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 +118,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,30 +129,47 @@ 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.copy_pop(&self, Allocator allocator = allocator::heap())
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return allocator::clone_any(allocator, self.entries[--self.size]);
}
/**
* Pop the last value and allocate the copy using the given allocator.
* @return! IteratorResult.NO_MORE_ELEMENT
* @deprecated `use copy_pop`
**/
fn any! AnyList.new_pop(&self, Allocator allocator = allocator::heap())
{
return self.copy_pop(allocator);
}
/**
* Pop the last value and allocate the copy using the temp allocator
* @return! IteratorResult.NO_MORE_ELEMENT
* @deprecated `use tcopy_pop`
**/
fn any! AnyList.temp_pop(&self) => self.copy_pop(allocator::temp());
/**
* 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.tcopy_pop(&self) => self.copy_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 +181,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 +191,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);
@@ -180,8 +200,17 @@ fn any*! GenericList.pop_first_retained(&self)
/**
* Same as new_pop() but pops the first value instead.
* @deprecated `use copy_pop_first`
**/
fn any*! GenericList.new_pop_first(&self, Allocator* allocator = allocator::heap())
fn any! AnyList.new_pop_first(&self, Allocator allocator = allocator::heap())
{
return self.copy_pop_first(allocator) @inline;
}
/**
* Same as new_pop() but pops the first value instead.
**/
fn any! AnyList.copy_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 +221,25 @@ 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.tcopy_pop_first(&self) => self.copy_pop_first(allocator::temp());
/**
* Same as temp_pop() but pops the first value instead.
* @deprecated `use tcopy_pop_first`
**/
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 +252,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 +263,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 +271,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 +279,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 +303,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 +311,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 +349,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 +357,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 +371,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 +382,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 +391,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 +422,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 +461,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 +479,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 +490,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,455 @@
// 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::format("%s", *self, allocator: allocator);
}
fn String ElasticArray.to_new_string(&self, Allocator allocator = nul) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
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)
{
return list_common::list_to_new_aligned_array(Type, self, allocator::heap());
}
/**
* IMPORTANT The returned array must be freed using free_aligned.
**/
fn Type[] ElasticArray.to_aligned_array(&self, Allocator allocator)
{
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)
{
return list_common::list_to_array(Type, self, allocator::heap());
}
/**
* @require !type_is_overaligned() : "This function is not available on overaligned types"
**/
macro Type[] ElasticArray.to_array(&self, Allocator allocator)
{
return list_common::list_to_new_array(Type, self, allocator);
}
fn Type[] ElasticArray.to_tarray(&self)
{
$if type_is_overaligned():
return self.to_aligned_array(allocator::temp());
$else
return self.to_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

@@ -1,3 +1,6 @@
/**
* @require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enummap"
**/
module std::collections::enummap(<Enum, ValueType>);
import std::io;
struct EnumMap (Printable)
@@ -25,9 +28,14 @@ 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_string(&self, Allocator allocator) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
return string::format("%s", *self, allocator: allocator);
}
fn String EnumMap.to_new_string(&self, Allocator allocator = null) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String EnumMap.to_tstring(&self) @dynamic

View File

@@ -3,7 +3,7 @@
// a copy of which can be found in the LICENSE_STDLIB file.
/**
* @require Enum.kindof == TypeKind.ENUM : "Only enums maybe be used with an enumset"
* @require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enumset"
**/
module std::collections::enumset(<Enum>);
import std::io;
@@ -141,9 +141,14 @@ 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);
return string::format("%s", *set, allocator: allocator);
}
fn String EnumSet.to_string(&set, Allocator allocator) @dynamic
{
return string::format("%s", *set, allocator: allocator);
}
fn String EnumSet.to_tstring(&set) @dynamic

View File

@@ -0,0 +1,523 @@
// 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 = null)
{
return self.init(allocator ?: allocator::heap(), capacity, 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.init(&self, Allocator allocator, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
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.init(allocator::temp(), capacity, load_factor) @inline;
}
/**
* @param [&inout] allocator "The allocator to use"
* @require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
* @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"
**/
macro HashMap* HashMap.new_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
self.new_init(capacity, load_factor, allocator);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return self;
}
/**
* @param [in] keys "The keys for the HashMap entries"
* @param [in] values "The values for the HashMap entries"
* @param [&inout] allocator "The allocator to use"
* @require keys.len == values.len "Both keys and values arrays must be the same length"
* @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_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
assert(keys.len == values.len);
self.new_init(capacity, load_factor, allocator);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
}
/**
* @require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
* @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"
**/
macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.temp_init(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return self;
}
/**
* @param [in] keys "The keys for the HashMap entries"
* @param [in] values "The values for the HashMap entries"
* @param [&inout] allocator "The allocator to use"
* @require keys.len == values.len "Both keys and values arrays must be the same length"
* @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_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
assert(keys.len == values.len);
self.temp_init(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
}
/**
* 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 [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map)
{
return self.init_from_map(other_map, allocator::heap()) @inline;
}
/**
* @param [&inout] allocator "The allocator to use"
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.init_from_map(&self, HashMap* other_map, Allocator allocator)
{
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.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.tcopy_keys(&map)
{
return map.copy_keys(allocator::temp()) @inline;
}
fn Key[] HashMap.key_tlist(&map) @deprecated("Use 'tcopy_keys'")
{
return map.copy_keys(allocator::temp()) @inline;
}
/**
* @deprecated "use copy_keys"
**/
fn Key[] HashMap.key_new_list(&map, Allocator allocator = allocator::heap())
{
return map.copy_keys() @inline;
}
fn Key[] HashMap.copy_keys(&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;
}
}
}
}
/**
* @deprecated `use tcopy_values`
**/
fn Value[] HashMap.value_tlist(&map)
{
return map.copy_values(allocator::temp()) @inline;
}
fn Value[] HashMap.tcopy_values(&map)
{
return map.copy_values(allocator::temp()) @inline;
}
/**
* @deprecated `use copy_values`
**/
fn Value[] HashMap.value_new_list(&map, Allocator allocator = allocator::heap())
{
return map.copy_values(allocator);
}
fn Value[] HashMap.copy_values(&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,49 +14,34 @@ 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.init(&self, Allocator allocator)
{
*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")
fn LinkedList* LinkedList.new_init(&self)
{
return self.new_init(allocator);
return self.init(allocator::heap()) @inline;
}
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;
return self.init(allocator::temp()) @inline;
}
/**
@@ -71,7 +58,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 +75,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 +159,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 +167,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 +219,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 +278,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 +290,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,9 +100,9 @@ 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);
return string::format("%s", *self, allocator: allocator);
}
fn String List.to_tstring(&self)
@@ -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,155 @@ 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;
MapImpl* map = mem::temp_alloc(MapImpl);
_init(map, capacity, load_factor, allocator::temp());
return (Map)map;
}
/**
* @param [&inout] allocator "The allocator to use"
* @require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
* @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")
macro Map new_init_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
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;
Map map = new(capacity, load_factor, allocator);
$for (var $i = 0; $i < $vacount; $i += 2)
map.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return map;
}
/**
* @param [in] keys "Array of keys for the Map entries"
* @param [in] values "Array of values for the Map entries"
* @param [&inout] allocator "The allocator to use"
* @param [&in] other_map "The map to copy from."
* @require keys.len == values.len "Both keys and values arrays must be the same length"
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map, Allocator* allocator = allocator::heap())
fn Map new_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, 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;
assert(keys.len == values.len);
Map map = new(capacity, load_factor, allocator);
for (usz i = 0; i < keys.len; i++)
{
map.set(keys[i], values[i]);
}
return map;
}
/**
* @require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
macro Map temp_new_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
Map map = temp(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
map.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return map;
}
/**
* @param [in] keys "The keys for the HashMap entries"
* @param [in] values "The values for the HashMap entries"
* @param [&inout] allocator "The allocator to use"
* @param [&in] other_map "The map to copy from."
* @require keys.len == values.len "Both keys and values arrays must be the same length"
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.init_new_from_map(&self, HashMap* other_map, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init_from_map")
fn Map temp_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
return self.new_init_from_map(other_map, allocator) @inline;
assert(keys.len == values.len);
Map map = temp(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
map.set(keys[i], values[i]);
}
return 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 +167,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 +183,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 +203,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 +234,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 +256,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 +297,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 +340,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 +357,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 +366,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 +380,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 +397,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 +416,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 +439,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 +468,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 +477,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 +488,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 +502,4 @@ struct Entry
Key key;
Value value;
Entry* next;
}
}

View File

@@ -1,6 +1,6 @@
module std::collections::maybe(<Type>);
struct Maybe
struct Maybe @adhoc
{
Type value;
bool has_value;

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 });
}
@@ -128,9 +128,9 @@ fn void Object.free(&self)
self.array.free();
case ObjectInternalMap:
self.map.@each_entry(; ObjectInternalMapEntry* entry) {
allocator::free(self.allocator, entry.key);
entry.value.free();
};
self.map.free();
default:
break;
}
@@ -156,7 +156,7 @@ fn void Object.init_map_if_needed(&self) @private
if (self.is_empty())
{
self.type = ObjectInternalMap.typeid;
self.map.new_init(.allocator = self.allocator);
self.map.new_init(allocator: self.allocator);
}
}
@@ -168,7 +168,7 @@ fn void Object.init_array_if_needed(&self) @private
if (self.is_empty())
{
self.type = ObjectInternalList.typeid;
self.array.new_init(.allocator = self.allocator);
self.array.new_init(allocator: self.allocator);
}
}
@@ -181,16 +181,14 @@ fn void Object.set_object(&self, String key, Object* new_object) @private
ObjectInternalMapEntry*! entry = self.map.get_entry(key);
defer
{
(void)allocator::free(self.allocator, entry.key);
(void)entry.value.free();
}
self.map.set(key.copy(self.map.allocator), new_object);
self.map.set(key, new_object);
}
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 +232,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 +266,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 +280,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,14 +29,19 @@ 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 @deprecated
{
return string::new_format("[%s..%s]", self.start, self.end, .allocator = allocator);
return string::format("[%s..%s]", self.start, self.end, allocator: allocator);
}
fn String Range.to_string(&self, Allocator allocator) @dynamic
{
return string::format("[%s..%s]", self.start, self.end, allocator: allocator);
}
fn String Range.to_tstring(&self)
{
return self.to_new_string(allocator::temp());
return self.to_string(allocator::temp());
}
fn usz! Range.to_format(&self, Formatter* formatter) @dynamic
@@ -66,9 +71,14 @@ 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 = null) @dynamic
{
return string::new_format("[%s..<%s]", self.start, self.end, .allocator = allocator);
return self.to_string(allocator ?: allocator::heap());
}
fn String ExclusiveRange.to_string(&self, Allocator allocator) @dynamic
{
return string::format("[%s..<%s]", self.start, self.end, allocator: allocator);
}
fn String ExclusiveRange.to_tstring(&self)

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

@@ -1,6 +1,6 @@
module std::collections::tuple(<Type1, Type2>);
struct Tuple
struct Tuple @adhoc
{
Type1 first;
Type2 second;
@@ -8,7 +8,7 @@ struct Tuple
module std::collections::triple(<Type1, Type2, Type3>);
struct Triple
struct Triple @adhoc
{
Type1 first;
Type2 second;

476
lib/std/compression/qoi.c3 Normal file
View File

@@ -0,0 +1,476 @@
module std::compression::qoi;
const uint PIXELS_MAX = 400000000;
/**
* Colorspace.
* Purely informative. It will be saved to the file header,
* but does not affect how chunks are en-/decoded.
*/
enum QOIColorspace : char (char id)
{
SRGB = 0, // sRGB with linear alpha
LINEAR = 1 // all channels linear
}
/**
* Channels.
* The channels used in an image.
* AUTO can be used when decoding to automatically determine
* the channels from the file's header.
*/
enum QOIChannels : char (char id)
{
AUTO = 0,
RGB = 3,
RGBA = 4
}
/**
* Descriptor.
* Contains information about an image.
*/
struct QOIDesc
{
uint width;
uint height;
QOIChannels channels;
QOIColorspace colorspace;
}
/**
* QOI Errors.
* These are all the possible bad outcomes.
*/
fault QOIError
{
INVALID_PARAMETERS,
FILE_OPEN_FAILED,
FILE_WRITE_FAILED,
INVALID_DATA,
TOO_MANY_PIXELS
}
// Let the user decide if they want to use std::io
module std::compression::qoi @if(!$feature(QOI_NO_STDIO));
import std::io;
/**
* Encode raw RGB or RGBA pixels into a QOI image and write it to the
* file system.
*
* The desc struct must be filled with the image width, height, the
* used channels (QOIChannels.RGB or RGBA) and the colorspace
* (QOIColorspace.SRGB or LINEAR).
*
* The function returns an optional, which can either be a QOIError
* or the number of bytes written on success.
*
* @param [in] filename `The file's name to write the image to`
* @param [in] input `The raw RGB or RGBA pixels to encode`
* @param [&in] desc `The descriptor of the image`
*/
fn usz! write(String filename, char[] input, QOIDesc* desc)
{
@pool() {
// encode data
char[] output = encode(input, desc)!;
// open file
File! f = file::open(filename, "wb");
if (catch f) { return QOIError.FILE_OPEN_FAILED?; }
// write data to file and close it
usz! written = f.write(output);
if (catch written) { return QOIError.FILE_WRITE_FAILED?; }
if (catch f.close()) { return QOIError.FILE_WRITE_FAILED?; }
return written;
};
}
/**
* Read and decode a QOI image from the file system.
*
* If channels is set to QOIChannels.AUTO, the function will
* automatically determine the channels from the file's header.
* However, if channels is RGB or RGBA, the output format will be
* forced into this number of channels.
*
* The desc struct will be filled with the width, height,
* channels and colorspace of the image.
*
* The function returns an optional, which can either be a QOIError
* or a char[] pointing to the decoded pixels on success.
*
* The returned pixel data should be free()d after use, or the decoding
* and use of the data should be wrapped in a @pool() { ... }; block.
*
* @param [in] filename `The file's name to read the image from`
* @param [&out] desc `The descriptor to fill with the image's info`
* @param channels `The channels to be used`
*/
fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
{
// read file
char[]! data = file::load_new(filename);
if (catch data) return QOIError.FILE_OPEN_FAILED?;
defer mem::free(data);
// pass data to decode function
return decode(data, desc, channels, allocator);
}
// Back to basic non-stdio mode
module std::compression::qoi;
import std::bits;
/**
* Encode raw RGB or RGBA pixels into a QOI image in memory.
*
* The function returns an optional, which can either be a QOIError
* or a char[] pointing to the encoded data on success.
*
* The returned qoi data should be free()d after use, or the encoding
* and use of the data should be wrapped in a @pool() { ... }; block.
* See the write() function for an example.
*
* @param [in] input `The raw RGB or RGBA pixels to encode`
* @param [&in] desc `The descriptor of the image`
*/
fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap())
{
// check info in desc
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_PARAMETERS?;
if (desc.channels == AUTO) return QOIError.INVALID_PARAMETERS?;
uint pixels = desc.width * desc.height;
if (pixels > PIXELS_MAX) return QOIError.TOO_MANY_PIXELS?;
// check input data size
uint image_size = pixels * desc.channels.id;
if (image_size != input.len) return QOIError.INVALID_DATA?;
// allocate memory for encoded data (output)
// header + chunk tag and RGB(A) data for each pixel + end of stream
uint max_size = Header.sizeof + pixels + image_size + END_OF_STREAM.len;
char[] output = allocator::alloc_array(allocator, char, max_size); // no need to init
defer catch allocator::free(allocator, output);
// write header
*(Header*)output.ptr = {
.be_magic = bswap('qoif'),
.be_width = bswap(desc.width),
.be_height = bswap(desc.height),
.channels = desc.channels.id,
.colorspace = desc.colorspace.id
};
uint pos = Header.sizeof; // Current position in output
uint loc; // Current position in image (top-left corner)
uint loc_end = image_size - desc.channels.id; // End of image data
char run_length = 0; // Length of the current run
Pixel[64] palette; // Zero-initialized by default
Pixel prev = { 0, 0, 0, 255 };
Pixel p = { 0, 0, 0, 255 };
ichar[<3>] diff; // pre-allocate for diff
ichar[<3>] luma; // ...and luma
// write chunks
for (loc = 0; loc < image_size; loc += desc.channels.id)
{
// set previous pixel
prev = p;
// get current pixel
p[:3] = input[loc:3]; // cutesy slices :3
if (desc.channels == RGBA) p.a = input[loc + 3];
// check if we can run the previous pixel
if (prev == p) {
run_length++;
if (run_length == 62 || loc == loc_end) {
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
run_length = 0;
}
} else {
// end last run if there was one
if (run_length > 0) {
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
run_length = 0;
}
switch {
// check if we can index the palette
case (palette[p.hash()] == p):
*@extract(OpIndex, output, &pos) = {
OP_INDEX,
p.hash()
};
// check if we can use diff or luma
case (prev != p && prev.a == p.a):
// diff the pixels
diff = p.rgb - prev.rgb;
if (
diff.r > -3 && diff.r < 2 &&
diff.g > -3 && diff.g < 2 &&
diff.b > -3 && diff.b < 2
) {
*@extract(OpDiff, output, &pos) = {
OP_DIFF,
(char)diff.r + 2,
(char)diff.g + 2,
(char)diff.b + 2
};
palette[p.hash()] = p;
} else {
// check luma eligibility
luma = { diff.r - diff.g, diff.g, diff.b - diff.g };
if (
luma.r >= -8 && luma.r <= 7 &&
luma.g >= -32 && luma.g <= 31 &&
luma.b >= -8 && luma.b <= 7
) {
*@extract(OpLuma, output, &pos) = {
OP_LUMA,
(char)luma.g + 32,
(char)luma.r + 8,
(char)luma.b + 8
};
palette[p.hash()] = p;
} else { nextcase; }
}
// worst case scenario: just encode the raw pixel
default:
if (prev.a != p.a) {
*@extract(OpRGBA, output, &pos) = { OP_RGBA, p.r, p.g, p.b, p.a };
} else {
*@extract(OpRGB, output, &pos) = { OP_RGB, p.r, p.g, p.b };
}
palette[p.hash()] = p;
}
}
}
// write end of stream
output[pos:END_OF_STREAM.len] = END_OF_STREAM;
pos += END_OF_STREAM.len;
return output[:pos];
}
/**
* Decode a QOI image from memory.
*
* If channels is set to QOIChannels.AUTO, the function will
* automatically determine the channels from the file's header.
* However, if channels is RGB or RGBA, the output format will be
* forced into this number of channels.
*
* The desc struct will be filled with the width, height,
* channels and colorspace of the image.
*
* The function returns an optional, which can either be a QOIError
* or a char[] pointing to the decoded pixels on success.
*
* The returned pixel data should be free()d after use, or the decoding
* and use of the data should be wrapped in a @pool() { ... }; block.
*
* @param [in] data `The QOI image data to decode`
* @param [&out] desc `The descriptor to fill with the image's info`
* @param channels `The channels to be used`
*/
fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
{
// check input data
if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?;
// get header
Header* header = (Header*)data.ptr;
// check magic bytes (FourCC)
if (bswap(header.be_magic) != 'qoif') return QOIError.INVALID_DATA?;
// copy header data to desc
desc.width = bswap(header.be_width);
desc.height = bswap(header.be_height);
desc.channels = @enumcast(QOIChannels, header.channels)!; // Rethrow if invalid
desc.colorspace = @enumcast(QOIColorspace, header.colorspace)!; // Rethrow if invalid
if (desc.channels == AUTO) return QOIError.INVALID_DATA?; // Channels must be specified in the header
// check width and height
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_DATA?;
// check pixel count
ulong pixels = (ulong)desc.width * (ulong)desc.height;
if (pixels > PIXELS_MAX) return QOIError.TOO_MANY_PIXELS?;
uint pos = Header.sizeof; // Current position in data
uint loc; // Current position in image (top-left corner)
char run_length = 0; // Length of the current run
char tag; // Current chunk tag
Pixel[64] palette; // Zero-initialized by default
Pixel p = { 0, 0, 0, 255 };
if (channels == AUTO) channels = desc.channels;
// allocate memory for image data
usz image_size = (usz)pixels * channels.id;
char[] image = allocator::alloc_array(allocator, char, image_size);
defer catch allocator::free(allocator, image);
for (loc = 0; loc < image_size; loc += channels.id)
{
// get chunk tag
tag = data[pos];
// check for chunk type
switch
{
case run_length > 0:
run_length--;
case tag == OP_RGB:
OpRGB* op = @extract(OpRGB, data, &pos);
p = { op.red, op.green, op.blue, p.a };
palette[p.hash()] = p;
case tag == OP_RGBA:
OpRGBA* op = @extract(OpRGBA, data, &pos);
p = { op.red, op.green, op.blue, op.alpha };
palette[p.hash()] = p;
case tag >> 6 == OP_INDEX:
OpIndex* op = @extract(OpIndex, data, &pos);
p = palette[op.index];
case tag >> 6 == OP_DIFF:
OpDiff* op = @extract(OpDiff, data, &pos);
p.r += op.diff_red - 2;
p.g += op.diff_green - 2;
p.b += op.diff_blue - 2;
palette[p.hash()] = p;
case tag >> 6 == OP_LUMA:
OpLuma* op = @extract(OpLuma, data, &pos);
int diff_green = op.diff_green - 32;
p.r += (char)(op.diff_red_minus_green - 8 + diff_green);
p.g += (char)(diff_green);
p.b += (char)(op.diff_blue_minus_green - 8 + diff_green);
palette[p.hash()] = p;
case tag >> 6 == OP_RUN:
OpRun* op = @extract(OpRun, data, &pos);
run_length = op.run;
}
// draw the pixel
if (channels == RGBA) { image[loc:4] = p.rgba; } else { image[loc:3] = p.rgb; }
}
return image;
}
// ***************************************************************************
// *** ***
// *** Main functions are at the top to make the file more readable. ***
// *** From here on, helper functions and types are defined. ***
// *** ***
// ***************************************************************************
module std::compression::qoi @private;
// 8-bit opcodes
const OP_RGB = 0b11111110;
const OP_RGBA = 0b11111111;
// 2-bit opcodes
const OP_INDEX = 0b00;
const OP_DIFF = 0b01;
const OP_LUMA = 0b10;
const OP_RUN = 0b11;
struct Header @packed
{
uint be_magic; // magic bytes "qoif"
uint be_width; // image width in pixels (BE)
uint be_height; // image height in pixels (BE)
// informative fields
char channels; // 3 = RGB, 4 = RGB
char colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
}
const char[*] END_OF_STREAM = {0, 0, 0, 0, 0, 0, 0, 1};
// inefficient, but it's only run once at a time
macro @enumcast($Type, raw)
{
foreach (value : $Type.values) {
if (value.id == raw) return value;
}
return QOIError.INVALID_DATA?;
}
distinct Pixel = inline char[<4>];
macro char Pixel.hash(Pixel p) {
return (p.r * 3 + p.g * 5 + p.b * 7 + p.a * 11) % 64;
}
struct OpRGB // No need to use @packed here, the alignment is 1 anyways.
{
char tag;
char red;
char green;
char blue;
}
struct OpRGBA @packed
{
char tag;
char red;
char green;
char blue;
char alpha;
}
bitstruct OpIndex : char
{
char tag : 6..7;
char index : 0..5;
}
bitstruct OpDiff : char
{
char tag : 6..7;
char diff_red : 4..5;
char diff_green : 2..3;
char diff_blue : 0..1;
}
bitstruct OpLuma : ushort
{
char tag : 6..7;
char diff_green : 0..5;
char diff_red_minus_green : 12..15;
char diff_blue_minus_green : 8..11;
}
bitstruct OpRun : char
{
char tag : 6..7;
char run : 0..5;
}
// Macro used to locate chunks in data buffers.
// Can be used both for reading and writing.
macro @extract($Type, char[] data, uint* pos)
{
// slice data, then double cast
$Type* chunk = ($Type*)data[*pos : $Type.sizeof].ptr;
*pos += $Type.sizeof;
return chunk;
}

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;
@@ -30,6 +30,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;
}
@@ -37,6 +38,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 +79,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 +104,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 +133,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 +160,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 +197,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

@@ -2,19 +2,24 @@
// 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::core::mem::allocator;
module std::core::mem::allocator @if(env::LIBC);
import std::io;
import libc;
const LibcAllocator LIBC_ALLOCATOR = {};
distinct LibcAllocator (Allocator) = uptr;
distinct LibcAllocator (Allocator, Printable) = uptr;
fn String LibcAllocator.to_string(&self, Allocator allocator) @dynamic => "Libc allocator".copy(allocator);
fn usz! LibcAllocator.to_format(&self, Formatter *format) @dynamic => format.print("Libc allocator");
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 +48,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 +78,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 +96,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)
{
@@ -120,12 +115,12 @@ fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
libc::free(old_ptr);
}
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX);
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX && env::LIBC);
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 +137,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
@@ -65,11 +68,25 @@ fn void TempAllocator.reset(&self, usz mark) @dynamic
TempAllocatorPage *last_page = self.last_page;
while (last_page && last_page.mark > mark)
{
self.used = last_page.mark;
TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page;
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 +111,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 +128,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 +139,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 +152,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 +171,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 +188,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,10 +31,10 @@ 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);
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,21 +79,20 @@ 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);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
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 +106,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 +118,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(arr1, arr2, Allocator allocator) @nodiscard
{
var $Type = $typeof(arr1[0]);
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
@@ -69,19 +69,34 @@ macro concat_new(arr1, arr2, Allocator* allocator = allocator::heap())
}
return result;
}
/**
* 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) == 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()) @nodiscard
{
return concat(arr1, arr2, allocator);
}
/**
* 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
**/
macro tconcat(arr1, arr2) => concat(arr1, arr2, allocator::temp());
macro tconcat(arr1, arr2) @nodiscard => concat(arr1, arr2, allocator::temp());
module std::core::array::slice(<Type>);

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;
};
@@ -110,13 +104,29 @@ fn void default_panic(String message, String file, String function, uint line) @
$$trap();
}
macro void abort(String string = "Unrecoverable error reached", ...) @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
$$trap();
}
bool in_panic @local = false;
fn void default_panic(String message, String file, String function, uint line) @if(!env::NATIVE_STACKTRACE)
{
if (in_panic)
{
io::eprintn("Panic inside of panic.");
return;
}
in_panic = true;
$if $defined(io::stderr):
io::eprint("\nERROR: '");
io::eprint(message);
io::eprintfn("', in %s (%s:%d)", function, file, line);
$endif
in_panic = false;
$$trap();
}
@@ -126,11 +136,19 @@ PanicFn panic = &default_panic;
fn void panicf(String fmt, String file, String function, uint line, args...)
{
@stack_mem(512; Allocator* allocator)
if (in_panic)
{
io::eprint("Panic inside of panic: ");
io::eprintn(fmt);
return;
}
in_panic = true;
@stack_mem(512; Allocator allocator)
{
DString s;
s.new_init(.allocator = allocator);
s.new_init(allocator: allocator);
s.appendf(fmt, ...args);
in_panic = false;
panic(s.str_view(), file, function, line);
};
}
@@ -142,7 +160,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 +172,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 +317,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 +342,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 +363,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 +700,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();
@@ -122,13 +154,15 @@ fn String DString.str_view(self)
return (String)data.chars[:data.len];
}
fn void DString.append_utf32(&self, Char32[] chars)
fn usz DString.append_utf32(&self, Char32[] chars)
{
self.reserve(chars.len);
usz end = self.len();
foreach (Char32 c : chars)
{
self.append_char32(c);
}
return self.data().len - end;
}
/**
@@ -153,7 +187,7 @@ fn void DString.append_repeat(&self, char c, usz times)
/**
* @require c <= 0x10ffff
*/
fn void DString.append_char32(&self, Char32 c)
fn usz DString.append_char32(&self, Char32 c)
{
char[4] buffer @noinit;
char* p = &buffer;
@@ -162,11 +196,12 @@ fn void DString.append_char32(&self, Char32 c)
StringData* data = self.data();
data.chars[data.len:n] = buffer[:n];
data.len += n;
return n;
}
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 +215,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 +229,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,9 +293,9 @@ 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!!;
return self.str_view().to_utf32(allocator) @inline!!;
}
fn void DString.append_string(&self, DString str)
@@ -355,7 +390,10 @@ macro void DString.append(&self, value)
$endswitch
}
fn void DString.insert_at(&self, usz index, String s)
/**
* @require index <= self.len()
**/
fn void DString.insert_chars_at(&self, usz index, String s)
{
if (s.len == 0) return;
self.reserve(s.len);
@@ -387,23 +425,128 @@ fn void DString.insert_at(&self, usz index, String s)
}
}
/**
* @require index <= self.len()
**/
fn void DString.insert_string_at(&self, usz index, DString str)
{
StringData* other = str.data();
if (!other) return;
self.insert_at(index, str.str_view());
}
/**
* @require index <= self.len()
**/
fn void DString.insert_char_at(&self, usz index, char c)
{
self.reserve(1);
StringData* data = self.data();
char* start = &data.chars[index];
mem::move(start + 1, start, self.len() - index);
data.chars[index] = c;
data.len++;
}
/**
* @require index <= self.len()
**/
fn usz DString.insert_char32_at(&self, usz index, Char32 c)
{
char[4] buffer @noinit;
char* p = &buffer;
usz n = conv::char32_to_utf8_unsafe(c, &p);
self.reserve(n);
StringData* data = self.data();
char* start = &data.chars[index];
mem::move(start + n, start, self.len() - index);
data.chars[index:n] = buffer[:n];
data.len += n;
return n;
}
/**
* @require index <= self.len()
**/
fn usz DString.insert_utf32_at(&self, usz index, Char32[] chars)
{
usz n = conv::utf8len_for_utf32(chars);
self.reserve(n);
StringData* data = self.data();
char* start = &data.chars[index];
mem::move(start + n, start, self.len() - index);
char[4] buffer @noinit;
foreach(c : chars)
{
char* p = &buffer;
usz m = conv::char32_to_utf8_unsafe(c, &p);
data.chars[index:m] = buffer[:m];
index += m;
}
data.len += n;
return n;
}
macro void DString.insert_at(&self, usz index, value)
{
var $Type = $typeof(value);
$switch ($Type)
$case char:
$case ichar:
self.insert_char_at(index, value);
$case DString:
self.insert_string_at(index, value);
$case String:
self.insert_chars_at(index, value);
$case Char32:
self.insert_char32_at(index, value);
$default:
$switch
$case $defined((Char32)value):
self.insert_char32_at(index, (Char32)value);
$case $defined((String)value):
self.insert_chars_at(index, (String)value);
$default:
$error "Unsupported type for insert";
$endswitch
$endswitch
}
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;
@@ -427,6 +570,19 @@ fn void! out_string_append_fn(void* data, char c) @private
s.append_char(c);
}
fn void DString.reverse(self)
{
StringData *data = self.data();
if (!data) return;
isz mid = data.len / 2;
for (isz i = 0; i < mid; i++)
{
char temp = data.chars[i];
isz reverse_index = data.len - 1 - i;
data.chars[i] = data.chars[reverse_index];
data.chars[reverse_index] = temp;
}
}
fn StringData* DString.data(self) @inline @private
{
@@ -450,7 +606,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 +638,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,15 @@ enum ArchType
WASM64, // WebAssembly with 64-bit pointers
RSCRIPT32, // 32-bit RenderScript
RSCRIPT64, // 64-bit RenderScript
XTENSA, // Xtensa
}
const String COMPILER_BUILD_HASH = $$BUILD_HASH;
const String COMPILER_BUILD_DATE = $$BUILD_DATE;
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 +128,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;
@@ -141,10 +148,15 @@ const bool POSIX = LIBC && os_is_posix();
const bool OPENBSD = LIBC && OS_TYPE == OPENBSD;
const bool FREEBSD = LIBC && OS_TYPE == FREEBSD;
const bool NETBSD = LIBC && OS_TYPE == NETBSD;
const bool BSD_FAMILY = env::FREEBSD || env::OPENBSD || env::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 +169,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);
@@ -251,6 +281,11 @@ fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
return (uptr)ptr & ((uptr)alignment - 1) == 0;
}
macro void zero_volatile(char[] data)
{
$$memset(data.ptr, (char)0, data.len, true, (usz)1);
}
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
@@ -343,9 +378,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 +391,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 +438,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 +450,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 +461,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,31 +533,24 @@ 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;
fn void initialize_wasm_mem() @init(1) @private
fn void initialize_wasm_mem() @init(1024) @private
{
allocator::wasm_memory.allocate_block(mem::DEFAULT_MEM_ALIGNMENT)!!; // Give us a valid null.
// Check if we need to move the heap.
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::temp_base_allocator = &wasm_allocator;
allocator::init_default_temp_allocators();
}
module std::core::mem;
@@ -552,15 +580,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 +606,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 +615,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 +623,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 +645,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 +655,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 +665,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 +697,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 +705,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 +754,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,24 @@ 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 = base_allocator();
Allocator temp_base_allocator @private = base_allocator();
tlocal TempAllocator* thread_temp_allocator @private = null;
tlocal TempAllocator*[2] temp_allocator_pair @private;
Allocator* temp_base_allocator @private = &allocator::LIBC_ALLOCATOR;
macro TempAllocator* create_default_sized_temp_allocator(Allocator* allocator) @local
macro Allocator base_allocator() @private
{
$if env::LIBC:
return &allocator::LIBC_ALLOCATOR;
$else
return &allocator::NULL_ALLOCATOR;
$endif
}
macro TempAllocator* create_default_sized_temp_allocator(Allocator allocator) @local
{
$switch (env::MEMORY_ENV)
$case NORMAL:
@@ -414,7 +389,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 +407,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 +433,22 @@ 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];
}
}
const NullAllocator NULL_ALLOCATOR = {};
distinct NullAllocator (Allocator) = uptr;
fn void*! NullAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
return AllocationFailure.OUT_OF_MEMORY?;
}
fn void*! NullAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
return AllocationFailure.OUT_OF_MEMORY?;
}
fn void NullAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
}

View File

@@ -30,6 +30,7 @@ enum X86Feature
AMX_FP16,
AMX_INT8,
AMX_TILE,
APXF,
AVX,
AVX10_1_256,
AVX10_1_512,
@@ -156,6 +157,7 @@ fn void x86_initialize_cpu_features()
add_feature_if_bit(AMX_FP16, leaf7s1.eax, 21);
add_feature_if_bit(AMX_INT8, leaf7.edx, 25);
add_feature_if_bit(AMX_TILE, leaf7.edx, 24);
add_feature_if_bit(APXF, leaf7s1.edx, 21);
add_feature_if_bit(AVX, feat.ecx, 28);
add_feature_if_bit(AVX10_1_256, leaf7s1.edx, 19);
add_feature_if_bit(AVX10_1_512, leaf_24.ebx, 18);
@@ -255,4 +257,4 @@ fn void x86_initialize_cpu_features()
add_feature_if_bit(XSAVEOPT, leaf_d.eax, 0);
add_feature_if_bit(XSAVES, leaf_d.eax, 3);
}
}

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,19 @@
module std::core::runtime;
import libc, std::time, std::io, std::sort;
struct AnyStruct
struct ReflectedParam @if(!$defined(ReflectedParam))
{
String name;
typeid type;
}
struct AnyRaw
{
void* ptr;
typeid type;
}
struct SubArrayStruct
struct SliceRaw
{
void* ptr;
usz len;
@@ -24,7 +30,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 +148,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;
@@ -215,6 +221,7 @@ fn bool run_tests(TestUnit[] tests)
name.appendf("Testing %s ", unit.name);
name.append_repeat('.', max_name - unit.name.len + 2);
io::printf("%s ", name.str_view());
(void)io::stdout().flush();
if (libc::setjmp(&context.buf) == 0)
{
if (catch err = unit.func())

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,47 +32,85 @@ fault NumberConversion
FLOAT_OUT_OF_RANGE,
}
macro String tformat(String fmt, ...)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat());
return str.str_view();
}
macro ZString tformat_zstr(String fmt, ...)
/**
* Return a temporary ZString created using the formatting function.
*
* @param [in] fmt `The formatting string`
**/
fn ZString tformat_zstr(String fmt, args...)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat());
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.zstr_view();
}
macro String new_format(String fmt, ..., Allocator* allocator = allocator::heap())
/**
* Return a new String created using the formatting function.
*
* @param [inout] allocator `The allocator to use`
* @param [in] fmt `The formatting string`
**/
fn String format(String fmt, args..., Allocator allocator)
{
@pool(allocator)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat());
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.copy_str(allocator);
};
}
macro ZString new_format_zstr(String fmt, ..., Allocator* allocator = allocator::heap())
/**
* Return a heap allocated String created using the formatting function.
*
* @param [in] fmt `The formatting string`
**/
fn String new_format(String fmt, args..., Allocator allocator = null) => format(fmt, ...args, allocator: allocator ?: allocator::heap());
/**
* Return a temporary String created using the formatting function.
*
* @param [in] fmt `The formatting string`
**/
fn String tformat(String fmt, args...)
{
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.str_view();
}
/**
* Return a new ZString created using the formatting function.
*
* @param [in] fmt `The formatting string`
* @param [inout] allocator `The allocator to use`
**/
fn ZString new_format_zstr(String fmt, args..., Allocator allocator = allocator::heap())
{
@pool(allocator)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat());
DString str = dstring::temp_with_capacity(fmt.len + args.len * 8);
str.appendf(fmt, ...args);
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)
{
@@ -97,8 +136,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 ")
{
@@ -112,8 +155,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)
{
@@ -123,8 +170,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)
{
@@ -138,6 +189,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)
{
@@ -150,6 +203,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)
{
@@ -158,18 +213,17 @@ fn String String.strip_end(string, String needle)
return string[:(string.len - needle.len)];
}
/**
* Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
*
* @param [in] s
* @param [in] needle
* @param [&inout] allocator "The allocator, defaults to the heap allocator"
* @param [&inout] allocator "The allocator to use for the String[]"
* @param max "Max number of elements, 0 means no limit, defaults to 0"
* @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;
@@ -199,6 +253,18 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator* allocator = a
return holder[:i];
}
/**
* Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }, using the heap allocator
* to store the parts.
*
* @param [in] s
* @param [in] needle
* @param max "Max number of elements, 0 means no limit, defaults to 0"
* @require needle.len > 0 "The needle must be at least 1 character long"
* @ensure return.len > 0
**/
fn String[] String.new_split(s, String needle, usz max = 0) => s.split(needle, max, allocator::heap()) @inline;
/**
* This function is identical to String.split, but implicitly uses the
* temporary allocator.
@@ -207,11 +273,16 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator* allocator = a
* @param [in] needle
* @param max "Max number of elements, 0 means no limit, defaults to 0"
**/
fn String[] String.tsplit(s, String needle, usz max = 0)
{
return s.split(needle, max, allocator::temp()) @inline;
}
fn String[] String.tsplit(s, String needle, usz max = 0) => 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));
@@ -221,6 +292,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"
@@ -236,9 +308,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"
@@ -328,7 +423,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);
@@ -337,7 +432,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);
@@ -353,7 +448,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);
@@ -362,7 +457,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);
@@ -371,7 +466,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;
}
@@ -387,7 +482,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)!;
@@ -407,17 +502,15 @@ 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_wstring(s, Allocator allocator)
{
return (WString)s.to_new_utf16(allocator).ptr;
}
fn WString! String.to_temp_wstring(s)
{
return (WString)s.to_temp_utf16().ptr;
}
fn WString! String.to_temp_wstring(s) => s.to_wstring(allocator::temp());
fn WString! String.to_new_wstring(s) => s.to_wstring(allocator::heap());
fn Char32[]! String.to_new_utf32(s, Allocator* allocator = allocator::heap())
fn Char32[]! String.to_utf32(s, Allocator allocator)
{
usz codepoints = conv::utf8_codepoints(s);
Char32* data = allocator::alloc_array_try(allocator, Char32, codepoints + 1)!;
@@ -426,34 +519,52 @@ fn Char32[]! String.to_new_utf32(s, Allocator* allocator = allocator::heap())
return data[:codepoints];
}
fn Char32[]! String.to_temp_utf32(s)
{
return s.to_new_utf32(allocator::temp());
}
fn Char32[]! String.to_new_utf32(s) => s.to_utf32(allocator::heap()) @inline;
fn Char32[]! String.to_temp_utf32(s) => s.to_utf32(allocator::temp()) @inline;
/**
* 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)
{
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();
@@ -465,12 +576,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)!;
@@ -480,7 +595,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)!;
@@ -490,7 +605,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,51 @@ 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)
/**
* @require is_numerical($Type) "Expected a numerical type"
**/
macro bool is_signed($Type) @const
{
$switch (inner_kind($Type))
$case SIGNED_INT:
$case FLOAT:
return true;
$case VECTOR:
return is_signed($typefrom($Type.inner));
$default:
return false;
$endswitch
}
/**
* @require is_numerical($Type) "Expected a numerical type"
**/
macro bool is_unsigned($Type) @const
{
$switch (inner_kind($Type))
$case UNSIGNED_INT:
return true;
$case VECTOR:
return is_unsigned($typefrom($Type.inner));
$default:
return false;
$endswitch
}
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 +177,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 +190,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,40 +204,45 @@ 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 typeid inner_type($Type) @const
{
$if $Type.kindof == TypeKind.DISTINCT:
return inner_kind($typefrom($Type.inner));
return inner_type($typefrom($Type.inner));
$else
return $Type.kindof;
return $Type.typeid;
$endif
}
macro bool is_same($TypeA, $TypeB)
macro TypeKind inner_kind($Type) @const
{
return inner_type($Type).kindof;
}
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 +257,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 +283,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 +295,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 +307,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 +358,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

@@ -1,2 +1,12 @@
module std::crypto;
fn bool safe_compare(void* data1, void* data2, usz len)
{
char match = 0;
for (usz i = 0; i < len; i++)
{
match = match | (mem::@volatile_load(((char*)data1)[i]) ^ mem::@volatile_load(((char*)data2)[i]));
}
return match == 0;
}

12
lib/std/crypto/dh.c3 Normal file
View File

@@ -0,0 +1,12 @@
module std::crypto::dh;
import std::math::bigint;
fn BigInt generate_secret(BigInt p, BigInt x, BigInt y)
{
return y.mod_pow(x, p);
}
fn BigInt public_key(BigInt p, BigInt g, BigInt x)
{
return g.mod_pow(x, p);
}

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,26 +3,26 @@ 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)
{
return io::treadline(self.stream).split(self.separator, .allocator = allocator);
return io::treadline(self.stream).split(self.separator, allocator: 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()
@@ -56,7 +56,7 @@ macro CsvReader.@each_row(self, int rows = int.max; @body(String[] row))
if (err == IoError.EOF) return;
return err?;
}
parts = s.split(sep, .allocator = mem);
parts = s.split(sep, allocator: mem);
};
@body(parts);
};

View File

@@ -15,11 +15,31 @@ fault JsonParsingError
INVALID_NUMBER,
}
fn Object*! parse(InStream* s, Allocator* allocator = allocator::heap())
fn Object*! parse_string(String 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);
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())
{
@stack_mem(512; Allocator mem)
{
JsonContext context = { .last_string = dstring::new_with_capacity(64, mem), .stream = s, .allocator = allocator };
@pool(allocator)
{
return parse_any(&context);
};
};
}
fn Object*! temp_parse(InStream s)
{
return parse(s, allocator::temp());
}
// -- Implementation follows --
@@ -44,8 +64,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;
@@ -84,9 +104,9 @@ fn Object*! parse_any(JsonContext* context) @local
fn JsonTokenType! lex_number(JsonContext *context, char c) @local
{
@pool()
@stack_mem(256; Allocator mem)
{
DString t = dstring::temp_with_capacity(32);
DString t = dstring::new_with_capacity(32, allocator: mem);
bool negate = c == '-';
if (negate)
{
@@ -134,32 +154,34 @@ fn JsonTokenType! lex_number(JsonContext *context, char c) @local
fn Object*! parse_map(JsonContext* context) @local
{
Object* map = object::new_obj(context.allocator);
JsonTokenType token = advance(context)!;
defer catch map.free();
JsonTokenType token = advance(context)!;
DString temp_key = dstring::new_with_capacity(32, context.allocator);
defer temp_key.free();
while (token != JsonTokenType.RBRACE)
@stack_mem(256; Allocator mem)
{
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
DString string = context.last_string;
if (map.has_key(string.str_view())) return JsonParsingError.DUPLICATE_MEMBERS?;
// Copy the key to our temp holder. We do this to work around the issue
// if the temp allocator should be used as the default allocator.
temp_key.clear();
temp_key.append(string);
parse_expected(context, COLON)!;
Object* element = parse_any(context)!;
map.set(temp_key.str_view(), element);
token = advance(context)!;
if (token == JsonTokenType.COMMA)
DString temp_key = dstring::new_with_capacity(32, mem);
while (token != JsonTokenType.RBRACE)
{
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
DString string = context.last_string;
if (map.has_key(string.str_view())) return JsonParsingError.DUPLICATE_MEMBERS?;
// Copy the key to our temp holder, since our
// last_string may be used in parse_any
temp_key.clear();
temp_key.append(string);
parse_expected(context, COLON)!;
Object* element = parse_any(context)!;
map.set(temp_key.str_view(), element);
token = advance(context)!;
continue;
if (token == JsonTokenType.COMMA)
{
token = advance(context)!;
continue;
}
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
return map;
return map;
};
}
fn Object*! parse_array(JsonContext* context) @local
@@ -170,7 +192,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)
{
@@ -364,6 +386,7 @@ fn JsonTokenType! lex_string(JsonContext* context)
default:
return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
}
context.last_string.append(c);
}
return STRING;
}

107
lib/std/hash/hmac.c3 Normal file
View File

@@ -0,0 +1,107 @@
module std::hash::hmac(<HashAlg, HASH_BYTES, BLOCK_BYTES>);
import std::crypto;
struct Hmac
{
HashAlg a, b;
}
fn char[HASH_BYTES] hash(char[] key, char[] message)
{
Hmac hmac @noinit;
hmac.init(key);
hmac.update(message);
return hmac.final();
}
/**
* @require output.len > 0 "Output must be greater than zero"
* @require output.len < int.max / HASH_BYTES "Output is too large"
**/
fn void pbkdf2(char[] pw, char[] salt, uint iterations, char[] output)
{
usz l = output.len / HASH_BYTES;
usz r = output.len % HASH_BYTES;
Hmac hmac;
hmac.init(pw);
char[] dst_curr = output;
for (usz i = 1; i <= l; i++)
{
@derive(&hmac, salt, iterations, i, dst_curr[:HASH_BYTES]);
dst_curr = dst_curr[HASH_BYTES..];
}
if (r > 0)
{
char[HASH_BYTES] tmp;
@derive(&hmac, salt, iterations, l + 1, &tmp);
dst_curr[..] = tmp[:dst_curr.len];
mem::zero_volatile(&tmp);
}
}
fn void Hmac.init(&self, char[] key)
{
char[BLOCK_BYTES] buffer;
if (key.len > BLOCK_BYTES)
{
self.a.init();
self.a.update(key);
buffer[:HASH_BYTES] = self.a.final()[..];
}
else
{
buffer[:key.len] = key[..];
}
foreach (&b : buffer) *b ^= IPAD;
self.a.init();
self.a.update(&buffer);
foreach (&b : buffer) *b ^= IPAD ^ OPAD;
self.b.init();
self.b.update(&buffer);
mem::zero_volatile(&buffer);
}
fn void Hmac.update(&self, char[] data)
{
self.a.update(data);
}
fn char[HASH_BYTES] Hmac.final(&self)
{
self.b.update(&&self.a.final());
return self.b.final();
}
const IPAD @private = 0x36;
const OPAD @private = 0x5C;
macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[] out)
{
assert(out.len == HASH_BYTES);
char[HASH_BYTES] tmp @noinit;
defer mem::zero_volatile(&tmp);
Hmac hmac = *hmac_start;
hmac.update(salt);
UIntBE be = { (uint)index };
hmac.update(&&bitcast(be, char[4]));
tmp = hmac.final();
out[..] = tmp;
for (int it = 1; it < iterations; it++)
{
hmac = *hmac_start;
hmac.update(&tmp);
tmp = hmac.final();
foreach (i, v : tmp)
{
out[i] ^= v;
}
}
}

225
lib/std/hash/md5.c3 Normal file
View File

@@ -0,0 +1,225 @@
module std::hash::md5;
import std::hash::hmac;
import std::bits;
const BLOCK_BYTES = 64;
const HASH_BYTES = 16;
struct Md5
{
uint lo, hi;
uint a, b, c, d;
char[64] buffer;
uint[16] block;
}
def HmacMd5 = Hmac(<Md5, HASH_BYTES, BLOCK_BYTES>);
def hmac = hmac::hash(<Md5, HASH_BYTES, BLOCK_BYTES>);
def pbkdf2 = hmac::pbkdf2(<Md5, HASH_BYTES, BLOCK_BYTES>);
fn char[HASH_BYTES] hash(char[] data)
{
Md5 md5;
md5.init();
md5.update(data);
return md5.final();
}
fn void Md5.init(&self)
{
self.a = 0x67452301;
self.b = 0xefcdab89;
self.c = 0x98badcfe;
self.d = 0x10325476;
self.lo = 0;
self.hi = 0;
}
fn void Md5.update(&ctx, char[] data)
{
uint saved_lo = ctx.lo;
if ((ctx.lo = (saved_lo + data.len) & 0x1fffffff) < saved_lo) ctx.hi++;
ctx.hi += data.len >> 29;
usz used = (usz)saved_lo & 0x3f;
if (used)
{
usz available = 64 - used;
if (data.len < available)
{
ctx.buffer[used:data.len] = data[..];
return;
}
ctx.buffer[used:available] = data[:available];
data = data[available..];
body(ctx, &ctx.buffer, 64);
}
if (data.len >= 64)
{
data = body(ctx, data, data.len & ~(usz)0x3f)[:data.len & 0x3f];
}
ctx.buffer[:data.len] = data[..];
}
fn char[HASH_BYTES] Md5.final(&ctx)
{
usz used = (usz)ctx.lo & 0x3f;
ctx.buffer[used++] = 0x80;
usz available = 64 - used;
if (available < 8)
{
ctx.buffer[used:available] = 0;
body(ctx, &ctx.buffer, 64);
used = 0;
available = 64;
}
ctx.buffer[used:available - 8] = 0;
ctx.lo <<= 3;
ctx.buffer[56:4] = bitcast(ctx.lo, char[4])[..];
ctx.buffer[60:4] = bitcast(ctx.hi, char[4])[..];
body(ctx, &ctx.buffer, 64);
char[16] res @noinit;
res[0:4] = bitcast(ctx.a, char[4]);
res[4:4] = bitcast(ctx.b, char[4]);
res[8:4] = bitcast(ctx.c, char[4]);
res[12:4] = bitcast(ctx.d, char[4]);
*ctx = {};
return res;
}
module std::hash::md5 @private;
// Implementation
macro @f(x, y, z) => z ^ (x & (y ^ z));
macro @g(x, y, z) => y ^ (z & (x ^ y));
macro @h(x, y, z) => (x ^ y) ^ z;
macro @h2(x, y, z) => x ^ (y ^ z);
macro @i(x, y, z) => y ^ (x | ~z);
macro @step(#f, &a, b, c, d, ptr, n, t, s)
{
*a += #f(b, c, d) + *(uint *)&ptr[n * 4] + t;
*a = (*a << s) | ((*a & 0xffffffff) >> (32 - s));
*a += b;
}
fn char* body(Md5* ctx, void* data, usz size)
{
char* ptr;
uint a, b, c, d;
uint saved_a, saved_b, saved_c, saved_d;
ptr = data;
a = ctx.a;
b = ctx.b;
c = ctx.c;
d = ctx.d;
do
{
saved_a = a;
saved_b = b;
saved_c = c;
saved_d = d;
/* Round 1 */
@step(@f, a, b, c, d, ptr, 0, 0xd76aa478, 7) ;
@step(@f, d, a, b, c, ptr, 1, 0xe8c7b756, 12) ;
@step(@f, c, d, a, b, ptr, 2, 0x242070db, 17) ;
@step(@f, b, c, d, a, ptr, 3, 0xc1bdceee, 22) ;
@step(@f, a, b, c, d, ptr, 4, 0xf57c0faf, 7) ;
@step(@f, d, a, b, c, ptr, 5, 0x4787c62a, 12) ;
@step(@f, c, d, a, b, ptr, 6, 0xa8304613, 17) ;
@step(@f, b, c, d, a, ptr, 7, 0xfd469501, 22) ;
@step(@f, a, b, c, d, ptr, 8, 0x698098d8, 7) ;
@step(@f, d, a, b, c, ptr, 9, 0x8b44f7af, 12) ;
@step(@f, c, d, a, b, ptr, 10, 0xffff5bb1, 17);
@step(@f, b, c, d, a, ptr, 11, 0x895cd7be, 22);
@step(@f, a, b, c, d, ptr, 12, 0x6b901122, 7) ;
@step(@f, d, a, b, c, ptr, 13, 0xfd987193, 12);
@step(@f, c, d, a, b, ptr, 14, 0xa679438e, 17);
@step(@f, b, c, d, a, ptr, 15, 0x49b40821, 22);
/* Round 2 */
@step(@g, a, b, c, d, ptr, 1, 0xf61e2562, 5) ;
@step(@g, d, a, b, c, ptr, 6, 0xc040b340, 9) ;
@step(@g, c, d, a, b, ptr, 11, 0x265e5a51, 14);
@step(@g, b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ;
@step(@g, a, b, c, d, ptr, 5, 0xd62f105d, 5) ;
@step(@g, d, a, b, c, ptr, 10, 0x02441453, 9) ;
@step(@g, c, d, a, b, ptr, 15, 0xd8a1e681, 14);
@step(@g, b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ;
@step(@g, a, b, c, d, ptr, 9, 0x21e1cde6, 5) ;
@step(@g, d, a, b, c, ptr, 14, 0xc33707d6, 9) ;
@step(@g, c, d, a, b, ptr, 3, 0xf4d50d87, 14) ;
@step(@g, b, c, d, a, ptr, 8, 0x455a14ed, 20) ;
@step(@g, a, b, c, d, ptr, 13, 0xa9e3e905, 5) ;
@step(@g, d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ;
@step(@g, c, d, a, b, ptr, 7, 0x676f02d9, 14) ;
@step(@g, b, c, d, a, ptr, 12, 0x8d2a4c8a, 20);
/* Round 3 */
@step(@h, a, b, c, d, ptr, 5, 0xfffa3942, 4);
@step(@h2, d, a, b, c, ptr, 8, 0x8771f681, 11);
@step(@h, c, d, a, b, ptr, 11, 0x6d9d6122, 16);
@step(@h2, b, c, d, a, ptr, 14, 0xfde5380c, 23);
@step(@h, a, b, c, d, ptr, 1, 0xa4beea44, 4);
@step(@h2, d, a, b, c, ptr, 4, 0x4bdecfa9, 11);
@step(@h, c, d, a, b, ptr, 7, 0xf6bb4b60, 16);
@step(@h2, b, c, d, a, ptr, 10, 0xbebfbc70, 23);
@step(@h, a, b, c, d, ptr, 13, 0x289b7ec6, 4) ;
@step(@h2, d, a, b, c, ptr, 0, 0xeaa127fa, 11) ;
@step(@h, c, d, a, b, ptr, 3, 0xd4ef3085, 16) ;
@step(@h2, b, c, d, a, ptr, 6, 0x04881d05, 23) ;
@step(@h, a, b, c, d, ptr, 9, 0xd9d4d039, 4) ;
@step(@h2, d, a, b, c, ptr, 12, 0xe6db99e5, 11) ;
@step(@h, c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ;
@step(@h2, b, c, d, a, ptr, 2, 0xc4ac5665, 23) ;
/* Round 4 */
@step(@i, a, b, c, d, ptr, 0, 0xf4292244, 6) ;
@step(@i, d, a, b, c, ptr, 7, 0x432aff97, 10) ;
@step(@i, c, d, a, b, ptr, 14, 0xab9423a7, 15) ;
@step(@i, b, c, d, a, ptr, 5, 0xfc93a039, 21) ;
@step(@i, a, b, c, d, ptr, 12, 0x655b59c3, 6) ;
@step(@i, d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ;
@step(@i, c, d, a, b, ptr, 10, 0xffeff47d, 15) ;
@step(@i, b, c, d, a, ptr, 1, 0x85845dd1, 21) ;
@step(@i, a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ;
@step(@i, d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ;
@step(@i, c, d, a, b, ptr, 6, 0xa3014314, 15) ;
@step(@i, b, c, d, a, ptr, 13, 0x4e0811a1, 21) ;
@step(@i, a, b, c, d, ptr, 4, 0xf7537e82, 6) ;
@step(@i, d, a, b, c, ptr, 11, 0xbd3af235, 10) ;
@step(@i, c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ;
@step(@i, b, c, d, a, ptr, 9, 0xeb86d391, 21) ;
a += saved_a;
b += saved_b;
c += saved_c;
d += saved_d;
ptr += 64;
} while (size -= 64);
ctx.a = a;
ctx.b = b;
ctx.c = c;
ctx.d = d;
return ptr;
}

View File

@@ -5,13 +5,29 @@
// Implementation was off Steve Reid's SHA-1 C implementation
module std::hash::sha1;
import std::hash::hmac;
import std::bits;
const BLOCK_BYTES = 64;
const HASH_BYTES = 20;
struct Sha1
{
uint[5] state;
uint[2] count;
char[64] buffer;
char[BLOCK_BYTES] buffer;
}
def HmacSha1 = Hmac(<Sha1, HASH_BYTES, BLOCK_BYTES>);
def hmac = hmac::hash(<Sha1, HASH_BYTES, BLOCK_BYTES>);
def pbkdf2 = hmac::pbkdf2(<Sha1, HASH_BYTES, BLOCK_BYTES>);
fn char[HASH_BYTES] hash(char[] data)
{
Sha1 sha1 @noinit;
sha1.init();
sha1.update(data);
return sha1.final();
}
fn void Sha1.init(&self)
@@ -55,7 +71,7 @@ fn void Sha1.update(&self, char[] data)
}
fn char[20] Sha1.final(&self)
fn char[HASH_BYTES] Sha1.final(&self)
{
char[8] finalcount;
for (uint i = 0; i < 8; i++)
@@ -69,21 +85,21 @@ fn char[20] Sha1.final(&self)
}
self.update(&finalcount);
char[20] digest;
for (uint i = 0; i < 20; i++)
char[HASH_BYTES] digest;
for (uint i = 0; i < HASH_BYTES; i++)
{
digest[i] = (char)((self.state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF);
}
// Clear mem
mem::clear(self, Sha1.sizeof);
*self = {};
finalcount = {};
return digest;
}
union Long16 @local
{
char[64] c;
char[BLOCK_BYTES] c;
uint[16] l;
}

176
lib/std/hash/sha256.c3 Normal file
View File

@@ -0,0 +1,176 @@
module std::hash::sha256;
import std::hash::hmac;
const BLOCK_SIZE = 64;
const HASH_SIZE = 32;
const uint[64] K @local = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
// Right rotate function
macro uint @rotr(uint x, uint n) @local => (((x) >> (n)) | ((x) << (32 - (n))));
// SHA-256 functions
macro uint @ch(uint x, uint y, uint z) @local => (x & y) ^ (~x & z);
macro uint @maj(uint x, uint y, uint z) @local => (x & y) ^ (x & z) ^ (y & z);
macro uint @_sigma0(uint x) @local => @rotr(x, 2) ^ @rotr(x, 13) ^ @rotr(x, 22);
macro uint @_sigma1(uint x) @local => @rotr(x, 6) ^ @rotr(x, 11) ^ @rotr(x, 25);
macro uint @sigma0(uint x) @local => @rotr(x, 7) ^ @rotr(x, 18) ^ (x >> 3);
macro uint @sigma1(uint x) @local => @rotr(x, 17) ^ @rotr(x, 19) ^ (x >> 10);
struct Sha256
{
uint[8] state;
ulong bitcount;
char[BLOCK_SIZE] buffer;
}
def HmacSha256 = Hmac(<Sha256, HASH_SIZE, BLOCK_SIZE>);
def hmac = hmac::hash(<Sha256, HASH_SIZE, BLOCK_SIZE>);
def pbkdf2 = hmac::pbkdf2(<Sha256, HASH_SIZE, BLOCK_SIZE>);
fn char[HASH_SIZE] hash(char[] data)
{
Sha256 sha256 @noinit;
sha256.init();
sha256.update(data);
return sha256.final();
}
fn void Sha256.init(&self)
{
// Sha256 initialization constants
*self = {
.state = {
0x6A09E667,
0xBB67AE85,
0x3C6EF372,
0xA54FF53A,
0x510E527F,
0x9B05688C,
0x1F83D9AB,
0x5BE0CD19
}
};
}
/**
* @param [in] data
* @require data.len <= uint.max
**/
fn void Sha256.update(&self, char[] data) {
uint i = 0;
uint len = data.len;
uint buffer_pos = (uint)(self.bitcount / 8) % BLOCK_SIZE;
self.bitcount += (ulong)(len * 8);
while (len--) {
self.buffer[buffer_pos++] = data[i++];
if (buffer_pos == BLOCK_SIZE) {
sha256_transform(&self.state, &self.buffer);
buffer_pos = 0; // Reset buffer position
}
}
}
fn char[HASH_SIZE] Sha256.final(&self) {
char[HASH_SIZE] hash;
ulong i = (self.bitcount / 8) % BLOCK_SIZE;
// Append 0x80 to the buffer
self.buffer[i++] = 0x80;
// Pad the buffer with zeros
if (i > BLOCK_SIZE - 8) {
while (i < BLOCK_SIZE) {
self.buffer[i++] = 0x00;
}
sha256_transform(&self.state, &self.buffer);
i = 0; // Reset buffer index after transformation
}
while (i < BLOCK_SIZE - 8) {
self.buffer[i++] = 0x00;
}
// Append the bitcount in big-endian format
for (int j = 0; j < 8; ++j) {
self.buffer[BLOCK_SIZE - 8 + j] = (char)((self.bitcount >> (56 - j * 8)) & 0xFF);
}
sha256_transform(&self.state, &self.buffer);
// Convert state to the final hash
for (i = 0; i < 8; ++i) {
hash[i * 4] = (char)((self.state[i] >> 24) & 0xFF);
hash[i * 4 + 1] = (char)((self.state[i] >> 16) & 0xFF);
hash[i * 4 + 2] = (char)((self.state[i] >> 8) & 0xFF);
hash[i * 4 + 3] = (char)(self.state[i] & 0xFF);
}
return hash;
}
/**
* @param [&inout] state
* @param [&in] buffer
**/
fn void sha256_transform(uint* state, char* buffer) @local {
uint a, b, c, d, e, f, g, h, t1, t2;
uint[64] m;
int i;
// Prepare the message schedule
for (i = 0; i < 16; ++i) {
m[i] = ((uint)buffer[i * 4] << 24) | ((uint)buffer[i * 4 + 1] << 16) |
((uint)buffer[i * 4 + 2] << 8) | ((uint)buffer[i * 4 + 3]); // Ensure values are cast to uint for correct shifts
}
for (i = 16; i < 64; ++i) {
m[i] = @sigma1(m[i - 2]) + m[i - 7] + @sigma0(m[i - 15]) + m[i - 16];
}
// Initialize working variables
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
f = state[5];
g = state[6];
h = state[7];
// Perform the main SHA-256 compression function
for (i = 0; i < 64; ++i) {
t1 = h + @_sigma1(e) + @ch(e, f, g) + K[i] + m[i];
t2 = @_sigma0(a) + @maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
// Update the state
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;
a = b = c = d = e = f = g = h = t1 = t2 = i = 0;
m[:64] = buffer[:64] = 0;
}

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

@@ -76,7 +76,7 @@ fn void! File.memopen(File* file, char[] data, String mode)
*/
fn void! File.write_byte(&self, char c) @dynamic
{
if (!libc::fputc(c, self.file)) return IoError.EOF?;
return os::native_fputc(c, self.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,8 @@ const int PRINTF_NTOA_BUFFER_SIZE = 256;
interface Printable
{
fn String to_new_string(Allocator *allocator) @optional;
fn String to_string(Allocator allocator) @optional;
fn String to_new_string(Allocator allocator) @optional @deprecated("Use to_string");
fn usz! to_format(Formatter* formatter) @optional;
}
@@ -14,24 +15,14 @@ fault PrintFault
{
BUFFER_EXCEEDED,
INTERNAL_BUFFER_EXCEEDED,
INVALID_FORMAT_STRING,
MISSING_ARG,
INVALID_ARGUMENT_TYPE,
}
fault FormattingFault
{
UNTERMINATED_FORMAT,
MISSING_ARG,
INVALID_WIDTH_ARG,
INVALID_FORMAT_TYPE,
INVALID_FORMAT,
NOT_ENOUGH_ARGUMENTS,
INVALID_ARGUMENT,
}
def OutputFn = fn void!(void* buffer, char c);
def FloatType = double;
fn usz! Formatter.printf(&self, String format, args...)
{
return self.vprintf(format, args) @inline;
@@ -47,6 +38,7 @@ struct Formatter
uint width;
uint prec;
usz idx;
anyfault first_fault;
}
}
@@ -68,11 +60,16 @@ fn void Formatter.init(&self, OutputFn out_fn, void* data = null)
fn usz! Formatter.out(&self, char c) @private
{
self.out_fn(self.data, c)!;
if (catch err = self.out_fn(self.data, c))
{
if (self.first_fault) return self.first_fault?;
self.first_fault = err;
return err?;
}
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,9 +82,10 @@ 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)
if (&arg.to_string)
{
PrintFlags old = self.flags;
uint old_width = self.width;
@@ -98,16 +96,17 @@ 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));
return self.out_substr(arg.to_string(mem));
};
}
return SearchResult.MISSING?;
}
fn usz! Formatter.out_str(&self, any* arg) @private
fn usz! Formatter.out_str(&self, any arg) @private
{
switch (arg.type.kindof)
{
@@ -118,8 +117,9 @@ fn usz! Formatter.out_str(&self, any* arg) @private
case ANYFAULT:
case FAULT:
return self.out_substr((*(anyfault*)arg.ptr).nameof);
case INTERFACE:
case ANY:
return self.out_str(*(any**)arg);
return self.out_str(*(any*)arg);
case OPTIONAL:
unreachable();
case SIGNED_INT:
@@ -133,7 +133,7 @@ fn usz! Formatter.out_str(&self, any* arg) @private
}
self.flags = {};
self.width = 0;
return self.ntoa_any(arg, 10);
return self.ntoa_any(arg, 10) ?? self.out_substr("<INVALID>");
case FLOAT:
PrintFlags flags = self.flags;
uint width = self.width;
@@ -144,23 +144,14 @@ fn usz! Formatter.out_str(&self, any* arg) @private
}
self.flags = {};
self.width = 0;
return self.ftoa(float_from_any(arg)!!);
return self.ftoa(float_from_any(arg)) ?? self.out_substr("ERR");
case BOOL:
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:
@@ -168,6 +159,12 @@ fn usz! Formatter.out_str(&self, any* arg) @private
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
return self.out_substr(arg.type.names[i]);
case STRUCT:
if (arg.type == ReflectedParam.typeid)
{
ReflectedParam* param = arg.ptr;
return self.out_substr("[Parameter '")
+ self.out_substr(param.name) + self.out_substr("']");
}
return self.out_substr("<struct>");
case UNION:
return self.out_substr("<union>");
@@ -178,14 +175,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 +199,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 +252,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)
@@ -277,6 +283,9 @@ fn usz! Formatter.out_str(&self, any* arg) @private
}
len += self.out(']')!;
return len;
case ANY:
case INTERFACE:
unreachable("Already handled");
default:
}
return self.out_substr("Invalid type");
@@ -289,10 +298,31 @@ fn void! out_null_fn(void* data @unused, char c @unused) @private
{
}
fn usz! Formatter.vprintf(&self, String format, any*[] anys)
macro usz! @report_fault(Formatter* f, $fault)
{
(void)f.out_substr($fault);
return PrintFault.INVALID_FORMAT?;
}
macro usz! @wrap_bad(Formatter* f, #action)
{
usz! len = #action;
if (catch err = len)
{
case PrintFault.BUFFER_EXCEEDED:
case PrintFault.INTERNAL_BUFFER_EXCEEDED:
return f.first_err(err)?;
default:
err = f.first_err(PrintFault.INVALID_ARGUMENT);
f.out_substr("<INVALID>")!;
return err?;
}
return len;
}
fn usz! Formatter.vprintf(&self, String format, any[] anys)
{
self.first_fault = {};
if (!self.out_fn)
{
// use null output function
@@ -312,7 +342,7 @@ fn usz! Formatter.vprintf(&self, String format, any*[] anys)
continue;
}
i++;
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
if (i >= format_len) return @report_fault(self, "%ERR");
c = format[i];
if (c == '%')
{
@@ -332,11 +362,12 @@ fn usz! Formatter.vprintf(&self, String format, any*[] anys)
case '#': self.flags.hash = true;
default: break FLAG_EVAL;
}
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
if (++i >= format_len) return @report_fault(self, "%ERR");
c = format[i];
}
// evaluate width field
int w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
int! w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
if (catch w) return @report_fault(self, "%ERR");
c = format[i];
if (w < 0)
{
@@ -349,16 +380,22 @@ fn usz! Formatter.vprintf(&self, String format, any*[] anys)
if (c == '.')
{
self.flags.precision = true;
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
int prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
if (++i >= format_len) return @report_fault(self, "<BAD FORMAT>");
int! prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
if (catch prec) return @report_fault(self, "<BAD FORMAT>");
self.prec = prec < 0 ? 0 : prec;
c = format[i];
}
// evaluate specifier
uint base = 0;
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
any* current = anys[variant_index++];
if (variant_index >= anys.len)
{
self.first_err(PrintFault.NOT_ENOUGH_ARGUMENTS);
total_len += self.out_substr("<MISSING>")!;
continue;
}
any current = anys[variant_index++];
switch (c)
{
case 'd':
@@ -383,25 +420,25 @@ fn usz! Formatter.vprintf(&self, String format, any*[] anys)
self.flags.uppercase = true;
nextcase;
case 'a':
total_len += self.atoa(float_from_any(current)!!)!;
total_len += @wrap_bad(self, self.atoa(float_from_any(current)))!;
continue;
case 'F' :
self.flags.uppercase = true;
nextcase;
case 'f':
total_len += self.ftoa(float_from_any(current)!!)!;
total_len += @wrap_bad(self, self.ftoa(float_from_any(current)))!;
continue;
case 'E':
self.flags.uppercase = true;
nextcase;
case 'e':
total_len += self.etoa(float_from_any(current)!!)!;
total_len += @wrap_bad(self, self.etoa(float_from_any(current)))!;
continue;
case 'G':
self.flags.uppercase = true;
nextcase;
case 'g':
total_len += self.gtoa(float_from_any(current)!!)!;
total_len += @wrap_bad(self, self.gtoa(float_from_any(current)))!;
continue;
case 'c':
total_len += self.out_char(current)!;
@@ -426,7 +463,9 @@ fn usz! Formatter.vprintf(&self, String format, any*[] anys)
self.flags.hash = true;
base = 16;
default:
return PrintFault.INVALID_FORMAT_STRING?;
self.first_err(PrintFault.INVALID_FORMAT);
total_len += self.out_substr("<BAD FORMAT>")!;
continue;
}
if (base != 10)
{
@@ -437,14 +476,13 @@ fn usz! Formatter.vprintf(&self, String format, any*[] anys)
if (self.flags.precision) self.flags.zeropad = false;
bool is_neg;
uint128 v = int_from_any(current, &is_neg)!!;
total_len += self.ntoa(v, is_neg, base)!;
total_len += @wrap_bad(self, self.ntoa(int_from_any(current, &is_neg), is_neg, base))!;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
if (self.first_fault) return self.first_fault?;
return total_len;
}

View File

@@ -4,13 +4,25 @@ import std::math;
const char[16] XDIGITS_H = "0123456789ABCDEF";
const char[16] XDIGITS_L = "0123456789abcdef";
fault FormattingFault
{
BAD_FORMAT
}
macro Formatter.first_err(&self, anyfault f)
{
if (self.first_fault) return self.first_fault;
self.first_fault = f;
return f;
}
fn usz! Formatter.adjust(&self, usz len) @local
{
if (!self.flags.left) return 0;
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)
{
@@ -21,6 +33,7 @@ fn uint128! int_from_any(any* arg, bool *is_neg) @private
case TypeKind.ENUM:
return int_from_any(arg.as_inner(), is_neg);
default:
break;
}
*is_neg = false;
switch (arg)
@@ -59,18 +72,15 @@ fn uint128! int_from_any(any* arg, bool *is_neg) @private
double d = *arg;
return (uint128)((*is_neg = d < 0) ? -d : d);
default:
return PrintFault.INVALID_ARGUMENT_TYPE?;
return FormattingFault.BAD_FORMAT?;
}
}
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());
@@ -104,7 +114,7 @@ fn FloatType! float_from_any(any* arg) @private
case double:
return (FloatType)*arg;
default:
return PrintFault.INVALID_ARGUMENT_TYPE?;
return FormattingFault.BAD_FORMAT?;
}
}
@@ -588,14 +598,13 @@ 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;
return self.ntoa(int_from_any(arg, &is_neg)!!, 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,40 +640,31 @@ 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;
}
fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline @private
{
usz val = ++(*index_ptr);
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT?;
}
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)!;
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG?;
usz len = ++(*index_ptr);
if (len >= format_len) return FormattingFault.BAD_FORMAT?;
if (*args_index_ptr >= args_len) return FormattingFault.BAD_FORMAT?;
any val = args_ptr[(*args_index_ptr)++];
if (!val.type.kindof.is_int()) return FormattingFault.BAD_FORMAT?;
uint! intval = types::any_to_int(val, int);
return intval ?? FormattingFault.INVALID_WIDTH_ARG?;
return intval ?? FormattingFault.BAD_FORMAT?;
}

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,36 +245,65 @@ 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;
formatter.init(&out_putchar_fn);
usz len = formatter.vprintf(format, args)!;
usz! len = formatter.vprintf(format, args);
putchar('\n');
io::stdout().flush()!;
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;
usz! len = formatter.vprintf(format, args);
stderr().write_byte('\n')!;
stderr().flush()!;
return len;
return len + 1;
}
/**
* 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()
@@ -288,3 +401,11 @@ fn File* stdin()
{
return &stdin_file;
}
/**
* Wrap bytes for reading using io functions.
**/
fn ByteReader wrap_bytes(char[] bytes)
{
return { bytes, 0 };
}

View File

@@ -75,6 +75,11 @@ fn usz! native_fwrite(CFile file, char[] buffer) @inline
return libc::fwrite(buffer.ptr, 1, buffer.len, file);
}
fn void! native_fputc(CInt c, CFile stream) @inline
{
if (!libc::fputc(c, stream)) return IoError.EOF?;
}
fn usz! native_fread(CFile file, char[] buffer) @inline
{
return libc::fread(buffer.ptr, 1, buffer.len, file);

View File

@@ -9,6 +9,7 @@ def FtellFn = fn usz!(void*);
def FwriteFn = fn usz!(void*, char[] buffer);
def FreadFn = fn usz!(void*, char[] buffer);
def RemoveFn = fn void!(String);
def FputcFn = fn void!(int, void*);
FopenFn native_fopen_fn @weak @if(!$defined(native_fopen_fn));
FcloseFn native_fclose_fn @weak @if(!$defined(native_fclose_fn));
@@ -18,6 +19,7 @@ FtellFn native_ftell_fn @weak @if(!$defined(native_ftell_fn));
FwriteFn native_fwrite_fn @weak @if(!$defined(native_fwrite_fn));
FreadFn native_fread_fn @weak @if(!$defined(native_fread_fn));
RemoveFn native_remove_fn @weak @if(!$defined(native_remove_fn));
FputcFn native_fputc_fn @weak @if(!$defined(native_fputc_fn));
/**
* @require mode.len > 0
@@ -73,3 +75,9 @@ fn usz! native_fread(CFile file, char[] buffer) @inline
if (native_fread_fn) return native_fread_fn(file, buffer);
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! native_fputc(CInt c, CFile stream) @inline
{
if (native_fputc_fn) return native_fputc_fn(c, stream);
return IoError.UNSUPPORTED_OPERATION?;
}

View File

@@ -1,11 +1,11 @@
module std::io::os;
import libc, std::os, std::io;
fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX)
fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::BSD_FAMILY)
{
@pool()
{
$if env::DARWIN || env::LINUX:
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
int res = libc::stat(path.zstr_tcopy(), stat);
$else
unreachable("Stat unimplemented");
@@ -71,6 +71,9 @@ fn bool native_file_or_dir_exists(String path)
{
$switch
$case env::DARWIN:
$case env::FREEBSD:
$case env::NETBSD:
$case env::OPENBSD:
$case env::LINUX:
Stat stat;
return @ok(native_stat(&stat, path));
@@ -93,9 +96,12 @@ fn bool native_is_file(String path)
{
$switch
$case env::DARWIN:
$case env::FREEBSD:
$case env::NETBSD:
$case env::OPENBSD:
$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();
@@ -105,9 +111,9 @@ fn bool native_is_file(String path)
fn bool native_is_dir(String path)
{
$if env::DARWIN || env::LINUX:
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
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,10 +1,10 @@
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);
list.new_init(allocator: allocator);
DIRPtr directory = posix::opendir(dir.str_view() ? dir.as_zstr() : (ZString)".");
defer if (directory) posix::closedir(directory);
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
@@ -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,10 +24,10 @@ 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);
list.new_init(allocator: allocator);
@pool(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,20 +137,20 @@ 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)
{
return path::new(string::temp_from_wstring(path)!, .allocator = allocator);
return path::new(string::temp_from_wstring(path)!, allocator: allocator);
};
}
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]);
}
@@ -77,6 +77,22 @@ macro usz! read_all(stream, char[] buffer)
return n;
}
/**
* @require @is_instream(stream)
*/
macro char[]! read_new_fully(stream, Allocator allocator = allocator::heap())
{
usz len = available(stream)!;
char* data = allocator::malloc_try(allocator, len)!;
defer catch allocator::free(allocator, data);
usz read = 0;
while (read < len)
{
read += stream.read(data[read:len - read])!;
}
return data[:len];
}
/**
* @require @is_outstream(stream)
*/
@@ -134,29 +150,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());
@@ -146,7 +131,7 @@ fn usz! ByteBuffer.available(&self) @inline @dynamic
fn void! ByteBuffer.grow(&self, usz n)
{
n = math::next_power_of_2(n);
char* p = allocator::realloc_aligned(self.allocator, self.bytes, n, .alignment = char.alignof)!;
char* p = allocator::realloc_aligned(self.allocator, self.bytes, n, alignment: char.alignof)!;
self.bytes = p[:n];
}

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

@@ -3,7 +3,6 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module libc;
// Constants need to be per os/arch
const int EXIT_FAILURE = 1;
const int EXIT_SUCCESS = 0;
@@ -59,7 +58,7 @@ const CInt SIGTSTP = BSD_FLAVOR_SIG ? 18 : 20;
const CInt SIGCONT = BSD_FLAVOR_SIG ? 19 : 18;
const CInt SIGCHLD = BSD_FLAVOR_SIG ? 20 : 17;
const bool BSD_FLAVOR_SIG @local = env::OPENBSD || env::DARWIN || env::FREEBSD || env::NETBSD;
const bool BSD_FLAVOR_SIG @local = env::DARWIN || env::BSD_FAMILY;
def Time_t = $typefrom(env::WIN32 ? long.typeid : CLong.typeid);
def Off_t = $typefrom(env::WIN32 ? int.typeid : usz.typeid);
@@ -119,7 +118,7 @@ extern fn CLong labs(CLong x);
extern fn LongDivResult ldiv(CLong number, CLong denom);
extern fn Tm* localtime(Time_t* timer);
extern fn Tm* localtime_r(Time_t* timer, Tm* result) @if(!env::WIN32);
extern fn void longjmp(JmpBuf* buffer, CInt value);
extern fn void longjmp(JmpBuf* buffer, CInt value) @if(!env::NETBSD && !env::OPENBSD);
extern fn void* malloc(usz size);
extern fn void* memchr(void* str, CInt c, usz n);
extern fn CInt memcmp(void* buf1, void* buf2, usz count);
@@ -143,7 +142,7 @@ extern fn void rewind(CFile stream);
extern fn CInt scanf(ZString format, ...);
extern fn void setbuf(CFile stream, char* buffer);
extern fn int setenv(ZString name, ZString value, CInt overwrite);
extern fn CInt setjmp(JmpBuf* buffer) @if(!env::WIN32);
extern fn CInt setjmp(JmpBuf* buffer) @if(!env::WIN32 && !env::NETBSD && !env::OPENBSD);
extern fn void setvbuf(CFile stream, char* buf, CInt type, usz size);
extern fn SignalFunction signal(CInt sig, SignalFunction function);
extern fn CInt snprintf(char* buffer, usz size, ZString format, ...);
@@ -183,8 +182,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);
@@ -203,23 +202,39 @@ macro CFile stdin() => __stdin;
macro CFile stdout() => __stdout;
macro CFile stderr() => __stderr;
module libc @if(env::DARWIN);
module libc @if(env::NETBSD || env::OPENBSD);
extern fn int fcntl(CInt socket, int cmd, ...);
extern fn int _setjmp(void*);
macro int setjmp(void* ptr) => _setjmp(ptr);
extern fn int _longjmp(void*, int);
macro usz longjmp(void* ptr, CInt i) => _longjmp(ptr, i);
extern fn usz malloc_size(void* ptr);
extern fn void* aligned_alloc(usz align, usz size);
macro CFile stdin() { return fdopen(0, "r"); }
macro CFile stdout() { return fdopen(1, "w"); }
macro CFile stderr() { return fdopen(2, "w"); }
module libc @if(env::DARWIN || env::FREEBSD);
extern CFile __stdinp;
extern CFile __stdoutp;
extern CFile __stderrp;
extern fn usz malloc_size(void* ptr);
extern fn usz malloc_size(void* ptr) @if(!env::FREEBSD);
extern fn void* aligned_alloc(usz align, usz size);
macro CFile stdin() => __stdinp;
macro CFile stdout() => __stdoutp;
macro CFile stderr() => __stderrp;
module libc @if(env::FREEBSD);
extern fn usz malloc_usable_size(void* ptr);
macro usz malloc_size(void* ptr) => malloc_usable_size(ptr);
module libc @if(env::WIN32);
macro usz malloc_size(void* ptr) => _msize(ptr);
macro CFile stdin() => __acrt_iob_func(STDIN_FD);
macro CFile stdout() => __acrt_iob_func(STDOUT_FD);
macro CFile stderr() => __acrt_iob_func(STDERR_FD);
module libc @if(env::LIBC && !env::WIN32 && !env::LINUX && !env::DARWIN);
module libc @if(env::LIBC && !env::WIN32 && !env::LINUX && !env::DARWIN && !env::BSD_FAMILY);
macro CFile stdin() { return (CFile*)(uptr)STDIN_FD; }
macro CFile stdout() { return (CFile*)(uptr)STDOUT_FD; }
macro CFile stderr() { return (CFile*)(uptr)STDERR_FD; }
@@ -360,6 +375,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

@@ -0,0 +1,62 @@
module libc @if(env::FREEBSD);
// Checked for x86_64, this is notoriously incorrect when comparing with Rust code etc
def Blksize_t = $typefrom(env::X86_64 ? long.typeid : CInt.typeid);
def Nlink_t = $typefrom(env::X86_64 ? ulong.typeid : CUInt.typeid);
def Blkcnt_t = long;
def Ino_t = ulong;
def Dev_t = ulong;
def Mode_t = uint;
def Ino64_t = ulong;
def Blkcnt64_t = long;
struct Stat @if(env::X86_64)
{
Dev_t st_dev;
Ino_t st_ino;
Nlink_t st_nlink;
Mode_t st_mode;
Uid_t st_uid;
Gid_t st_gid;
CInt __pad0;
Dev_t st_rdev;
Off_t st_size;
Blksize_t st_blksize;
Blkcnt_t st_blocks;
Time_t st_atime;
long st_atime_nsec;
Time_t st_mtime;
long st_mtime_nsec;
Time_t st_ctime;
long st_ctime_nsec;
long[3] __unused;
}
struct Stat @if(!env::X86_64)
{
Dev_t st_dev;
Ino_t st_ino;
Mode_t st_mode;
Nlink_t st_nlink;
Uid_t st_uid;
Gid_t st_gid;
Dev_t st_rdev;
CInt __pad1;
Off_t st_size;
Blksize_t st_blksize;
CInt __pad2;
Blkcnt_t st_blocks;
Time_t st_atime;
long st_atime_nsec;
Time_t st_mtime;
long st_mtime_nsec;
Time_t st_ctime;
long st_ctime_nsec;
CInt[2] __unused;
}
extern fn CInt stat(ZString path, Stat* stat);
extern fn CInt get_nprocs();
extern fn CInt get_nprocs_conf();

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*);
@@ -30,9 +32,9 @@ struct Sigaction
SignalFunction sa_handler;
SigActionFunction sa_sigaction;
}
CInt sa_flags @if(env::FREEBSD);
CInt sa_flags @if(env::BSD_FAMILY);
Sigset_t sa_mask; // 128
CInt sa_flags @if(!env::FREEBSD);
CInt sa_flags @if(!env::BSD_FAMILY);
void* sa_restorer @if(env::LINUX);
}
@@ -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");
}

1120
lib/std/math/bigint.c3 Normal file

File diff suppressed because it is too large Load Diff

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

@@ -10,54 +10,186 @@ fn int128 __divti3(int128 a, int128 b) @extern("__divti3") @weak @nostrip
return __udivti3(unsigned_a, unsigned_b) @inline ^ sign_a + (-sign_a);
}
macro uint128 @__udivmodti4(uint128 a, uint128 b, bool $return_rem)
{
Int128bits n = { .all = a };
Int128bits d = { .all = b };
Int128bits q @noinit;
Int128bits r @noinit;
uint sr;
if (n.high == 0)
{
if (d.high == 0)
{
$if $return_rem:
return n.low % d.low;
$else
return n.low / d.low;
$endif
}
$if $return_rem:
return n.low;
$else
return 0;
$endif
}
if (d.low == 0)
{
if (d.high == 0)
{
$if $return_rem:
return n.high % d.low;
$else
return n.high / d.low;
$endif
}
if (n.low == 0)
{
$if $return_rem:
r.high = n.high % d.high;
r.low = 0;
return r.all;
$else
return n.high / d.high;
$endif
}
if (d.high & (d.high - 1) == 0) // d is pot
{
$if $return_rem:
r.low = n.low;
r.high = n.high & (d.high - 1);
return r.all;
$else
return (uint128)(n.high >> $$ctz(d.high));
$endif
}
sr = (uint)$$clz(d.high) - (uint)$$clz(n.high);
// 0 <= sr <= n_udword_bits - 2 or sr large
if (sr > 64 - 2)
{
$if $return_rem:
return n.all;
$else
return 0;
$endif
}
sr++;
// 1 <= sr <= n_udword_bits - 1
// q.all = n.all << (n_utword_bits - sr);
q.low = 0;
q.high = n.low << (64 - sr);
r.high = n.high >> sr;
r.low = (n.high << (64 - sr)) | (n.low >> sr);
}
else // d.s.low != 0
{
if (d.high == 0)
{
if (d.low & (d.low - 1) == 0) // if d is a power of 2
{
$if $return_rem:
return (uint128)(n.low & (d.low - 1));
$else
if (d.low == 1) return n.all;
sr = (uint)$$ctz(d.low);
q.high = n.high >> sr;
q.low = (n.high << (64 - sr)) | (n.low >> sr);
return q.all;
$endif
}
sr = 1 + 64 + (uint)$$clz(d.low) - (uint)$$clz(n.high);
// 2 <= sr <= n_utword_bits - 1
// q.all = n.all << (n_utword_bits - sr);
// r.all = n.all >> sr;
switch
{
case sr == 64:
q.low = 0;
q.high = n.low;
r.high = 0;
r.low = n.high;
case sr < 64:
q.low = 0;
q.high = n.low << (64 - sr);
r.high = n.high >> sr;
r.low = (n.high << (64 - sr)) | (n.low >> sr);
default: // n_udword_bits + 1 <= sr <= n_utword_bits - 1
q.low = n.low << (128 - sr);
q.high = (n.high << (128 - sr)) | (n.low >> (sr - 64));
r.high = 0;
r.low = n.high >> (sr - 64);
}
}
else
{
sr = (uint)$$clz(d.high) - (uint)$$clz(n.high);
// 0 <= sr <= n_udword_bits - 1 or sr large
if (sr > 64 - 1)
{
$if $return_rem:
return n.all;
$else
return 0;
$endif
}
sr++;
// 1 <= sr <= n_udword_bits
// q.all = n.all << (n_utword_bits - sr);
// r.all = n.all >> sr;
q.low = 0;
if (sr == 64)
{
q.high = n.low;
r.high = 0;
r.low = n.high;
}
else
{
r.high = n.high >> sr;
r.low = (n.high << (64 - sr)) | (n.low >> sr);
q.high = n.low << (64 - sr);
}
}
}
// Not a special case
// q and r are initialized with:
// q.all = n.all << (128 - sr);
// r.all = n.all >> sr;
// 1 <= sr <= n_utword_bits - 1
uint carry = 0;
for (; sr > 0; sr--)
{
// r:q = ((r:q) << 1) | carry
r.high = (r.high << 1) | (r.low >> (64 - 1));
r.low = (r.low << 1) | (q.high >> (64 - 1));
q.high = (q.high << 1) | (q.low >> (64 - 1));
q.low = (q.low << 1) | carry;
// carry = 0;
// if (r.all >= d.all)
// {
// r.all -= d.all;
// carry = 1;
// }
int128 s = (int128)(d.all - r.all - 1) >> (128 - 1);
carry = (uint)(s & 1);
r.all -= d.all & s;
}
$if $return_rem:
return r.all;
$else
return (q.all << 1) | carry;
$endif
}
fn uint128 __umodti3(uint128 n, uint128 d) @extern("__umodti3") @weak @nostrip
{
// Ignore d = 0
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
// If n < d then sr is wrapping.
// which means we can just return n.
if (sr > 127) return n;
// If d == 1 and n = MAX
if (sr == 127) return 0;
sr++;
uint128 r = n >> sr;
// Follow known algorithm:
n <<= 128 - sr;
for (uint128 carry = 0; sr > 0; sr--)
{
r = (r << 1) | (n >> 127);
n = (n << 1) | carry;
int128 sign = (int128)(d - r - 1) >> 127;
carry = sign & 1;
r -= d & sign;
}
return r;
return @__udivmodti4(n, d, true);
}
fn uint128 __udivti3(uint128 n, uint128 d) @extern("__udivti3") @weak @nostrip
{
// Ignore d = 0
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
// If n < d then sr is wrapping.
// which means we can just return 0.
if (sr > 127) return 0;
// If d == 1 and n = MAX
if (sr == 127) return n;
sr++;
uint128 r = n >> sr;
// Follow known algorithm:
n <<= 128 - sr;
uint128 carry = 0;
for (; sr > 0; sr--)
{
r = (r << 1) | (n >> 127);
n = (n << 1) | carry;
int128 sign = (int128)(d - r - 1) >> 127;
carry = sign & 1;
r -= d & sign;
}
n = (n << 1) | carry;
return n;
return @__udivmodti4(n, d, false);
}
fn int128 __modti3(int128 a, int128 b) @extern("__modti3") @weak @nostrip
@@ -74,11 +206,8 @@ union Int128bits @private
{
struct
{
ulong ulow, uhigh;
}
struct
{
long ilow, ihigh;
ulong low;
ulong high;
}
uint128 all;
}
@@ -89,14 +218,14 @@ fn uint128 __lshrti3(uint128 a, uint b) @extern("__lshrti3") @weak @nostrip
result.all = a;
if (b >= 64)
{
result.ulow = result.uhigh >> (b - 64);
result.uhigh = 0;
result.low = result.high >> (b - 64);
result.high = 0;
}
else
{
if (b == 0) return a;
result.ulow = (result.uhigh << (64 - b)) | (result.ulow >> b);
result.uhigh = result.uhigh >> b;
result.low = (result.high << (64 - b)) | (result.low >> b);
result.high = result.high >> b;
}
return result.all;
}
@@ -107,14 +236,14 @@ fn int128 __ashrti3(int128 a, uint b) @extern("__ashrti3") @weak @nostrip
result.all = a;
if (b >= 64)
{
result.ilow = result.ihigh >> (b - 64);
result.ihigh = result.ihigh >> 63;
result.low = result.high >> (b - 64);
result.high = result.high >> 63;
}
else
{
if (b == 0) return a;
result.ilow = result.ihigh << (64 - b) | (result.ilow >> b);
result.ihigh = result.ihigh >> b;
result.low = result.high << (64 - b) | (result.low >> b);
result.high = result.high >> b;
}
return result.all;
}
@@ -125,14 +254,14 @@ fn int128 __ashlti3(int128 a, uint b) @extern("__ashlti3") @weak @nostrip
result.all = a;
if (b >= 64)
{
result.ulow = 0;
result.uhigh = result.ulow << (b - 64);
result.low = 0;
result.high = result.low << (b - 64);
}
else
{
if (b == 0) return a;
result.uhigh = (result.uhigh << b) | (result.ulow >> (64 - b));
result.ulow = result.ulow << b;
result.high = (result.high << b) | (result.low >> (64 - b));
result.low = result.low << b;
}
return result.all;
}
@@ -143,18 +272,18 @@ fn int128 __mulddi3(ulong a, ulong b) @private
{
Int128bits r;
const ulong LOWER_MASK = 0xffff_ffff;
r.ulow = (a & LOWER_MASK) * (b & LOWER_MASK);
ulong t = r.ulow >> 32;
r.ulow &= LOWER_MASK;
r.low = (a & LOWER_MASK) * (b & LOWER_MASK);
ulong t = r.low >> 32;
r.low &= LOWER_MASK;
t += (a >> 32) * (b & LOWER_MASK);
r.ulow += (t & LOWER_MASK) << 32;
r.uhigh = t >> 32;
t = r.ulow >> 32;
r.ulow &= LOWER_MASK;
r.low += (t & LOWER_MASK) << 32;
r.high = t >> 32;
t = r.low >> 32;
r.low &= LOWER_MASK;
t += (b >> 32) * (a & LOWER_MASK);
r.ulow += (t & LOWER_MASK) << 32;
r.uhigh += t >> 32;
r.uhigh += (a >> 32) * (b >> 32);
r.low += (t & LOWER_MASK) << 32;
r.high += t >> 32;
r.high += (a >> 32) * (b >> 32);
return r.all;
}
@@ -162,8 +291,8 @@ fn int128 __multi3(int128 a, int128 b) @extern("__multi3") @weak @nostrip
{
Int128bits x = { .all = a };
Int128bits y = { .all = b };
Int128bits r = { .all = __mulddi3(x.ulow, y.ulow) };
r.uhigh += x.uhigh * y.ulow + x.ulow * y.uhigh;
Int128bits r = { .all = __mulddi3(x.low, y.low) };
r.high += x.high * y.low + x.low * y.high;
return r.all;
}

View File

@@ -1,4 +1,5 @@
module std::math::matrix(<Real>);
import std::math::vector;
struct Matrix2x2
{
@@ -132,6 +133,7 @@ fn Matrix2x2 Matrix2x2.sub(&self, Matrix2x2 mat2) => matrix_sub(self, mat2);
fn Matrix3x3 Matrix3x3.sub(&self, Matrix3x3 mat2) => matrix_sub(self, mat2);
fn Matrix4x4 Matrix4x4.sub(&self, Matrix4x4 mat2) => matrix_sub(self, mat2);
fn Matrix4x4 look_at(Real[<3>] eye, Real[<3>] target, Real[<3>] up) => matrix_look_at(Matrix4x4, eye, target, up);
fn Matrix2x2 Matrix2x2.transpose(&self)
@@ -432,3 +434,17 @@ macro matrix_sub(mat, mat2) @private
var $Type = Real[<$typeof(mat.m).len>];
return $typeof(*mat) { .m = ($Type)mat.m - ($Type)mat2.m };
}
macro matrix_look_at($Type, eye, target, up) @private
{
var vz = (eye - target).normalize();
var vx = up.cross(vz).normalize();
var vy = vz.cross(vx);
return $Type {
vx[0], vx[1], vx[2], - (Real)vx.dot(eye),
vy[0], vy[1], vy[2], - (Real)vy.dot(eye),
vz[0], vz[1], vz[2], - (Real)vz.dot(eye),
0.0, 0.0, 0.0, 1
};
}

View File

@@ -0,0 +1,78 @@
module std::math::nolibc @if(env::NO_LIBC);
union DoubleInternal
{
double f;
ulong i;
}
// Based on the musl implementation
fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
{
DoubleInternal ux = { .f = x };
DoubleInternal uy = { .f = y };
int ex = (int)((ux.i >> 52) & 0x7ff);
int ey = (int)((uy.i >> 52) & 0x7ff);
int sx = (int)(ux.i >> 63);
ulong uxi = ux.i;
if (uy.i << 1 == 0 || math::is_nan(y) || ex == 0x7ff) return (x * y)/(x * y);
if (uxi << 1 <= uy.i << 1)
{
if (uxi << 1 == uy.i << 1) return 0 * x;
return x;
}
if (!ex)
{
for (ulong i = uxi << 12; i >> 63 == 0; ex--, i <<= 1);
uxi <<= -ex + 1;
}
else
{
uxi &= -1UL >> 12;
uxi |= 1UL << 52;
}
if (!ey)
{
for (ulong i = uy.i << 12; i >> 63 == 0; ey--, i <<= 1);
uy.i <<= -ey + 1;
}
else
{
uy.i &= -1UL >> 12;
uy.i |= 1UL << 52;
}
/* x mod y */
for (; ex > ey; ex--)
{
ulong i = uxi - uy.i;
if (i >> 63 == 0)
{
if (i == 0) return 0 * x;
uxi = i;
}
uxi <<= 1;
}
ulong i = uxi - uy.i;
if (i >> 63 == 0)
{
if (i == 0) return 0*x;
uxi = i;
}
for (; uxi>>52 == 0; uxi <<= 1, ex--);
/* scale result */
if (ex > 0)
{
uxi -= 1UL << 52;
uxi |= (ulong)ex << 52;
}
else
{
uxi >>= -ex + 1;
}
uxi |= (ulong)sx << 63;
ux.i = uxi;
return ux.f;
}

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,39 +18,101 @@ 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 range (not including range).
*
* @require is_random(random)
* @require range > 0
**/
macro int next(random, int max)
macro int next(random, uint range)
{
return (int)(next_double(random) * max);
if (range == 1) return 0;
uint mask = ~0U;
range--;
mask >>= range.clz();
uint x @noinit;
do
{
x = random.next_int() & mask;
}
while (x > range);
return x;
}
fn int rand(int max) @builtin
{
tlocal Sfc64Random default_random;
tlocal bool initialized = false;
if (!initialized)
{
seed_entropy(&default_random);
initialized = true;
}
return next(&default_random, max);
}
/**
* Get a random in the range [min, max], both included.
*
* @require is_random(random)
* @require max >= min
**/
macro int next_in_range(random, int min, int max)
{
return next(random, max - min + 1) + min;
}
def DefaultRandom = Sfc64Random;
tlocal Sfc64Random default_random @private;
tlocal bool default_random_initialized @private = false;
/**
* Seed the default random function.
*/
fn void srand(ulong seed) @builtin
{
default_random.set_seed(@as_char_view(seed));
default_random_initialized = true;
}
/**
* Get a default random value between 0 and range (not including range)
**/
fn int rand(int range) @builtin
{
init_default_random();
return next(&default_random, range);
}
/**
* Get a random in the range, both included.
* @require max >= min
**/
fn int rand_in_range(int min, int max) @builtin
{
init_default_random();
return next_in_range(&default_random, min, max);
}
fn double rnd() @builtin
{
init_default_random();
ulong val = default_random.next_long() & (1UL << 53 - 1);
return val * 0x1.0p-53;
}
/**
* Get 'true' or 'false'
*
* @require is_random(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 +122,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 +132,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;
@@ -95,3 +159,26 @@ macro @random_value_to_bytes(#function, char[] bytes)
}
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);
}
macro init_default_random() @private
{
if (!default_random_initialized)
{
seed_entropy(&default_random);
default_random_initialized = true;
}
}

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