Compare commits

...

203 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
276 changed files with 15007 additions and 6184 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

View File

@@ -8,7 +8,7 @@ on:
env:
LLVM_RELEASE_VERSION_WINDOWS: 18
LLVM_RELEASE_VERSION_MAC: 18
LLVM_RELEASE_VERSION_MAC: 17
LLVM_RELEASE_VERSION_LINUX: 17
LLVM_RELEASE_VERSION_UBUNTU20: 17
LLVM_DEV_VERSION: 20
@@ -65,15 +65,15 @@ jobs:
- 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: |
@@ -91,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
@@ -143,7 +145,7 @@ jobs:
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib
./build/c3c vendor-fetch raylib5
- name: Build testproject lib
run: |
@@ -214,7 +216,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [17, 18, 19, 20]
llvm_version: [17, 18, 19]
steps:
- uses: actions/checkout@v4
@@ -322,7 +324,7 @@ jobs:
- name: Test WASM
run: |
cd resources/testfragments
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
- name: Install QEMU and Risc-V toolchain
run: |
@@ -361,7 +363,7 @@ jobs:
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: c3-linux-${{matrix.build_type}}
path: c3-linux-${{matrix.build_type}}.tar.gz
@@ -482,7 +484,7 @@ jobs:
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: c3-ubuntu-20-${{matrix.build_type}}
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
@@ -494,7 +496,7 @@ jobs:
matrix:
ubuntu_version: [20.04, 22.04]
build_type: [Release, Debug]
llvm_version: [17, 18, 19, 20]
llvm_version: [17, 18, 19]
steps:
- uses: actions/checkout@v4
@@ -556,7 +558,7 @@ jobs:
- name: Test WASM
run: |
cd resources/testfragments
../../build/c3c compile --reloc=none --target wasm32 -g0 --link-libc=no --no-entry -Os wasm4.c3
../../build/c3c compile --reloc=none --target wasm32 -g0 --no-entry -Os wasm4.c3
- name: Build testproject direct linker
run: |
@@ -605,7 +607,7 @@ jobs:
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib
./build/c3c vendor-fetch raylib5
- name: Compile and run some examples
run: |
@@ -655,7 +657,7 @@ jobs:
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: c3-macos-${{matrix.build_type}}
path: c3-macos-${{matrix.build_type}}.zip
@@ -663,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:
@@ -690,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:
@@ -707,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

5
.gitignore vendored
View File

@@ -70,3 +70,8 @@ out/
# Emacs files
TAGS
# Clangd LSP files
/.cache/
/compile_commands.json

View File

@@ -63,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)
@@ -80,9 +81,11 @@ endif()
if (NOT WIN32)
find_package(CURL)
endif()
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!")
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()
@@ -101,108 +104,126 @@ 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 "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
# 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_18_1_8_with_rt/llvm-18.1.8-windows-amd64-msvc17-libcmt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows_debug)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_debug_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
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()
endif()
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
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 (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()
if(LLVM_ENABLE_RTTI)
message(STATUS "LLVM was built with RTTI")
else()
message(STATUS "LLVM was not built with RTTI")
endif()
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
add_definitions(${LLVM_DEFINITIONS})
string(REPLACE "." ";" VERSION_LIST ${LLVM_PACKAGE_VERSION})
list(GET VERSION_LIST 0 LLVM_MAJOR_VERSION)
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
)
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})
add_definitions(${LLVM_DEFINITIONS})
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
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
)
# 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})
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 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)
# 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()
if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
@@ -210,7 +231,7 @@ if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
endif()
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}
@@ -220,26 +241,26 @@ 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
@@ -263,7 +284,6 @@ add_executable(c3c
src/compiler/json_output.c
src/compiler/lexer.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
@@ -271,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
@@ -321,18 +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/build/common_build.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
@@ -366,24 +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/"
"${CMAKE_SOURCE_DIR}/wrapper/include/")
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/include/")
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)
@@ -393,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})
@@ -401,41 +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)
if (NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PUBLIC /GR-)
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()
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
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 (NOT LLVM_ENABLE_RTTI)
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.
@@ -137,7 +138,7 @@ fn void main()
### Current status
The current stable version of the compiler is **version 0.6.1**.
The current stable version of the compiler is **version 0.6.2**.
The upcoming 0.6.x releases will focus on expanding the standard library.
Follow the issues [here](https://github.com/c3lang/c3c/issues).
@@ -205,7 +206,13 @@ 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))
@@ -215,9 +222,16 @@ More platforms will be supported in the future.
(*Note that there is a known issue with debug symbol generation on MacOS 13, see [issue #1086](https://github.com/c3lang/c3c/issues/1086))
#### Installing on Arch Linux
There is an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git).
Arch includes c3c in the official 'extra' repo. It can be easily installed the usual way:
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.
```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
@@ -304,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 17+ (or greater: C3C supports LLVM 17+): `sudo apt-get install clang-17 zlib1g zlib1g-dev libllvm17 llvm-17 llvm-17-dev llvm-17-runtime liblld-17-dev liblld-17`
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
5. Enter the C3C directory `cd c3c`.
6. Create a build directory `mkdir build`
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.
@@ -323,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 llvm17 lld17-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`
@@ -369,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.

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

@@ -18,9 +18,18 @@ struct AnyList (Printable)
/**
* @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 AnyList* AnyList.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;
@@ -44,7 +53,7 @@ fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocat
**/
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! AnyList.to_format(&self, Formatter* formatter) @dynamic
@@ -67,16 +76,19 @@ fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String AnyList.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 AnyList.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.
**/
@@ -117,18 +129,35 @@ macro AnyList.pop(&self, $Type)
* Pop the last value and allocate the copy using the given allocator.
* @return! IteratorResult.NO_MORE_ELEMENT
**/
fn any! AnyList.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! AnyList.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()
@@ -171,8 +200,17 @@ fn any! AnyList.pop_first_retained(&self)
/**
* Same as new_pop() but pops the first value instead.
* @deprecated `use copy_pop_first`
**/
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]);
@@ -183,6 +221,12 @@ fn any! AnyList.new_pop_first(&self, Allocator allocator = allocator::heap())
/**
* Same as temp_pop() but pops the first value instead.
**/
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());
/**

View File

@@ -41,7 +41,12 @@ fn usz! ElasticArray.to_format(&self, Formatter* formatter) @dynamic
fn String ElasticArray.to_string(&self, Allocator allocator) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
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)
@@ -158,7 +163,15 @@ fn void ElasticArray.add_array(&self, Type[] array)
/**
* IMPORTANT The returned array must be freed using free_aligned.
**/
fn Type[] ElasticArray.to_new_aligned_array(&self, Allocator allocator = allocator::heap())
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);
}
@@ -166,7 +179,15 @@ fn Type[] ElasticArray.to_new_aligned_array(&self, Allocator allocator = allocat
/**
* @require !type_is_overaligned() : "This function is not available on overaligned types"
**/
macro Type[] ElasticArray.to_new_array(&self, Allocator allocator = allocator::heap())
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);
}
@@ -174,9 +195,9 @@ macro Type[] ElasticArray.to_new_array(&self, Allocator allocator = allocator::h
fn Type[] ElasticArray.to_tarray(&self)
{
$if type_is_overaligned():
return self.to_new_aligned_array(allocator::temp());
return self.to_aligned_array(allocator::temp());
$else
return self.to_new_array(allocator::temp());
return self.to_array(allocator::temp());
$endif;
}

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;
@@ -143,7 +143,12 @@ fn usz! EnumSet.to_format(&set, Formatter* formatter) @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

@@ -20,7 +20,19 @@ struct HashMap
* @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 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;
@@ -38,9 +50,83 @@ fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, fl
**/
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.new_init(capacity, load_factor, allocator::temp()) @inline;
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?
@@ -53,11 +139,19 @@ 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.new_init_from_map(&self, HashMap* other_map, Allocator allocator = allocator::heap())
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);
@@ -69,7 +163,7 @@ fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map, Allocator alloc
**/
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map)
{
return map.new_init_from_map(other_map, allocator::temp()) @inline;
return map.init_from_map(other_map, allocator::temp()) @inline;
}
fn bool HashMap.is_empty(&map) @inline
@@ -191,12 +285,25 @@ fn void HashMap.free(&map)
map.table = {};
}
fn Key[] HashMap.key_tlist(&map)
fn Key[] HashMap.tcopy_keys(&map)
{
return map.key_new_list(allocator::temp()) @inline;
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 {};
@@ -235,12 +342,28 @@ macro HashMap.@each_entry(map; @body(entry))
}
}
/**
* @deprecated `use tcopy_values`
**/
fn Value[] HashMap.value_tlist(&map)
{
return map.value_new_list(allocator::temp()) @inline;
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);

View File

@@ -24,16 +24,24 @@ struct LinkedList
* @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;
}
/**
* @return "the initialized list"
**/
fn LinkedList* LinkedList.new_init(&self)
{
return self.init(allocator::heap()) @inline;
}
fn LinkedList* LinkedList.temp_init(&self)
{
return self.new_init(allocator::temp()) @inline;
return self.init(allocator::temp()) @inline;
}
/**

View File

@@ -27,20 +27,9 @@ fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = a
{
self.allocator = allocator;
self.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
$if type_is_overaligned():
self.entries = allocator::malloc_aligned(allocator, Type.sizeof * initial_capacity, .alignment = Type[1].alignof)!!;
$else
self.entries = allocator::malloc(allocator, Type.sizeof * initial_capacity);
$endif
}
else
{
self.entries = null;
}
self.capacity = initial_capacity;
self.capacity = 0;
self.entries = null;
self.reserve(initial_capacity);
return self;
}
@@ -81,14 +70,14 @@ fn List* List.temp_init_with_array(&self, Type[] values)
}
/**
* @require self.size == 0 "The List must be empty"
* @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
@@ -113,7 +102,7 @@ fn usz! List.to_format(&self, Formatter* formatter) @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)
@@ -124,18 +113,19 @@ fn String List.to_tstring(&self)
fn void List.push(&self, Type element) @inline
{
self.reserve(1);
self.entries[self.size++] = element;
self.entries[self.set_size(self.size + 1)] = element;
}
fn Type! List.pop(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[--self.size];
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);
}
/**
@@ -153,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];
}
@@ -161,9 +152,10 @@ 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;
}
}
@@ -216,8 +208,8 @@ fn void List.add_array(&self, Type[] array)
{
if (!array.len) return;
self.reserve(array.len);
self.entries[self.size : array.len] = array[..];
self.size += array.len;
usz index = self.set_size(self.size + array.len);
self.entries[index : array.len] = array[..];
}
fn void List.push_front(&self, Type type) @inline
@@ -235,7 +227,7 @@ fn void List.insert_at(&self, usz index, Type type)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.set_size(self.size + 1);
self.entries[index] = type;
}
@@ -250,7 +242,7 @@ fn void List.set_at(&self, usz index, Type type)
fn void! List.remove_last(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
self.size--;
self.set_size(self.size - 1);
}
fn void! List.remove_first(&self) @maydiscard
@@ -293,11 +285,14 @@ fn Type List.get(&self, usz index) @inline
fn void List.free(&self)
{
if (!self.allocator) return;
if (!self.allocator || !self.capacity) return;
self.pre_free(); // Remove sanitizer annotation
$if type_is_overaligned():
allocator::free_aligned(self.allocator, self.entries);
allocator::free_aligned(self.allocator, self.entries);
$else
allocator::free(self.allocator, self.entries);
allocator::free(self.allocator, self.entries);
$endif;
self.capacity = 0;
self.size = 0;
@@ -329,11 +324,21 @@ fn usz List.retain_if(&self, ElementPredicate selection)
fn usz List.remove_using_test(&self, ElementTest filter, any context)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_remove_using_test(self, filter, false, context);
}
fn usz List.retain_using_test(&self, ElementTest filter, any context)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_remove_using_test(self, filter, true, context);
}
@@ -342,13 +347,18 @@ 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);
$if type_is_overaligned():
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof)!!;
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);
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([])
@@ -377,6 +387,39 @@ fn void List.reserve(&self, usz added)
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
@@ -443,7 +486,6 @@ 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"
@@ -451,6 +493,10 @@ fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
**/
fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_remove_item(self, value);
}
@@ -459,6 +505,10 @@ fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
fn void List.remove_all_from(&self, List* other_list) @if(ELEMENT_IS_EQUATABLE)
{
if (!other_list.size) return;
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);
}
@@ -475,6 +525,10 @@ fn usz List.compact_count(&self) @if(ELEMENT_IS_POINTER)
fn usz List.compact(&self) @if(ELEMENT_IS_POINTER)
{
usz old_size = self.size;
defer {
if (old_size != self.size) self._update_size_change(old_size, self.size);
}
return list_common::list_compact(self);
}

View File

@@ -45,6 +45,77 @@ fn Map temp(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAUL
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 capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
macro Map new_init_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
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"
* @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 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())
{
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"
* @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 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())
{
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."
**/
@@ -53,7 +124,7 @@ fn Map new_from_map(Map other_map, Allocator allocator = null)
MapImpl* other_map_impl = (MapImpl*)other_map;
if (!other_map_impl)
{
if (allocator) return new(.allocator = allocator);
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());

View File

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

View File

@@ -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,10 +181,9 @@ 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

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

@@ -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

@@ -24,7 +24,6 @@ fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator allocator)
self.backing_allocator = allocator;
}
import std::io;
fn void DynamicArenaAllocator.free(&self)
{
DynamicArenaPage* page = self.page;

View File

@@ -2,16 +2,21 @@
// 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, AllocInitType init_type, usz alignment) @dynamic
{
if (init_type == ZERO)
@@ -110,7 +115,7 @@ 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, AllocInitType init_type, usz alignment) @dynamic

View File

@@ -60,28 +60,33 @@ 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
{
TempAllocatorPage *last_page = self.last_page;
$if env::COMPILER_SAFE_MODE:
if (!last_page)
{
usz cleaned = self.used - mark;
if (cleaned > 0)
{
self.data[mark : cleaned] = 0xAA;
}
}
$endif
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;
}
@@ -147,9 +152,10 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
}
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;

View File

@@ -34,7 +34,7 @@ struct TrackingAllocator (Allocator)
fn void TrackingAllocator.init(&self, Allocator allocator)
{
*self = { .inner_allocator = allocator };
self.map.new_init(.allocator = allocator);
self.map.new_init(allocator: allocator);
}
/**
@@ -87,7 +87,6 @@ fn void*! TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, us
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
return data;
}

View File

@@ -55,7 +55,7 @@ macro rindex_of(array, element)
* @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,6 +69,21 @@ 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 slices, returning a slice containing the concatenation of them,
@@ -81,7 +96,7 @@ macro concat_new(arr1, arr2, Allocator allocator = allocator::heap())
* @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

@@ -19,6 +19,8 @@ fault SearchResult { MISSING }
**/
fault CastResult { TYPE_MISMATCH }
def VoidFn = fn void();
/**
* Stores a variable on the stack, then restores it at the end of the
* macro scope.
@@ -102,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();
}
@@ -118,11 +136,19 @@ PanicFn panic = &default_panic;
fn void panicf(String fmt, String file, String function, uint line, args...)
{
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);
};
}

View File

@@ -154,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;
}
/**
@@ -185,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;
@@ -194,6 +196,7 @@ 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());
@@ -292,7 +295,7 @@ fn void DString.append_chars(&self, String str)
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)
@@ -387,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);
@@ -419,6 +425,103 @@ 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
{
if (!self.data()) self.new_init(format.len + 20);
@@ -467,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
{

View File

@@ -115,6 +115,8 @@ enum ArchType
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;
@@ -146,8 +148,13 @@ 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() @const
{

View File

@@ -281,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);
@@ -536,7 +541,7 @@ 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.
@@ -544,6 +549,7 @@ fn void initialize_wasm_mem() @init(1) @private
if (start > mem::DEFAULT_MEM_ALIGNMENT) allocator::wasm_memory.use = start;
wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x));
allocator::thread_allocator = &wasm_allocator;
allocator::temp_base_allocator = &wasm_allocator;
allocator::init_default_temp_allocators();
}

View File

@@ -143,7 +143,7 @@ macro void free_aligned(Allocator allocator, void* ptr)
$if env::TESTING:
((char*)ptr)[0] = 0xBA;
$endif
allocator.release(ptr, .aligned = true);
allocator.release(ptr, aligned: true);
}
/**
@@ -358,10 +358,22 @@ 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 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
{
@@ -422,3 +434,21 @@ 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

@@ -4,6 +4,12 @@
module std::core::runtime;
import libc, std::time, std::io, std::sort;
struct ReflectedParam @if(!$defined(ReflectedParam))
{
String name;
typeid type;
}
struct AnyRaw
{
void* ptr;
@@ -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

@@ -32,58 +32,66 @@ fault NumberConversion
FLOAT_OUT_OF_RANGE,
}
/**
* Return a temporary String created using the formatting function.
*
* @param [in] fmt `The formatting string`
**/
macro String tformat(String fmt, ...)
{
DString str = dstring::temp_with_capacity(fmt.len + $vacount * 8);
str.appendf(fmt, $vasplat);
return str.str_view();
}
/**
* Return a temporary ZString created using the formatting function.
*
* @param [in] fmt `The formatting string`
**/
macro ZString tformat_zstr(String fmt, ...)
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();
}
/**
* Return a new String created using the formatting function.
*
* @param [in] fmt `The formatting string`
* @param [inout] allocator `The allocator to use`
* @param [in] fmt `The formatting string`
**/
macro String new_format(String fmt, ..., Allocator allocator = allocator::heap())
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);
};
}
/**
* 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`
**/
macro ZString new_format_zstr(String fmt, ..., Allocator allocator = allocator::heap())
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);
};
}
@@ -205,13 +213,12 @@ 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
@@ -246,6 +253,18 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
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.
@@ -254,10 +273,7 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator allocator = al
* @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.
@@ -486,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)!;
@@ -505,10 +519,8 @@ 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.
@@ -528,7 +540,7 @@ fn String String.new_ascii_to_lower(s, Allocator allocator = allocator::heap())
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());
}

View File

@@ -123,6 +123,37 @@ macro bool is_slice_convertable($Type)
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;
/**
* @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]);
@@ -178,15 +209,20 @@ macro bool is_vector($Type) @const
return $Type.kindof == TypeKind.VECTOR;
}
macro TypeKind inner_kind($Type) @const
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 TypeKind inner_kind($Type) @const
{
return inner_type($Type).kindof;
}
macro bool is_same($TypeA, $TypeB) @const
{
return $TypeA.typeid == $TypeB.typeid;

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

@@ -22,7 +22,7 @@ fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator allocator = a
{
@pool(allocator)
{
return io::treadline(self.stream).split(self.separator, .allocator = allocator);
return io::treadline(self.stream).split(self.separator, allocator: allocator);
};
}
@@ -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

@@ -27,11 +27,13 @@ fn Object*! temp_parse_string(String s)
fn Object*! parse(InStream s, Allocator allocator = allocator::heap())
{
JsonContext context = { .last_string = dstring::new_with_capacity(64, allocator), .stream = s, .allocator = allocator };
defer context.last_string.free();
@pool(allocator)
@stack_mem(512; Allocator mem)
{
return parse_any(&context);
JsonContext context = { .last_string = dstring::new_with_capacity(64, mem), .stream = s, .allocator = allocator };
@pool(allocator)
{
return parse_any(&context);
};
};
}
@@ -102,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)
{
@@ -152,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
@@ -382,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

@@ -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);
}
/**

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,7 +60,12 @@ 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;
}
@@ -88,7 +85,7 @@ fn usz! Formatter.print_with_function(&self, Printable arg)
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;
@@ -102,7 +99,7 @@ fn usz! Formatter.print_with_function(&self, Printable arg)
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?;
@@ -120,6 +117,7 @@ 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);
case OPTIONAL:
@@ -135,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;
@@ -146,7 +144,7 @@ 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:
@@ -161,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>");
@@ -279,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");
@@ -291,10 +298,31 @@ fn void! out_null_fn(void* data @unused, char c @unused) @private
{
}
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
@@ -314,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 == '%')
{
@@ -334,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)
{
@@ -351,15 +380,21 @@ 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?;
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)
{
@@ -385,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)!;
@@ -428,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)
{
@@ -439,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,6 +4,18 @@ 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;
@@ -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,7 +72,7 @@ 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?;
}
}
@@ -101,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,8 +601,7 @@ 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
{
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
@@ -640,17 +652,6 @@ fn usz! Formatter.out_reverse(&self, char[] buf) @private
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,
@@ -659,9 +660,11 @@ fn int! printf_parse_format_field(
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

@@ -256,7 +256,7 @@ 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;
@@ -290,10 +290,10 @@ fn usz! eprintfn(String format, args...) @maydiscard
Formatter formatter;
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;
}
/**
@@ -401,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,6 +96,9 @@ 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)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFREG);
@@ -105,7 +111,7 @@ 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)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFDIR);
$else

View File

@@ -4,7 +4,7 @@ import std::io, std::os;
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)?;
@@ -27,7 +27,7 @@ import std::time, std::os, std::io;
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)
{

View File

@@ -141,7 +141,7 @@ 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);
};
}
@@ -520,7 +520,7 @@ fn bool! Path.walk(self, PathWalker w, void* data)
@stack_mem(PATH_MAX; Allocator allocator)
{
Path abs = self.new_absolute(allocator)!;
PathList files = new_ls(abs, .allocator = allocator)!;
PathList files = new_ls(abs, allocator: allocator)!;
foreach (f : files)
{
if (f.str_view() == "." || f.str_view() == "..") continue;

View File

@@ -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)
*/

View File

@@ -131,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

@@ -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, ...);
@@ -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; }

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

@@ -32,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);
}

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

File diff suppressed because it is too large Load Diff

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

@@ -29,30 +29,75 @@ macro void seed_entropy(random)
}
/**
* Get the next value between 0 and max (not including max).
* 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;
}
/**
* 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;
/**
* Get a default random value between 0 and max (not including max)
**/
fn int rand(int max) @builtin
* Seed the default random function.
*/
fn void srand(ulong seed) @builtin
{
tlocal Sfc64Random default_random;
tlocal bool initialized = false;
if (!initialized)
{
seed_entropy(&default_random);
initialized = true;
}
return next(&default_random, max);
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;
}
/**
@@ -127,3 +172,13 @@ interface Random
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;
}
}

View File

@@ -66,8 +66,8 @@ fn Vec3 Vec3.refract(self, Vec3 n, double r) => refract3(self, n, r);
fn void ortho_normalize(Vec3f* v1, Vec3f* v2) => ortho_normalize3(v1, v2);
fn void ortho_normalized(Vec3* v1, Vec3* v2) => ortho_normalize3(v1, v2);
fn Matrix4f matrix4f_look_at(Vec3f eye, Vec3f target, Vec3f up) => matrix_look_at(Matrix4f, eye, target, up);
fn Matrix4 matrix4_look_at(Vec3 eye, Vec3 target, Vec3 up) => matrix_look_at(Matrix4, eye, target, up);
fn Matrix4f matrix4f_look_at(Vec3f eye, Vec3f target, Vec3f up) @deprecated => matrix::look_at(<float>)(eye, target, up);
fn Matrix4 matrix4_look_at(Vec3 eye, Vec3 target, Vec3 up) @deprecated => matrix::look_at(<double>)(eye, target, up);
fn Vec3f Vec3f.rotate_quat(self, Quaternionf q) => rotate_by_quat3(self, q);
fn Vec3 Vec3.rotate_quat(self, Quaternion q) => rotate_by_quat3(self, q);
@@ -196,20 +196,6 @@ macro rotate_axis_angle(v, axis, angle) @private
return v + wv + wwv;
}
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], - vx.dot(eye),
vy[0], vy[1], vy[2], - vy.dot(eye),
vz[0], vz[1], vz[2], - vz.dot(eye),
0.0, 0.0, 0.0, 1
};
}
macro unproject3(v, m1, m2) @private
{
return v;
@@ -256,4 +242,4 @@ macro refract3(v, n, r) @private
var d = 1 - r * r * (1 - dot * dot);
return d < 0 ? v : r * v - (r * dot + math::sqrt(d)) * n;
}
}

View File

@@ -72,8 +72,9 @@ macro uint hash(value) @local
return fnv32a::encode(&&bitcast(value, char[$sizeof(value)]));
}
fn char[8 * 4] entropy()
fn char[8 * 4] entropy() @if(!env::WASM_NOLIBC)
{
void* addr = malloc(1);
free(addr);
static uint random_int;
@@ -89,4 +90,21 @@ fn char[8 * 4] entropy()
hash(allocator::heap())
};
return bitcast(entropy_data, char[8 * 4]);
}
fn char[8 * 4] entropy() @if(env::WASM_NOLIBC)
{
static uint random_int;
random_int += 0xedf19156;
uint[8] entropy_data = {
hash($$TIME),
hash(&entropy),
random_int,
hash($$TIME),
hash(&entropy),
random_int,
hash($$TIME),
hash(&entropy),
};
return bitcast(entropy_data, char[8 * 4]);
}

41
lib/std/math/uuid.c3 Normal file
View File

@@ -0,0 +1,41 @@
module std::math::uuid;
import std::math::random @public;
import std::io;
distinct Uuid (Printable) = char[16];
/**
* Generate a version 4 UUID from the default random.
**/
fn Uuid generate()
{
random::init_default_random();
return generate_from_random(&random::default_random);
}
/**
* Generate a version 4 UUID from the given random.
**/
fn Uuid generate_from_random(Random random)
{
Uuid uuid;
random.next_bytes(&(char[16])uuid);
uuid[6] = (uuid[6] & 0b0000_1111) | 0b0100_0000;
uuid[8] = (uuid[8] & 0b0011_1111) | 0b1000_0000;
return uuid;
}
fn usz! Uuid.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.printf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
(*self)[0], (*self)[1], (*self)[2], (*self)[3],
(*self)[4], (*self)[5],
(*self)[6], (*self)[7],
(*self)[8], (*self)[9],
(*self)[10], (*self)[11], (*self)[12], (*self)[13], (*self)[14], (*self)[15]);
}
fn String Uuid.to_string(&self, Allocator allocator) @dynamic
{
return string::new_format("%s", *self, allocator: allocator);
}

View File

@@ -22,7 +22,7 @@ const int SO_ERROR = 4;
const int SO_DONTROUTE = 5; // just use interface addresses
const int SO_BROADCAST = 6; // permit sending of broadcast msgs
const int SO_SNDBUF = 7; // Send buffer size
const int SO_RCVBUF = 8; // Recieve buffer size
const int SO_RCVBUF = 8; // Receive buffer size
const int SO_KEEPALIVE = 9; // keep connections alive
const int SO_OOBINLINE = 10; // leave received OOB data in line
const int SO_NO_CHECK = 11;

View File

@@ -105,7 +105,7 @@ fn Path! new_get_config_dir(Allocator allocator = allocator::heap())
String s = get_var_temp("XDG_CONFIG_HOME") ?? get_var_temp("HOME")!;
const DIR = ".config";
$endif
return path::temp_new(s).new_append(DIR, .allocator = allocator);
return path::temp_new(s).new_append(DIR, allocator: allocator);
$endif
};
}

View File

@@ -7,15 +7,22 @@ distinct DIRPtr = void*;
struct Posix_dirent
{
Ino_t d_fileno;
Off_t d_off;
Off_t d_off @if(!env::NETBSD);
ushort d_reclen;
ushort d_namelen @if(env::DARWIN);
ushort d_namelen @if(env::DARWIN || env::NETBSD);
char d_type;
char d_namelen @if(env::FREEBSD || env::OPENBSD);
uint d_pad0 @if(env::FREEBSD);
char d_namelen @if(env::OPENBSD);
char[4] d_pad0 @if(env::OPENBSD);
char d_pad0 @if(env::FREEBSD);
ushort d_namelen @if(env::FREEBSD);
ushort d_pad1 @if(env::FREEBSD);
char[255+1] name @if(env::FREEBSD || env::OPENBSD);
char[511+1] name @if(env::NETBSD);
char[1024] name @if(env::DARWIN);
char[*] name @if(!env::DARWIN);
char[*] name @if(!env::DARWIN && !env::BSD_FAMILY);
}
extern fn int rmdir(ZString);
@@ -41,4 +48,3 @@ const DT_WHT = 14;
const USE_DARWIN_INODE64 = env::DARWIN && env::X86_64;
extern fn Posix_dirent* readdir(DIRPtr) @extern("readdir$INODE64") @if(USE_DARWIN_INODE64);

View File

@@ -3,8 +3,8 @@ import std::thread;
import libc;
const PTHREAD_MUTEX_NORMAL = 0;
const PTHREAD_MUTEX_ERRORCHECK = 1;
const PTHREAD_MUTEX_RECURSIVE = 2;
const PTHREAD_MUTEX_ERRORCHECK = env::LINUX ? 2 : 1;
const PTHREAD_MUTEX_RECURSIVE = env::LINUX ? 1 : 2;
def PosixThreadFn = fn void*(void*);
distinct Pthread_t = void*;

View File

@@ -193,10 +193,10 @@ fn Backtrace! resolve_backtrace(void* addr, Win32_HANDLE process, Allocator allo
ZString zname = (ZString)&name;
if (!symGetLineFromAddr64(process, (Win32_ULONG64)addr - 1, &offset, &line))
{
backtrace.init((uptr)addr, .function = zname.str_view(), .object_file = module_name.str_view(), .allocator = allocator);
backtrace.init((uptr)addr, zname.str_view(), module_name.str_view(), allocator: allocator);
return backtrace;
}
String filename = ((ZString)line.fileName).str_view();
backtrace.init((uptr)addr, .function = zname.str_view(), .object_file = module_name.str_view(), .file = filename, .line = line.lineNumber, .allocator = allocator);
backtrace.init((uptr)addr, zname.str_view(), module_name.str_view(), file: filename, line: line.lineNumber, allocator: allocator);
return backtrace;
}

View File

@@ -32,7 +32,7 @@ def Indexs = char[256] @private;
def ElementType = $typeof(Type{}[0]);
const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot);
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable(Type{}[0], $typefrom(KeyFn.params[0]));
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable(Type{}[0], $typefrom(KeyFn.paramsof[0].type));
const bool LIST_HAS_REF @private = $defined(&Type{}[0]);
def KeyFnReturnType = $typefrom(KeyFn.returns) @if(!NO_KEY_FN);

View File

@@ -20,7 +20,7 @@ fn void isort(Type list, usz low, usz high, CmpFn comp, Context context)
{
var $has_cmp = @is_valid_macro_slot(comp);
var $has_context = @is_valid_macro_slot(context);
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.params[0]));
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type));
var $has_get_ref = $defined(&list[0]);
for (usz i = low; i < high; ++i)
{

View File

@@ -31,7 +31,7 @@ fn void qsort(Type list, isz low, isz high, CmpFn cmp, Context context)
{
var $has_cmp = @is_valid_macro_slot(cmp);
var $has_context = @is_valid_macro_slot(context);
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.params[0]));
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type));
if (low >= 0 && high >= 0 && low < high)
{

View File

@@ -0,0 +1,178 @@
module std::thread::threadpool @if (env::POSIX || env::WIN32);
import std::thread;
// Please do not use this one in production.
fault ThreadPoolResult
{
QUEUE_FULL
}
def ThreadPoolFn = fn void(any[] args);
struct FixedThreadPool @adhoc
{
Mutex mu;
QueueItem[] queue;
usz qindex;
usz num_threads;
bitstruct : char {
bool initialized;
bool stop;
bool stop_now;
}
Thread[] pool;
ConditionVariable notify;
}
struct QueueItem @private
{
ThreadPoolFn func;
any[] args;
}
/**
* @require !self.initialized "ThreadPool must not be already initialized"
* @require threads > 0 && threads < 0x1000 `Threads should be greater than 0 and less than 0x1000`
* @require queue_size < 0x10000 `Queue size must be less than 65536`
**/
fn void! FixedThreadPool.init(&self, usz threads, usz queue_size = 0)
{
if (queue_size == 0) queue_size = threads * 32;
defer catch @ok(self.destroy());
assert(queue_size > 0);
*self = {
.num_threads = threads,
.initialized = true,
.queue = mem::alloc_array(QueueItem, queue_size),
.pool = mem::new_array(Thread, threads)
};
self.mu.init()!;
self.notify.init()!;
foreach (&thread : self.pool)
{
thread.create(&process_work, self)!;
// The thread resources will be cleaned up when the thread exits.
thread.detach()!;
}
}
/*
* Stop all the threads and cleanup the pool.
* Any pending work will be dropped.
*/
fn void! FixedThreadPool.destroy(&self)
{
return self.@shutdown(stop_now);
}
/*
* Stop all the threads and cleanup the pool.
* Any pending work will be processed.
*/
fn void! FixedThreadPool.stop_and_destroy(&self)
{
return self.@shutdown(stop);
}
macro void! FixedThreadPool.@shutdown(&self, #stop) @private
{
if (self.initialized)
{
self.mu.lock()!;
self.#stop = true;
self.notify.broadcast()!;
self.mu.unlock()!;
// Wait for all threads to shutdown.
while (true)
{
self.mu.lock()!;
defer self.mu.unlock()!!;
if (self.num_threads == 0)
{
break;
}
self.notify.signal()!;
}
self.mu.destroy()!;
self.initialized = false;
while (self.qindex)
{
free_qitem(self.queue[--self.qindex]);
}
free(self.queue);
self.queue = {};
}
}
/*
* Push a new job to the pool.
* Returns whether the queue is full, in which case the job is ignored.
*/
fn void! FixedThreadPool.push(&self, ThreadPoolFn func, args...)
{
self.mu.lock()!;
defer self.mu.unlock()!!;
if (self.qindex == self.queue.len) return ThreadPoolResult.QUEUE_FULL?;
any[] data;
if (args.len)
{
data = mem::alloc_array(any, args.len);
foreach (i, arg : args) data[i] = allocator::clone_any(allocator::heap(), arg);
}
self.queue[self.qindex] = { .func = func, .args = data };
self.qindex++;
defer catch
{
free_qitem(self.queue[--self.qindex]);
}
// Notify the threads that work is available.
self.notify.broadcast()!;
}
fn int process_work(void* self_arg) @private
{
FixedThreadPool* self = self_arg;
while (true)
{
self.mu.lock()!!;
if (self.stop_now)
{
// Shutdown requested.
self.num_threads--;
self.mu.unlock()!!;
return 0;
}
// Wait for work.
while (self.qindex == 0)
{
if (self.stop)
{
// Shutdown requested.
self.num_threads--;
self.mu.unlock()!!;
return 0;
}
self.notify.wait(&self.mu)!!;
if (self.stop_now)
{
// Shutdown requested.
self.num_threads--;
self.mu.unlock()!!;
return 0;
}
}
// Process the job.
self.qindex--;
QueueItem item = self.queue[self.qindex];
self.mu.unlock()!!;
defer free_qitem(item);
item.func(item.args);
}
}
fn void free_qitem(QueueItem item) @private
{
foreach (arg : item.args) free(arg.ptr);
free(item.args);
}

View File

@@ -24,6 +24,12 @@ fn void! NativeMutex.init(&self, MutexType type)
{
if (posix::pthread_mutexattr_settype(&attr, posix::PTHREAD_MUTEX_RECURSIVE)) return ThreadFault.INIT_FAILED?;
}
else
{
$if env::COMPILER_SAFE_MODE:
if (posix::pthread_mutexattr_settype(&attr, posix::PTHREAD_MUTEX_ERRORCHECK)) return ThreadFault.INIT_FAILED?;
$endif
}
if (posix::pthread_mutex_init(&self.mutex, &attr)) return ThreadFault.INIT_FAILED?;
self.initialized = true;
}

View File

@@ -12,7 +12,7 @@ struct NativeMutex
}
// Size is less than a Win32_HANDLE so due to alignment
// there is no benefit to pack these into a bitstruct.
bool already_locked;
uint locks;
bool recursive;
bool timed;
}
@@ -40,7 +40,7 @@ struct NativeConditionVariable
fn void! NativeMutex.init(&mtx, MutexType type)
{
mtx.already_locked = false;
mtx.locks = 0;
mtx.recursive = (bool)(type & thread::MUTEX_RECURSIVE);
mtx.timed = (bool)(type & thread::MUTEX_TIMED);
if (!mtx.timed)
@@ -53,6 +53,7 @@ fn void! NativeMutex.init(&mtx, MutexType type)
fn void! NativeMutex.destroy(&mtx)
{
mtx.locks = 0;
if (!mtx.timed)
{
win32::deleteCriticalSection(&mtx.critical_section);
@@ -79,11 +80,11 @@ fn void! NativeMutex.lock(&mtx)
}
}
if (!mtx.recursive)
if (!mtx.recursive && mtx.locks)
{
while (mtx.already_locked) win32::sleep(1);
return ThreadFault.LOCK_FAILED?;
}
mtx.already_locked = true;
mtx.locks++;
}
@@ -103,20 +104,11 @@ fn void! NativeMutex.lock_timeout(&mtx, ulong ms)
default:
return ThreadFault.LOCK_FAILED?;
}
if (!mtx.recursive)
if (!mtx.recursive && mtx.locks)
{
usz left_timeout = ms - 1;
while (mtx.already_locked)
{
if (left_timeout-- == 0)
{
win32::releaseMutex(mtx.handle);
return ThreadFault.LOCK_TIMEOUT?;
}
win32::sleep(1);
}
mtx.already_locked = true;
return ThreadFault.LOCK_FAILED?;
}
mtx.locks++;
}
fn bool NativeMutex.try_lock(&mtx)
@@ -128,7 +120,7 @@ fn bool NativeMutex.try_lock(&mtx)
if (!success) return false;
if (!mtx.recursive)
{
if (mtx.already_locked)
if (mtx.locks)
{
if (mtx.timed)
{
@@ -140,14 +132,15 @@ fn bool NativeMutex.try_lock(&mtx)
}
return false;
}
mtx.already_locked = true;
}
mtx.locks++;
return true;
}
fn void! NativeMutex.unlock(&mtx)
{
mtx.already_locked = false;
if (!mtx.locks) return ThreadFault.UNLOCK_FAILED?;
mtx.locks--;
if (!mtx.timed)
{
win32::leaveCriticalSection(&mtx.critical_section);

View File

@@ -1,7 +1,7 @@
module std::thread::pool(<SIZE>);
import std::thread;
struct ThreadPool
struct ThreadPool @adhoc
{
Mutex mu;
QueueItem[SIZE] queue;
@@ -17,7 +17,7 @@ struct ThreadPool
ConditionVariable notify;
}
struct QueueItem
struct QueueItem @private
{
ThreadFn func;
void* arg;

View File

@@ -67,7 +67,7 @@ macro bool Thread.equals(thread, Thread other) => NativeThread.equals((NativeThr
macro void OnceFlag.call(&flag, OnceFn func) => NativeOnceFlag.call_once((NativeOnceFlag*)flag, func);
macro void yield() => os::native_thread_yield();
macro Thread current() => os::native_thread_current();
macro Thread current() => (Thread)os::native_thread_current();
macro void exit(int result) => os::native_thread_exit(result);
macro void! sleep(Duration d) @maydiscard => os::native_sleep_nano(d.to_nano());
macro void! sleep_ms(ulong ms) @maydiscard => sleep(time::ms(ms));

View File

@@ -20,6 +20,19 @@ fn DateTime from_date(int year, Month month = JANUARY, int day = 1, int hour = 0
return dt;
}
/**
* @require day >= 1 && day < 32
* @require hour >= 0 && hour < 24
* @require min >= 0 && min < 60
* @require sec >= 0 && sec < 60
* @require us >= 0 && us < 999_999
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
**/
fn TzDateTime from_date_tz(int year, Month month = JANUARY, int day = 1, int hour = 0, int min = 0, int sec = 0, int us = 0, int gmt_offset = 0)
{
return from_date(year, month, day, hour, min, sec, us).with_gmt_offset(gmt_offset);
}
fn TzDateTime DateTime.to_local(&self)
{
Tm tm @noinit;
@@ -47,6 +60,58 @@ fn TzDateTime DateTime.to_local(&self)
return dt;
}
/**
* Update timestamp to gmt_offset while keeping the date and time
* values unchanged.
*
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
**/
fn TzDateTime DateTime.with_gmt_offset(self, int gmt_offset)
{
TzDateTime dt = { self, 0 };
return dt.with_gmt_offset(gmt_offset);
}
/**
* Update timestamp to gmt_offset while keeping the date and time
* values unchanged.
*
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
**/
fn TzDateTime TzDateTime.with_gmt_offset(self, int gmt_offset)
{
self.time -= (Time)(gmt_offset - self.gmt_offset) * (Time)time::SEC;
return { self.date_time, gmt_offset };
}
/**
* Update the date and time values to gmt_offset while keeping the
* timestamp unchanged.
*
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
* @ensure self.time == return.time
**/
fn TzDateTime DateTime.to_gmt_offset(self, int gmt_offset)
{
TzDateTime dt = { self, 0 };
return dt.to_gmt_offset(gmt_offset);
}
/**
* Update the date and time values to gmt_offset while keeping the
* timestamp unchanged.
*
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
* @ensure self.time == return.time
**/
fn TzDateTime TzDateTime.to_gmt_offset(self, int gmt_offset) {
if (self.gmt_offset == gmt_offset) return self;
Time time = self.time + (Time)gmt_offset * (Time)time::SEC;
DateTime dt = from_time(time);
dt.time = self.time;
return { dt, gmt_offset };
}
/**
* @require day >= 1 && day < 32
* @require hour >= 0 && hour < 24
@@ -121,6 +186,16 @@ fn DateTime DateTime.add_months(&self, int months)
return from_date(year, (Month)month, self.day, self.hour, self.min, self.sec, self.usec);
}
fn TzDateTime TzDateTime.add_seconds(&self, int seconds) => self.date_time.add_seconds(seconds).to_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_minutes(&self, int minutes) => self.date_time.add_minutes(minutes).to_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_hours(&self, int hours) => self.date_time.add_hours(hours).to_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_days(&self, int days) => self.date_time.add_days(days).to_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_weeks(&self, int weeks) => self.date_time.add_weeks(weeks).to_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_years(&self, int years) => self.date_time.add_years(years).with_gmt_offset(self.gmt_offset);
fn TzDateTime TzDateTime.add_months(&self, int months) => self.date_time.add_months(months).with_gmt_offset(self.gmt_offset);
fn DateTime from_time(Time time)
{
DateTime dt @noinit;
@@ -128,6 +203,15 @@ fn DateTime from_time(Time time)
return dt;
}
/**
* @require gmt_offset >= -12 * 3600 && gmt_offset <= 14 * 3600
* @ensure time == return.time
**/
fn TzDateTime from_time_tz(Time time, int gmt_offset)
{
return from_time(time).to_gmt_offset(gmt_offset);
}
fn Time DateTime.to_time(&self) @inline
{
return self.time;
@@ -167,4 +251,4 @@ fn double DateTime.diff_sec(self, DateTime from)
fn Duration DateTime.diff_us(self, DateTime from)
{
return self.time.diff_us(from.time);
}
}

View File

@@ -1,5 +1,92 @@
# C3C Release Notes
## 0.6.3 Change list
### Changes / improvements
- Introduce `arg: x` named arguments instead of `.arg = x`, deprecate old style.
- Support splat for varargs #1352.
- Allow `var` in lambdas in macros.
- Support `int[*] { 1, 2, 3 }` expressions.
- Support inline struct designated init as if inline was anonymous.
- Introduce the `.paramsof` property.
- Support environment variable 'C3C_LIB' to find the standard library.
- Support environment variable 'C3C_CC' to find the default C compiler.
- Support casting bitstructs to bool.
- Allow user-defined attributes to have typed parameters.
- Add `.gitkeep` files to project subfolders.
- Add `env::COMPILER_BUILD_HASH` and `env::COMPILER_BUILD_DATE`
- Support linking .o files in compilation command. #1417
- Slicing constant strings at compile time works.
- Add `project fetch` subcommand to fetch missing project dependencies (general and target specific)
- Ability of `vendor-fetch` to download the dependencies in the first specified path `dependencies-search-path`
- Ability of `vendor-fetch` to register the fetched dependencies in the project file.
- Allow the "self" parameter to be $/# for macro methods.
- Support compile time slicing of untyped lists.
- Allow specifying an import module using `@wasm` #1305.
- Deprecated inline generic types outside of struct definitions and macros unless marked `@adhoc`.
- Improved method detection in earlier stages of checking.
- Allow `@norecurse` attribute for non-recursive imports #1480.
- wasm32 / wasm64 targets are use-libc=no by default.
- Add hash/sha256 module
### Fixes
- Issue where a lambda wasn't correctly registered as external. #1408
- Generic methods were incorrectly registered as functions, leading to naming collisions. #1402
- Deprecated tuple / triple types.
- Converting a slice to a vector/array would copy too little data.
- Crash when reading an empty 'manifest.json'.
- "optsize" did not work correctly in project.json.
- `l[0].a = 1` now supported for overloads due to better lvalue handling #1357.
- Asserts are retained regardless of optimization when running tests.
- Limit object filename lengths. #1415
- Fix regression for `$include`.
- Correct '.so' suffix on dynamic libraries on Linux.
- Fix bug where inline index access to array in a struct would crash the compiler.
- Asserts are now correctly included and traced in when running tests.
- Use atexit to fix finalizers on Windows #1361.
- Fix bugs in "trap-on-wrap" #1434.
- Bug with casting anyfault to error.
- Lambda / function type would accidentally be processed as a method.
- Fix error message when not finding a particular function.
- Crash invoking a `@body` argument with the wrong number of parameters.
- Fix reordering semantics in struct assignment.
- Regression when passing types as `#expr` arguments. #1461
- Temp allocator overwrites data when doing reset on extra allocated pages. #1462
- User defined attributes could not have more than 1 parameter due to bug.
- Folding a constant array of structs at compile time would cause an assert.
- Enum attributes would be overwritten by enum value attributes.
- LLVM issue with try when bool is combined #1467.
- Segfault using ternary with no assignment #1468.
- Inner types make some errors misleading #1471.
- Fix bug when passing a type as a compile time value.
- Fix bug due to enum associated values not being checked for liveness.
- Regression when compile time accessing a union field not last assigned to.
- Safer seed of rand() for WASM without libc.
- Bad error message aliasing an ident with a path. #1481.
- Error when slicing a struct with an inline array #1488.
- Improved error messages on `Foo a = foo { 1 };` #1496
- Bug in json decoder escape handling.
- Fix bug when reading zip manifest, that would not return a zero terminated string. #1490
- Fix thread tests.
- Detect recursion errors on non-recursive mutexes in safe mode.
- Foreach over distinct pointer failed to be caught as error #1506.
- Foreach over distinct iterable would ignore operator(len).
- Compiler crash when compiling c code in a library without --obj-out #1503.
### Stdlib changes
- Additional init functions for hashmap.
- `format` functions are now functions and work better with splat.
- Add support for the QOI format.
- Add `io::read_new_fully` for reading to the end of a stream.
- Add `io::wrap_bytes` for reading bytes with `io` functions.
- Add `rnd` and `rand_in_range` default random functions.
- Additional timezone related functions for `datetime`.
- Added MD5 and crypto::safe_compare.
- Added generic HMAC.
- Added generic PBKDF2 implementation.
- DString `reverse`.
- `DString.insert_at` now has variants for other types.
## 0.6.2 Change list
### Changes / improvements
@@ -36,6 +123,8 @@
- Introduce `$vaarg[...]` syntax and deprecate the old `$vaarg(...)`.
- Similar change to `$vasplat`: `$vasplat` and `$vasplat[1..]`.
- Add `$member.get(value)` to replace `value.$eval($member.nameof)`
- Improve the error message when the compilation does not produce any files #1390.
- Add `fmod` implementation for nolibc.
### Fixes
@@ -82,6 +171,22 @@
- Bug when compile time subtracting a distinct type.
- `insert_at` incorrectly prevented inserts at the end of a list.
- Fix aligned alloc for Win32 targets.
- Compiler didn't detect when a module name was used both as a generic and regular module.
- Assigning a const zero to an aliased distinct caused an error.
- `--path` is now properly respected.
- `--test` will now provide the full filename and the column.
- Fix of bug in `defer (catch err)` with a direct return error.
- Too restrictive compile time checks for @const.
- Fixes to wasm nolibc in the standard library.
- Fixed int128 div/mod.
- Fix WASM memory init priority.
- Fix bug with `defer (catch err)` when used together with regular defer.
- Methods can now properly be aliased using `def` #1393.
- Memory leak in Object when not using temp allocators.
- Tracking allocator would double the allocations in the report.
- `printf` will now show errors in the output when there are errors.
- Bug where `if try` would work incorrectly in a macro.
- Prevent loading / storing large structs with LLVM.
### Stdlib changes
@@ -93,6 +198,7 @@
- Added `dstring.replace`
- New hashmap type, `Map`
- Added `ElasticArray`.
- Added `types::is_signed`, `types::is_unsigned` and `types::inner_type`.
## 0.6.1 Change list
@@ -118,6 +224,7 @@
- Added `--list-manifest-properties` to list the available properties in `manifest.json`.
- Indexing into a constant array / struct now works at compile time.
- Improved error message when trying user foreach with an untyped list.
- RISCV asm support.
### Fixes
- Error with unsigned compare in `@ensure` when early returning 0 #1207.

View File

@@ -2,8 +2,8 @@ import std::io;
fn void main()
{
Path path = path::getcwd()!!;
foreach (i, p : path::ls(path)!!)
Path path = path::new_cwd()!!;
foreach (i, p : path::new_ls(path)!!)
{
io::printfn("%02d %s", i, p.str_view());
}

View File

@@ -0,0 +1,22 @@
fn int main()
{
String msg = "Hello, C3 World!\n";
$$syscall(4, 1, (uptr)msg.ptr, msg.len); // __NR_write, STDOUT
return 0;
}
fn void _start() @export("_start")
{
int ret = main();
$$syscall(1, ret); // __NR_exit
}
module std::core::builtin;
def PanicFn = fn void(String message, String file, String function, uint line);
PanicFn panic = &default_panic;
fn void default_panic(String message, String file, String function, uint line)
{
}

View File

@@ -0,0 +1,79 @@
{
// Language version of C3.
"langrev": "1",
// Warnings used for all targets.
"warnings": [ "no-unused" ],
// Directories where C3 library files may be found.
"dependency-search-paths": [ ],
// Libraries to use for all targets.
"dependencies": [ ],
// Authors, optionally with email.
"authors": [ "John Doe <john.doe@example.com>" ],
// Version using semantic versioning.
"version": "0.1.0",
// Sources compiled for all targets.
"sources": [ "./**" ],
// C sources if the project also compiles C sources
// relative to the project file.
// "c-sources": [ "csource/**" ],
// Output location, relative to project file.
"output": ".",
// Architecture and OS target.
// You can use 'c3c --list-targets' to list all valid targets.
"target": "freebsd-x64",
// Targets.
"targets": {
"hello_world": {
"type": "executable",
"debug-info": "none",
"link-libc": false,
"opt": "O0",
"safe": false,
"linker": "builtin",
"use-stdlib": false,
},
},
// Global settings.
// C compiler if the project also compiles C sources
// defaults to 'cc'.
"cc": "cc",
// CPU name, used for optimizations in the LLVM backend.
"cpu": "generic",
// Debug information, may be "none", "full" and "line-tables".
"debug-info": "full",
// FP math behaviour: "strict", "relaxed", "fast".
"fp-math": "strict",
// Link libc other default libraries.
"link-libc": true,
// Memory environment: "normal", "small", "tiny", "none".
"memory-env": "normal",
// Optimization: "O0", "O1", "O2", "O3", "O4", "O5", "Os", "Oz".
"opt": "O0",
// Code optimization level: "none", "less", "more", "max".
"optlevel": "none",
// Code size optimization: "none", "small", "tiny".
"optsize": "none",
// Relocation model: "none", "pic", "PIC", "pie", "PIE".
"reloc": "none",
// Trap on signed and unsigned integer wrapping for testing.
"trap-on-wrap": false,
// Turn safety (contracts, runtime bounds checking, null pointer checks etc).
"safe": true,
// Compile all modules together, enables more inlining.
"single-module": true,
// Use / don't use soft float, value is otherwise target default.
"soft-float": false,
// Strip unused code and globals from the output.
"strip-unused": true,
// The size of the symtab, which limits the amount
// of symbols that can be used. Should usually not be changed.
"symtab": 1048576,
// Select linker.
"linker": "cc",
// Include the standard library.
"use-stdlib": true,
// Set general level of x64 cpu: "baseline", "ssse3", "sse4", "avx1", "avx2-v1", "avx2-v2", "avx512", "native".
"x86cpu": "native",
// Set max type of vector use: "none", "mmx", "sse", "avx", "avx512", "native".
"x86vec": "sse",
}

76
resources/examples/opengl/.gitignore vendored Normal file
View File

@@ -0,0 +1,76 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
*.ll
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
/build/
.idea/
/resources/grammar.tab.c
/resources/grammar.vcg
/resources/lex.yy.c
/resources/y.tab.c
/resources/y.tab.h
/bin/
#visual studio files
.vs/
.vscode/
out/
/cmake-build-debug/
/cmake-build-release/
# Emacs files
TAGS
# Clangd LSP files
/.cache/
/compile_commands.json

View File

View File

View File

View File

View File

@@ -0,0 +1,28 @@
{
"version": "0.1.0",
"authors": [
"John Doe <john.doe@example.com>"
],
"langrev": "1",
"warnings": [
"no-unused"
],
"sources": [
"./**"
],
"dependency-search-paths": [],
"dependencies": [],
"cc": "cc",
"c-sources": [],
"targets": {
"build/triangle": {
"type": "executable"
}
},
"linked-libraries": [
"glfw",
"GL"
],
"cpu": "generic",
"opt": "O0"
}

View File

@@ -0,0 +1,36 @@
module gl;
def BitField = int;
enum BufferBit : int(int value) {
COLOR = 0x00004000,
STENCIL = 0x00000400,
DEPTH = 0x00000100,
}
enum Primitive : int(int value) {
POINTS = 0,
LINES = 1,
LINE_LOOP = 2,
LINE_STRIP = 3,
TRIANGLES = 4,
TRIANGLE_STRIP = 5,
TRIANGLE_FAN = 6,
QUADS = 7,
QUAD_STRIP = 8,
POLYGON = 9,
}
extern fn void clear(BitField mask) @extern("glClear") @public;
extern fn void begin(BitField mask) @extern("glBegin") @public;
extern fn void end() @extern("glEnd") @public;
extern fn void flush() @extern("glFlush") @public;
extern fn void color3f(float r, float g, float b)
@extern("glColor3f") @public;
extern fn void vertex3f(float x, float y, float z)
@extern("glVertex3f") @public;

View File

@@ -0,0 +1,64 @@
module glfw;
import std::io;
distinct Window @public = _Window*;
distinct Monitor @public = _Monitor*;
fn void Window.create(
&self,
int width,
int height,
String title,
Monitor monitor = {},
Window share = {}
) @public {
*self = (Window)_glfwCreateWindow(
width,
height,
title,
(_Monitor*)monitor,
(_Window*)share
);
}
fn void Window.destroy(self) @public {
_glfwDestroyWindow((_Window*)self);
}
fn bool Window.shouldClose(self) @public {
return _glfwWindowShouldClose((_Window*)self);
}
fn void Window.swapBuffers(self) @public {
return _glfwSwapBuffers((_Window*)self);
}
fn void Window.makeContextCurrent(self) @public {
return _glfwMakeContextCurrent((_Window*)self);
}
extern fn void initialize() @extern("glfwInit") @public;
extern fn void terminate() @extern("glfwTerminate") @public;
extern fn void pollEvents() @extern("glfwPollEvents") @public;
distinct _Window @private = void;
distinct _Monitor @private = void;
extern fn _Window* _glfwCreateWindow(
int width, int height, char* title, _Monitor* monitor, _Window* share
) @extern("glfwCreateWindow") @private;
extern fn bool _glfwWindowShouldClose(_Window* window)
@extern("glfwWindowShouldClose") @private;
extern fn void _glfwDestroyWindow(_Window* window)
@extern("glfwDestroyWindow") @private;
extern fn void _glfwSwapBuffers(_Window* window)
@extern("glfwSwapBuffers") @private;
extern fn void _glfwMakeContextCurrent(_Window* window)
@extern("glfwMakeContextCurrent") @private;

View File

@@ -0,0 +1,30 @@
import std::io;
import glfw;
import gl;
fn void main() {
glfw::initialize();
Window window;
window.create(1280, 720, "Triangle example");
window.makeContextCurrent();
while(!window.shouldClose()) {
gl::clear(BufferBit.COLOR.value);
gl::begin(Primitive.TRIANGLES.value);
gl::color3f(1, 0, 0); gl::vertex3f(-0.6, -0.75, 0.5);
gl::color3f(0, 1, 0); gl::vertex3f(0.6, -0.75, 0);
gl::color3f(0, 0, 1); gl::vertex3f(0, 0.75, 0);
gl::end();
gl::flush();
window.swapBuffers();
glfw::pollEvents();
}
window.destroy();
glfw::terminate();
}

View File

View File

@@ -1,5 +1,5 @@
module arkanoid;
import raylib;
import raylib5;
import std::math;
/**
*
@@ -72,15 +72,15 @@ fn void main()
{
// Initialization (Note windowTitle is unused on Android)
//---------------------------------------------------------
raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: arkanoid");
rl::initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: arkanoid");
init_game();
raylib::set_target_fps(60);
rl::setTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!raylib::window_should_close()) // Detect window close button or ESC key
while (!rl::windowShouldClose()) // Detect window close button or ESC key
{
// Update and Draw
//----------------------------------------------------------------------------------
@@ -91,7 +91,7 @@ fn void main()
//--------------------------------------------------------------------------------------
unload_game(); // Unload loaded data (textures, sounds, models...)
raylib::close_window(); // Close window and OpenGL context
rl::closeWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
@@ -103,7 +103,7 @@ fn void main()
// Initialize game variables
fn void init_game()
{
brick_size = { (float)raylib::get_screen_width() / BRICKS_PER_LINE, 40 };
brick_size = { (float)rl::getScreenWidth() / BRICKS_PER_LINE, 40 };
// Initialize player
player.position = { SCREEN_WIDTH/2, SCREEN_HEIGHT * 7 / 8 };
@@ -134,25 +134,25 @@ fn void update_game()
{
if (game_over)
{
if (raylib::is_key_pressed(keyboard::ENTER))
if (rl::isKeyPressed(rl::KEY_ENTER))
{
init_game();
game_over = false;
}
}
if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause;
if (rl::isKeyPressed((KeyboardKey)'P')) pause = !pause;
if (pause) return;
// Player movement logic
if (raylib::is_key_down(keyboard::LEFT)) player.position.x -= 5;
if (rl::isKeyDown(rl::KEY_LEFT)) player.position.x -= 5;
if ((player.position.x - player.size.x/2) <= 0) player.position.x = player.size.x/2;
if (raylib::is_key_down(keyboard::RIGHT)) player.position.x += 5;
if (rl::isKeyDown(rl::KEY_RIGHT)) player.position.x += 5;
if ((player.position.x + player.size.x/2) >= SCREEN_WIDTH) player.position.x = SCREEN_WIDTH - player.size.x/2;
// Ball launching logic
if (!ball.active)
{
if (raylib::is_key_pressed(keyboard::SPACE))
if (rl::isKeyPressed(rl::KEY_SPACE))
{
ball.active = true;
ball.speed = { 0, -5 };
@@ -182,7 +182,7 @@ fn void update_game()
}
// Collision logic: ball vs player
if (raylib::check_collision_circle_rec(ball.position, ball.radius,
if (rl::checkCollisionCircleRec(ball.position, ball.radius,
Rectangle{ player.position.x - player.size.x / 2, player.position.y - player.size.y / 2, player.size.x, player.size.y}))
{
if (ball.speed.y > 0)
@@ -256,20 +256,20 @@ fn void update_game()
// Draw game (one frame)
fn void draw_game()
{
raylib::begin_drawing();
raylib::clear_background(raylib::RAYWHITE);
rl::beginDrawing();
defer rl::endDrawing();
rl::clearBackground(rl::RAYWHITE);
if (!game_over)
{
// Draw player bar
raylib::draw_rectangle((int)(player.position.x - player.size.x/2), (int)(player.position.y - player.size.y/2), (int)player.size.x, (int)player.size.y, raylib::BLACK);
rl::drawRectangle((int)(player.position.x - player.size.x/2), (int)(player.position.y - player.size.y/2), (int)player.size.x, (int)player.size.y, rl::BLACK);
// Draw player lives
for (int i = 0; i < player.life; i++) raylib::draw_rectangle(20 + 40*i, SCREEN_HEIGHT - 30, 35, 10, raylib::LIGHTGRAY);
for (int i = 0; i < player.life; i++) rl::drawRectangle(20 + 40*i, SCREEN_HEIGHT - 30, 35, 10, rl::LIGHTGRAY);
// Draw ball
raylib::draw_circle_v(ball.position, ball.radius, raylib::MAROON);
rl::drawCircleV(ball.position, ball.radius, rl::MAROON);
// Draw bricks
for (int i = 0; i < LINES_OF_BRICKS; i++)
@@ -280,24 +280,22 @@ fn void draw_game()
{
if ((i + j) % 2 == 0)
{
raylib::draw_rectangle((int)(brick[i][j].position.x - brick_size.x/2), (int)(brick[i][j].position.y - brick_size.y/2), (int)brick_size.x, (int)brick_size.y, raylib::GRAY);
rl::drawRectangle((int)(brick[i][j].position.x - brick_size.x/2), (int)(brick[i][j].position.y - brick_size.y/2), (int)brick_size.x, (int)brick_size.y, rl::GRAY);
}
else
{
raylib::draw_rectangle((int)(brick[i][j].position.x - brick_size.x/2), (int)(brick[i][j].position.y - brick_size.y/2), (int)brick_size.x, (int)brick_size.y, raylib::DARKGRAY);
rl::drawRectangle((int)(brick[i][j].position.x - brick_size.x/2), (int)(brick[i][j].position.y - brick_size.y/2), (int)brick_size.x, (int)brick_size.y, rl::DARKGRAY);
}
}
}
}
if (pause) raylib::draw_text("GAME PAUSED", SCREEN_WIDTH/2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, raylib::GRAY);
if (pause) rl::drawText("GAME PAUSED", SCREEN_WIDTH/2 - rl::measureText("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, rl::GRAY);
}
else
{
raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width()/2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib::get_screen_height()/2 - 50, 20, raylib::GRAY);
rl::drawText("PRESS [ENTER] TO PLAY AGAIN", rl::getScreenWidth()/2 - rl::measureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, rl::getScreenHeight()/2 - 50, 20, rl::GRAY);
}
raylib::end_drawing();
}
// Unload game variables

View File

@@ -1,5 +1,5 @@
module snake;
import raylib;
import raylib5;
/**
*
* raylib - classic game: snake
@@ -53,18 +53,18 @@ int counter_tail = 0;
fn void main()
{
raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: snake");
rl::initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: snake");
init_game();
raylib::set_target_fps(60);
rl::setTargetFPS(60);
while (!raylib::window_should_close()) // Detect window close button or ESC key
while (!rl::windowShouldClose()) // Detect window close button or ESC key
{
update_draw_frame();
}
unload_game();
raylib::close_window();
rl::closeWindow();
}
// Initialize game variables
@@ -87,11 +87,11 @@ fn void init_game()
if (i == 0)
{
snake[i].color = raylib::DARKBLUE;
snake[i].color = rl::DARKBLUE;
}
else
{
snake[i].color = raylib::BLUE;
snake[i].color = rl::BLUE;
}
}
@@ -101,7 +101,7 @@ fn void init_game()
}
fruit.size = { SQUARE_SIZE, SQUARE_SIZE };
fruit.color = raylib::SKYBLUE;
fruit.color = rl::SKYBLUE;
fruit.active = false;
}
@@ -110,7 +110,7 @@ fn void update_game()
{
if (game_over)
{
if (raylib::is_key_pressed(keyboard::ENTER))
if (rl::isKeyPressed(rl::KEY_ENTER))
{
init_game();
game_over = false;
@@ -118,16 +118,16 @@ fn void update_game()
return;
}
if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause;
if (rl::isKeyPressed((KeyboardKey)'P')) pause = !pause;
if (pause) return;
if (raylib::is_key_pressed(keyboard::RIGHT) && allow_move)
if (rl::isKeyPressed(rl::KEY_RIGHT) && allow_move)
{
snake_direction = (SnakeDirection)((snake_direction.ordinal + 1) % 4);
allow_move = false;
}
if (raylib::is_key_pressed(keyboard::LEFT) && allow_move)
if (rl::isKeyPressed(rl::KEY_LEFT) && allow_move)
{
snake_direction = (SnakeDirection)((snake_direction.ordinal + 3) % 4);
allow_move = false;
@@ -179,13 +179,13 @@ fn void update_game()
if (!fruit.active)
{
fruit.active = true;
fruit.position = { (float)raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x / 2, (float)raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
fruit.position = { (float)rl::getRandomValue(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x / 2, (float)rl::getRandomValue(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
for (int i = 0; i < counter_tail; i++)
{
while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y))
{
fruit.position = { (float)raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, (float)raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
fruit.position = { (float)rl::getRandomValue(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, (float)rl::getRandomValue(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
i = 0;
}
}
@@ -204,37 +204,37 @@ fn void update_game()
// Draw game (one frame)
fn void draw_game()
{
raylib::begin_drawing();
rl::beginDrawing();
raylib::clear_background(raylib::RAYWHITE);
rl::clearBackground(rl::RAYWHITE);
if (!game_over)
{
// Draw grid lines
for (int i = 0; i < SCREEN_WIDTH / SQUARE_SIZE + 1; i++)
{
raylib::draw_line_v({(float)SQUARE_SIZE * i + offset.x/2, offset.y/2}, {(float)SQUARE_SIZE * i + offset.x/2, SCREEN_HEIGHT - offset.y/2}, raylib::LIGHTGRAY);
rl::drawLineV({(float)SQUARE_SIZE * i + offset.x/2, offset.y/2}, {(float)SQUARE_SIZE * i + offset.x/2, SCREEN_HEIGHT - offset.y/2}, rl::LIGHTGRAY);
}
for (int i = 0; i < SCREEN_HEIGHT/SQUARE_SIZE + 1; i++)
{
raylib::draw_line_v({offset.x/2, (float)SQUARE_SIZE * i + offset.y / 2 }, { SCREEN_WIDTH - offset.x/2, (float)SQUARE_SIZE * i + offset.y / 2 }, raylib::LIGHTGRAY);
rl::drawLineV({offset.x/2, (float)SQUARE_SIZE * i + offset.y / 2 }, { SCREEN_WIDTH - offset.x/2, (float)SQUARE_SIZE * i + offset.y / 2 }, rl::LIGHTGRAY);
}
// Draw snake
for (int i = 0; i < counter_tail; i++) raylib::draw_rectangle_v(snake[i].position, snake[i].size, snake[i].color);
for (int i = 0; i < counter_tail; i++) rl::drawRectangleV(snake[i].position, snake[i].size, snake[i].color);
// Draw fruit to pick
raylib::draw_rectangle_v(fruit.position, fruit.size, fruit.color);
rl::drawRectangleV(fruit.position, fruit.size, fruit.color);
if (pause) raylib::draw_text("GAME PAUSED", SCREEN_WIDTH/2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT / 2 - 40, 40, raylib::GRAY);
if (pause) rl::drawText("GAME PAUSED", SCREEN_WIDTH/2 - rl::measureText("GAME PAUSED", 40)/2, SCREEN_HEIGHT / 2 - 40, 40, rl::GRAY);
}
else
{
raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width()/2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib::get_screen_height()/2 - 50, 20, raylib::GRAY);
rl::drawText("PRESS [ENTER] TO PLAY AGAIN", rl::getScreenWidth()/2 - rl::measureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, rl::getScreenHeight()/2 - 50, 20, rl::GRAY);
}
raylib::end_drawing();
rl::endDrawing();
}
// Unload game variables

View File

@@ -1,5 +1,5 @@
module tetris;
import raylib;
import raylib5;
/**
* raylib - classic game: tetris
*
@@ -83,15 +83,15 @@ fn void main()
{
// Initialization (Note windowTitle is unused on Android)
//---------------------------------------------------------
raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: tetris");
rl::initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: tetris");
init_game();
raylib::set_target_fps(60);
rl::setTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!raylib::window_should_close()) // Detect window close button or ESC key
while (!rl::windowShouldClose()) // Detect window close button or ESC key
{
// Update and Draw
//----------------------------------------------------------------------------------
@@ -102,7 +102,7 @@ fn void main()
//--------------------------------------------------------------------------------------
unload_game(); // Unload loaded data (textures, sounds, models...)
raylib::close_window(); // Close window and OpenGL context
rl::closeWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
@@ -118,7 +118,7 @@ fn void init_game()
level = 1;
lines = 0;
fading_color = raylib::GRAY;
fading_color = rl::GRAY;
piece_position_x = 0;
piece_position_y = 0;
@@ -170,14 +170,14 @@ fn void update_game()
{
if (game_over)
{
if (raylib::is_key_pressed(keyboard::ENTER))
if (rl::isKeyPressed(rl::KEY_ENTER))
{
init_game();
game_over = false;
}
return;
}
if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause;
if (rl::isKeyPressed((KeyboardKey)'P')) pause = !pause;
if (pause) return;
if (line_to_delete)
@@ -185,7 +185,7 @@ fn void update_game()
// Animation when deleting lines
fade_line_counter++;
fading_color = fade_line_counter % 8 < 4 ? raylib::MAROON : raylib::GRAY;
fading_color = fade_line_counter % 8 < 4 ? rl::MAROON : rl::GRAY;
if (fade_line_counter >= FADING_TIME)
{
@@ -213,11 +213,11 @@ fn void update_game()
turn_movement_counter++;
// We make sure to move if we've pressed the key this frame
if (raylib::is_key_pressed(keyboard::LEFT) || raylib::is_key_pressed(keyboard::RIGHT)) lateral_movement_counter = LATERAL_SPEED;
if (raylib::is_key_pressed(keyboard::UP)) turn_movement_counter = TURNING_SPEED;
if (rl::isKeyPressed(rl::KEY_LEFT) || rl::isKeyPressed(rl::KEY_RIGHT)) lateral_movement_counter = LATERAL_SPEED;
if (rl::isKeyPressed(rl::KEY_UP)) turn_movement_counter = TURNING_SPEED;
// Fall down
if (raylib::is_key_down(keyboard::DOWN) && (fast_fall_movement_counter >= FAST_FALL_AWAIT_COUNTER))
if (rl::isKeyDown(rl::KEY_DOWN) && (fast_fall_movement_counter >= FAST_FALL_AWAIT_COUNTER))
{
// We make sure the piece is going to fall this frame
gravity_movement_counter += gravity_speed;
@@ -268,14 +268,13 @@ fn void update_game()
// Draw game (one frame)
fn void draw_game()
{
raylib::begin_drawing();
raylib::clear_background(raylib::RAYWHITE);
rl::beginDrawing();
defer rl::endDrawing();
rl::clearBackground(rl::RAYWHITE);
if (game_over)
{
raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width() / 2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20) / 2, raylib::get_screen_height() / 2 - 50, 20, raylib::GRAY);
raylib::end_drawing();
rl::drawText("PRESS [ENTER] TO PLAY AGAIN", rl::getScreenWidth() / 2 - rl::measureText("PRESS [ENTER] TO PLAY AGAIN", 20) / 2, rl::getScreenHeight() / 2 - 50, 20, rl::GRAY);
return;
}
@@ -296,22 +295,22 @@ fn void draw_game()
switch (grid[i][j])
{
case EMPTY:
raylib::draw_line(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, raylib::LIGHTGRAY );
raylib::draw_line(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY );
raylib::draw_line(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY );
raylib::draw_line(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY );
rl::drawLine(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, rl::LIGHTGRAY );
rl::drawLine(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, rl::LIGHTGRAY );
rl::drawLine(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, rl::LIGHTGRAY );
rl::drawLine(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, rl::LIGHTGRAY );
offset.x += SQUARE_SIZE;
case FULL:
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::GRAY);
rl::drawRectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, rl::GRAY);
offset.x += SQUARE_SIZE;
case MOVING:
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::DARKGRAY);
rl::drawRectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, rl::DARKGRAY);
offset.x += SQUARE_SIZE;
case BLOCK:
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::LIGHTGRAY);
rl::drawRectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, rl::LIGHTGRAY);
offset.x += SQUARE_SIZE;
case FADING:
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, fading_color);
rl::drawRectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, fading_color);
offset.x += SQUARE_SIZE;
default:
}
@@ -334,13 +333,13 @@ fn void draw_game()
switch (incoming_piece[i][j])
{
case EMPTY:
raylib::draw_line(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, raylib::LIGHTGRAY);
raylib::draw_line(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY);
raylib::draw_line(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY);
raylib::draw_line(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY);
rl::drawLine(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, rl::LIGHTGRAY);
rl::drawLine(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, rl::LIGHTGRAY);
rl::drawLine(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, rl::LIGHTGRAY);
rl::drawLine(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, rl::LIGHTGRAY);
offset.x += SQUARE_SIZE;
case MOVING:
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::GRAY);
rl::drawRectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, rl::GRAY);
offset.x += SQUARE_SIZE;
default:
break;
@@ -351,14 +350,13 @@ fn void draw_game()
offset.y += SQUARE_SIZE;
}
raylib::draw_text("INCOMING:", offset.x, offset.y - 100, 10, raylib::GRAY);
raylib::draw_text(raylib::text_format("LINES: %04i", lines), offset.x, offset.y + 20, 10, raylib::GRAY);
rl::drawText("INCOMING:", offset.x, offset.y - 100, 10, rl::GRAY);
rl::drawText(rl::textFormat("LINES: %04i", lines), offset.x, offset.y + 20, 10, rl::GRAY);
if (pause)
{
raylib::draw_text("GAME PAUSED", SCREEN_WIDTH / 2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, raylib::GRAY);
rl::drawText("GAME PAUSED", SCREEN_WIDTH / 2 - rl::measureText("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, rl::GRAY);
}
raylib::end_drawing();
}
// Unload game variables
@@ -415,7 +413,7 @@ fn bool create_piece()
fn void get_random_piece()
{
int random = raylib::get_random_value(0, 6);
int random = rl::getRandomValue(0, 6);
for (int i = 0; i < 4; i++)
{
@@ -508,7 +506,7 @@ fn bool resolve_lateral_movement()
bool collision = false;
// Piece movement
if (raylib::is_key_down(keyboard::LEFT)) // Move left
if (rl::isKeyDown(rl::KEY_LEFT)) // Move left
{
// Check if is possible to move to left
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
@@ -542,7 +540,7 @@ fn bool resolve_lateral_movement()
piece_position_x--;
}
}
else if (raylib::is_key_down(keyboard::RIGHT)) // Move right
else if (rl::isKeyDown(rl::KEY_RIGHT)) // Move right
{
// Check if is possible to move to right
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
@@ -587,7 +585,7 @@ fn bool resolve_lateral_movement()
fn bool resolve_turn_movement()
{
// Input for turning the piece
if (raylib::is_key_down(keyboard::UP))
if (rl::isKeyDown(rl::KEY_UP))
{
GridSquare aux;
bool checker = false;
@@ -790,5 +788,4 @@ fn int delete_complete_lines()
}
}
return lines_to_erase;
}
}

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