Compare commits

..

153 Commits

Author SHA1 Message Date
Christoffer Lerno
bdbe81fedd Fix libm regression. 2025-08-02 13:22:48 +02:00
Christoffer Lerno
f0142e3b1a Fix json check. 2025-08-01 17:07:37 +02:00
Christoffer Lerno
2c6ff00261 Fix alignment. 2025-08-01 10:55:04 +02:00
Christoffer Lerno
61a21203f4 Assigning string literal to char[<*>] stores pointer rather than characters. #2357 2025-08-01 10:32:53 +02:00
Christoffer Lerno
d7affc5028 Fix regression in file output placement. 2025-08-01 10:13:22 +02:00
Christoffer Lerno
eb9549a818 Release candidate 0.7.4 2025-08-01 00:06:16 +02:00
Christoffer Lerno
6d9906db0a Fix implicit linking from macros when it's not valid to add dependencies. 2025-07-31 16:21:50 +02:00
Christoffer Lerno
334ee975b9 Assignment evaluation order now right->left, following C++17. 2025-07-31 11:49:42 +02:00
Christoffer Lerno
d600d0898c Fix regression, not permitting macro parameters inlined into naked functions. 2025-07-30 10:38:37 +02:00
Christoffer Lerno
44f4efa5aa Do not allow parameters in naked functions. 2025-07-30 01:01:56 +02:00
Christoffer Lerno
8151305701 Fix so that rethrow is detected as well. 2025-07-29 20:50:32 +02:00
Christoffer Lerno
3ac9bfc387 Allow accessing parameters in a naked function, just disallow return, this fixes #1955. 2025-07-29 20:38:53 +02:00
BWindey
51b9cb85bc Fix get_config_dir() implementation (#2348)
* Fix get_config_dir() implementation

* Formatting and fallback to home if the path is empty.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-29 19:11:56 +02:00
Christoffer Lerno
d805ff9782 Lambda deduplication would be incorrect when generated at the global scope. 2025-07-29 17:45:11 +02:00
Christoffer Lerno
fa9ba3f607 Not setting android-ndk resulted in a "set ndk-path" error. 2025-07-29 11:33:40 +02:00
Zack Puhl
9d2be2851b Add @try Builtin (#2333)
* Add @try macro
* Add @try_catch. Update release notes.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-29 00:50:56 +02:00
Zack Puhl
2639338426 Support full paths with $embed (#2335)
* Support full paths with `$embed`

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-28 23:44:17 +02:00
Christoffer Lerno
d8daa4ac83 Fix dir check Win32 CI 2025-07-28 23:23:09 +02:00
Christoffer Lerno
6641155892 Lambdas now properly follow its attributes #2346. 2025-07-28 19:49:40 +02:00
Christoffer Lerno
86034353ec Remove unnecessary "ret" in naked functions #2344. 2025-07-28 18:36:33 +02:00
Christoffer Lerno
944ef0fc8d Fix incorrect LLVM output folder. 2025-07-28 18:07:47 +02:00
Christoffer Lerno
194b7c4772 Crash when parsing recursive type declaration #2345. 2025-07-28 18:02:25 +02:00
m0tholith
6963e143a1 Add gitignore template to project creation 2025-07-28 10:39:55 +02:00
Christoffer Lerno
4977bd1d78 @format did not work correctly with macros #2341. 2025-07-28 10:38:18 +02:00
Velikiy Kirill
a21e641748 LinkedHashMap: Fix head being null when initializing from map (#2334)
* LinkedHashMap: Fix head being null when initializing from map
2025-07-28 00:26:14 +02:00
Christoffer Lerno
208b0f6d0e Fix issue where recursively creating a dir would be incorrectly marked as a failure the first time.
Place output in `out` by default for projects. Use temp folder for building at the command line.
2025-07-28 00:19:12 +02:00
Christoffer Lerno
0bc546595d - Using @noreturn in a trailing body macro would not work properly #2326.
- Bug when reporting error in a macro return would crash the compiler #2326.
- Short body return expression would not have the correct span.
2025-07-26 02:03:02 +02:00
Velikiy Kirill
a673b4ad66 Add LinkedHashMap and LinkedHashSet implementations (#2324)
* Add LinkedHashMap and LinkedHashSet implementations

Add two new ordered collection types to std::collections:
- LinkedHashMap: insertion-order preserving hash map
- LinkedHashSet: insertion-order preserving hash set
2025-07-25 23:28:10 +02:00
Velikiy Kirill
9a68a5c063 HashSet: Contract message fixes 2025-07-25 23:06:18 +02:00
Velikiy Kirill
e790df539d Add HashSet implementation (#2322)
* Add HashSet implementation
Add a generic HashSet with full allocator support and standard set operations.
- Basic operations: add/remove/contains/clear
- Set operations:union_set/intersection/symmetric_difference/difference/is_subset
- Memory management with allocator support
- Iteration support
- Automatic resizing with load factor control
* Add "add_all" "add_all_from" "remove_all" "remove_all_from"
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-25 19:53:39 +02:00
Christoffer Lerno
fbeb779335 Fix typo in wait_timeout 2025-07-24 14:12:39 +02:00
Christoffer Lerno
625bfa5713 Updated releasenotes. 2025-07-23 17:04:33 +02:00
Christoffer Lerno
943a294900 Fix missing const cast when casting a const int expression. 2025-07-23 17:03:43 +02:00
Christoffer Lerno
3400dd5e42 char[*] b = *(char[*]*)&a; would crash the compiler if a was a slice. #2320 2025-07-23 16:10:31 +02:00
Christoffer Lerno
e8b3c44de3 Add a "wchar" type. 2025-07-23 15:09:55 +02:00
Christoffer Lerno
9575698fa4 Add String conversion functions snake_case -> PascalCase and vice versa. 2025-07-23 00:26:44 +02:00
Christoffer Lerno
5f0a7dd63e Rename AlignedRef -> UnalignedRef 2025-07-22 15:55:43 +02:00
Kiana
38bc11b7b8 Add libc::readlink() (#2316)
* Add libc::readlink()

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-22 15:46:23 +02:00
LowByteFox
04528aee1f fix openbsd CI not uploading artifacts 2025-07-22 15:44:58 +02:00
LowByteFox
5c82747970 Improved OpenBSD support (#2317)
* add openbsd support, compiles and passses all tests
* fix backtrace
* gh actions should include openbsd artifacts
2025-07-22 15:15:20 +02:00
Christoffer Lerno
b45c337515 Contract fix. 2025-07-22 15:14:10 +02:00
Christoffer Lerno
9dc6b0e660 Added AlignedRef generic type. 2025-07-22 15:11:14 +02:00
Christoffer Lerno
428165590e Add Volatile type. 2025-07-22 14:13:48 +02:00
Christoffer Lerno
53051e04a3 Add thread::fence (from $$fence builtin). Ref and RefCounted types. 2025-07-22 00:37:41 +02:00
Christoffer Lerno
869bcf8b2b Removing use of $assignable and deprecate it. Fix regression for stacktraces on MacOS. Added readline_to_stream. Regression: Chaining an optional together with contracts could in some cases lose the optional. 2025-07-21 03:20:40 +02:00
hamkoroke
382a65abcd Support asm x86 popcnt (#2314)
* Support asm x86 popcnt
2025-07-21 02:40:17 +02:00
Christoffer Lerno
908d705669 Removing use of $assignable and deprecate it. 2025-07-20 20:07:06 +02:00
Airtz
d422fb699f More Ed25519 cleanup (#2315)
* Cleanup Ed25519
2025-07-20 19:57:37 +02:00
Kiana
506e63284b Tidy up path.c3 (#2309)
* Tidy up path.c3
2025-07-20 12:26:44 +02:00
Zack Puhl
ed92476916 Add wyhash2 and metro64/128 modern hashing (#2293)
* add wyhash2, metro64, and metro128 hashes; best performing non-crypto hash functions
* add superfast 64-bit a5hash; not streamed, no 128-bit impl
* add komihash and associated tests/benchmarks
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-20 00:06:10 +02:00
LowByteFox
1218afd51f Add ability to compile on OpenBSD (#2310)
* fix curl in cmake, make c3c compile on OpenBSD, fix typo in OS_TYPE_FREEBSD
2025-07-19 21:09:39 +02:00
Christoffer Lerno
b88722b4a6 Copying const enums and regular enums incorrect #2313. 2025-07-19 21:07:25 +02:00
Christoffer Lerno
694d297eb8 "poison" the current function early when a declaration can't be correctly resolved. 2025-07-19 20:49:26 +02:00
Christian Buttner
2053f2767b Add ConditionVariable.wait_until and ConditionVariable.wait_for (#2302)
* Add `ConditionVariable.wait_until` and `ConditionVariable.wait_for`
* Add "@structlike" for typedefs.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-19 13:12:14 +02:00
Christoffer Lerno
448176b0b7 Rename ed25519 keys/signature. 2025-07-19 10:06:51 +02:00
Airtz
b1a22b5002 Implement Ed25519 (#2297)
* Implement Ed25519
* Overloading addition with a pointer would not work.
* Added @addr macro.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-19 01:13:15 +02:00
Christoffer Lerno
e9aee55714 A distinct type based on an array would yield .len == 0 2025-07-18 21:24:09 +02:00
Christoffer Lerno
2acf3c57c7 Check unaligned array access. 2025-07-18 20:45:57 +02:00
Christoffer Lerno
f2babb6063 Remove requirement of license. 2025-07-18 16:49:04 +02:00
Zack Puhl
b8d07474fe Add SipHash Family of Keyed PRFs (#2287)
* implement SipHash family of keyed PRFs
2025-07-18 16:16:47 +02:00
Christian Buttner
cf913b41c6 Fix Formatter.print returning incorrect size (#2306)
* Fix `Formatter.print` returning incorrect size
2025-07-18 15:54:07 +02:00
Christoffer Lerno
adb3df05c6 Allow even smaller memory limits. 2025-07-18 10:54:03 +02:00
Christoffer Lerno
34bded30eb Fix typo 2025-07-17 15:19:02 +02:00
Christoffer Lerno
774a375ce7 @links on macros would not be added to calling functions. 2025-07-17 15:17:58 +02:00
Christoffer Lerno
ee35001732 Suppress codegen of panic printing with when panic messages are set to "off".
Implicit linking of libc math when libc math functions are used.
2025-07-17 14:45:02 +02:00
Christoffer Lerno
da4105ffb1 Whereami fix 2025-07-16 22:34:14 +02:00
Christoffer Lerno
5c5692ae98 Dev (#2307)
* Refactor whereami
2025-07-16 15:56:36 +02:00
Christoffer Lerno
379d16abe7 Some refactoring of ranges. env::AUTHORS and env::AUTHOR_EMAILS 2025-07-16 12:23:24 +02:00
Christoffer Lerno
078ce38c57 Test fix. 2025-07-16 00:22:28 +02:00
Christoffer Lerno
f99b903d78 - Fix unexpected display of macro definition when passing a poisoned expression #2305. 2025-07-15 23:31:44 +02:00
Christoffer Lerno
3650b81970 Segfault when failing to cast subexpression to 'isz' in pointer subtraction #2305. 2025-07-15 21:47:49 +02:00
Christian Buttner
bb6fcdfa6f Improve atomic.c3
- Change `Atomic.xor` from function to macro
- Allow function pointers as native atomic type
- Use enum inference
2025-07-15 20:32:18 +02:00
Christian Buttner
8df112e157 Add WString.len 2025-07-15 20:30:14 +02:00
Christoffer Lerno
af91f35017 Fix stringify of $vaexpr #2301. 2025-07-15 17:13:10 +02:00
Zack Puhl
3cce90bba1 fix hashmap tinit_with_key_values 2025-07-15 00:26:59 +02:00
Christoffer Lerno
1a351bdb6d Error message for missing arg incorrect for methods with zero args #2296. 2025-07-14 23:21:15 +02:00
Christoffer Lerno
efaac43248 - Virtual memory library.
- New virtual emory arena allocator.
- Fixed resize bug when resizing memory down in ArenaAllocator, DynamicArenaAllocator, BackedArenaAllocator.
- Added feature flag "SLOW_TESTS"
2025-07-14 22:36:43 +02:00
Christoffer Lerno
f082cac762 Updates to API 2025-07-14 03:44:52 +02:00
Christoffer Lerno
2bd289ebd6 Added VirtualMemory 2025-07-14 03:07:03 +02:00
Christoffer Lerno
aba9baf207 Added Vmem allocator 2025-07-13 17:26:57 +02:00
Christoffer Lerno
e755c36ea2 Removed the use of temp allocator in backtrace printing.
Added string::bformat.
2025-07-13 02:58:57 +02:00
Christoffer Lerno
6c7dc2a28e Improved error on using cast expression as lvalue. 2025-07-11 13:47:52 +02:00
Christoffer Lerno
cdd530d807 Fixed bug splatting constants into constants. 2025-07-11 01:55:09 +02:00
Christoffer Lerno
02c0db7b8b Multiline contract comments #2113 2025-07-11 01:18:58 +02:00
Christoffer Lerno
8a62c12089 Update releasenotes with whirlpool 2025-07-10 18:32:26 +02:00
Christoffer Lerno
988549599d $is_const is deprecated in favour of @is_const based on $defined.
`$foo` variables could be assigned non-compile time values.
`$foo[0] = ...` was incorrectly requiring that the assigned values were compile time constants.
2025-07-10 18:31:44 +02:00
Zack Puhl
70159c00cc Add WHIRLPOOL hashing to stdlib (#2273)
* add WHIRLPOOL hashing to stdlib
2025-07-10 16:39:42 +02:00
Christoffer Lerno
2dfbdea889 Update error message for struct initialization #2286 2025-07-10 16:29:49 +02:00
Christoffer Lerno
299d1f530f Correctly poison the analysis after a failed $assert or $error. #2284 2025-07-09 16:51:43 +02:00
Christoffer Lerno
bf0ff8abbc Splat 8 arguments can sometimes cause incorrect behaviour in the compiler. #2283 2025-07-09 16:36:02 +02:00
Ero Mrinin
123b1c8f44 Added unpack macro for Triple (#2277) 2025-07-09 03:11:13 +02:00
Christoffer Lerno
a314e05826 Use hex consistently for .max is 64 bits or more. 2025-07-09 03:09:10 +02:00
Christoffer Lerno
83fd24faa2 Improve error on unsigned implicit conversion to signed. 2025-07-09 02:49:19 +02:00
Christoffer Lerno
1d4ad5f1d5 Function pointers are now compile time constants.
const enum cannot be set to function pointer unless it's a lambda #2282.
2025-07-08 12:42:16 +02:00
Christoffer Lerno
26d5cc694a Formatting option "%h" now supports pointers. 2025-07-08 11:43:49 +02:00
Christoffer Lerno
a2122e0153 Update error message. 2025-07-08 00:24:29 +02:00
Christoffer Lerno
10fc94aaa7 Add RISCV asm print. 2025-07-07 23:52:43 +02:00
Christoffer Lerno
0835bada39 Add --list-asm to view all supported asm instructions. 2025-07-07 23:49:39 +02:00
Christoffer Lerno
277af1a2b6 Fix rdtsc instruction. 2025-07-07 19:02:13 +02:00
Christoffer Lerno
5b835bec3e Fix to codegen when using a bitstruct constant defined using a cast with an operator #2248. 2025-07-07 17:09:32 +02:00
Christoffer Lerno
dc23cef59a LLVM 20 compatibility for test. 2025-07-07 03:01:09 +02:00
Christoffer Lerno
098079d317 Fix analysis error compiling. Fix $define for updated shift. 2025-07-07 02:50:17 +02:00
Christoffer Lerno
1ab57ecf20 Improve contract for readline. #2280 2025-07-07 02:42:48 +02:00
Christoffer Lerno
808ab56545 - Bit shift operation fails with inline uint enum despite matching underlying type #2279. 2025-07-07 02:30:54 +02:00
Christoffer Lerno
19acdc7a19 Do not do certain implicit conversions on enums in binary expression. 2025-07-06 21:44:06 +02:00
Christoffer Lerno
e15fdc709f Improve error message when doing a rethrow in a function that doesn't return an optional.
Array indices are now using int64 internally.
2025-07-06 20:20:42 +02:00
Disheng Su
457244e3de Fix json parser (#2272)
* Fix json parser number

* Fix json parser leading zero

* Fix json parser with duplicated keys

* Fix json parser with trailing characters

* Fix json parser: set recursive depth to 128

* Fix json parser: skip comment to false

* Fix json parser: reject number trailing with null

* Make max depth configurable. Simplify with defer catch. Accept `2.`

* Make max depth configurable. Simplify with defer catch. Accept `2.`

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-06 03:07:46 +02:00
Ero Mrinin
d5559ecafd Tuple update (#2235)
* 'next_float' macro patch

More optimized implementation.

* tuple-type update

* U suffix in 'next_float'

* Do not add triplet, quadruplet, keep Tuple but deprecate. Add unpack

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-06 02:08:59 +02:00
Kiana
802fbfcf1e Add ansi.c3 for AnsiColor support (#2261)
* Add ansi.c3 for AnsiColor support

* Added tests

updated functions to macros
added formatting codes

* Fixed indentation

* Update names. Add plain rgb version. Add runtime colors.

* Update indentation, add 21-29

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-07-06 01:37:54 +02:00
Christoffer Lerno
a20e74c401 Fixes to thread local pointer handling. 2025-07-06 00:22:21 +02:00
Christoffer Lerno
7cdb1ce9eb Catch accidental foo == BAR; where foo = BAR; was most likely intended. #2274 2025-07-05 20:40:55 +02:00
Christoffer Lerno
0d170a70b6 Make to_float more tolerant to spaces. 2025-07-05 19:42:44 +02:00
Christoffer Lerno
b19cd0b87d Casting a fault to a pointer would trigger an assert. 2025-07-05 01:38:44 +02:00
Christoffer Lerno
50efc95c83 Update sponsors 2025-07-05 00:57:23 +02:00
Christoffer Lerno
fa50268b4e Update max memory. 2025-07-04 17:46:02 +02:00
Christoffer Lerno
ae1d51d089 --max-mem now works correctly again. 2025-07-04 17:06:52 +02:00
Christoffer Lerno
1b8355ff07 Update error message on invalid operator. 2025-07-04 14:33:06 +02:00
Christoffer Lerno
f32afb70b8 Add contract to create thread. 2025-07-04 11:02:23 +02:00
Christoffer Lerno
60d96ca7b7 Initialize pool correctly in print_backtrace. 2025-07-04 02:43:49 +02:00
Christoffer Lerno
014f734260 Remove unused code. 2025-07-04 02:26:01 +02:00
Christoffer Lerno
de4963ef95 Add --echo-prefix to edit the prefix with $echo statements. Supports {FILE} and {LINE} 2025-07-04 01:09:02 +02:00
ryuukk
e7d3e60ebd Disable libxml2 dependency on linux (#2268)
* Disable libxml2 depdendency

* All the linuxes
2025-07-04 00:14:10 +02:00
Christoffer Lerno
a46f73ad24 Const slice indexing was not bounds checked. 2025-07-03 23:53:01 +02:00
Christoffer Lerno
759bc1d909 Const slice lengths were not always detected as constant. 2025-07-03 23:32:02 +02:00
Christoffer Lerno
c79c9dac8d Inline r / complex for complex numbers fixed. 2025-07-03 23:04:16 +02:00
Christoffer Lerno
635d4babc4 Fix tests. 2025-07-03 22:37:23 +02:00
Christoffer Lerno
9b3b4ae8be $for ct-state not properly popped. 2025-07-03 22:20:14 +02:00
Christoffer Lerno
b3e7f074e9 Forgot the fix... 2025-07-03 15:46:35 +02:00
Christoffer Lerno
ee1ed73fc5 Non-const macros may not return untyped lists. 2025-07-03 15:45:14 +02:00
Christoffer Lerno
10e11fb742 Better detect offending cast. 2025-07-03 15:14:24 +02:00
Christoffer Lerno
8b47317ec7 Fix issue with labelled break inside of a $switch. 2025-07-03 13:11:12 +02:00
Christoffer Lerno
04626b72cd Check up the hierarchy when considering if an interface cast is valid #2267. 2025-07-03 12:36:35 +02:00
Christoffer Lerno
2151cd0929 Missing bounds check on upper bound with const ranges foo[1:3]. 2025-07-02 13:12:49 +02:00
Christoffer Lerno
93ded9c1e0 Switch case with const non-int / enum would be treated as ints and crash. #2263 2025-07-02 12:26:26 +02:00
Christoffer Lerno
20964b43ce Fix of const enum resolution order #2264 2025-07-02 12:01:52 +02:00
Christoffer Lerno
af192354fd Comparing a null ZString with a non-null ZString would crash. 2025-07-02 01:22:34 +02:00
Christoffer Lerno
ad48637cbb Correctly reject interface methods type and ptr. 2025-07-01 20:08:46 +02:00
Christoffer Lerno
89507bd335 Improved error messages on missing qualifier on enum value. #2260 2025-07-01 17:08:12 +02:00
Christoffer Lerno
21533ffee4 Update sponsors. 2025-07-01 16:15:38 +02:00
Christoffer Lerno
4502a9286c Fix typeid on compile time types. 2025-07-01 00:27:17 +02:00
Christoffer Lerno
ba11511c69 Update release notes. 2025-07-01 00:03:28 +02:00
Christoffer Lerno
965ef19a5b Allow $typeof to return a compile time type. 2025-07-01 00:02:12 +02:00
Christoffer Lerno
8f86b331c1 Fix --use-old-enums 2025-06-30 23:10:32 +02:00
Christoffer Lerno
59a1590955 Hex string formatter check incorrectly rejected slices. 2025-06-30 21:41:52 +02:00
Christoffer Lerno
fad87b294b mkdir/rmdir would not work properly with substring paths on non-windows platforms. 2025-06-30 21:41:52 +02:00
Christoffer Lerno
13bb2b6690 Const Enums From / to ordinal using casts is back. Add "--use-old-enums", deprecating lookup. 2025-06-30 21:41:52 +02:00
Christoffer Lerno
4a803ed0cf Bump version 2025-06-30 19:33:41 +02:00
Christoffer Lerno
9e80f1b26c Release candidate. 2025-06-30 17:21:32 +02:00
Christoffer Lerno
9299c78747 Detect when a slice on the stack is accidentally returned from a function. 2025-06-30 15:56:19 +02:00
Christoffer Lerno
e1a125e326 - Initial support for #1925, does not affect C compilation yet, and doesn't try to link etc. Using "--emit-only" 2025-06-29 23:50:17 +02:00
cubedium
a13eb99962 Added colored error and warning compiler messages. (#2253)
* Added colored error and warning compiler messages.
* Fixed the warning messages to be colored yellow instead of blue.
* Made the use_ansi function public with compiler_internal.h and toggleable colored error messages with the --ansi flag
* Moved use_ansi declaration. No ansi on test/lsp output.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-06-29 20:22:09 +02:00
Disheng Su
d46733e11a Add string escaping and unescaping functionality (#2243)
* Add `String.escape`, `String.unescape` for escaping and unescaping a string.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-06-29 20:11:11 +02:00
Christian Buttner
ce569462f6 Improve CMakeLists.txt and Windows build 2025-06-29 18:21:11 +02:00
249 changed files with 13624 additions and 2632 deletions

View File

@@ -10,6 +10,7 @@ env:
LLVM_RELEASE_VERSION_WINDOWS: 18
LLVM_RELEASE_VERSION_MAC: 17
LLVM_RELEASE_VERSION_LINUX: 17
LLVM_RELEASE_VERSION_OPENBSD: 19
LLVM_RELEASE_VERSION_UBUNTU22: 17
LLVM_DEV_VERSION: 21
jobs:
@@ -52,9 +53,9 @@ jobs:
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32 --trust=full
dir build\llvm\windows-x64
dir out\llvm\windows-x64
..\..\build\${{ matrix.build_type }}\c3c.exe clean
dir build\llvm\windows-x64
dir out\llvm\windows-x64
- name: Build testproject lib
@@ -90,7 +91,7 @@ jobs:
- name: Compile run unit tests
run: |
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1 -D SLOW_TESTS
- name: run compiler tests
run: |
@@ -273,6 +274,7 @@ jobs:
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
cmake --build build
- name: CMake18
@@ -287,6 +289,7 @@ jobs:
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
cmake --build build
@@ -345,7 +348,7 @@ jobs:
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit
../build/c3c compile-test unit -D SLOW_TESTS
- name: Build testproject
run: |
@@ -439,6 +442,7 @@ jobs:
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
cmake --build build
- name: CMake
@@ -453,6 +457,7 @@ jobs:
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
cmake --build build
- name: Compile and run some examples
@@ -489,7 +494,7 @@ jobs:
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit --sanitize=address
../build/c3c compile-test unit --sanitize=address -D SLOW_TESTS
- name: Build testproject
run: |
@@ -583,7 +588,7 @@ jobs:
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit
../build/c3c compile-test unit -D SLOW_TESTS
- name: Build testproject
run: |
@@ -665,7 +670,7 @@ jobs:
- name: Compile run unit tests
run: |
cd test
../build/c3c compile-test unit -O1
../build/c3c compile-test unit -O1 -D SLOW_TESTS
- name: Test WASM
run: |
@@ -747,6 +752,119 @@ jobs:
nix build -L ".#c3c-checks"
fi
build-openbsd:
runs-on: ubuntu-latest
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
steps:
- uses: actions/checkout@v4
- name: OpenBSD VM
uses: vmactions/openbsd-vm@main
with:
prepare: |
pkg_add cmake llvm-19.1.7p3 ninja
run: |
echo "CMake"
cmake -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${LLVM_RELEASE_VERSION_OPENBSD}
cmake --build build
echo "Compile and run some examples"
cd resources
../build/c3c compile examples/base64.c3
../build/c3c compile examples/binarydigits.c3
../build/c3c compile examples/brainfk.c3
../build/c3c compile examples/factorial_macro.c3
../build/c3c compile examples/fasta.c3
../build/c3c compile examples/gameoflife.c3
../build/c3c compile examples/hash.c3
../build/c3c compile-only examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile-only examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/nbodies.c3
../build/c3c compile examples/spectralnorm.c3
../build/c3c compile examples/swap.c3
../build/c3c compile examples/contextfree/boolerr.c3
../build/c3c compile examples/contextfree/dynscope.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
../build/c3c compile-run examples/process.c3
../build/c3c compile-run examples/ls.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
cd ..
echo "Compile and run dynlib-test"
cd resources/examples/dynlib-test
../../../build/c3c -vv dynamic-lib add.c3
mv add.so libadd.so
cc test.c -L. -ladd -Wl,-rpath=.
./a.out
../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=.
cd ../../../
echo "Compile and run staticlib-test"
cd resources/examples/staticlib-test
../../../build/c3c -vv static-lib add.c3
mv add.a libadd.a
cc test.c -L. -ladd
./a.out
../../../build/c3c compile-run test.c3 -L . -l add
cd ../../../
echo "Compile run unit tests"
cd test
../build/c3c --max-mem 128 compile-test unit -D SLOW_TESTS
cd ..
echo "Build testproject"
cd resources/testproject
../../build/c3c run -vvv --trust=full
cd ../../
echo "Test WASM"
cd resources/testfragments
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
cd ../../
echo "Build testproject direct linker"
cd resources/testproject
../../build/c3c run -vvv --linker=builtin --trust=full
cd ../../
echo "Init a library & a project"
./build/c3c init-lib mylib
ls mylib.c3l
./build/c3c init myproject
ls myproject
echo "run compiler tests"
cd test
../build/c3c --max-mem 128 compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/
cd ..
- name: bundle_output
run: |
mkdir c3
cp -r lib c3
cp msvc_build_libraries.py c3
cp build/c3c c3
cp README.md c3
cp releasenotes.md c3
tar czf c3-openbsd-${{matrix.build_type}}.tar.gz c3
- name: upload artifacts
uses: actions/upload-artifact@v4
with:
name: c3-openbsd-${{matrix.build_type}}
path: c3-openbsd-${{matrix.build_type}}.tar.gz
release:
runs-on: ubuntu-22.04
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu22]
@@ -769,6 +887,8 @@ jobs:
- 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-openbsd-Release/c3-openbsd-Release.tar.gz c3-openbsd-Release/c3-openbsd.tar.gz
- run: mv c3-openbsd-Debug/c3-openbsd-Debug.tar.gz c3-openbsd-Debug/c3-openbsd-debug.tar.gz
- run: mv c3-ubuntu-22-Release/c3-ubuntu-22-Release.tar.gz c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz
- run: mv c3-ubuntu-22-Debug/c3-ubuntu-22-Debug.tar.gz c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
- run: mv c3-macos-Release/c3-macos-Release.zip c3-macos-Release/c3-macos.zip
@@ -790,6 +910,8 @@ jobs:
c3-windows-debug.zip
c3-linux-Release/c3-linux.tar.gz
c3-linux-Debug/c3-linux-debug.tar.gz
c3-openbsd-Release/c3-openbsd.tar.gz
c3-openbsd-Debug/c3-openbsd-debug.tar.gz
c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz
c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
c3-macos-Release/c3-macos.zip

View File

@@ -1,5 +1,13 @@
cmake_minimum_required(VERSION 3.20)
set(C3_LLVM_MIN_VERSION 17)
set(C3_LLVM_MAX_VERSION 21)
set(C3_LLVM_DEFAULT_VERSION 19)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
message(FATAL_ERROR "In-tree build detected, please build in a separate directory")
endif()
# Grab the version
file(READ "src/version.h" ver)
if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
@@ -7,8 +15,20 @@ if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
endif()
# Set the project and version
project(c3c VERSION ${CMAKE_MATCH_1})
message("C3C version: ${CMAKE_PROJECT_VERSION}")
project(c3c VERSION ${CMAKE_MATCH_1} LANGUAGES C CXX)
message("Configuring C3C ${CMAKE_PROJECT_VERSION} for ${CMAKE_SYSTEM_NAME}")
# Helper functions
function(c3_print_variables)
set(msg "")
foreach(var ${ARGN})
if(msg)
string(APPEND msg " ; ")
endif()
string(APPEND msg "${c3_print_prefix}${var}=\"${${var}}\"")
endforeach()
message(STATUS "${msg}")
endfunction()
# Avoid warning for FetchContent
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
@@ -16,7 +36,7 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
endif()
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
if (MSVC)
if (WIN32)
set(CMAKE_INSTALL_LIBDIR "c:\\c3c\\lib")
set(CMAKE_INSTALL_BINDIR "c:\\c3c")
else ()
@@ -36,36 +56,41 @@ set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
# Use /MT or /MTd
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
if(MSVC)
message(STATUS "MSVC version ${MSVC_VERSION}")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc /utf-8")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc /utf-8")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
add_compile_options(/utf-8)
else()
if (true)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O0 -fno-exceptions")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O0 -fno-exceptions")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fno-exceptions")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fno-exceptions")
else()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined,address -fno-exceptions")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined,address -fno-exceptions")
endif()
add_compile_options(-gdwarf-3 -fno-exceptions)
# add_compile_options(-fsanitize=address,undefined)
# add_link_options(-fsanitize=address,undefined)
endif()
option(C3_LINK_DYNAMIC "link dynamically with LLVM/LLD libs")
# Options
set(C3_LINK_DYNAMIC OFF CACHE BOOL "Link dynamically with LLVM/LLD libs")
set(C3_WITH_LLVM ON CACHE BOOL "Build with LLVM")
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
set(C3_USE_MIMALLOC OFF CACHE BOOL "Use built-in mimalloc")
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
set(C3_USE_TB OFF CACHE BOOL "Use TB")
set(C3_LLD_DIR "" CACHE STRING "Use custom LLD directory")
set(C3_ENABLE_CLANGD_LSP OFF CACHE BOOL "Enable/Disable output of compile commands during generation")
set(LLVM_CRT_LIBRARY_DIR "" CACHE STRING "Use custom llvm's compiler-rt directory")
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)
option(C3_LLD_DIR "Use custom LLD directory" "")
option(LLVM_CRT_LIBRARY_DIR "Use custom llvm's compiler-rt directory" "")
set(C3_OPTIONS
C3_LINK_DYNAMIC
C3_WITH_LLVM
C3_LLVM_VERSION
C3_USE_MIMALLOC
C3_MIMALLOC_TAG
C3_USE_TB
C3_LLD_DIR
C3_ENABLE_CLANGD_LSP
LLVM_CRT_LIBRARY_DIR
)
set(C3_USE_MIMALLOC OFF)
if(C3_USE_MIMALLOC)
@@ -83,13 +108,6 @@ endif()
if (NOT WIN32)
find_package(CURL)
endif()
if(C3_WITH_LLVM)
if (NOT C3_LLVM_VERSION STREQUAL "auto")
if (${C3_LLVM_VERSION} VERSION_LESS 17 OR ${C3_LLVM_VERSION} VERSION_GREATER 21)
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
endif()
endif()
endif()
find_package(Git QUIET)
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
@@ -107,7 +125,6 @@ if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
endif()
# 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(
@@ -120,7 +137,7 @@ 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 "19")
set(C3_LLVM_VERSION ${C3_LLVM_DEFAULT_VERSION})
endif()
FetchContent_Declare(
LLVM_Windows
@@ -139,6 +156,7 @@ if(C3_WITH_LLVM)
FetchContent_MakeAvailable(LLVM_Windows)
set(llvm_dir ${llvm_windows_SOURCE_DIR})
endif()
message("Loaded Windows LLVM libraries into ${llvm_dir}")
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
find_package(LLVM REQUIRED CONFIG)
@@ -149,7 +167,7 @@ if(C3_WITH_LLVM)
#
# Because of CMAKE_FIND_PACKAGE_SORT_ORDER CMAKE_FIND_PACKAGE_SORT_DIRECTION,
# the newest version will always be found first.
message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}")
c3_print_variables(CMAKE_PREFIX_PATH)
if (DEFINED LLVM_DIR)
message(STATUS "Looking for LLVM CMake files in user-specified directory ${LLVM_DIR}")
else()
@@ -176,12 +194,15 @@ if(C3_WITH_LLVM)
list(APPEND LLVM_LIBRARY_DIRS /usr/lib)
endif()
list(REMOVE_DUPLICATES 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}")
message(STATUS "LLVM 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.")
if (${LLVM_PACKAGE_VERSION} VERSION_LESS C3_LLVM_MIN_VERSION OR
${LLVM_PACKAGE_VERSION} VERSION_GREATER C3_LLVM_MAX_VERSION)
message(FATAL_ERROR "LLVM ${LLVM_PACKAGE_VERSION} is not supported! LLVM version between ${C3_LLVM_MIN_VERSION} and ${C3_LLVM_MAX_VERSION} is required.")
endif()
if(LLVM_ENABLE_RTTI)
@@ -231,44 +252,43 @@ if(C3_WITH_LLVM)
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
message("C3_LLD_DIR: " ${C3_LLD_DIR})
set(LLVM_LIBRARY_DIRS
"${LLVM_LIBRARY_DIRS}"
"${C3_LLD_DIR}"
)
list(APPEND LLVM_LIBRARY_DIRS ${C3_LLD_DIR})
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
endif()
message(STATUS "Looking for static lld libraries in ${LLVM_LIBRARY_DIRS}")
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
else()
set(LLD_MACHO "")
endif()
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
else()
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
message(STATUS "Looking for shared lld libraries in ${LLVM_LIBRARY_DIRS}")
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
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)
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
else()
set(LLD_MACHO "")
endif()
find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
endif()
endif()
if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
endif()
if(C3_WITH_LLVM)
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
# find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(lld_libs
${LLD_COFF}
${LLD_WASM}
@@ -293,8 +313,8 @@ if(C3_WITH_LLVM)
)
endif()
message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Found lld libs ${lld_libs}")
message(STATUS "Linking to llvm libs ${llvm_libs}")
message(STATUS "Linking to lld libs ${lld_libs}")
endif()
add_library(miniz STATIC dependencies/miniz/miniz.c)
@@ -412,6 +432,11 @@ if(C3_WITH_LLVM)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
if (MSVC)
target_compile_options(c3c PRIVATE
"$<$<CONFIG:Debug>:/EHa>"
"$<$<CONFIG:Release>:/EHsc>")
endif()
else()
target_sources(c3c PRIVATE src/utils/hostinfo.c)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
@@ -495,7 +520,7 @@ endif ()
if (CURL_FOUND)
target_link_libraries(c3c ${CURL_LIBRARIES})
target_include_directories(c3c PRIVATE ${CURL_INCLUDES})
target_include_directories(c3c PRIVATE ${CURL_INCLUDE_DIRS})
target_compile_definitions(c3c PUBLIC CURL_FOUND=1)
else()
target_compile_definitions(c3c PUBLIC CURL_FOUND=0)
@@ -503,34 +528,27 @@ endif()
if(MSVC)
message("Adding MSVC options")
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
target_compile_options(c3c PRIVATE
/wd4068
/wd4090
/WX
/Wv:18
)
if(C3_WITH_LLVM)
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
target_compile_options(c3c_wrappers PUBLIC
/wd4624
/wd4267
/wd4244
/WX
/Wv:18
)
if(NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PUBLIC /GR-)
endif()
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(c3c 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)
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
@@ -540,13 +558,20 @@ if(MSVC)
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
endif()
else()
message(STATUS "using gcc/clang warning switches")
target_link_options(c3c PRIVATE -pthread)
if (C3_WITH_LLVM AND NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
endif()
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
target_compile_options(c3c PRIVATE
-pthread
-Wall
-Werror
-Wno-unknown-pragmas
-Wno-unused-result
-Wno-unused-function
-Wno-unused-variable
-Wno-unused-parameter
)
target_link_options(c3c PRIVATE -pthread)
endif()
install(TARGETS c3c DESTINATION bin)
@@ -557,6 +582,12 @@ if (NOT WIN32)
install(FILES c3c.1 DESTINATION "share/man/man1")
endif()
# Copy stdlib
if (NOT ${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR})
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
endif()
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
@@ -576,3 +607,35 @@ if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
endif()
feature_summary(WHAT ALL)
message(STATUS "Building ${CMAKE_PROJECT_NAME} with the following configuration:")
set(c3_print_prefix " ")
foreach(option IN LISTS C3_OPTIONS)
if (DEFINED ${option})
c3_print_variables(${option})
endif()
endforeach()
foreach(flag_var
CMAKE_BUILD_TYPE
CMAKE_C_COMPILER
CMAKE_CXX_COMPILER
CMAKE_LINKER
CMAKE_OBJCOPY
CMAKE_STRIP
CMAKE_DLLTOOL)
c3_print_variables(${flag_var})
endforeach()
message(STATUS "Build flags:")
foreach(flag_var
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
c3_print_variables(${flag_var})
endforeach()
message(STATUS "Output to: \"${CMAKE_BINARY_DIR}\"")

57
CMakePresets.json Normal file
View File

@@ -0,0 +1,57 @@
{
"version": 3,
"configurePresets": [
{
"name": "windows-base",
"hidden": true,
"architecture": {
"value": "x64"
},
"toolset": {
"value": "host=x64"
}
},
{
"name": "windows-vs-2022-release",
"generator": "Visual Studio 17 2022",
"displayName": "Windows x64 Visual Studio 17 2022",
"inherits": "windows-base",
"binaryDir": "build",
"cacheVariables": {
"CMAKE_CONFIGURATION_TYPES": "Release;RelWithDebInfo",
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "windows-vs-2022-debug",
"generator": "Visual Studio 17 2022",
"displayName": "Windows x64 Visual Studio 17 2022 (Debug)",
"inherits": "windows-base",
"binaryDir": "build-debug",
"cacheVariables": {
"CMAKE_CONFIGURATION_TYPES": "Debug",
"CMAKE_BUILD_TYPE": "Debug"
}
}
],
"buildPresets": [
{
"name": "windows-vs-2022-debug",
"displayName": "Debug",
"configurePreset": "windows-vs-2022-debug",
"configuration": "Debug"
},
{
"name": "windows-vs-2022-release",
"displayName": "Release",
"configurePreset": "windows-vs-2022-release",
"configuration": "Release"
},
{
"name": "windows-vs-2022-release-with-debug-info",
"displayName": "RelWithDebInfo",
"configurePreset": "windows-vs-2022-release",
"configuration": "RelWithDebInfo"
}
]
}

View File

@@ -12,6 +12,7 @@ Precompiled binaries for the following operating systems are available:
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
- OpenBSD x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz), [install instructions](#installing-on-openbsd-with-precompiled-binaries).
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
@@ -141,7 +142,7 @@ fn void main()
### Current status
The current stable version of the compiler is **version 0.7.2**.
The current stable version of the compiler is **version 0.7.4**.
The upcoming 0.7.x releases will focus on expanding the standard library,
fixing bugs and improving compile time analysis.
@@ -176,12 +177,14 @@ The compiler is currently verified to compile on Linux, Windows and MacOS.
| NetBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
| NetBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
| OpenBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
| OpenBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
| OpenBSD x64 | Yes* | Yes | Yes* | Yes | Untested | Yes* |
| MCU x86 | No | Untested | No | No | No | Yes* |
| Wasm32 | No | Yes | No | No | No | No |
| Wasm64 | No | Untested | No | No | No | No |
*\* Inline asm is still a work in progress*
*\* Inline asm is still a work in progress*<br>
*\* OpenBSD 7.7 is the only tested version*<br>
*\* OpenBSD has limited stacktrace, needs to be tested further*
More platforms will be supported in the future.
@@ -227,6 +230,14 @@ This installs the latest prerelease build, as opposed to the latest released ver
(*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 OpenBSD with precompiled binaries
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd-debug.tar.gz))
2. Unpack executable and standard lib.
3. Run `./c3c`.
(*Note that this is specifically for OpenBSD 7.7, running it on any other version is prone to ABI breaks)
#### Installing on Arch Linux
Arch includes c3c in the official 'extra' repo. It can be easily installed the usual way:
@@ -313,17 +324,25 @@ called `hello_world` or `hello_world.exe`depending on platform.
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++"
2. Install CMake
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory `cd c3c`.
5. Set up the CMake build `cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release`
6. Build: `cmake --build build --config Release`
7. You should now have the c3c.exe
4. Enter the C3C directory: `cd c3c`.
5. Set up the CMake build: `cmake --preset windows-vs-2022-release`
6. Build: `cmake --build --preset windows-vs-2022-release`
You should now have a `c3c` executable.
You should now have a `c3c` executable in `build\Release`.
You can try it out by running some sample code: `c3c.exe compile ../resources/examples/hash.c3`
You can try it out by running some sample code: `c3c.exe compile ../../resources/examples/hash.c3`
Building `c3c` using Visual Studio Code is also supported when using the `CMake Tools` extension. Simply select the `Windows x64 Visual Studio 17 2022` configure preset and build.
*Note that if you run into linking issues when building, make sure that you are using the latest version of VS17.*
#### Compiling on Windows (Debug)
Debug build requires a different set of LLVM libraries to be loaded for which a separate CMake configuration is used to avoid conflicts.
1. Configure: `cmake --preset windows-vs-2022-debug`
2. Build: `cmake --build --preset windows-vs-2022-debug`
You should now have a `c3c` executable in `build-debug\Debug`.
#### Compiling on Ubuntu 24.04 LTS
@@ -425,7 +444,12 @@ Editor plugins can be found at https://github.com/c3lang/editor-plugins.
A huge **THANK YOU** goes out to all contributors and sponsors.
A special thank you to sponsors [Caleb-o](https://github.com/Caleb-o) and [devdad](https://github.com/devdad) for going the extra mile.
A special thank you to sponsors [Zack Puhl](https://github.com/NotsoanoNimus) and [konimarti](https://github.com/konimarti) for going the extra mile.
And honorable mention goes to past sponsors:
[Ygor Pontelo](https://github.com/ygorpontelo), [Simone Raimondi](https://github.com/SRaimondi),
[Jan Válek](https://github.com/jan-valek), [Pierre Curto](https://github.com/pierrec),
[Caleb-o](https://github.com/Caleb-o) and [devdad](https://github.com/devdad)
## Star History

View File

@@ -0,0 +1,94 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module non_crypto_benchmarks;
const usz COMMON_ITERATIONS = 1 << 18;
const char[] COMMON_1 = { 0xA5 };
const char[] COMMON_4 = { 0xA5, 0xA5, 0xA5, 0xA5, };
const char[] COMMON_8 = { [0..7] = 0xA5 };
const char[] COMMON_16 = { [0..15] = 0xA5 };
const char[] COMMON_32 = { [0..31] = 0xA5 };
const char[] COMMON_64 = { [0..63] = 0xA5 };
const char[] COMMON_128 = { [0..127] = 0xA5 };
const char[] COMMON_1024 = { [0..1023] = 0xA5 };
fn void initialize_bench() @init
{
set_benchmark_warmup_iterations(3);
set_benchmark_max_iterations(COMMON_ITERATIONS + 3);
}
// =======================================================================================
module non_crypto_benchmarks @benchmark;
import std::hash;
fn void fnv64a_1() => fnv64a::hash(COMMON_1);
fn void fnv32a_1() => fnv32a::hash(COMMON_1);
fn void wyhash2_1() => wyhash2::hash(COMMON_1);
fn void metro64_1() => metro64::hash(COMMON_1);
fn void metro128_1() => metro128::hash(COMMON_1);
fn void a5hash_1() => a5hash::hash(COMMON_1);
fn void komi_1() => komi::hash(COMMON_1);
fn void fnv64a_4() => fnv64a::hash(COMMON_4);
fn void fnv32a_4() => fnv32a::hash(COMMON_4);
fn void wyhash2_4() => wyhash2::hash(COMMON_4);
fn void metro64_4() => metro64::hash(COMMON_4);
fn void metro128_4() => metro128::hash(COMMON_4);
fn void a5hash_4() => a5hash::hash(COMMON_4);
fn void komi_4() => komi::hash(COMMON_4);
fn void fnv64a_8() => fnv64a::hash(COMMON_8);
fn void fnv32a_8() => fnv32a::hash(COMMON_8);
fn void wyhash2_8() => wyhash2::hash(COMMON_8);
fn void metro64_8() => metro64::hash(COMMON_8);
fn void metro128_8() => metro128::hash(COMMON_8);
fn void a5hash_8() => a5hash::hash(COMMON_8);
fn void komi_8() => komi::hash(COMMON_8);
fn void fnv64a_16() => fnv64a::hash(COMMON_16);
fn void fnv32a_16() => fnv32a::hash(COMMON_16);
fn void wyhash2_16() => wyhash2::hash(COMMON_16);
fn void metro64_16() => metro64::hash(COMMON_16);
fn void metro128_16() => metro128::hash(COMMON_16);
fn void a5hash_16() => a5hash::hash(COMMON_16);
fn void komi_16() => komi::hash(COMMON_16);
fn void fnv64a_32() => fnv64a::hash(COMMON_32);
fn void fnv32a_32() => fnv32a::hash(COMMON_32);
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
fn void metro64_32() => metro64::hash(COMMON_32);
fn void metro128_32() => metro128::hash(COMMON_32);
fn void a5hash_32() => a5hash::hash(COMMON_32);
fn void komi_32() => komi::hash(COMMON_32);
fn void fnv64a_64() => fnv64a::hash(COMMON_64);
fn void fnv32a_64() => fnv32a::hash(COMMON_64);
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
fn void metro64_64() => metro64::hash(COMMON_64);
fn void metro128_64() => metro128::hash(COMMON_64);
fn void a5hash_64() => a5hash::hash(COMMON_64);
fn void komi_64() => komi::hash(COMMON_64);
fn void fnv64a_128() => fnv64a::hash(COMMON_128);
fn void fnv32a_128() => fnv32a::hash(COMMON_128);
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
fn void metro64_128() => metro64::hash(COMMON_128);
fn void metro128_128() => metro128::hash(COMMON_128);
fn void a5hash_128() => a5hash::hash(COMMON_128);
fn void komi_128() => komi::hash(COMMON_128);
fn void fnv64a_1024() => fnv64a::hash(COMMON_1024);
fn void fnv32a_1024() => fnv32a::hash(COMMON_1024);
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
fn void metro64_1024() => metro64::hash(COMMON_1024);
fn void metro128_1024() => metro128::hash(COMMON_1024);
fn void a5hash_1024() => a5hash::hash(COMMON_1024);
fn void komi_1024() => komi::hash(COMMON_1024);

View File

@@ -91,7 +91,7 @@ macro Type Atomic.or(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT
return @atomic_exec(atomic::fetch_or, data, value, ordering);
}
fn Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
macro Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_xor, data, value, ordering);
@@ -171,6 +171,7 @@ macro bool is_native_atomic_type($Type)
$case SIGNED_INT:
$case UNSIGNED_INT:
$case POINTER:
$case FUNC:
$case FLOAT:
$case BOOL:
return true;
@@ -193,7 +194,7 @@ macro bool is_native_atomic_type($Type)
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr + y) : "+ must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
@@ -213,7 +214,7 @@ macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr - y) : "- must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
@@ -232,13 +233,13 @@ macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr * y) : "* must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$load_ordering = SEQ_CONSISTENT;
$endif
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
@@ -272,13 +273,13 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr * y) : "/ must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$load_ordering = SEQ_CONSISTENT;
$endif
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
@@ -313,7 +314,7 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr | y) : "| must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
@@ -330,7 +331,7 @@ macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr ^ y) : "^ must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
@@ -347,7 +348,7 @@ macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr ^ y) : "& must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
@@ -364,13 +365,13 @@ macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::is_int($typeof(*ptr)) : "Only integer pointers may be used."
@require types::is_int($typeof(y)) : "The value for shift right must be an integer"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$load_ordering = SEQ_CONSISTENT;
$endif
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
@@ -406,13 +407,13 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::is_int($typeof(*ptr)) : "Only integer pointers may be used."
@require types::is_int($typeof(y)) : "The value for shift left must be an integer"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$load_ordering = SEQ_CONSISTENT;
$endif
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
@@ -447,7 +448,7 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
@@ -455,7 +456,7 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
$typeof(*ptr) new_value = true;
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$load_ordering = SEQ_CONSISTENT;
$endif
do
{
@@ -474,7 +475,7 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
@@ -482,7 +483,7 @@ macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
$typeof(*ptr) new_value = false;
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$load_ordering = SEQ_CONSISTENT;
$endif
do
{
@@ -502,7 +503,7 @@ macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr > y) : "Only values that are comparable with > may be used"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
@@ -521,7 +522,7 @@ macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr > y) : "Only values that are comparable with > may be used"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_min(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{

View File

@@ -90,7 +90,7 @@ macro HashMap* HashMap.init_with_key_values(&self, Allocator allocator, ..., uin
*>
macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.tinit_with_key_values(tmem, capacity, load_factor);
return self.init_with_key_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
}
<*
@@ -195,7 +195,7 @@ fn Entry*? HashMap.get_entry(&map, Key key)
<*
Get the value or update and
@require $assignable(#expr, Value)
@require @assignable_to(#expr, Value)
*>
macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
{

View File

@@ -0,0 +1,636 @@
<*
@require $defined((Value){}.hash()) : `No .hash function found on the value`
*>
module std::collections::set {Value};
import std::math;
import std::io @norecurse;
const uint DEFAULT_INITIAL_CAPACITY = 16;
const uint MAXIMUM_CAPACITY = 1u << 31;
const float DEFAULT_LOAD_FACTOR = 0.75;
const Allocator SET_HEAP_ALLOCATOR = (Allocator)&dummy;
<* Copy the ONHEAP allocator to initialize to a set that is heap allocated *>
const HashSet ONHEAP = { .allocator = SET_HEAP_ALLOCATOR };
struct Entry
{
uint hash;
Value value;
Entry* next;
}
struct HashSet (Printable)
{
Entry*[] table;
Allocator allocator;
usz count; // Number of elements
usz threshold; // Resize limit
float load_factor;
}
fn int HashSet.len(&self) @operator(len) => (int) self.count;
<*
@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.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashSet* HashSet.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
self.threshold = (usz) (capacity * load_factor);
self.load_factor = load_factor;
self.table = allocator::new_array(allocator, Entry*, capacity);
return self;
}
<*
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashSet* HashSet.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init(tmem, capacity, load_factor) @inline;
}
<*
@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.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro HashSet* HashSet.init_with_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(allocator, capacity, load_factor);
$for var $i = 0; $i < $vacount; $i++:
self.add($vaarg[$i]);
$endfor
return self;
}
<*
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro HashSet* HashSet.tinit_with_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_with_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
}
<*
@param [in] values : "The values for the HashSet"
@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.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashSet* HashSet.init_from_values(&self, Allocator allocator, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(allocator, capacity, load_factor);
foreach (v : values) self.add(v);
return self;
}
<*
@param [in] values : "The values for the HashSet entries"
@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.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashSet* HashSet.tinit_from_values(&self, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_from_values(tmem, values, capacity, load_factor);
}
<*
Has this hash set been initialized yet?
@param [&in] set : "The hash set we are testing"
@return "Returns true if it has been initialized, false otherwise"
*>
fn bool HashSet.is_initialized(&set)
{
return set.allocator && set.allocator.ptr != &dummy;
}
<*
@param [&inout] allocator : "The allocator to use"
@param [&in] other_set : "The set to copy from."
@require !self.is_initialized() : "Set was already initialized"
*>
fn HashSet* HashSet.init_from_set(&self, Allocator allocator, HashSet* other_set)
{
self.init(allocator, other_set.table.len, other_set.load_factor);
self.put_all_for_create(other_set);
return self;
}
<*
@param [&in] other_set : "The set to copy from."
@require !set.is_initialized() : "Set was already initialized"
*>
fn HashSet* HashSet.tinit_from_set(&set, HashSet* other_set)
{
return set.init_from_set(tmem, other_set) @inline;
}
<*
Check if the set is empty
@return "true if it is empty"
@pure
*>
fn bool HashSet.is_empty(&set) @inline
{
return !set.count;
}
<*
Add all elements in the slice to the set.
@param [in] list
@return "The number of new elements added"
@ensure total <= list.len
*>
fn usz HashSet.add_all(&set, Value[] list)
{
usz total;
foreach (v : list)
{
if (set.add(v)) total++;
}
return total;
}
<*
@param [&in] other
@return "The number of new elements added"
@ensure return <= other.count
*>
fn usz HashSet.add_all_from(&set, HashSet* other)
{
usz total;
other.@each(;Value value)
{
if (set.add(value)) total++;
};
return total;
}
<*
@param value : "The value to add"
@return "true if the value didn't exist in the set"
*>
fn bool HashSet.add(&set, Value value)
{
// If the set isn't initialized, use the defaults to initialize it.
switch (set.allocator.ptr)
{
case &dummy:
set.init(mem);
case null:
set.tinit();
default:
break;
}
uint hash = rehash(value.hash());
uint index = index_for(hash, set.table.len);
for (Entry *e = set.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value)) return false;
}
set.add_entry(hash, value, index);
return true;
}
<*
Iterate over all the values in the set
*>
macro HashSet.@each(set; @body(value))
{
if (!set.count) return;
foreach (Entry* entry : set.table)
{
while (entry)
{
@body(entry.value);
entry = entry.next;
}
}
}
<*
Check if the set contains the given value.
@param value : "The value to check"
@return "true if it exists in the set"
*>
fn bool HashSet.contains(&set, Value value)
{
if (!set.count) return false;
uint hash = rehash(value.hash());
for (Entry *e = set.table[index_for(hash, set.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value)) return true;
}
return false;
}
<*
Remove a single value from the set.
@param value : "The value to remove"
@return? NOT_FOUND : "If the entry is not found"
*>
fn void? HashSet.remove(&set, Value value) @maydiscard
{
if (!set.remove_entry_for_value(value)) return NOT_FOUND?;
}
fn usz HashSet.remove_all(&set, Value[] values)
{
usz total;
foreach (v : values)
{
if (set.remove_entry_for_value(v)) total++;
}
return total;
}
<*
@param [&in] other : "Other set"
*>
fn usz HashSet.remove_all_from(&set, HashSet* other)
{
usz total;
other.@each(;Value val)
{
if (set.remove_entry_for_value(val)) total++;
};
return total;
}
<*
Free all memory allocated by the hash set.
*>
fn void HashSet.free(&set)
{
if (!set.is_initialized()) return;
set.clear();
set.free_internal(set.table.ptr);
*set = {};
}
<*
Clear all elements from the set while keeping the underlying storage
@ensure set.count == 0
*>
fn void HashSet.clear(&set)
{
if (!set.count) return;
foreach (Entry** &entry_ref : set.table)
{
Entry* entry = *entry_ref;
if (!entry) continue;
Entry *next = entry.next;
while (next)
{
Entry *to_delete = next;
next = next.next;
set.free_entry(to_delete);
}
set.free_entry(entry);
*entry_ref = null;
}
set.count = 0;
}
fn void HashSet.reserve(&set, usz capacity)
{
if (capacity > set.threshold)
{
set.resize(math::next_power_of_2(capacity));
}
}
// --- Set Operations ---
<*
Returns the union of two sets (A | B)
@param [&in] other : "The other set to union with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing the union of both sets"
*>
fn HashSet HashSet.set_union(&self, Allocator allocator, HashSet* other)
{
usz new_capacity = math::next_power_of_2(self.count + other.count);
HashSet result;
result.init(allocator, new_capacity, self.load_factor);
result.add_all_from(self);
result.add_all_from(other);
return result;
}
fn HashSet HashSet.tset_union(&self, HashSet* other) => self.set_union(tmem, other);
<*
Returns the intersection of the two sets (A & B)
@param [&in] other : "The other set to intersect with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing the intersection of both sets"
*>
fn HashSet HashSet.intersection(&self, Allocator allocator, HashSet* other)
{
HashSet result;
result.init(allocator, math::min(self.table.len, other.table.len), self.load_factor);
// Iterate through the smaller set for efficiency
HashSet* smaller = self.count <= other.count ? self : other;
HashSet* larger = self.count > other.count ? self : other;
smaller.@each(;Value value)
{
if (larger.contains(value)) result.add(value);
};
return result;
}
fn HashSet HashSet.tintersection(&self, HashSet* other) => self.intersection(tmem, other);
<*
Return this set - other, so (A & ~B)
@param [&in] other : "The other set to compare with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing elements in this set but not in the other"
*>
fn HashSet HashSet.difference(&self, Allocator allocator, HashSet* other)
{
HashSet result;
result.init(allocator, self.table.len, self.load_factor);
self.@each(;Value value)
{
if (!other.contains(value))
{
result.add(value);
}
};
return result;
}
fn HashSet HashSet.tdifference(&self, HashSet* other) => self.difference(tmem, other) @inline;
<*
Return (A ^ B)
@param [&in] other : "The other set to compare with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing elements in this set or the other, but not both"
*>
fn HashSet HashSet.symmetric_difference(&self, Allocator allocator, HashSet* other)
{
HashSet result;
result.init(allocator, self.table.len, self.load_factor);
result.add_all_from(self);
other.@each(;Value value)
{
if (!result.add(value))
{
result.remove(value);
}
};
return result;
}
fn HashSet HashSet.tsymmetric_difference(&self, HashSet* other) => self.symmetric_difference(tmem, other) @inline;
<*
Check if this hash set is a subset of another set.
@param [&in] other : "The other set to check against"
@return "True if all elements of this set are in the other set"
*>
fn bool HashSet.is_subset(&self, HashSet* other)
{
if (self.count == 0) return true;
if (self.count > other.count) return false;
self.@each(;Value value)
{
if (!other.contains(value)) return false;
};
return true;
}
// --- private methods
fn void HashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
{
Entry* entry = allocator::new(set.allocator, Entry, { .hash = hash, .value = value, .next = set.table[bucket_index] });
set.table[bucket_index] = entry;
if (set.count++ >= set.threshold)
{
set.resize(set.table.len * 2);
}
}
fn void HashSet.resize(&self, usz new_capacity) @private
{
Entry*[] old_table = self.table;
usz old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
self.threshold = uint.max;
return;
}
Entry*[] new_table = allocator::new_array(self.allocator, Entry*, new_capacity);
self.transfer(new_table);
self.table = new_table;
self.free_internal(old_table.ptr);
self.threshold = (uint)(new_capacity * self.load_factor);
}
fn usz? HashSet.to_format(&self, Formatter* f) @dynamic
{
usz len;
len += f.print("{ ")!;
self.@each(; Value value)
{
if (len > 2) len += f.print(", ")!;
len += f.printf("%s", value)!;
};
return len + f.print(" }");
}
fn void HashSet.transfer(&self, Entry*[] new_table) @private
{
Entry*[] src = self.table;
uint new_capacity = new_table.len;
foreach (uint j, Entry *e : src)
{
if (!e) continue;
do
{
Entry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
fn void HashSet.put_all_for_create(&set, HashSet* other_set) @private
{
if (!other_set.count) return;
foreach (Entry *e : other_set.table)
{
while (e)
{
set.put_for_create(e.value);
e = e.next;
}
}
}
fn void HashSet.put_for_create(&set, Value value) @private
{
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
for (Entry *e = set.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value))
{
// Value already exists, no need to do anything
return;
}
}
set.create_entry(hash, value, i);
}
fn void HashSet.free_internal(&self, void* ptr) @inline @private
{
allocator::free(self.allocator, ptr);
}
fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
{
Entry* entry = allocator::new(set.allocator, Entry, {
.hash = hash,
.value = value,
.next = set.table[bucket_index]
});
set.table[bucket_index] = entry;
set.count++;
}
<*
Removes the entry for the specified value if present
@return "true if found and removed, false otherwise"
*>
fn bool HashSet.remove_entry_for_value(&set, Value value) @private
{
if (!set.count) return false;
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
Entry* prev = set.table[i];
Entry* e = prev;
while (e)
{
Entry *next = e.next;
if (e.hash == hash && equals(value, e.value))
{
set.count--;
if (prev == e)
{
set.table[i] = next;
}
else
{
prev.next = next;
}
set.free_entry(e);
return true;
}
prev = e;
e = next;
}
return false;
}
fn void HashSet.free_entry(&set, Entry *entry) @private
{
allocator::free(set.allocator, entry);
}
struct HashSetIterator
{
HashSet* set;
usz bucket_index;
Entry* current;
}
fn HashSetIterator HashSet.iter(&set) => { .set = set, .bucket_index = 0, .current = null };
fn Value? HashSetIterator.next(&self)
{
if (self.current)
{
Value value = self.current.value;
self.current = self.current.next;
return value;
}
while (self.bucket_index < self.set.table.len)
{
self.current = self.set.table[self.bucket_index++];
if (self.current)
{
Value value = self.current.value;
self.current = self.current.next;
return value;
}
}
return NOT_FOUND?;
}
fn usz HashSetIterator.len(&self) @operator(len)
{
return self.set.count;
}
<* @pure *>
fn uint rehash(uint hash) @inline @private
{
hash ^= (hash >> 20) ^ (hash >> 12);
return hash ^ ((hash >> 7) ^ (hash >> 4));
}
macro uint index_for(uint hash, uint capacity) @private => hash & (capacity - 1);
int dummy @local;

View File

@@ -0,0 +1,645 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
<*
@require $defined((Key){}.hash()) : `No .hash function found on the key`
*>
module std::collections::map{Key, Value};
import std::math;
import std::io @norecurse;
const LinkedHashMap LINKEDONHEAP = { .allocator = MAP_HEAP_ALLOCATOR };
struct LinkedEntry
{
uint hash;
Key key;
Value value;
LinkedEntry* next; // For bucket chain
LinkedEntry* before; // Previous in insertion order
LinkedEntry* after; // Next in insertion order
}
struct LinkedHashMap (Printable)
{
LinkedEntry*[] table;
Allocator allocator;
usz count;
usz threshold;
float load_factor;
LinkedEntry* head; // First inserted LinkedEntry
LinkedEntry* tail; // Last inserted LinkedEntry
}
<*
@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.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashMap* LinkedHashMap.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
self.load_factor = load_factor;
self.threshold = (usz)(capacity * load_factor);
self.table = allocator::new_array(allocator, LinkedEntry*, capacity);
self.head = null;
self.tail = null;
return self;
}
<*
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashMap* LinkedHashMap.tinit(&self, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init(tmem, 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.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro LinkedHashMap* LinkedHashMap.init_with_key_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(allocator, capacity, load_factor);
$for var $i = 0; $i < $vacount; $i += 2:
self.set($vaarg[$i], $vaarg[$i + 1]);
$endfor
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.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro LinkedHashMap* LinkedHashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_with_key_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
}
<*
@param [in] keys : "The keys for the LinkedHashMap entries"
@param [in] values : "The values for the LinkedHashMap 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.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashMap* LinkedHashMap.init_from_keys_and_values(&self, Allocator allocator, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
assert(keys.len == values.len);
self.init(allocator, capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
}
<*
@param [in] keys : "The keys for the LinkedHashMap entries"
@param [in] values : "The values for the LinkedHashMap entries"
@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.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashMap* LinkedHashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_from_keys_and_values(tmem, keys, values, capacity, load_factor);
}
<*
Has this hash map been initialized yet?
@param [&in] map : "The hash map we are testing"
@return "Returns true if it has been initialized, false otherwise"
*>
fn bool LinkedHashMap.is_initialized(&map)
{
return map.allocator && map.allocator.ptr != &dummy;
}
<*
@param [&inout] allocator : "The allocator to use"
@param [&in] other_map : "The map to copy from."
@require !self.is_initialized() : "Map was already initialized"
*>
fn LinkedHashMap* LinkedHashMap.init_from_map(&self, Allocator allocator, LinkedHashMap* other_map)
{
self.init(allocator, other_map.table.len, other_map.load_factor);
self.put_all_for_create(other_map);
return self;
}
<*
@param [&in] other_map : "The map to copy from."
@require !map.is_initialized() : "Map was already initialized"
*>
fn LinkedHashMap* LinkedHashMap.tinit_from_map(&map, LinkedHashMap* other_map)
{
return map.init_from_map(tmem, other_map) @inline;
}
fn bool LinkedHashMap.is_empty(&map) @inline
{
return !map.count;
}
fn usz LinkedHashMap.len(&map) @inline => map.count;
fn Value*? LinkedHashMap.get_ref(&map, Key key)
{
if (!map.count) return NOT_FOUND?;
uint hash = rehash(key.hash());
for (LinkedEntry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return NOT_FOUND?;
}
fn LinkedEntry*? LinkedHashMap.get_entry(&map, Key key)
{
if (!map.count) return NOT_FOUND?;
uint hash = rehash(key.hash());
for (LinkedEntry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return NOT_FOUND?;
}
<*
Get the value or update and
@require @assignable_to(#expr, Value)
*>
macro Value LinkedHashMap.@get_or_set(&map, Key key, Value #expr)
{
if (!map.count)
{
Value val = #expr;
map.set(key, val);
return val;
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (LinkedEntry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e.value;
}
Value val = #expr;
map.add_entry(hash, key, val, index);
return val;
}
fn Value? LinkedHashMap.get(&map, Key key) @operator([]) => *map.get_ref(key) @inline;
fn bool LinkedHashMap.has_key(&map, Key key) => @ok(map.get_ref(key));
fn bool LinkedHashMap.set(&map, Key key, Value value) @operator([]=)
{
// If the map isn't initialized, use the defaults to initialize it.
switch (map.allocator.ptr)
{
case &dummy:
map.init(mem);
case null:
map.tinit();
default:
break;
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (LinkedEntry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return true;
}
}
map.add_entry(hash, key, value, index);
return false;
}
fn void? LinkedHashMap.remove(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return NOT_FOUND?;
}
fn void LinkedHashMap.clear(&map)
{
if (!map.count) return;
LinkedEntry* entry = map.head;
while (entry)
{
LinkedEntry* next = entry.after;
map.free_entry(entry);
entry = next;
}
foreach (LinkedEntry** &bucket : map.table)
{
*bucket = null;
}
map.count = 0;
map.head = null;
map.tail = null;
}
fn void LinkedHashMap.free(&map)
{
if (!map.is_initialized()) return;
map.clear();
map.free_internal(map.table.ptr);
map.table = {};
}
fn Key[] LinkedHashMap.tkeys(&self)
{
return self.keys(tmem) @inline;
}
fn Key[] LinkedHashMap.keys(&self, Allocator allocator)
{
if (!self.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, self.count);
usz index = 0;
LinkedEntry* entry = self.head;
while (entry)
{
$if COPY_KEYS:
list[index++] = entry.key.copy(allocator);
$else
list[index++] = entry.key;
$endif
entry = entry.after;
}
return list;
}
macro LinkedHashMap.@each(map; @body(key, value))
{
map.@each_entry(; LinkedEntry* entry)
{
@body(entry.key, entry.value);
};
}
macro LinkedHashMap.@each_entry(map; @body(entry))
{
LinkedEntry* entry = map.head;
while (entry)
{
@body(entry);
entry = entry.after;
}
}
fn Value[] LinkedHashMap.tvalues(&map) => map.values(tmem) @inline;
fn Value[] LinkedHashMap.values(&self, Allocator allocator)
{
if (!self.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, self.count);
usz index = 0;
LinkedEntry* entry = self.head;
while (entry)
{
list[index++] = entry.value;
entry = entry.after;
}
return list;
}
fn bool LinkedHashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
{
if (!map.count) return false;
LinkedEntry* entry = map.head;
while (entry)
{
if (equals(v, entry.value)) return true;
entry = entry.after;
}
return false;
}
fn LinkedHashMapIterator LinkedHashMap.iter(&self) => { .map = self, .current = self.head, .started = false };
fn LinkedHashMapValueIterator LinkedHashMap.value_iter(&self) => { .map = self, .current = self.head, .started = false };
fn LinkedHashMapKeyIterator LinkedHashMap.key_iter(&self) => { .map = self, .current = self.head, .started = false };
fn bool LinkedHashMapIterator.next(&self)
{
if (!self.started)
{
self.current = self.map.head;
self.started = true;
}
else if (self.current)
{
self.current = self.current.after;
}
return self.current != null;
}
fn LinkedEntry*? LinkedHashMapIterator.get(&self)
{
return self.current ? self.current : NOT_FOUND?;
}
fn Value*? LinkedHashMapValueIterator.get(&self)
{
return self.current ? &self.current.value : NOT_FOUND?;
}
fn Key*? LinkedHashMapKeyIterator.get(&self)
{
return self.current ? &self.current.key : NOT_FOUND?;
}
fn bool LinkedHashMapIterator.has_next(&self)
{
if (!self.started) return self.map.head != null;
return self.current && self.current.after != null;
}
// --- private methods
fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, {
.hash = hash,
.key = key,
.value = value,
.next = map.table[bucket_index],
.before = map.tail,
.after = null
});
// Update bucket chain
map.table[bucket_index] = entry;
// Update linked list
if (map.tail)
{
map.tail.after = entry;
entry.before = map.tail;
}
else
{
map.head = entry;
}
map.tail = entry;
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
}
}
fn void LinkedHashMap.resize(&map, uint new_capacity) @private
{
LinkedEntry*[] old_table = map.table;
uint old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
map.threshold = uint.max;
return;
}
LinkedEntry*[] new_table = allocator::new_array(map.allocator, LinkedEntry*, new_capacity);
map.table = new_table;
map.threshold = (uint)(new_capacity * map.load_factor);
// Rehash all entries - linked list order remains unchanged
foreach (uint i, LinkedEntry *e : old_table)
{
if (!e) continue;
// Split the bucket chain into two chains based on new bit
LinkedEntry* lo_head = null;
LinkedEntry* lo_tail = null;
LinkedEntry* hi_head = null;
LinkedEntry* hi_tail = null;
do
{
LinkedEntry* next = e.next;
if ((e.hash & old_capacity) == 0)
{
if (!lo_tail)
{
lo_head = e;
}
else
{
lo_tail.next = e;
}
lo_tail = e;
}
else
{
if (!hi_tail)
{
hi_head = e;
}
else
{
hi_tail.next = e;
}
hi_tail = e;
}
e.next = null;
e = next;
}
while (e);
if (lo_tail)
{
lo_tail.next = null;
new_table[i] = lo_head;
}
if (hi_tail)
{
hi_tail.next = null;
new_table[i + old_capacity] = hi_head;
}
}
map.free_internal(old_table.ptr);
}
fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
{
usz len;
len += f.print("{ ")!;
self.@each_entry(; LinkedEntry* entry)
{
if (len > 2) len += f.print(", ")!;
len += f.printf("%s: %s", entry.key, entry.value)!;
};
return len + f.print(" }");
}
fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
{
LinkedEntry*[] src = map.table;
uint new_capacity = new_table.len;
foreach (uint j, LinkedEntry *e : src)
{
if (!e) continue;
do
{
LinkedEntry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @private
{
if (!other_map.count) return;
other_map.@each(; Key key, Value value) {
map.set(key, value);
};
}
fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
for (LinkedEntry *e = map.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return;
}
}
map.create_entry(hash, key, value, i);
}
fn void LinkedHashMap.free_internal(&map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
{
if (!map.count) return false;
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
LinkedEntry* prev = null;
LinkedEntry* e = map.table[i];
while (e)
{
if (e.hash == hash && equals(key, e.key))
{
if (prev)
{
prev.next = e.next;
}
else
{
map.table[i] = e.next;
}
if (e.before)
{
e.before.after = e.after;
}
else
{
map.head = e.after;
}
if (e.after)
{
e.after.before = e.before;
}
else
{
map.tail = e.before;
}
map.count--;
map.free_entry(e);
return true;
}
prev = e;
e = e.next;
}
return false;
}
fn void LinkedHashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
{
LinkedEntry *e = map.table[bucket_index];
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
map.count++;
}
fn void LinkedHashMap.free_entry(&self, LinkedEntry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
$endif
self.free_internal(entry);
}
struct LinkedHashMapIterator
{
LinkedHashMap* map;
LinkedEntry* current;
bool started;
}
typedef LinkedHashMapValueIterator = inline LinkedHashMapIterator;
typedef LinkedHashMapKeyIterator = inline LinkedHashMapIterator;
fn usz LinkedHashMapValueIterator.len(self) @operator(len) => self.map.count;
fn usz LinkedHashMapKeyIterator.len(self) @operator(len) => self.map.count;
fn usz LinkedHashMapIterator.len(self) @operator(len) => self.map.count;
int dummy @local;

View File

@@ -0,0 +1,723 @@
<*
@require $defined((Value){}.hash()) : `No .hash function found on the value`
*>
module std::collections::set {Value};
import std::math;
import std::io @norecurse;
const LinkedHashSet LINKEDONHEAP = { .allocator = SET_HEAP_ALLOCATOR };
struct LinkedEntry
{
uint hash;
Value value;
LinkedEntry* next; // For bucket chain
LinkedEntry* before; // Previous in insertion order
LinkedEntry* after; // Next in insertion order
}
struct LinkedHashSet (Printable)
{
LinkedEntry*[] table;
Allocator allocator;
usz count; // Number of elements
usz threshold; // Resize limit
float load_factor;
LinkedEntry* head; // First inserted LinkedEntry
LinkedEntry* tail; // Last inserted LinkedEntry
}
fn int LinkedHashSet.len(&self) @operator(len) => (int) self.count;
<*
@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.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashSet* LinkedHashSet.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
self.threshold = (usz)(capacity * load_factor);
self.load_factor = load_factor;
self.table = allocator::new_array(allocator, LinkedEntry*, capacity);
self.head = null;
self.tail = null;
return self;
}
<*
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashSet* LinkedHashSet.tinit(&self, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init(tmem, capacity, load_factor) @inline;
}
<*
@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.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro LinkedHashSet* LinkedHashSet.init_with_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(allocator, capacity, load_factor);
$for var $i = 0; $i < $vacount; $i++:
self.add($vaarg[$i]);
$endfor
return self;
}
<*
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro LinkedHashSet* LinkedHashSet.tinit_with_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_with_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
}
<*
@param [in] values : "The values for the LinkedHashSet"
@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.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashSet* LinkedHashSet.init_from_values(&self, Allocator allocator, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(allocator, capacity, load_factor);
foreach (v : values) self.add(v);
return self;
}
<*
@param [in] values : "The values for the LinkedHashSet entries"
@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.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashSet* LinkedHashSet.tinit_from_values(&self, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_from_values(tmem, values, capacity, load_factor);
}
<*
Has this linked hash set been initialized yet?
@param [&in] set : "The linked hash set we are testing"
@return "Returns true if it has been initialized, false otherwise"
*>
fn bool LinkedHashSet.is_initialized(&set)
{
return set.allocator && set.allocator.ptr != &dummy;
}
<*
@param [&inout] allocator : "The allocator to use"
@param [&in] other_set : "The set to copy from."
@require !self.is_initialized() : "Set was already initialized"
*>
fn LinkedHashSet* LinkedHashSet.init_from_set(&self, Allocator allocator, LinkedHashSet* other_set)
{
self.init(allocator, other_set.table.len, other_set.load_factor);
LinkedEntry* entry = other_set.head;
while (entry) // Save insertion order
{
self.put_for_create(entry.value);
entry = entry.after;
}
return self;
}
<*
@param [&in] other_set : "The set to copy from."
@require !set.is_initialized() : "Set was already initialized"
*>
fn LinkedHashSet* LinkedHashSet.tinit_from_set(&set, LinkedHashSet* other_set)
{
return set.init_from_set(tmem, other_set) @inline;
}
<*
Check if the set is empty
@return "true if it is empty"
@pure
*>
fn bool LinkedHashSet.is_empty(&set) @inline
{
return !set.count;
}
<*
Add all elements in the slice to the set.
@param [in] list
@return "The number of new elements added"
@ensure total <= list.len
*>
fn usz LinkedHashSet.add_all(&set, Value[] list)
{
usz total;
foreach (v : list)
{
if (set.add(v)) total++;
}
return total;
}
<*
@param [&in] other
@return "The number of new elements added"
@ensure return <= other.count
*>
fn usz LinkedHashSet.add_all_from(&set, LinkedHashSet* other)
{
usz total;
other.@each(;Value value)
{
if (set.add(value)) total++;
};
return total;
}
<*
@param value : "The value to add"
@return "true if the value didn't exist in the set"
*>
fn bool LinkedHashSet.add(&set, Value value)
{
// If the set isn't initialized, use the defaults to initialize it.
switch (set.allocator.ptr)
{
case &dummy:
set.init(mem);
case null:
set.tinit();
default:
break;
}
uint hash = rehash(value.hash());
uint index = index_for(hash, set.table.len);
for (LinkedEntry *e = set.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value)) return false;
}
set.add_entry(hash, value, index);
return true;
}
<*
Iterate over all the values in the set
*>
macro LinkedHashSet.@each(set; @body(value))
{
if (!set.count) return;
LinkedEntry* entry = set.head;
while (entry)
{
@body(entry.value);
entry = entry.after;
}
}
<*
Check if the set contains the given value.
@param value : "The value to check"
@return "true if it exists in the set"
*>
fn bool LinkedHashSet.contains(&set, Value value)
{
if (!set.count) return false;
uint hash = rehash(value.hash());
for (LinkedEntry *e = set.table[index_for(hash, set.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value)) return true;
}
return false;
}
<*
Remove a single value from the set.
@param value : "The value to remove"
@return? NOT_FOUND : "If the entry is not found"
*>
fn void? LinkedHashSet.remove(&set, Value value) @maydiscard
{
if (!set.remove_entry_for_value(value)) return NOT_FOUND?;
}
fn usz LinkedHashSet.remove_all(&set, Value[] values)
{
usz total;
foreach (v : values)
{
if (set.remove_entry_for_value(v)) total++;
}
return total;
}
<*
@param [&in] other : "Other set"
*>
fn usz LinkedHashSet.remove_all_from(&set, LinkedHashSet* other)
{
usz total;
other.@each(;Value val)
{
if (set.remove_entry_for_value(val)) total++;
};
return total;
}
<*
Free all memory allocated by the hash set.
*>
fn void LinkedHashSet.free(&set)
{
if (!set.is_initialized()) return;
set.clear();
set.free_internal(set.table.ptr);
set.table = {};
}
<*
Clear all elements from the set while keeping the underlying storage
@ensure set.count == 0
*>
fn void LinkedHashSet.clear(&set)
{
if (!set.count) return;
LinkedEntry* entry = set.head;
while (entry)
{
LinkedEntry* next = entry.after;
set.free_entry(entry);
entry = next;
}
foreach (LinkedEntry** &bucket : set.table)
{
*bucket = null;
}
set.count = 0;
set.head = null;
set.tail = null;
}
fn void LinkedHashSet.reserve(&set, usz capacity)
{
if (capacity > set.threshold)
{
set.resize(math::next_power_of_2(capacity));
}
}
// --- Set Operations ---
<*
Returns the union of two sets (A | B)
@param [&in] other : "The other set to union with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing the union of both sets"
*>
fn LinkedHashSet LinkedHashSet.set_union(&self, Allocator allocator, LinkedHashSet* other)
{
usz new_capacity = math::next_power_of_2(self.count + other.count);
LinkedHashSet result;
result.init(allocator, new_capacity, self.load_factor);
result.add_all_from(self);
result.add_all_from(other);
return result;
}
fn LinkedHashSet LinkedHashSet.tset_union(&self, LinkedHashSet* other) => self.set_union(tmem, other) @inline;
<*
Returns the intersection of the two sets (A & B)
@param [&in] other : "The other set to intersect with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing the intersection of both sets"
*>
fn LinkedHashSet LinkedHashSet.intersection(&self, Allocator allocator, LinkedHashSet* other)
{
LinkedHashSet result;
result.init(allocator, math::min(self.table.len, other.table.len), self.load_factor);
// Iterate through the smaller set for efficiency
LinkedHashSet* smaller = self.count <= other.count ? self : other;
LinkedHashSet* larger = self.count > other.count ? self : other;
smaller.@each(;Value value)
{
if (larger.contains(value)) result.add(value);
};
return result;
}
fn LinkedHashSet LinkedHashSet.tintersection(&self, LinkedHashSet* other) => self.intersection(tmem, other) @inline;
<*
Return this set - other, so (A & ~B)
@param [&in] other : "The other set to compare with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing elements in this set but not in the other"
*>
fn LinkedHashSet LinkedHashSet.difference(&self, Allocator allocator, LinkedHashSet* other)
{
LinkedHashSet result;
result.init(allocator, self.table.len, self.load_factor);
self.@each(;Value value)
{
if (!other.contains(value))
{
result.add(value);
}
};
return result;
}
fn LinkedHashSet LinkedHashSet.tdifference(&self, LinkedHashSet* other) => self.difference(tmem, other) @inline;
<*
Return (A ^ B)
@param [&in] other : "The other set to compare with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing elements in this set or the other, but not both"
*>
fn LinkedHashSet LinkedHashSet.symmetric_difference(&self, Allocator allocator, LinkedHashSet* other)
{
LinkedHashSet result;
result.init(allocator, self.table.len, self.load_factor);
result.add_all_from(self);
other.@each(;Value value)
{
if (!result.add(value))
{
result.remove(value);
}
};
return result;
}
fn LinkedHashSet LinkedHashSet.tsymmetric_difference(&self, LinkedHashSet* other) => self.symmetric_difference(tmem, other) @inline;
<*
Check if this hash set is a subset of another set.
@param [&in] other : "The other set to check against"
@return "True if all elements of this set are in the other set"
*>
fn bool LinkedHashSet.is_subset(&self, LinkedHashSet* other)
{
if (self.count == 0) return true;
if (self.count > other.count) return false;
self.@each(; Value value) {
if (!other.contains(value)) return false;
};
return true;
}
// --- private methods
fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
{
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
.hash = hash,
.value = value,
.next = set.table[bucket_index],
.before = set.tail,
.after = null
});
// Update bucket chain
set.table[bucket_index] = entry;
// Update linked list
if (set.tail)
{
set.tail.after = entry;
entry.before = set.tail;
}
else
{
set.head = entry;
}
set.tail = entry;
if (set.count++ >= set.threshold)
{
set.resize(set.table.len * 2);
}
}
fn void LinkedHashSet.resize(&set, usz new_capacity) @private
{
LinkedEntry*[] old_table = set.table;
usz old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
set.threshold = uint.max;
return;
}
LinkedEntry*[] new_table = allocator::new_array(set.allocator, LinkedEntry*, new_capacity);
set.table = new_table;
set.threshold = (uint)(new_capacity * set.load_factor);
// Rehash all entries - linked list order remains unchanged
foreach (uint i, LinkedEntry *e : old_table)
{
if (!e) continue;
// Split the bucket chain into two chains based on new bit
LinkedEntry* lo_head = null;
LinkedEntry* lo_tail = null;
LinkedEntry* hi_head = null;
LinkedEntry* hi_tail = null;
do
{
LinkedEntry* next = e.next;
if ((e.hash & old_capacity) == 0)
{
if (!lo_tail)
{
lo_head = e;
}
else
{
lo_tail.next = e;
}
lo_tail = e;
}
else
{
if (!hi_tail)
{
hi_head = e;
}
else
{
hi_tail.next = e;
}
hi_tail = e;
}
e.next = null;
e = next;
}
while (e);
if (lo_tail)
{
lo_tail.next = null;
new_table[i] = lo_head;
}
if (hi_tail)
{
hi_tail.next = null;
new_table[i + old_capacity] = hi_head;
}
}
set.free_internal(old_table.ptr);
}
fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
{
usz len;
len += f.print("{ ")!;
self.@each(; Value value)
{
if (len > 2) len += f.print(", ")!;
len += f.printf("%s", value)!;
};
return len + f.print(" }");
}
fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
{
LinkedEntry*[] src = set.table;
uint new_capacity = new_table.len;
foreach (uint j, LinkedEntry *e : src)
{
if (!e) continue;
do
{
LinkedEntry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
fn void LinkedHashSet.put_for_create(&set, Value value) @private
{
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
for (LinkedEntry *e = set.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value))
{
// Value already exists, no need to do anything
return;
}
}
set.create_entry(hash, value, i);
}
fn void LinkedHashSet.free_internal(&set, void* ptr) @inline @private
{
allocator::free(set.allocator, ptr);
}
fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
{
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
.hash = hash,
.value = value,
.next = set.table[bucket_index],
.before = set.tail,
.after = null
});
set.table[bucket_index] = entry;
// Update linked list
if (set.tail)
{
set.tail.after = entry;
entry.before = set.tail;
}
else
{
set.head = entry;
}
set.tail = entry;
set.count++;
}
fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
{
if (!set.count) return false;
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
LinkedEntry* prev = null;
LinkedEntry* e = set.table[i];
while (e)
{
if (e.hash == hash && equals(value, e.value))
{
if (prev)
{
prev.next = e.next;
}
else
{
set.table[i] = e.next;
}
if (e.before)
{
e.before.after = e.after;
}
else
{
set.head = e.after;
}
if (e.after)
{
e.after.before = e.before;
}
else
{
set.tail = e.before;
}
set.count--;
set.free_entry(e);
return true;
}
prev = e;
e = e.next;
}
return false;
}
fn void LinkedHashSet.free_entry(&set, LinkedEntry *entry) @private
{
allocator::free(set.allocator, entry);
}
struct LinkedHashSetIterator
{
LinkedHashSet* set;
LinkedEntry* current;
bool started;
}
fn LinkedHashSetIterator LinkedHashSet.iter(&set) => { .set = set, .current = set.head, .started = false };
fn bool LinkedHashSetIterator.next(&self)
{
if (!self.started)
{
self.current = self.set.head;
self.started = true;
}
else if (self.current)
{
self.current = self.current.after;
}
return self.current != null;
}
fn Value*? LinkedHashSetIterator.get(&self)
{
return self.current ? &self.current.value : NOT_FOUND?;
}
fn bool LinkedHashSetIterator.has_next(&self)
{
if (!self.started) return self.set.head != null;
return self.current && self.current.after != null;
}
fn usz LinkedHashSetIterator.len(&self) @operator(len)
{
return self.set.count;
}
int dummy @local;

View File

@@ -178,11 +178,8 @@ fn void Object.init_array_if_needed(&self) @private
fn void Object.set_object(&self, String key, Object* new_object) @private
{
self.init_map_if_needed();
ObjectInternalMapEntry*? entry = self.map.get_entry(key);
defer
{
(void)entry.value.free();
}
Object*? val = self.map.get_entry(key).value;
defer (void)val.free();
self.map.set(key, new_object);
}
@@ -206,7 +203,7 @@ macro Object* Object.object_from_value(&self, value) @private
return value;
$case $Type.typeid == void*.typeid:
return &NULL_OBJECT;
$case $assignable(value, String):
$case @assignable_to(value, String):
return new_string(value, self.allocator);
$default:
$error "Unsupported object type.";

View File

@@ -1,11 +1,23 @@
module std::collections::tuple{Type1, Type2};
module std::collections::pair{Type1, Type2};
struct Tuple
struct Pair
{
Type1 first;
Type2 second;
}
<*
@param [&out] a
@param [&out] b
@require @assignable_to(self.first, $typeof(*a)) : "You cannot assign the first value to a"
@require @assignable_to(self.second, $typeof(*b)) : "You cannot assign the second value to b"
*>
macro void Pair.unpack(&self, a, b)
{
*a = self.first;
*b = self.second;
}
module std::collections::triple{Type1, Type2, Type3};
struct Triple
@@ -13,4 +25,27 @@ struct Triple
Type1 first;
Type2 second;
Type3 third;
}
}
<*
@param [&out] a
@param [&out] b
@param [&out] c
@require @assignable_to(self.first, $typeof(*a)) : "You cannot assign the first value to a"
@require @assignable_to(self.second, $typeof(*b)) : "You cannot assign the second value to b"
@require @assignable_to(self.third, $typeof(*c)) : "You cannot assign the second value to c"
*>
macro void Triple.unpack(&self, a, b, c)
{
*a = self.first;
*b = self.second;
*c = self.third;
}
module std::collections::tuple{Type1, Type2};
struct Tuple @deprecated("Use 'Pair' instead")
{
Type1 first;
Type2 second;
}

View File

@@ -138,7 +138,7 @@ fn void*? ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
}
// Otherwise just allocate new memory.
void* mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
mem::copy(mem, old_pointer, math::min(size, old_size), mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -137,7 +137,7 @@ fn void*? BackedArenaAllocator.resize(&self, void* pointer, usz size, usz alignm
}
AllocChunk* data = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
mem::copy(data, pointer, math::min(size, chunk.size), mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}

View File

@@ -117,7 +117,7 @@ fn void*? DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz a
return old_pointer;
}
void* new_mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
mem::copy(new_mem, old_pointer, math::min(old_size, size), mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}

View File

@@ -8,12 +8,9 @@ import libc;
<*
The LibcAllocator is a wrapper around malloc to conform to the Allocator interface.
*>
typedef LibcAllocator (Allocator, Printable) = uptr;
typedef LibcAllocator (Allocator) = uptr;
const LibcAllocator LIBC_ALLOCATOR = {};
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;

View File

@@ -1,5 +1,5 @@
module std::core::mem::allocator;
import std::math;
<*
The OnStackAllocator is similar to the ArenaAllocator: it allocates from a chunk of memory
given to it.
@@ -124,7 +124,7 @@ fn void*? OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignm
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
mem::copy(mem, old_pointer, math::min(old_size, size), mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -1,6 +1,5 @@
module std::core::mem::allocator;
module std::core::mem::allocator @if(!(env::POSIX || env::WIN32) || !$feature(VMEM_TEMP));
import std::io, std::math;
import std::core::sanitizer::asan;
// This implements the temp allocator.
// The temp allocator is a specialized allocator only intended for use where
@@ -327,3 +326,81 @@ fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
return &page.data[0];
}
module std::core::mem::allocator @if((env::POSIX || env::WIN32) && $feature(VMEM_TEMP));
import std::math;
tlocal VmemOptions temp_allocator_default_options = {
.shrink_on_reset = env::MEMORY_ENV != NORMAL,
.protect_unused_pages = env::COMPILER_OPT_LEVEL <= O1 || env::COMPILER_SAFE_MODE,
.scratch_released_data = env::COMPILER_SAFE_MODE
};
fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size, usz reserve = temp_allocator_reserve_size, usz min_size = temp_allocator_min_size, usz realloc_size = temp_allocator_realloc_size)
{
Vmem mem;
TempAllocator* t = allocator::new(allocator, TempAllocator);
defer catch allocator::free(allocator, t);
t.vmem.init(preferred_size: isz.sizeof > 4 ? 4 * mem::GB : 512 * mem::MB,
reserve_page_size: isz.sizeof > 4 ? 256 * mem::KB : 0,
options: temp_allocator_default_options)!;
t.allocator = allocator;
return t;
}
struct TempAllocator (Allocator)
{
Vmem vmem;
TempAllocator* derived;
Allocator allocator;
}
<*
@require size > 0
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
*>
fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
return self.vmem.acquire(size, init_type, alignment) @inline;
}
fn TempAllocator*? TempAllocator.derive_allocator(&self, usz reserve = 0)
{
if (self.derived) return self.derived;
return self.derived = new_temp_allocator(self.allocator, 0)!;
}
<*
Reset the entire temp allocator, destroying all children
*>
fn void TempAllocator.reset(&self)
{
TempAllocator* child = self.derived;
if (!child) return;
child.reset();
child.vmem.reset(0);
}
fn void TempAllocator.free(&self)
{
self.destroy();
}
fn void TempAllocator.destroy(&self) @local
{
TempAllocator* child = self.derived;
if (!child) return;
child.destroy();
self.vmem.free() @inline;
allocator::free(self.allocator, self) @inline;
}
fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
{
return self.vmem.resize(pointer, size, alignment) @inline;
}
fn void TempAllocator.release(&self, void* old_pointer, bool b) @dynamic
{
self.vmem.release(old_pointer, b) @inline;
}

View File

@@ -0,0 +1,252 @@
module std::core::mem::allocator @if(env::POSIX || env::WIN32);
import std::math, std::os::posix, libc, std::bits;
import std::core::mem;
import std::core::env;
// Virtual Memory allocator
faultdef VMEM_RESERVE_FAILED;
struct Vmem (Allocator)
{
VirtualMemory memory;
usz allocated;
usz pagesize;
usz page_pot;
usz last_page;
usz high_water;
VmemOptions options;
}
bitstruct VmemOptions : int
{
bool shrink_on_reset; // Release memory on reset
bool protect_unused_pages; // Protect unused pages on reset
bool scratch_released_data; // Overwrite released data with 0xAA
}
<*
Implements the Allocator interface method.
@require !reserve_page_size || math::is_power_of_2(reserve_page_size)
@require reserve_page_size <= preferred_size : "The min reserve_page_size size must be less or equal to the preferred size"
@require preferred_size >= 1 * mem::KB : "The preferred size must exceed 1 KB"
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY, VMEM_RESERVE_FAILED
*>
fn void? Vmem.init(&self, usz preferred_size, usz reserve_page_size = 0, VmemOptions options = { true, true, env::COMPILER_SAFE_MODE }, usz min_size = 0)
{
static usz page_size = 0;
if (!page_size) page_size = mem::os_pagesize();
if (page_size < reserve_page_size) page_size = reserve_page_size;
preferred_size = mem::aligned_offset(preferred_size, page_size);
if (!min_size) min_size = max(preferred_size / 1024, 1);
VirtualMemory? memory = mem::OUT_OF_MEMORY?;
while (preferred_size >= min_size)
{
memory = vm::virtual_alloc(preferred_size, PROTECTED);
// It worked?
if (try memory) break;
switch (@catch(memory))
{
case mem::OUT_OF_MEMORY:
case vm::RANGE_OVERFLOW:
// Try a smaller size.
preferred_size /= 2;
continue;
default:
break;
}
}
if (catch memory) return VMEM_RESERVE_FAILED?;
if (page_size > preferred_size) page_size = preferred_size;
$if env::ADDRESS_SANITIZER:
asan::poison_memory_region(memory.ptr, memory.size);
$endif
*self = { .memory = memory,
.high_water = 0,
.pagesize = page_size,
.page_pot = page_size.ctz(),
.options = options,
};
}
<*
Implements the Allocator interface method.
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
@require size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? Vmem.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
usz total_len = self.memory.size;
if (size > total_len) return mem::INVALID_ALLOC_SIZE?;
void* start_mem = self.memory.ptr;
void* unaligned_pointer_to_offset = start_mem + self.allocated + VmemHeader.sizeof;
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz after = (usz)(mem - start_mem) + size;
if (after > total_len) return mem::OUT_OF_MEMORY?;
if (init_type == ZERO && self.high_water <= self.allocated)
{
init_type = NO_ZERO;
}
protect(self, after)!;
VmemHeader* header = mem - VmemHeader.sizeof;
header.size = size;
if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
fn bool Vmem.owns_pointer(&self, void* ptr) @inline
{
return (uptr)ptr >= (uptr)self.memory.ptr && (uptr)ptr < (uptr)self.memory.ptr + self.memory.size;
}
<*
Implements the Allocator interface method.
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
@require old_pointer != null
@require size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? Vmem.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic
{
if (size > self.memory.size) return mem::INVALID_ALLOC_SIZE?;
alignment = alignment_for_allocation(alignment);
assert(self.owns_pointer(old_pointer), "Pointer originates from a different allocator: %p, not in %p - %p", old_pointer, self.memory.ptr, self.memory.ptr + self.allocated);
VmemHeader* header = old_pointer - VmemHeader.sizeof;
usz old_size = header.size;
if (old_size == size) return old_pointer;
// Do last allocation and alignment match?
if (self.memory.ptr + self.allocated == old_pointer + old_size && mem::ptr_is_aligned(old_pointer, alignment))
{
if (old_size > size)
{
unprotect(self, self.allocated + size - old_size);
}
else
{
usz allocated = self.allocated + size - old_size;
if (allocated > self.memory.size) return mem::OUT_OF_MEMORY?;
protect(self, allocated)!;
}
header.size = size;
return old_pointer;
}
if (old_size > size)
{
$if env::ADDRESS_SANITIZER:
asan::poison_memory_region(old_pointer + size, old_size - size);
$endif
header.size = size;
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = self.acquire(size, NO_ZERO, alignment)!;
assert(size > old_size);
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
<*
Implements the Allocator interface method.
@require ptr != null
*>
fn void Vmem.release(&self, void* ptr, bool) @dynamic
{
assert(self.owns_pointer(ptr), "Pointer originates from a different allocator %p.", ptr);
VmemHeader* header = ptr - VmemHeader.sizeof;
// Reclaim memory if it's the last element.
if (ptr + header.size == self.memory.ptr + self.allocated)
{
unprotect(self, self.allocated - header.size - VmemHeader.sizeof);
}
}
fn usz Vmem.mark(&self)
{
return self.allocated;
}
<*
@require mark <= self.allocated : "Invalid mark"
*>
fn void Vmem.reset(&self, usz mark)
{
if (mark == self.allocated) return;
unprotect(self, mark);
}
fn void Vmem.free(&self)
{
if (!self.memory.ptr) return;
$switch:
$case env::ADDRESS_SANITIZER:
asan::poison_memory_region(self.memory.ptr, self.memory.size);
$case env::COMPILER_SAFE_MODE:
((char*)self.memory.ptr)[0:self.allocated] = 0xAA;
$endswitch
(void)self.memory.destroy();
*self = {};
}
// Internal data
struct VmemHeader @local
{
usz size;
char[*] data;
}
macro void? protect(Vmem* mem, usz after) @local
{
usz shift = mem.page_pot;
usz page_after = (after + mem.pagesize - 1) >> shift;
usz last_page = mem.last_page;
bool over_high_water = mem.high_water < after;
if (page_after > last_page)
{
usz page_start = last_page << shift;
usz page_len = (page_after - last_page) << shift;
mem.memory.commit(page_start, page_len)!;
if (mem.options.protect_unused_pages || over_high_water)
{
mem.memory.protect(page_start, page_len, READWRITE)!;
}
mem.last_page = page_after;
}
$if env::ADDRESS_SANITIZER:
asan::unpoison_memory_region(mem.memory.ptr + mem.allocated, after - mem.allocated);
$endif
mem.allocated = after;
if (over_high_water) mem.high_water = after;
}
macro void unprotect(Vmem* mem, usz after) @local
{
usz shift = mem.page_pot;
usz last_page = mem.last_page;
usz page_after = mem.last_page = (after + mem.pagesize - 1) >> shift;
$if env::ADDRESS_SANITIZER:
asan::poison_memory_region(mem.memory.ptr + after, mem.allocated - after);
$else
if (mem.options.scratch_released_data)
{
mem::set(mem.memory.ptr + after, 0xAA, mem.allocated - after);
}
$endif
if ((mem.options.shrink_on_reset || mem.options.protect_unused_pages) && page_after < last_page)
{
usz start = page_after << shift;
usz len = (last_page - page_after) << shift;
if (mem.options.shrink_on_reset) (void)mem.memory.decommit(start, len, false);
if (mem.options.protect_unused_pages) (void)mem.memory.protect(start, len, PROTECTED);
}
mem.allocated = after;
}

133
lib/std/core/ansi.c3 Normal file
View File

@@ -0,0 +1,133 @@
module std::core::string::ansi;
enum Ansi : const inline String
{
RESET = "\e[0m",
BOLD = "\e[1m",
DIM = "\e[2m",
ITALIC = "\e[3m",
UNDERLINE = "\e[4m",
BLINK = "\e[5m",
BLINK_FAST = "\e[6m",
INVERT = "\e[7m",
HIDDEN = "\e[8m",
STRIKETHROUGH = "\e[9m",
DOUBLE_UNDER = "\e[21m",
NO_DIM = "\e[22m",
NO_ITALIC = "\e[23m",
NO_UNDERLINE = "\e[24m",
NO_BLINK = "\e[25m",
NO_INVERT = "\e[27m",
NO_HIDDEN = "\e[28m",
NO_STRIKETHROUGH = "\e[29m",
BLACK = "\e[30m",
RED = "\e[31m",
GREEN = "\e[32m",
YELLOW = "\e[33m",
BLUE = "\e[34m",
MAGENTA = "\e[35m",
CYAN = "\e[36m",
WHITE = "\e[37m",
DEFAULT = "\e[39m",
BRIGHT_BLACK = "\e[90m",
BRIGHT_RED = "\e[91m",
BRIGHT_GREEN = "\e[92m",
BRIGHT_YELLOW = "\e[93m",
BRIGHT_BLUE = "\e[94m",
BRIGHT_MAGENTA = "\e[95m",
BRIGHT_CYAN = "\e[96m",
BRIGHT_WHITE = "\e[97m",
BG_BLACK = "\e[40m",
BG_RED = "\e[41m",
BG_GREEN = "\e[42m",
BG_YELLOW = "\e[43m",
BG_BLUE = "\e[44m",
BG_MAGENTA = "\e[45m",
BG_CYAN = "\e[46m",
BG_WHITE = "\e[47m",
BG_DEFAULT = "\e[49m",
BG_BRIGHT_BLACK = "\e[100m",
BG_BRIGHT_RED = "\e[101m",
BG_BRIGHT_GREEN = "\e[102m",
BG_BRIGHT_YELLOW = "\e[103m",
BG_BRIGHT_BLUE = "\e[104m",
BG_BRIGHT_MAGENTA = "\e[105m",
BG_BRIGHT_CYAN = "\e[106m",
BG_BRIGHT_WHITE = "\e[107m",
}
<*
8-bit color code
@return `the formatting char for the given background color`
*>
macro String color_8bit(char $index, bool $bg = false) @const
{
int $mode = $bg ? 4 : 3;
return @sprintf("\e[%s8;5;%sm", $mode, $index);
}
<*
24-bit color code
@return `the string for the given foreground color`
*>
macro String color_rgb(char $r, char $g, char $b, bool $bg = false) @const
{
int $mode = $bg ? 4 : 3;
return @sprintf("\e[%s8;2;%s;%s;%sm", $mode, $r, $g, $b);
}
<*
24-bit color code rgb
@require $rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
@return `the string char for the given foreground color`
*>
macro String color(uint $rgb, bool $bg = false) @const
{
int $mode = $bg ? 4 : 3;
return @sprintf("\e[%s8;2;%s;%s;%sm", $mode, $rgb >> 16, ($rgb & 0xFF00) >> 8, $rgb & 0xFF);
}
<*
24-bit color code rgb
@require rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
@return `the string char for the given foreground color`
*>
fn String make_color(Allocator mem, uint rgb, bool bg = false)
{
return make_color_rgb(mem, (char)(rgb >> 16), (char)((rgb & 0xFF00) >> 8), (char)rgb, bg);
}
<*
24-bit color code rgb
@require rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
@return `the string char for the given foreground color`
*>
fn String make_tcolor(uint rgb, bool bg = false)
{
return make_color_rgb(tmem, (char)(rgb >> 16), (char)((rgb & 0xFF00) >> 8), (char)rgb, bg);
}
<*
24-bit color code rgb
@return `the string char for the given foreground color`
*>
fn String make_color_rgb(Allocator mem, char r, char g, char b, bool bg = false)
{
return string::format(mem, "\e[%s8;2;%s;%s;%sm", bg ? 4 : 3, r, g, b);
}
<*
24-bit color code rgb
@return `the string char for the given foreground color`
*>
fn String make_tcolor_rgb(char r, char g, char b, bool bg = false)
{
return string::format(tmem, "\e[%s8;2;%s;%s;%sm", bg ? 4 : 3, r, g, b);
}

View File

@@ -96,58 +96,80 @@ macro anycast(any v, $Type) @builtin
return ($Type*)v.ptr;
}
fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
macro bool @assignable_to(#foo, $Type) @const @builtin => $defined(*&&($Type){} = #foo);
macro @addr(#val) @builtin
{
$if $defined(&#val):
return &#val;
$else
return &&#val;
$endif
}
macro typeid @typeid(#value) @const @builtin
{
return $typeof(#value).typeid;
}
macro TypeKind @typekind(#value) @const @builtin
{
return $typeof(#value).kindof;
}
macro bool @typeis(#value, $Type) @const @builtin
{
return $typeof(#value).typeid == $Type.typeid;
}
fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
{
Allocator t = allocator::current_temp;
TempAllocator* new_t = allocator::new_temp_allocator(smem, 0x1000)!!;
allocator::current_temp = new_t;
defer
{
allocator::current_temp = t;
new_t.free();
}
void*[256] buffer;
void*[] backtraces = backtrace::capture_current(&buffer);
backtraces_to_ignore++;
BacktraceList? backtrace = backtrace::symbolize_backtrace(tmem, backtraces);
if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false;
io::eprint("\nERROR: '");
io::eprint(message);
io::eprintn("'");
foreach (i, &trace : backtrace)
@stack_mem(2048; Allocator mem)
{
if (i < backtraces_to_ignore) continue;
String inline_suffix = trace.is_inline ? " [inline]" : "";
if (trace.is_unknown())
BacktraceList? backtrace = backtrace::symbolize_backtrace(mem, backtraces);
if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false;
io::eprint("\nERROR: '");
io::eprint(message);
io::eprintn("'");
foreach (i, &trace : backtrace)
{
io::eprintfn(" in ???%s", inline_suffix);
continue;
if (i < backtraces_to_ignore) continue;
String inline_suffix = trace.is_inline ? " [inline]" : "";
if (trace.is_unknown())
{
io::eprintfn(" in ???%s", inline_suffix);
continue;
}
if (trace.has_file())
{
io::eprintfn(" in %s (%s:%d) [%s]%s", trace.function, trace.file, trace.line, trace.object_file, inline_suffix);
continue;
}
io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix);
}
if (trace.has_file())
{
io::eprintfn(" in %s (%s:%d) [%s]%s", trace.function, trace.file, trace.line, trace.object_file, inline_suffix);
continue;
}
io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix);
}
};
return true;
}
fn void default_panic(String message, String file, String function, uint line) @if(env::NATIVE_STACKTRACE)
{
$if $defined(io::stderr):
if (!print_backtrace(message, 2))
{
io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
}
$if $defined(io::stderr) && env::PANIC_MSG:
if (!print_backtrace(message, 2))
{
io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
}
$endif
$$trap();
}
macro void abort(String string = "Unrecoverable error reached", ...) @builtin @noreturn
macro void abort(String string = "Unrecoverable error reached", ...) @format(0) @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
$$trap();
@@ -155,20 +177,22 @@ macro void abort(String string = "Unrecoverable error reached", ...) @builtin @n
bool in_panic @local = false;
fn void default_panic(String message, String file, String function, uint line) @if(!env::NATIVE_STACKTRACE)
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);
$if $defined(io::stderr) && env::PANIC_MSG:
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;
$endif
in_panic = false;
$$trap();
}
@@ -178,21 +202,23 @@ 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.init(allocator);
s.appendf(fmt, ...args);
in_panic = false;
panic(s.str_view(), file, function, line);
};
$if $defined(io::stderr) && env::PANIC_MSG:
if (in_panic)
{
io::eprint("Panic inside of panic: ");
io::eprintn(fmt);
return;
}
in_panic = true;
@stack_mem(512; Allocator allocator)
{
DString s;
s.init(allocator);
s.appendf(fmt, ...args);
in_panic = false;
panic(s.str_view(), file, function, line);
};
$endif
}
<*
@@ -281,7 +307,7 @@ macro enum_by_name($Type, String enum_name) @builtin
@param $Type : `The type of the enum`
@require $Type.kindof == ENUM : `Only enums may be used`
@require $defined($Type.#value) : `Expected '#value' to match an enum associated value`
@require $assignable(value, $typeof(($Type){}.#value)) : `Expected the value to match the type of the associated value`
@require @assignable_to(value, $typeof(($Type){}.#value)) : `Expected the value to match the type of the associated value`
@ensure @typeis(return, $Type)
@return? NOT_FOUND
*>
@@ -334,7 +360,7 @@ macro bool @unlikely(bool #value, $probability = 1.0) @builtin
<*
@require values::@is_int(#value) || values::@is_bool(#value)
@require $assignable(expected, $typeof(#value))
@require @assignable_to(expected, $typeof(#value))
@require $probability >= 0 && $probability <= 1.0
*>
macro @expect(#value, expected, $probability = 1.0) @builtin
@@ -384,6 +410,7 @@ macro swizzle2(v, v2, ...) @builtin
{
return $$swizzle2(v, v2, $vasplat);
}
<*
Return the excuse in the Optional if it is Empty, otherwise
return a null fault.
@@ -408,6 +435,67 @@ macro bool @ok(#expr) @builtin
return true;
}
<*
Check if an Optional expression evaluates to a fault. If so, return it;
else, assign the result to an expression.
@require $defined(#v = #v) : "#v must be a variable"
@require $defined(#expr!) : "Expected an optional expression"
@require @assignable_to(#expr!!, $typeof(#v)) : `Type of #expr must be an optional of #v's type`
*>
macro void? @try(#v, #expr) @builtin
{
var res = #expr;
if (catch err = res) return err?;
#v = res;
}
<*
Check if an Optional expression evaluates to a fault. If so, return true if it is the
expected fault, the optional if it is unexpected, or false if there was no fault and
the assign happened.
This can be used in like this:
while (true)
{
char[] data;
// Read until end of file
if (@try_catch(data, load_line(), io::EOF)) break;
.. use data ..
}
In this example we read until we reach an EOF, which is expected. However, if we encounter some other
fault, we rethrow is. Without this macro, the code is instead written like:
while (true)
{
char[]? data;
data = load_line();
if (catch err = data)
{
if (err = io::EOF) break;
return err?
}
.. use data ..
}
@require $defined(#v = #v) : "#v must be a variable"
@require $defined(#expr!) : "Expected an optional expression"
@require @assignable_to(#expr!!, $typeof(#v)) : `Type of #expr must be an optional of #v's type`
@return "True if it was the expected fault, false if the variable was assigned, otherwise returns an optional."
*>
macro bool? @try_catch(#v, #expr, fault expected_fault) @builtin
{
var res = #expr;
if (catch err = res)
{
return err == expected_fault ? true : err?;
}
#v = res;
return false;
}
<*
@require $defined(&#value, (char*)&#value) : "This must be a value that can be viewed as a char array"
*>

View File

@@ -137,12 +137,13 @@ const bool BACKTRACE = $$BACKTRACE;
const usz LLVM_VERSION = $$LLVM_VERSION;
const bool BENCHMARKING = $$BENCHMARKING;
const bool TESTING = $$TESTING;
const bool PANIC_MSG = $$PANIC_MSG;
const MemoryEnvironment MEMORY_ENV = MemoryEnvironment.from_ordinal($$MEMORY_ENVIRONMENT);
const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING);
const bool X86_64 = ARCH_TYPE == X86_64;
const bool X86 = ARCH_TYPE == X86;
const bool AARCH64 = ARCH_TYPE == AARCH64;
const bool NATIVE_STACKTRACE = LINUX || DARWIN || WIN32;
const bool NATIVE_STACKTRACE = LINUX || DARWIN || OPENBSD || WIN32;
const bool LINUX = LIBC && OS_TYPE == LINUX;
const bool DARWIN = LIBC && os_is_darwin();
const bool WIN32 = LIBC && OS_TYPE == WIN32;
@@ -159,7 +160,7 @@ const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER;
const bool THREAD_SANITIZER = $$THREAD_SANITIZER;
const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER;
const int LANGUAGE_DEV_VERSION = $$LANGUAGE_DEV_VERSION;
const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::DARWIN || env::WIN32;
const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::OPENBSD || env::DARWIN || env::WIN32;
macro bool os_is_darwin() @const
{
@@ -198,6 +199,7 @@ macro bool os_is_posix() @const
return false;
$endswitch
}
const String[] AUTHORS = $$AUTHORS;
const String[] AUTHOR_EMAILS = $$AUTHOR_EMAILS;
const BUILTIN_EXPECT_IS_DISABLED = $feature(DISABLE_BUILTIN_EXPECT);
const BUILTIN_PREFETCH_IS_DISABLED = $feature(DISABLE_BUILTIN_PREFETCH);

View File

@@ -3,10 +3,15 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem;
import std::core::mem::allocator @public;
import std::os::posix, std::os::win32;
import std::math;
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
const ulong KB = 1024;
const ulong MB = KB * 1024;
const ulong GB = MB * 1024;
const ulong TB = GB * 1024;
faultdef OUT_OF_MEMORY, INVALID_ALLOC_SIZE;
@@ -15,13 +20,31 @@ macro bool @constant_is_power_of_2($x) @const @private
return $x != 0 && ($x & ($x - 1)) == 0;
}
fn usz os_pagesize()
{
$switch:
$case env::POSIX:
static usz pagesize;
if (pagesize) return pagesize;
return pagesize = posix::getpagesize();
$case env::WIN32:
static usz pagesize;
if (pagesize) return pagesize;
Win32_SYSTEM_INFO info;
win32::getSystemInfo(&info);
return pagesize = info.dwPageSize;
$default:
return 4096;
$endswitch
}
<*
Load a vector from memory according to a mask assuming default alignment.
@param ptr : "The pointer address to load from."
@param mask : "The mask for the load"
@param passthru : "The value to use for non masked values"
@require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
@require @assignable_to(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require passthru.len == mask.len : "Mask and passthru must have the same length"
@@ -40,7 +63,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru)
@param passthru : "The value to use for non masked values"
@param $alignment : "The alignment to assume for the pointer"
@require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
@require @assignable_to(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require passthru.len == mask.len : "Mask and passthru must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@@ -61,7 +84,7 @@ macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment)
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
@require @assignable_to(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
@require passthru.len == mask.len : "Mask and passthru must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@@ -83,7 +106,7 @@ macro gather(ptrvec, bool[<*>] mask, passthru)
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
@require @assignable_to(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
@require passthru.len == mask.len : "Mask and passthru must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@@ -103,7 +126,7 @@ macro @gather_aligned(ptrvec, bool[<*>] mask, passthru, usz $alignment)
@param value : "The value to store masked"
@param mask : "The mask for the store"
@require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
@require @assignable_to(&&value, $typeof(ptr)) : "Pointer and value must match"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@require value.len == mask.len : "Mask and value must have the same length"
*>
@@ -118,7 +141,7 @@ macro masked_store(ptr, value, bool[<*>] mask)
@param mask : "The mask for the store"
@param $alignment : "The alignment of the pointer"
@require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
@require @assignable_to(&&value, $typeof(ptr)) : "Pointer and value must match"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@require value.len == mask.len : "Mask and value must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@@ -135,7 +158,7 @@ macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment)
@param mask : "The mask for the store"
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
@require @assignable_to(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
@require value.len == mask.len : "Mask and value must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@@ -153,7 +176,7 @@ macro scatter(ptrvec, value, bool[<*>] mask)
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
@require @assignable_to(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
@require value.len == mask.len : "Mask and value must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@@ -282,7 +305,7 @@ macro compare_exchange_volatile(ptr, compare, value, AtomicOrdering $success = S
*>
fn usz aligned_offset(usz offset, usz alignment)
{
return alignment * ((offset + alignment - 1) / alignment);
return (offset + alignment - 1) & ~(alignment - 1);
}
macro void* aligned_pointer(void* ptr, usz alignment)
@@ -298,6 +321,11 @@ fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
return (uptr)ptr & ((uptr)alignment - 1) == 0;
}
fn bool ptr_is_page_aligned(void* ptr) @inline
{
return (uptr)ptr & ((uptr)os_pagesize() - 1) == 0;
}
macro void zero_volatile(char[] data)
{
$$memset(data.ptr, (char)0, data.len, true, (usz)1);
@@ -700,7 +728,7 @@ fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
<*
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
*>
macro new($Type, ...) @nodiscard
@@ -716,7 +744,7 @@ macro new($Type, ...) @nodiscard
<*
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
*>
macro new_with_padding($Type, usz padding, ...) @nodiscard
@@ -734,7 +762,7 @@ macro new_with_padding($Type, usz padding, ...) @nodiscard
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro new_aligned($Type, ...) @nodiscard
{
@@ -774,7 +802,7 @@ macro alloc_aligned($Type) @nodiscard
<*
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro tnew($Type, ...) @nodiscard
{
@@ -789,7 +817,7 @@ macro tnew($Type, ...) @nodiscard
<*
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro temp_with_padding($Type, usz padding, ...) @nodiscard
{
@@ -903,6 +931,18 @@ fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMEN
return tmem.resize(ptr, size, alignment)!!;
}
<*
Takes the address of a possibly unaligned variable or member,
and offers safe access to that member, by constructing an UnalignedRef.
@require $defined(&#arg) : "It must be possible to take the address of the argument."
@return "An 'UnalignedRef' with the proper type and alignment, with a pointer to argument"
*>
macro @unaligned_addr(#arg) @builtin
{
return (UnalignedRef{$typeof(#arg), $alignof(#arg)})&#arg;
}
module std::core::mem @if(env::NO_LIBC);
fn CInt __memcmp(void* s1, void* s2, usz n) @weak @export("memcmp")
@@ -940,3 +980,39 @@ fn void* __memcpy(void* dst, void* src, usz n) @weak @export("memcpy")
}
return dst;
}
module std::core::mem::volatile { Type };
typedef Volatile @structlike = Type;
macro Type Volatile.get(&self)
{
return @volatile_load(*(Type*)self);
}
macro Type Volatile.set(&self, Type val)
{
return @volatile_store(*(Type*)self, val);
}
<*
@require mem::@constant_is_power_of_2(ALIGNMENT) : "The alignment must be a power of 2"
*>
module std::core::mem::alignment { Type, ALIGNMENT };
import std::core::mem @public;
<*
An UnalignedRef offers correctly aligned access to addresses that may be unaligned or overaligned.
*>
typedef UnalignedRef = Type*;
macro Type UnalignedRef.get(self)
{
return @unaligned_load(*(Type*)self, ALIGNMENT);
}
macro Type UnalignedRef.set(&self, Type val)
{
return @unaligned_store(*(Type*)self, val, ALIGNMENT);
}

View File

@@ -1,7 +1,7 @@
module std::core::mem::allocator;
import std::math;
// C3 has multiple different allocators available:
// C3 has several different allocators available:
//
// Name Arena Uses buffer OOM Fallback? Mark? Reset?
// ArenaAllocator Yes Yes No Yes Yes
@@ -12,6 +12,7 @@ import std::math;
// OnStackAllocator Yes Yes Yes No No *Note: Used by @stack_mem
// TempAllocator Yes No Yes No* No* *Note: Mark/reset using @pool
// TrackingAllocator No No N/A No No *Note: Wraps other heap allocator
// Vmem Yes No No Yes Yes *Note: Can be set to huge sizes
const DEFAULT_SIZE_PREFIX = usz.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
@@ -166,7 +167,7 @@ macro void free_aligned(Allocator allocator, void* ptr)
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro new(Allocator allocator, $Type, ...) @nodiscard
{
@@ -182,7 +183,7 @@ macro new(Allocator allocator, $Type, ...) @nodiscard
<*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro new_try(Allocator allocator, $Type, ...) @nodiscard
{
@@ -199,7 +200,7 @@ macro new_try(Allocator allocator, $Type, ...) @nodiscard
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro new_aligned(Allocator allocator, $Type, ...) @nodiscard
{
@@ -463,7 +464,7 @@ macro usz temp_allocator_default_reserve_size() @local
$endswitch
}
macro Allocator heap() => thread_allocator;
macro Allocator heap() @deprecated("Use 'mem' instead.") => thread_allocator;
<*
@require !top_temp : "This should never be called when temp already exists"
@@ -486,7 +487,7 @@ fn Allocator create_temp_allocator(Allocator allocator, usz size, usz reserve, u
return current_temp = top_temp = allocator::new_temp_allocator(allocator, size, reserve, min_size, realloc_size)!!;
}
macro Allocator temp()
macro Allocator temp() @deprecated("Use 'tmem' instead")
{
return current_temp;
}

323
lib/std/core/os/mem_vm.c3 Normal file
View File

@@ -0,0 +1,323 @@
<*
The VM module holds code for working with virtual memory on supported platforms (currently Win32 and Posix)
*>
module std::core::mem::vm;
import std::os::win32, std::os::posix, libc;
<*
VirtualMemory is an abstraction for working with an allocated virtual memory area. It will invoke vm:: functions
but will perform more checks and track its size (required to unmap the memory on Posix)
*>
struct VirtualMemory
{
void* ptr;
usz size;
VirtualMemoryAccess default_access;
}
faultdef RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, UNMAPPED_ACCESS, UNALIGNED_ADDRESS, RELEASE_FAILED, UPDATE_FAILED, INVALID_ARGS;
enum VirtualMemoryAccess
{
PROTECTED,
READ,
WRITE,
READWRITE,
EXEC,
EXECREAD,
EXECWRITE,
ANY
}
fn usz aligned_alloc_size(usz size)
{
$if env::WIN32:
return size > 0 ? mem::aligned_offset(size, win32::allocation_granularity()) : win32::allocation_granularity();
$else
return size > 0 ? mem::aligned_offset(size, mem::os_pagesize()) : mem::os_pagesize();
$endif
}
<*
Allocate virtual memory, size is rounded up to platform granularity (Win32) / page size (Posix).
@param size : "The size of the memory to allocate, will be rounded up"
@param access : "The initial access permissions."
@return? mem::OUT_OF_MEMORY, RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, INVALID_ARGS
@return "Pointer to the allocated memory, page aligned"
*>
fn void*? alloc(usz size, VirtualMemoryAccess access)
{
$switch:
$case env::POSIX:
void* ptr = posix::mmap(null, aligned_alloc_size(size), access.to_posix(), posix::MAP_PRIVATE | posix::MAP_ANONYMOUS, -1, 0);
if (ptr != posix::MAP_FAILED) return ptr;
switch (libc::errno())
{
case errno::ENOMEM: return mem::OUT_OF_MEMORY?;
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
case errno::EPERM: return ACCESS_DENIED?;
case errno::EINVAL: return INVALID_ARGS?;
default: return UNKNOWN_ERROR?;
}
$case env::WIN32:
void* ptr = win32::virtualAlloc(null, aligned_alloc_size(size), MEM_RESERVE, access.to_win32());
if (ptr) return ptr;
switch (win32::getLastError())
{
case win32::ERROR_NOT_ENOUGH_MEMORY:
case win32::ERROR_COMMITMENT_LIMIT: return mem::OUT_OF_MEMORY?;
default: return UNKNOWN_ERROR?;
}
$default:
unsupported("Virtual alloc only available on Win32 and Posix");
$endswitch
}
<*
Release memory allocated with "alloc".
@param [&inout] ptr : "Pointer to page to release, should be allocated using vm::alloc"
@param size : "The size of the allocated pointer"
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
*>
fn void? release(void* ptr, usz size)
{
$switch:
$case env::POSIX:
if (posix::munmap(ptr, aligned_alloc_size(size)))
{
switch (libc::errno())
{
case errno::EINVAL: return INVALID_ARGS?; // Not a valid mapping or size
case errno::ENOMEM: return UNMAPPED_ACCESS?; // Address not mapped
default: return RELEASE_FAILED?;
}
}
$case env::WIN32:
if (win32::virtualFree(ptr, 0, MEM_RELEASE)) return;
switch (win32::getLastError())
{
case win32::ERROR_INVALID_ADDRESS: return INVALID_ARGS?;
case win32::ERROR_NOT_ENOUGH_MEMORY: return mem::OUT_OF_MEMORY?;
default: return RELEASE_FAILED?;
}
$default:
unsupported("Virtual free only available on Win32 and Posix");
$endswitch
}
<*
Change the access protection of a region in memory. The region must be page aligned.
@param [&inout] ptr : "Pointer to page to update, must be page aligned"
@param len : "To what len to update, must be page aligned"
@param access : "The new access"
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
@require mem::ptr_is_page_aligned(ptr + len) : "The length must be page aligned"
@return? ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UPDATE_FAILED, UNMAPPED_ACCESS, INVALID_ARGS
*>
fn void? protect(void* ptr, usz len, VirtualMemoryAccess access)
{
$switch:
$case env::POSIX:
if (!posix::mprotect(ptr, len, access.to_posix())) return;
switch (libc::errno())
{
case errno::EACCES: return ACCESS_DENIED?;
case errno::EINVAL: return UNALIGNED_ADDRESS?;
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
case errno::ENOMEM: return UNMAPPED_ACCESS?;
default: return UPDATE_FAILED?;
}
$case env::WIN32:
Win32_Protect old;
if (win32::virtualProtect(ptr, len, access.to_win32(), &old)) return;
switch (win32::getLastError())
{
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
default: return UPDATE_FAILED?;
}
$default:
unsupported("'virtual_protect' is only available on Win32 and Posix.");
$endswitch
}
<*
Makes a region of memory available that was previously retrieved using 'alloc'. This is necessary on Win32,
but optional on Posix.
@param [&inout] ptr : "Pointer to page to update, must be page aligned"
@param len : "To what len to commit, must be page aligned"
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
@require mem::ptr_is_page_aligned(ptr + len) : "The length must be page aligned"
@return? UNKNOWN_ERROR, mem::OUT_OF_MEMORY, ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UPDATE_FAILED, UNMAPPED_ACCESS, INVALID_ARGS
*>
fn void? commit(void* ptr, usz len, VirtualMemoryAccess access = READWRITE)
{
$switch:
$case env::POSIX:
return protect(ptr, len, READWRITE) @inline;
$case env::WIN32:
void* result = win32::virtualAlloc(ptr, len, MEM_COMMIT, access.to_win32());
if (result) return;
switch (win32::getLastError())
{
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
case win32::ERROR_COMMITMENT_LIMIT:
case win32::ERROR_NOT_ENOUGH_MEMORY: return mem::OUT_OF_MEMORY?;
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
default: return UNKNOWN_ERROR?;
}
$default:
unsupported("'virtual_commit' is only available on Win32 and Posix.");
$endswitch
}
<*
Notifies that the memory in the region can be released back to the OS. On Win32 this decommits the region,
whereas on Posix it tells the system that it may be reused using madvise. The "block" parameter is only
respected on Posix, and protects the region from read/write/exec. On Win32 this always happens.
@param [&inout] ptr : "Pointer to page to update, must be page aligned"
@param len : "To what len to commit, must be page aligned"
@param block : "Set the released memory to protected"
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
@require mem::ptr_is_page_aligned(ptr + len) : "The length must be page aligned"
@return? ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UPDATE_FAILED, UNMAPPED_ACCESS, INVALID_ARGS
*>
fn void? decommit(void* ptr, usz len, bool block = true)
{
$switch:
$case env::POSIX:
if (posix::madvise(ptr, len, posix::MADV_DONTNEED))
{
switch (libc::errno())
{
case errno::EINVAL: return UNALIGNED_ADDRESS?;
case errno::ENOMEM: return UNMAPPED_ACCESS?;
default: return UPDATE_FAILED?;
}
}
if (block) (void)protect(ptr, len, PROTECTED) @inline;
$case env::WIN32:
if (!win32::virtualFree(ptr, len, MEM_DECOMMIT))
{
switch (win32::getLastError())
{
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
default: return UPDATE_FAILED?;
}
}
$default:
unsupported("'virtual_decommit' is only available on Win32 and Posix.");
$endswitch
}
<*
Create a VirtualMemory using
@param size : "The size of the memory to allocate."
@require size > 0 : "The size must be non-zero"
@return? mem::OUT_OF_MEMORY, RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, INVALID_ARGS
*>
fn VirtualMemory? virtual_alloc(usz size, VirtualMemoryAccess access = PROTECTED)
{
size = aligned_alloc_size(size);
void* ptr = alloc(size, access)!;
return { ptr, size, access };
}
<*
Commits memory, using vm::commit
@param offset : "Starting from what offset to commit"
@param len : "To what len to commit"
@require mem::ptr_is_page_aligned(self.ptr + offset) : "The offset should be page aligned"
@require mem::ptr_is_page_aligned(self.ptr + offset + len) : "The length must be page aligned"
@require offset < self.size : "Offset out of range"
@require offset + len <= self.size : "Length out of range"
@return? UPDATE_FAILED, ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UNKNOWN_ERROR
*>
macro void? VirtualMemory.commit(self, usz offset, usz len)
{
return commit(self.ptr + offset, len, self.default_access);
}
<*
Changes protection of a part of memory using vm::protect
@param offset : "Starting from what offset to update"
@param len : "To what len to update"
@require mem::ptr_is_page_aligned(self.ptr + offset) : "The offset should be page aligned"
@require mem::ptr_is_page_aligned(self.ptr + offset + len) : "The length must be page aligned"
@require offset < self.size : "Offset out of range"
@require offset + len < self.size : "Length out of range"
@return? UPDATE_FAILED, ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UNKNOWN_ERROR
*>
macro void? VirtualMemory.protect(self, usz offset, usz len, VirtualMemoryAccess access)
{
return protect(self.ptr + offset, len, access);
}
<*
Decommits a part of memory using vm::decommit
@param offset : "Starting from what offset to decommit"
@param len : "To what len to decommit"
@param block : "Should the memory be blocked from access after decommit"
@require mem::ptr_is_page_aligned(self.ptr + offset) : "The offset should be page aligned"
@require mem::ptr_is_page_aligned(self.ptr + offset + len) : "The length must be page aligned"
@require offset < self.size : "Offset out of range"
@require offset + len < self.size : "Length out of range"
@return? UPDATE_FAILED
*>
fn void? VirtualMemory.decommit(self, usz offset, usz len, bool block = true)
{
return decommit(self.ptr + offset, len, block);
}
<*
Releases the memory region
@require self.ptr != null : "Virtual memory must be initialized to call destroy"
*>
fn void? VirtualMemory.destroy(&self)
{
return release(self.ptr, self.size);
}
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
{
switch (self)
{
case PROTECTED: return posix::PROT_NONE;
case READ: return posix::PROT_READ;
case WRITE: return posix::PROT_WRITE;
case EXEC: return posix::PROT_EXEC;
case READWRITE: return posix::PROT_READ | posix::PROT_WRITE;
case EXECREAD: return posix::PROT_READ | posix::PROT_EXEC;
case EXECWRITE: return posix::PROT_WRITE | posix::PROT_EXEC;
case ANY: return posix::PROT_WRITE | posix::PROT_READ | posix::PROT_EXEC;
}
}
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32) @private
{
switch (self)
{
case PROTECTED: return PAGE_NOACCESS;
case READ: return PAGE_READONLY;
case WRITE: return PAGE_READWRITE;
case EXEC: return PAGE_EXECUTE;
case READWRITE: return PAGE_READWRITE;
case EXECWRITE: return PAGE_EXECUTE_READWRITE;
case EXECREAD: return PAGE_EXECUTE_READ;
case ANY: return PAGE_EXECUTE_READWRITE;
}
}

132
lib/std/core/refcount.c3 Normal file
View File

@@ -0,0 +1,132 @@
<*
Ref provides a general *external* ref counted wrapper for a pointer. For convenience, a ref count of 0
means the reference is still valid.
When the rc drops to -1, it will first run the dealloc function on the underlying pointer (if it exists),
then free the pointer and the atomic variable assuming that they are allocated using the Allocator in the Ref.
@require !$defined(Type.dealloc) ||| $defined(Type.dealloc(&&(Type){})) : "'dealloc' must only take a pointer to the underlying type"
@require !$defined(Type.dealloc) ||| @typeis((Type){}.dealloc(), void) : "'dealloc' must return 'void'"
*>
module std::core::mem::ref { Type };
import std::thread, std::atomic;
const OVERALIGNED @private = Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
alias DeallocFn = fn void(void*);
fn Ref wrap(Type* ptr, Allocator allocator = mem)
{
return { .refcount = allocator::new(allocator, Atomic{int}), .ptr = ptr, .allocator = allocator };
}
<*
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| @assignable_to($vaexpr[0], Type) : "The first argument must be an initializer for the type"
*>
macro Ref new(..., Allocator allocator = mem)
{
$switch:
$case OVERALIGNED && !$vacount:
Type* ptr = allocator::calloc_aligned(allocator, Type.sizeof, Type.alignof)!!;
$case OVERALIGNED:
Type* ptr = allocator::malloc_aligned(allocator, Type.sizeof, Type.alignof)!!;
*ptr = $vaexpr[0];
$case !$vacount:
Type* ptr = allocator::calloc(allocator, Type.sizeof);
$default:
Type* ptr = allocator::malloc(allocator, Type.sizeof);
*ptr = $vaexpr[0];
$endswitch
return { .refcount = allocator::new(allocator, Atomic{int}),
.ptr = ptr,
.allocator = allocator };
}
struct Ref
{
Atomic{int}* refcount;
Type* ptr;
Allocator allocator;
}
fn Ref* Ref.retain(&self)
{
assert(self.refcount != null, "Reference already released");
assert(self.refcount.load(RELAXED) >= 0, "Retaining zombie");
self.refcount.add(1, RELAXED);
return self;
}
fn void Ref.release(&self)
{
assert(self.refcount != null, "Reference already released");
assert(self.refcount.load(RELAXED) >= 0, "Overrelease of refcount");
if (self.refcount.sub(1, RELAXED) == 0)
{
thread::fence(ACQUIRE);
$if $defined(Type.dealloc):
self.ptr.dealloc();
$endif
$if OVERALIGNED:
allocator::free_aligned(self.allocator, self.ptr);
$else
allocator::free(self.allocator, self.ptr);
$endif
allocator::free(self.allocator, self.refcount);
*self = {};
}
}
module std::core::mem::rc;
import std::thread, std::atomic;
<*
A RefCounted struct should be an inline base of a struct.
If a `dealloc` is defined, then it will be called rather than `free`
For convenience, a ref count of 0 is still valid, and the struct is
only freed when when ref count drops to -1.
The macros rc::retain and rc::release must be used on the full pointer,
not on the RefCounted substruct.
So `Foo* f = ...; RefCounted* rc = f; rc::release(rc);` will not do the right thing.
*>
struct RefCounted
{
Atomic{int} refcount;
}
<*
@require @assignable_to(refcounted, RefCounted*) : "Expected a ref counted value"
*>
macro retain(refcounted)
{
if (refcounted)
{
assert(refcounted.refcount.load(RELAXED) >= 0, "Retaining zombie");
refcounted.refcount.add(1, RELAXED);
}
return refcounted;
}
<*
@require @assignable_to(refcounted, RefCounted*) : "Expected a ref counted value"
@require !$defined(refcounted.dealloc()) ||| @typeis(refcounted.dealloc(), void)
: "Expected refcounted type to have a valid dealloc"
*>
macro void release(refcounted)
{
if (!refcounted) return;
assert(refcounted.refcount.load(RELAXED) >= 0, "Overrelease of refcount");
if (refcounted.refcount.sub(1, RELAXED) == 0)
{
thread::fence(ACQUIRE);
$if $defined(refcounted.dealloc):
refcounted.dealloc();
$else
free(refcounted);
$endif
}
}

View File

@@ -1,5 +1,7 @@
module std::core::string;
import std::io;
import std::core::mem::allocator;
typedef String @if(!$defined(String)) = inline char[];
<*
@@ -107,6 +109,19 @@ fn String format(Allocator allocator, String fmt, args...) @format(1) => @pool()
return str.copy_str(allocator);
}
<*
Return a new String created using the formatting function.
@param [inout] buffer : `The buffer to use`
@param [in] fmt : `The formatting string`
*>
fn String bformat(char[] buffer, String fmt, args...) @format(1)
{
DString str = dstring::new_with_capacity(allocator::wrap(buffer), fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.str_view();
}
<*
Return a temporary String created using the formatting function.
@@ -582,6 +597,7 @@ fn bool ZString.eq(self, ZString other) @operator(==)
char* a = self;
char* b = other;
if (a == b) return true;
if (!a || !b) return false;
for (;; a++, b++)
{
char c = *a;
@@ -608,12 +624,17 @@ fn usz ZString.char_len(str)
fn usz ZString.len(self)
{
usz len = 0;
char* ptr = (char*)self;
while (char c = ptr++[0]) len++;
usz len;
for (char* ptr = (char*)self; *ptr; ptr++) len++;
return len;
}
fn usz WString.len(self)
{
usz len;
for (Char16* ptr = (Char16*)self; *ptr; ptr++) len++;
return len;
}
fn ZString String.zstr_copy(self, Allocator allocator)
{
@@ -756,6 +777,92 @@ fn String String.to_upper_copy(self, Allocator allocator)
return copy;
}
fn String String.capitalize_copy(self, Allocator allocator)
{
String s = self.copy(allocator);
if (s.len > 0 && s[0].is_lower())
{
s[0] &= (char)~0x20;
}
return s;
}
<*
Convert a string from `snake_case` to PascalCase.
@param [in] self
@return `"FooBar" from "foo_bar" the resulting pointer may safely be cast to ZString.`
*>
fn String String.snake_to_pascal_copy(self, Allocator allocator)
{
Splitter splitter = self.tokenize("_");
char[] new_string = allocator::alloc_array(allocator, char, self.len + 1);
usz index = 0;
while (try s = splitter.next())
{
assert(s.len > 0);
char c = s[0];
if (c.is_lower()) c = c.to_upper();
new_string[index++] = c;
s = s[1..];
new_string[index:s.len] = s[..];
index += s.len;
}
new_string[index] = 0;
return (String)new_string[:index];
}
<*
Movifies the current string from `snake_case` to PascalCase.
@param [inout] self
*>
fn void String.convert_snake_to_pascal(&self)
{
Splitter splitter = self.tokenize("_");
String new_string = *self;
usz index = 0;
while (try s = splitter.next())
{
assert(s.len > 0);
char c = s[0];
if (c.is_lower()) c = c.to_upper();
new_string[index++] = c;
s = s[1..];
new_string[index:s.len] = s[..];
index += s.len;
}
*self = new_string[:index];
}
<*
Convert a string from `PascalCase` to `snake_case`.
@param [in] self
@return `"foo_bar" from "FooBar" the resulting pointer may safely be cast to ZString.`
*>
fn String String.pascal_to_snake_copy(self, Allocator allocator) => @pool()
{
DString d;
d.init(tmem, (usz)(self.len * 1.5));
usz index = 0;
foreach (i, c : self)
{
if (c.is_upper())
{
if (i > 0 && ((self[i - 1].is_lower() || self[i - 1].is_digit()) || (i < self.len - 1 && self[i + 1].is_lower())))
{
d.append_char('_');
}
d.append_char(c.to_lower());
continue;
}
d.append_char(c);
}
return d.copy_str(allocator);
}
fn StringIterator String.iterator(self)
{
return { self, 0 };

View File

@@ -0,0 +1,233 @@
// Copyright (c) 2024 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
<*
This module provides functionality for escaping and unescaping strings
with standard C-style escape sequences, similar to what's used in JSON
and other string literals.
*>
module std::core::string;
import std::io;
faultdef INVALID_ESCAPE_SEQUENCE, UNTERMINATED_STRING, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE;
<*
Escape a string by adding quotes and converting special characters to escape sequences.
@param allocator : "The allocator to use for the result"
@param s : "The string to escape"
@param strip_quotes : "Do not include beginning and end quotes, defaults to false"
@return "The escaped string with surrounding quotes, can safely be cast to ZString"
*>
fn String String.escape(String s, Allocator allocator, bool strip_quotes = true)
{
// Conservative allocation: most strings need minimal escaping
usz initial_capacity = s.len + s.len / 5 + 2; // ~1.2x + quotes
DString result = dstring::new_with_capacity(allocator, initial_capacity);
if (!strip_quotes) result.append_char('"');
foreach (char c : s)
{
switch (c)
{
case '"': result.append(`\"`);
case '\\': result.append(`\\`);
case '\b': result.append(`\b`);
case '\f': result.append(`\f`);
case '\n': result.append(`\n`);
case '\r': result.append(`\r`);
case '\t': result.append(`\t`);
case '\v': result.append(`\v`);
case '\0': result.append(`\0`);
default:
if (c >= 32 && c <= 126)
{
// Printable ASCII
result.append_char(c);
}
else
{
// Non-printable, use hex escape
result.appendf("\\x%02x", (uint)c);
}
}
}
if (!strip_quotes) result.append_char('"');
return result.copy_str(allocator);
}
<*
Escape a string using the temp allocator.
@param s : "The string to escape"
@param strip_quotes : "Do not include beginning and end quotes, defaults to false"
@return "The escaped string with surrounding quotes"
*>
fn String String.tescape(String s, bool strip_quotes = false) => s.escape(tmem, strip_quotes);
<*
Calculate the length needed for an escaped string (including quotes).
@param s : "The string to check"
@return "The length needed for the escaped version"
*>
fn usz escape_len(String s)
{
usz len = 2; // For quotes
foreach (char c : s)
{
switch (c)
{
case '"':
case '\\':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
case '\0':
len += 2; // \X
default:
if (c >= 32 && c <= 126)
{
len += 1;
}
else
{
len += 4; // \xHH
}
}
}
return len;
}
<*
Unescape a quoted string by parsing escape sequences.
@param allocator : "The allocator to use for the result"
@param s : "The quoted string to unescape"
@param allow_unquoted : "Set to true to unescape strings not surrounded by quotes, defaults to false"
@return "The unescaped string without quotes, safe to convert to ZString"
@return? UNTERMINATED_STRING, INVALID_ESCAPE_SEQUENCE, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE
*>
fn String? String.unescape(String s, Allocator allocator, bool allow_unquoted = false)
{
if (s.len >= 2 && s[0] == '"' && s[^1] == '"')
{
// Remove quotes.
s = s[1:^2];
}
else if (!allow_unquoted) return UNTERMINATED_STRING?;
// Handle empty string case
if (!s.len)
{
return "".copy(allocator);
}
DString result = dstring::new_with_capacity(allocator, s.len);
usz len = s.len;
for (usz i = 0; i < len; i++)
{
char c = s[i];
if (c != '\\')
{
result.append_char(c);
continue;
}
// Handle escape sequence
if (i + 1 >= len) return INVALID_ESCAPE_SEQUENCE?;
char escape_char = s[++i];
switch (escape_char)
{
case '"': result.append_char('"');
case '\\': result.append_char('\\');
case '/': result.append_char('/');
case 'b': result.append_char('\b');
case 'f': result.append_char('\f');
case 'n': result.append_char('\n');
case 'r': result.append_char('\r');
case 't': result.append_char('\t');
case 'v': result.append_char('\v');
case '0': result.append_char('\0');
case 'x':
// Hex escape \xHH
if (i + 2 >= len) return INVALID_HEX_ESCAPE?;
char h1 = s[++i];
char h2 = s[++i];
if (!h1.is_xdigit() || !h2.is_xdigit()) return INVALID_HEX_ESCAPE?;
uint val = h1 > '9' ? (h1 | 32) - 'a' + 10 : h1 - '0';
val = val << 4;
val += h2 > '9' ? (h2 | 32) - 'a' + 10 : h2 - '0';
result.append_char((char)val);
case 'u':
// Unicode escape \uHHHH
if (i + 4 >= len) return INVALID_UNICODE_ESCAPE?;
uint val;
for (int j = 0; j < 4; j++)
{
char hex_char = s[++i];
if (!hex_char.is_xdigit()) return INVALID_UNICODE_ESCAPE?;
val = val << 4 + (hex_char > '9' ? (hex_char | 32) - 'a' + 10 : hex_char - '0');
}
result.append_char32(val);
case 'U':
// Unicode escape \UHHHHHHHH
if (i + 8 >= len) return INVALID_UNICODE_ESCAPE?;
uint val;
for (int j = 0; j < 8; j++)
{
char hex_char = s[++i];
if (!hex_char.is_xdigit()) return INVALID_UNICODE_ESCAPE?;
val = val << 4 + (hex_char > '9' ? (hex_char | 32) - 'a' + 10 : hex_char - '0');
}
result.append_char32(val);
default:
return INVALID_ESCAPE_SEQUENCE?;
}
}
return result.copy_str(allocator);
}
<*
Unescape a quoted string using the temp allocator.
@param s : "The quoted string to unescape"
@param allow_unquoted : "Set to true to unescape strings not surrounded by quotes, defaults to false"
@return "The unescaped string without quotes"
@return? UNTERMINATED_STRING, INVALID_ESCAPE_SEQUENCE, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE
*>
fn String? String.tunescape(String s, bool allow_unquoted = false) => s.unescape(tmem, allow_unquoted);
<*
Check if a character needs to be escaped in a string literal.
@param c : "The character to check"
@return "True if the character needs escaping"
*>
fn bool needs_escape(char c)
{
switch (c)
{
case '"':
case '\\':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
case '\0':
return true;
default:
return c < 32 || c > 126;
}
}

View File

@@ -462,7 +462,7 @@ macro String.to_real(chars, $Type) @private
$error "Unexpected type";
$endswitch
while (chars.len && chars[0] == ' ') chars = chars[1..];
chars = chars.trim();
if (!chars.len) return MALFORMED_FLOAT?;
if (chars.len != 1)
@@ -476,6 +476,9 @@ macro String.to_real(chars, $Type) @private
chars = chars[1..];
}
}
chars = chars.trim();
if (!chars.len) return MALFORMED_FLOAT?;
if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf;
if (chars == "NAN" || chars == "nan") return $Type.nan;

View File

@@ -2,9 +2,6 @@ module std::core::values;
import std::core::types;
macro typeid @typeid(#value) @const @builtin => $typeof(#value).typeid;
macro TypeKind @typekind(#value) @const @builtin => $typeof(#value).kindof;
macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $Type.typeid;
<*
Return true if two values have the same type before any conversions.
*>
@@ -18,8 +15,13 @@ macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to
macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_float($typeof(#value));
macro bool @is_vector(#value) @const => types::is_vector($typeof(#value));
macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2));
macro bool @assign_to(#value1, #value2) @const => @assignable_to(#value1, $typeof(#value2));
macro bool @is_lvalue(#value) => $defined(#value = #value);
macro bool @is_const(#foo) @const @builtin
{
var $v;
return $defined($v = #foo);
}
macro promote_int(x)
{

736
lib/std/crypto/ed25519.c3 Normal file
View File

@@ -0,0 +1,736 @@
/*
Ed25519 Digital Signature Algorithm
*/
module std::crypto::ed25519;
import std::hash::sha512;
alias Ed25519PrivateKey = char[32];
alias Ed25519PublicKey = char[Ed25519PrivateKey.len];
alias Ed25519Signature = char[2 * Ed25519PublicKey.len];
<*
Generate a public key from a private key.
@param [in] private_key : "32 bytes of cryptographically secure random data"
@require private_key.len == Ed25519PrivateKey.len
*>
fn Ed25519PublicKey public_keygen(char[] private_key)
{
return pack(&&unproject(&&(BASE * expand_private_key(private_key)[:FBaseInt.len])));
}
<*
Sign a message.
@param [in] message
@param [in] private_key
@param [in] public_key
@require private_key.len == Ed25519PrivateKey.len
@require public_key.len == Ed25519PublicKey.len
*>
fn Ed25519Signature sign(char[] message, char[] private_key, char[] public_key)
{
Ed25519Signature r @noinit;
char[*] exp = expand_private_key(private_key);
Sha512 sha @noinit;
sha.init();
sha.update(exp[FBaseInt.len..]);
sha.update(message);
FBaseInt k = from_bytes(&&sha.final());
r[:F25519Int.len] = pack(&&unproject(&&(BASE * k[..])))[..];
sha.init();
sha.update(r[:F25519Int.len]);
sha.update(public_key);
sha.update(message);
FBaseInt z = from_bytes(&&sha.final());
FBaseInt e = from_bytes(exp[:FBaseInt.len]);
r[F25519Int.len..] = (z * e + k)[..];
return r;
}
<*
Verify the signature of a message.
@param [in] message
@param [in] signature
@param [in] public_key
@require signature.len == Ed25519Signature.len
@require public_key.len == Ed25519PublicKey.len
*>
fn bool verify(char[] message, char[] signature, char[] public_key)
{
char ok = 1;
F25519Int lhs = pack(&&unproject(&&(BASE * signature[F25519Int.len..])));
Unpacking unp_p = unpack_on_curve((F25519Int*)public_key);
Projection p = project(&unp_p.point);
ok &= unp_p.on_curve;
Sha512 sha @noinit;
sha.init();
sha.update(signature[:F25519Int.len]);
sha.update(public_key);
sha.update(message);
FBaseInt z = from_bytes(&&sha.final());
p = p * z[..];
Unpacking unp_q = unpack_on_curve((F25519Int*)signature[:F25519Int.len]);
Projection q = project(&unp_q.point);
ok &= unp_q.on_curve;
p = p + q;
F25519Int rhs = pack(&&unproject(&p));
return (bool)(ok & eq(&lhs, &rhs));
}
// Base point for Ed25519. Generate a subgroup of order 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
const Projection BASE @private =
{
x"1ad5258f602d56c9 b2a7259560c72c69 5cdcd6fd31e2a4c0 fe536ecdd3366921",
x"5866666666666666 6666666666666666 6666666666666666 6666666666666666",
x"a3ddb7a5b38ade6d f5525177809ff020 7de3ab648e4eea66 65768bd70f5f8767",
ONE
};
<*
Compute the pruned SHA-512 hash of a private key.
@param [in] private_key
@require private_key.len == Ed25519PrivateKey.len
*>
fn char[sha512::HASH_SIZE] expand_private_key(char[] private_key) @local
{
char[*] r = sha512::hash(private_key);
r[0] &= 0b11111000;
r[FBaseInt.len - 1] &= 0b01111111;
r[FBaseInt.len - 1] |= 0b01000000;
return r;
}
/*
Operations on the twisted Edwards curve -x^2+y^2=1-121665/121666*x^2*y^2 over the prime field F_(2^255-19) (edwards25519)
The set of F_(2^255-19)-rational curve points is a group of order 2^3*(2^252+0x14def9dea2f79cd65812631a5cf5d3ed)
*/
module std::crypto::ed25519 @private;
// Affine coordinates.
struct Point
{
F25519Int x;
F25519Int y;
}
// Projective coordinates.
struct Projection
{
F25519Int x;
F25519Int y;
F25519Int t;
F25519Int z;
}
// Neutral.
const Projection NEUTRAL =
{
ZERO,
ONE,
ZERO,
ONE
};
<*
Convert affine to projective coordinates.
@param [&in] p
*>
fn Projection project(Point* p) => { p.x, p.y, p.x * p.y, ONE };
<*
Convert projective to affine coordinates.
@param [&in] p
*>
fn Point unproject(Projection* p)
{
Point r @noinit;
F25519Int inv = p.z.inv();
r.x = p.x * inv;
r.y = p.y * inv;
r.x.normalize();
r.y.normalize();
return r;
}
// d parameter for edwards25519 : -121665/121666
const F25519Int D = x"a3785913ca4deb75 abd841414d0a7000 98e879777940c78c 73fe6f2bee6c0352";
// 2*d
const F25519Int DD = x"59f1b226949bd6eb 56b183829a14e000 30d1f3eef2808e19 e7fcdf56dcd90624";
<*
Compress a point.
@param [&in] p
*>
fn F25519Int pack(Point* p)
{
Point r = *p;
r.x.normalize();
r.y.normalize();
r.y[^1] |= (r.x[0] & 1) << 7;
return r.y;
}
struct Unpacking
{
Point point;
char on_curve; // Non-zero if true.
}
<*
Uncompress a point. Check if it is on the curve.
@param [&in] encoding
*>
fn Unpacking unpack_on_curve(F25519Int* encoding)
{
Point p @noinit;
char parity = (*encoding)[^1] >> 7;
p.y = *encoding;
p.y[^1] &= 0b01111111;
F25519Int y2 = p.y * p.y;
F25519Int x2 = (D * y2 + ONE).inv() * (y2 - ONE);
F25519Int x = x2.sqrt();
p.x = f25519_select(&x, &&-x, (x[0] ^ parity) & 1);
F25519Int _x2 = p.x * p.x;
x2.normalize();
_x2.normalize();
return {p, eq(&x2, &_x2)};
}
macro Projection Projection.@add(&s, Projection #p) @operator(+) => s.add(@addr(#p));
<*
Addition.
@param [&in] s
*>
fn Projection Projection.add(&s, Projection* p) @operator(+)
{
Projection r @noinit;
F25519Int a = (s.y - s.x) * (p.y - p.x);
F25519Int b = (s.y + s.x) * (p.y + p.x);
F25519Int c = s.t * DD * p.t;
F25519Int d = (s.z * p.z).mul_s(2);
F25519Int e = b - a;
F25519Int f = d - c;
F25519Int g = d + c;
F25519Int h = b + a;
r.x = e * f;
r.y = g * h;
r.t = e * h;
r.z = f * g;
return r;
}
<*
Double a point.
@param [&in] s
*>
fn Projection Projection.twice(&s)
{
Projection r @noinit;
F25519Int a = s.x * s.x;
F25519Int b = s.y * s.y;
F25519Int c = (s.z * s.z).mul_s(2);
F25519Int d = s.x + s.y;
F25519Int e = d * d - a - b;
F25519Int g = b - a;
F25519Int f = g - c;
F25519Int h = -b - a;
r.x = e * f;
r.y = g * h;
r.t = e * h;
r.z = f * g;
return r;
}
<*
Variable base scalar multiplication.
@param [&in] s
@param [in] n
*>
fn Projection Projection.mul(&s, char[] n) @operator(*)
{
Projection r = NEUTRAL;
for (isz i = n.len << 3 - 1; i >= 0; i--)
{
r = r.twice();
Projection t = r + s;
char bit = n[i >> 3] >> (i & 7) & 1;
r.x = f25519_select(&r.x, &t.x, bit);
r.y = f25519_select(&r.y, &t.y, bit);
r.z = f25519_select(&r.z, &t.z, bit);
r.t = f25519_select(&r.t, &t.t, bit);
}
return r;
}
/*
Modular arithmetic over the prime field F_(2^255-19)
*/
module std::crypto::ed25519 @private;
typedef F25519Int = inline char[32];
const F25519Int ZERO = {};
const F25519Int ONE = {[0] = 1};
<*
Reduce an element with carry to at most 2^255+18 (32 bytes)
@param [&inout] s
*>
fn void F25519Int.reduce_carry(&s, uint carry)
{
// Reduce using 2^255 = 19 mod p
(*s)[^1] &= 0b01111111;
carry *= 19;
foreach (i, &v : s)
{
carry += *v;
*v = (char)carry;
carry >>= 8;
}
}
<*
Reduce an element to at most 2^255-19
@param [&inout] s
*>
fn void F25519Int.normalize(&s)
{
s.reduce_carry((*s)[^1] >> 7);
// Substract p
F25519Int sub @noinit;
ushort c = 19;
foreach (i, v : (*s)[:^1])
{
c += v;
sub[i] = (char)c;
c >>= 8;
}
c += (*s)[^1] - 0b10000000;
sub[^1] = (char)c;
*s = f25519_select(&sub, s, (char)(c >> 15));
}
<*
Constant-time equality comparison. Return is non-zero if true.
@param [&in] a
@param [&in] b
*>
fn char eq(F25519Int* a, F25519Int* b)
{
char e;
foreach (i, v : a) e |= v ^ (*b)[i];
e |= (e >> 4);
e |= (e >> 2);
e |= (e >> 1);
return e ^ 1;
}
<*
Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1.
@param [&in] zero : "selected if condition is 0"
@param [&in] one : "selected if condition is 1"
*>
fn F25519Int f25519_select(F25519Int* zero, F25519Int* one, char condition)
{
F25519Int r @noinit;
foreach (i, z : zero) r[i] = z ^ (-condition & ((*one)[i] ^ z));
return r;
}
macro F25519Int F25519Int.@add(&s, F25519Int #n) @operator(+) => s.add(@addr(#n));
<*
Addition.
@param [&in] s
@param [&in] n
*>
fn F25519Int F25519Int.add(&s, F25519Int* n) @operator(+)
{
F25519Int r @noinit;
ushort c;
foreach (i, v : s)
{
c >>= 8;
c += v + (*n)[i];
r[i] = (char)c;
}
r.reduce_carry(c >> 7);
return r;
}
macro F25519Int F25519Int.@sub(&s, F25519Int #n) @operator(-) => s.sub(@addr(#n));
<*
Substraction.
@param [&in] s
@param [&in] n
*>
fn F25519Int F25519Int.sub(&s, F25519Int* n) @operator(-)
{
// Compute s+2*p-n instead of s-n to avoid underflow.
F25519Int r @noinit;
uint c = (char)~(2 * 19 - 1);
foreach (i, v : (*s)[:^1])
{
c += 0b11111111_00000000 + v - (*n)[i];
r[i] = (char)c;
c >>= 8;
}
c += (*s)[^1] - (*n)[^1];
r[^1] = (char)c;
r.reduce_carry(c >> 7);
return r;
}
<*
Negation.
@param [&in] s
*>
fn F25519Int F25519Int.neg(&s) @operator(-)
{
// Compute 2*p-s instead of -s to avoid underflow.
F25519Int r @noinit;
uint c = (char)~(2 * 19 - 1);
foreach (i, v : (*s)[:^1])
{
c += 0b11111111_00000000 - v;
r[i] = (char)c;
c >>= 8;
}
c -= (*s)[^1];
r[^1] = (char)c;
r.reduce_carry(c >> 7);
return r;
}
macro F25519Int F25519Int.@mul(&s, F25519Int #n) @operator(*) => s.mul(@addr(#n));
<*
Multiplication.
@param [&in] s
@param [&in] n
*>
fn F25519Int F25519Int.mul(&s, F25519Int* n) @operator(*)
{
F25519Int r @noinit;
uint c;
for (usz i = 0; i < F25519Int.len; i++)
{
c >>= 8;
for (usz j; j <= i; j++) c += (*s)[j] * (*n)[i - j];
// Reduce using 2^256 = 2*19 mod p
for (usz j = i + 1; j < F25519Int.len; j++) c += (*s)[j] * (*n)[^j - i] * 2 * 19;
r[i] = (char)c;
}
r.reduce_carry(c >> 7);
return r;
}
<*
Multiplication by a small element.
@param [&in] s
*>
fn F25519Int F25519Int.mul_s(&s, uint n)
{
F25519Int r @noinit;
uint c;
foreach (i, v : s)
{
c >>= 8;
c += v * n;
r[i] = (char)c;
}
r.reduce_carry(c >> 7);
return r;
}
<*
Inverse an element.
@param [&in] s
*>
fn F25519Int F25519Int.inv(&s)
{
//Compute s^(p-2)
F25519Int r = *s;
for (usz i; i < 255 - 1 - 5; i++) r = r * r * s;
r *= r;
r = r * r * s;
r *= r;
r = r * r * s;
r = r * r * s;
return r;
}
<*
Raise an element to the power of 2^252-3
@param [&in] s
*>
fn F25519Int F25519Int.pow_2523(&s) @local
{
F25519Int r = *s;
for (usz i; i < 252 - 1 - 2; i++) r = r * r * s;
r *= r;
r = r * r * s;
return r;
}
<*
Compute the square root of an element.
@param [&in] s
*>
fn F25519Int F25519Int.sqrt(&s)
{
F25519Int twice = s.mul_s(2);
F25519Int pow = twice.pow_2523();
return (twice * pow * pow - ONE) * s * pow;
}
/*
Modular arithmetic over the prime field F_(2^252+0x14def9dea2f79cd65812631a5cf5d3ed)
*/
module std::crypto::ed25519 @private;
import std::math;
typedef FBaseInt = inline char[32];
// Order of the field : 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
const FBaseInt ORDER = x"edd3f55c1a631258 d69cf7a2def9de14 0000000000000000 0000000000000010";
<*
Interpret bytes as a normalized element.
@param [in] bytes
*>
fn FBaseInt from_bytes(char[] bytes)
{
FBaseInt r;
usz bitc = min(252 - 1, bytes.len << 3);
usz bytec = bitc >> 3;
usz mod = bitc & 7;
usz rem = bytes.len << 3 - bitc;
r[:bytec] = bytes[^bytec..];
if (mod)
{
r <<= mod;
r[0] |= bytes[^bytec + 1] >> (8 - mod);
}
for (isz i = rem - 1; i >= 0; i--)
{
r <<= 1;
r[0] |= bytes[i >> 3] >> (i & 7) & 1;
r = r.sub_l(&ORDER);
}
return r;
}
<*
Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1.
@param [&in] zero : "selected if condition is 0"
@param [&in] one : "selected if condition is 1"
*>
fn FBaseInt fbase_select(FBaseInt* zero, FBaseInt* one, char condition)
{
FBaseInt r @noinit;
foreach (i, z : zero) r[i] = z ^ (-condition & ((*one)[i] ^ z));
return r;
}
macro FBaseInt FBaseInt.@add(&s, FBaseInt #n) @operator(+) => s.add(@addr(#n));
<*
Addition.
@param [&in] s
@param [&in] n
*>
fn FBaseInt FBaseInt.add(&s, FBaseInt* n) @operator(+)
{
FBaseInt r @noinit;
ushort c;
foreach (i, v : s)
{
c += v + (*n)[i];
r[i] = (char)c;
c >>= 8;
}
return r.sub_l(&ORDER);
}
<*
Substraction if RHS is less than LHS else identity.
@param [&in] s
@param [&in] n
*>
fn FBaseInt FBaseInt.sub_l(&s, FBaseInt* n)
{
FBaseInt sub @noinit;
ushort c;
foreach (i, v : s)
{
c = v - (*n)[i] - c;
sub[i] = (char)c;
c = (c >> 8) & 1;
}
return fbase_select(&sub, s, (char)c);
}
<*
Left shift.
@param [&in] s
*>
fn FBaseInt FBaseInt.shl(&s, usz n) @operator(<<)
{
FBaseInt r @noinit;
ushort c;
foreach (i, v : s)
{
c |= v << n;
r[i] = (char)c;
c >>= 8;
}
return r;
}
macro FBaseInt FBaseInt.@mul(&s, FBaseInt #n) @operator(*) => s.mul(@addr(#n));
<*
Multiplication.
@param [&in] s
@param [&in] n
*>
fn FBaseInt FBaseInt.mul(&s, FBaseInt* n) @operator(*)
{
FBaseInt r;
for (isz i = 252; i >= 0; i--)
{
r = (r << 1).sub_l(&ORDER);
r = fbase_select(&r, &&(r + s), (*n)[i >> 3] >> (i & 7) & 1);
}
return r;
}

View File

@@ -5,7 +5,9 @@ module std::encoding::json;
import std::io;
import std::collections::object;
faultdef UNEXPECTED_CHARACTER, INVALID_ESCAPE_SEQUENCE, DUPLICATE_MEMBERS, INVALID_NUMBER;
faultdef UNEXPECTED_CHARACTER, INVALID_ESCAPE_SEQUENCE, INVALID_NUMBER, MAX_DEPTH_REACHED;
int max_depth = 128;
fn Object*? parse_string(Allocator allocator, String s)
{
@@ -24,7 +26,15 @@ fn Object*? parse(Allocator allocator, InStream s)
JsonContext context = { .last_string = dstring::new_with_capacity(smem, 64), .stream = s, .allocator = allocator };
@pool()
{
return parse_any(&context);
Object* o = parse_any(&context)!;
defer catch o.free();
while (char c = read_next(&context)!, c != 0)
{
if (c.is_space()) continue;
return UNEXPECTED_CHARACTER?;
}
if (!@catch(context.stream.read_byte())) return UNEXPECTED_CHARACTER?;
return o;
};
};
}
@@ -62,11 +72,14 @@ struct JsonContext @local
DString last_string;
double last_number;
char current;
bitstruct : char {
int depth;
bitstruct : char
{
bool skip_comments;
bool reached_end;
bool pushed_back;
}
}
@@ -105,10 +118,16 @@ fn JsonTokenType? lex_number(JsonContext *context, char c) @local
t.append(c);
c = read_next(context)!;
}
bool leading_zero = c == '0';
while (c.is_digit())
{
t.append(c);
c = read_next(context)!;
if (leading_zero)
{
if (c.is_digit()) return INVALID_NUMBER?;
leading_zero = false;
}
}
if (c == '.')
{
@@ -148,6 +167,8 @@ fn Object*? parse_map(JsonContext* context) @local
Object* map = object::new_obj(context.allocator);
defer catch map.free();
JsonTokenType token = advance(context)!;
defer context.depth--;
if (++context.depth >= max_depth) return json::MAX_DEPTH_REACHED?;
@stack_mem(256; Allocator mem)
{
@@ -156,7 +177,6 @@ fn Object*? parse_map(JsonContext* context) @local
{
if (token != JsonTokenType.STRING) return UNEXPECTED_CHARACTER?;
DString string = context.last_string;
if (map.has_key(string.str_view())) return DUPLICATE_MEMBERS?;
// Copy the key to our temp holder, since our
// last_string may be used in parse_any
temp_key.clear();
@@ -180,6 +200,8 @@ fn Object*? parse_array(JsonContext* context) @local
{
Object* list = object::new_obj(context.allocator);
defer catch list.free();
defer context.depth--;
if (++context.depth >= max_depth) return json::MAX_DEPTH_REACHED?;
JsonTokenType token = advance(context)!;
while (token != JsonTokenType.RBRACKET)
{
@@ -248,7 +270,7 @@ fn JsonTokenType? advance(JsonContext* context) @local
case '\v':
continue;
case '/':
if (!context.skip_comments) break;
if (!context.skip_comments) break WS;
c = read_next(context)!;
if (c != '*')
{

96
lib/std/hash/a5hash.c3 Normal file
View File

@@ -0,0 +1,96 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// An implementation of Aleksey Vaneev's a5hash, version 5.16, in C3:
// https://github.com/avaneev/komihash
//
// The license for komihash from the above repository at the time of writing is as follows:
//
// >> MIT License
// >>
// >> Copyright (c) 2025 Aleksey Vaneev
// >>
// >> Permission is hereby granted, free of charge, to any person obtaining a copy
// >> of this software and associated documentation files (the "Software"), to deal
// >> in the Software without restriction, including without limitation the rights
// >> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// >> copies of the Software, and to permit persons to whom the Software is
// >> furnished to do so, subject to the following conditions:
// >>
// >> The above copyright notice and this permission notice shall be included in all
// >> copies or substantial portions of the Software.
// >>
// >> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// >> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// >> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// >> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// >> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// >> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// >> SOFTWARE.
//
//
module std::hash::a5hash;
macro @a5mul(#u, #v, #lo, #hi) @local
{
uint128 imd = (uint128)#u * (uint128)#v;
#lo = (ulong)imd;
#hi = (ulong)(imd >> 64);
}
fn ulong hash(char[] data, ulong seed = 0)
{
ulong seed1 = 0x243F_6A88_85A3_08D3 ^ data.len;
ulong seed2 = 0x4528_21E6_38D0_1377 ^ data.len;
ulong val10 = 0xAAAA_AAAA_AAAA_AAAA;
ulong val01 = 0x5555_5555_5555_5555;
ulong a, b;
@a5mul(seed2 ^ (seed & val10), seed1 ^ (seed & val01), seed1, seed2);
val10 ^= seed2;
if (@likely(data.len > 3))
{
if (data.len > 16)
{
val01 ^= seed1;
for (; data.len > 16; data = data[16..])
{
@a5mul(
@unaligned_load(((ulong*)data.ptr)[0], 1) ^ seed1,
@unaligned_load(((ulong*)data.ptr)[1], 1) ^ seed2,
seed1, seed2
);
seed1 += val01;
seed2 += val10;
}
a = @unaligned_load(*(ulong*)(data.ptr + (uptr)data.len - 16), 1);
b = @unaligned_load(*(ulong*)(data.ptr + (uptr)data.len - 8), 1);
}
else
{
a = ((ulong)@unaligned_load(*(uint*)&data[0], 1) << 32)
| @unaligned_load(*(uint*)&data[^4], 1);
b = ((ulong)@unaligned_load(*(uint*)&data[(data.len >> 3) * 4], 1) << 32)
| @unaligned_load(*(uint*)(data.ptr + data.len - 4 - (data.len >> 3) * 4), 1);
}
}
else
{
a = data.len ? (data[0] | (data.len > 1 ? ((ulong)data[1] << 8) : 0) | (data.len > 2 ? ((ulong)data[2] << 16) : 0)) : 0;
b = 0;
}
@a5mul(a ^ seed1, b ^ seed2, seed1, seed2);
@a5mul(val01 ^ seed1, seed2, a, b);
return a ^ b;
}

156
lib/std/hash/komi.c3 Normal file
View File

@@ -0,0 +1,156 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// An implementation of Aleksey Vaneev's komihash, version 5.27, in C3:
// https://github.com/avaneev/komihash
//
// The license for komihash from the above repository at the time of writing is as follows:
//
// >> MIT License
// >>
// >> Copyright (c) 2021-2025 Aleksey Vaneev
// >>
// >> Permission is hereby granted, free of charge, to any person obtaining a copy
// >> of this software and associated documentation files (the "Software"), to deal
// >> in the Software without restriction, including without limitation the rights
// >> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// >> copies of the Software, and to permit persons to whom the Software is
// >> furnished to do so, subject to the following conditions:
// >>
// >> The above copyright notice and this permission notice shall be included in all
// >> copies or substantial portions of the Software.
// >>
// >> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// >> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// >> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// >> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// >> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// >> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// >> SOFTWARE.
//
//
module std::hash::komi;
macro @komimul(#u, #v, #lo, #hi) @local
{
uint128 imd = (uint128)#u * (uint128)#v;
#lo = (ulong)imd;
#hi += (ulong)(imd >> 64);
}
fn ulong hash(char[] data, ulong seed = 0)
{
ulong seed1 = 0x243F_6A88_85A3_08D3 ^ (seed & 0x5555_5555_5555_5555);
ulong seed5 = 0x4528_21E6_38D0_1377 ^ (seed & 0xAAAA_AAAA_AAAA_AAAA);
ulong r1h, r2h;
// HASHROUND
@komimul(seed1, seed5, seed1, seed5);
seed1 ^= seed5;
if (@likely(data.len < 16))
{
r1h = seed1;
r2h = seed5;
if (@likely(data.len >= 8))
{
r1h ^= @unaligned_load(*(ulong*)data.ptr, 1);
r2h ^= (data.len < 12)
? ((data[data.len - 3] | ((ulong)data[data.len - 2] << 8) | ((ulong)data[data.len - 1] << 16) | ((ulong)1 << 24)) >> ((data.len * 8) ^ 88))
: (((@unaligned_load(*(uint*)&data[^4], 1) | ((ulong)1 << 32)) >> (128 - data.len * 8)) << 32 | @unaligned_load(*(uint*)&data[8], 1));
}
else if (data.len != 0)
{
r1h ^= (data.len < 4)
? (((ulong)1 << (data.len * 8)) ^ data[0] ^ (data.len > 1 ? (ulong)data[1] << 8 : 0) ^ (data.len > 2 ? (ulong)data[2] << 16 : 0))
: (((@unaligned_load(*(uint*)&data[^4], 1) | ((ulong)1 << 32)) >> (64 - data.len * 8)) << 32 | @unaligned_load(*(uint*)&data[0], 1));
}
}
else if (data.len < 32)
{
// HASH16
@komimul(
@unaligned_load(*(ulong*)&data[0], 1) ^ seed1,
@unaligned_load(*(ulong*)&data[8], 1) ^ seed5,
seed1, seed5
);
seed1 ^= seed5;
if (data.len < 24)
{
r1h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> (((int)(data.len * 8) ^ 184))) ^ seed1;
r2h = seed5;
}
else
{
r1h = @unaligned_load(*(ulong*)&data[16], 1) ^ seed1;
r2h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> (((int)(data.len * 8) ^ 248))) ^ seed5;
}
}
else
{
if (data.len >= 64)
{
ulong[8] seeds = {
seed1, 0x1319_8A2E_0370_7344 ^ seed1, 0xA409_3822_299F_31D0 ^ seed1, 0x082E_FA98_EC4E_6C89 ^ seed1,
seed5, 0xBE54_66CF_34E9_0C6C ^ seed5, 0xC0AC_29B7_C97C_50DD ^ seed5, 0x3F84_D5B5_B547_0917 ^ seed5,
};
// HASHLOOP64
for (; data.len >= 64; data = data[64:^64])
{
$for var $x = 0; $x < 4; ++$x :
@komimul(
@unaligned_load(*(ulong*)&data[0 + ($x * 8)], 1) ^ seeds[$x],
@unaligned_load(*(ulong*)&data[32 + ($x * 8)], 1) ^ seeds[4 + $x],
seeds[$x], seeds[4 + $x]
);
$endfor
seeds[3] ^= seeds[6];
seeds[0] ^= seeds[7];
seeds[2] ^= seeds[5];
seeds[1] ^= seeds[4];
}
seed1 = seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3];
seed5 = seeds[4] ^ seeds[5] ^ seeds[6] ^ seeds[7];
}
for (; data.len >= 16; data = data[16:^16])
{
@komimul(
@unaligned_load(*(ulong*)&data[0], 1) ^ seed1,
@unaligned_load(*(ulong*)&data[8], 1) ^ seed5,
seed1, seed5
);
seed1 ^= seed5;
}
if (data.len < 8)
{
// NOTE: This is translated from the original code. It grabs the last ulong off the buffer even though the
// data slice is less than 8 bytes. This is possible because this branch only occurs in a loop where
// the original data slice length is >= 32.
r1h = (((@unaligned_load(*(ulong*)(data.ptr + data.len - 8), 1) >> 8) | ((ulong)1 << 56)) >> ((data.len * 8) ^ 0x38)) ^ seed1;
r2h = seed5;
}
else
{
r1h = @unaligned_load(*(ulong*)data.ptr, 1) ^ seed1;
r2h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> ((data.len * 8) ^ 0x78)) ^ seed5;
}
}
// HASHFIN
@komimul(r1h, r2h, seed1, seed5);
seed1 ^= seed5;
@komimul(seed1, seed5, seed1, seed5);
seed1 ^= seed5;
return seed1;
}

149
lib/std/hash/metro128.c3 Normal file
View File

@@ -0,0 +1,149 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// MetroHash64 and MetroHash128 are different enough to warrant their own
// modules, and there would be no reason to create a generic module just
// for the two. If you inspect the differences, the only shared portion
// of the entire process is the `update` method.
//
module std::hash::metro128;
const ulong[4] K @local = {
0xc83a91e1,
0x8648dbdb,
0x7bdec03b,
0x2f5870a5,
};
struct MetroHash128
{
union
{
ulong[4] state;
uint128 result;
}
union
{
ulong[4] stomach_64;
char[32] stomach;
}
ulong bytes;
}
fn uint128 hash(char[] data, ulong seed = 0)
{
MetroHash128 m;
m.init(seed);
m.update(data);
return m.final();
}
fn void MetroHash128.init(&self, ulong seed = 0)
{
self.state = {
(seed - K[0]) * K[3],
(seed + K[1]) * K[2],
(seed + K[0]) * K[2],
(seed - K[1]) * K[3],
};
}
fn void MetroHash128.update(&self, char[] data)
{
if (self.bytes % 32) // partial buffer
{
ulong to_fill = min(data.len, (32 - (self.bytes % 32)));
self.stomach[(self.bytes % 32):to_fill] = data[:to_fill];
data = data[to_fill..];
self.bytes += to_fill;
if (self.bytes % 32) return; // still awaiting more input, or final
self.state[0] += self.stomach_64[0] * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
self.state[1] += self.stomach_64[1] * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
self.state[2] += self.stomach_64[2] * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
self.state[3] += self.stomach_64[3] * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
}
self.bytes += data.len;
for (; data.len >= 32; data = data[32:^32])
{
self.state[0] += @unaligned_load(((ulong*)data.ptr)[0], 1) * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
self.state[1] += @unaligned_load(((ulong*)data.ptr)[1], 1) * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
self.state[2] += @unaligned_load(((ulong*)data.ptr)[2], 1) * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
self.state[3] += @unaligned_load(((ulong*)data.ptr)[3], 1) * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
}
// Gobble up the leftover bytes. Nom nom.
if (data.len > 0) self.stomach[:data.len] = data[..];
}
fn uint128 MetroHash128.final(&self)
{
if (self.bytes >= 32)
{
self.state[2] ^= (((self.state[0] + self.state[3]) * K[0]) + self.state[1]).rotr(21) * K[1];
self.state[3] ^= (((self.state[1] + self.state[2]) * K[1]) + self.state[0]).rotr(21) * K[0];
self.state[0] ^= (((self.state[0] + self.state[2]) * K[0]) + self.state[3]).rotr(21) * K[1];
self.state[1] ^= (((self.state[1] + self.state[3]) * K[1]) + self.state[2]).rotr(21) * K[0];
}
char[] final_data = self.stomach[:(self.bytes % 32)];
if (final_data.len >= 16)
{
self.state[0] += ((ulong*)final_data.ptr)[0] * K[2]; self.state[0] = self.state[0].rotr(33) * K[3];
self.state[1] += ((ulong*)final_data.ptr)[1] * K[2]; self.state[1] = self.state[1].rotr(33) * K[3];
self.state[0] ^= ((self.state[0] * K[2]) + self.state[1]).rotr(45) * K[1];
self.state[1] ^= ((self.state[1] * K[3]) + self.state[0]).rotr(45) * K[0];
final_data = final_data[16:^16];
}
if (final_data.len >= 8)
{
self.state[0] += @unaligned_load(((ulong*)final_data.ptr)[0], 1) * K[2]; self.state[0] = self.state[0].rotr(33) * K[3];
self.state[0] ^= ((self.state[0] * K[2]) + self.state[1]).rotr(27) * K[1];
final_data = final_data[8:^8];
}
if (final_data.len >= 4)
{
self.state[1] += @unaligned_load(((uint*)final_data.ptr)[0], 1) * K[2]; self.state[1] = self.state[1].rotr(33) * K[3];
self.state[1] ^= ((self.state[1] * K[3]) + self.state[0]).rotr(46) * K[0];
final_data = final_data[4:^4];
}
if (final_data.len >= 2)
{
self.state[0] += @unaligned_load(((ushort*)final_data.ptr)[0], 1) * K[2]; self.state[0] = self.state[0].rotr(33) * K[3];
self.state[0] ^= ((self.state[0] * K[2]) + self.state[1]).rotr(22) * K[1];
final_data = final_data[2:^2];
}
if (final_data.len >= 1)
{
self.state[1] += ((char*)final_data.ptr)[0] * K[2]; self.state[1] = self.state[1].rotr(33) * K[3];
self.state[1] ^= ((self.state[1] * K[3]) + self.state[0]).rotr(58) * K[0];
}
self.state[0] += ((self.state[0] * K[0]) + self.state[1]).rotr(13);
self.state[1] += ((self.state[1] * K[1]) + self.state[0]).rotr(37);
self.state[0] += ((self.state[0] * K[2]) + self.state[1]).rotr(13);
self.state[1] += ((self.state[1] * K[3]) + self.state[0]).rotr(37);
return self.result;
}

152
lib/std/hash/metro64.c3 Normal file
View File

@@ -0,0 +1,152 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// MetroHash64 and MetroHash128 are different enough to warrant their own
// modules, and there would be no reason to create a generic module just
// for the two. If you inspect the differences, the only shared portion
// of the entire process is the `update` method.
//
module std::hash::metro64;
const ulong[4] K @local = {
0xd6d018f5,
0xa2aa033b,
0x62992fc1,
0x30bc5b29,
};
struct MetroHash64
{
union
{
ulong[4] state;
ulong result;
}
union
{
ulong[4] stomach_64;
char[32] stomach;
}
ulong bytes;
ulong vseed;
}
fn ulong hash(char[] data, ulong seed = 0)
{
MetroHash64 m;
m.init(seed);
m.update(data);
return m.final();
}
fn void MetroHash64.init(&self, ulong seed = 0)
{
self.vseed = (seed + K[2]) * K[0];
self.state[0] = self.vseed;
self.state[1] = self.vseed;
self.state[2] = self.vseed;
self.state[3] = self.vseed;
}
fn void MetroHash64.update(&self, char[] data)
{
if (self.bytes % 32) // partial buffer
{
ulong to_fill = min(data.len, (32 - (self.bytes % 32)));
self.stomach[(self.bytes % 32):to_fill] = data[:to_fill];
data = data[to_fill..];
self.bytes += to_fill;
if (self.bytes % 32) return; // still awaiting more input, or final
self.state[0] += self.stomach_64[0] * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
self.state[1] += self.stomach_64[1] * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
self.state[2] += self.stomach_64[2] * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
self.state[3] += self.stomach_64[3] * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
}
self.bytes += data.len;
for (; data.len >= 32; data = data[32:^32])
{
self.state[0] += @unaligned_load(((ulong*)data.ptr)[0], 1) * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
self.state[1] += @unaligned_load(((ulong*)data.ptr)[1], 1) * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
self.state[2] += @unaligned_load(((ulong*)data.ptr)[2], 1) * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
self.state[3] += @unaligned_load(((ulong*)data.ptr)[3], 1) * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
}
// Gobble up the leftover bytes. Nom nom.
if (data.len > 0) self.stomach[:data.len] = data[..];
}
fn ulong MetroHash64.final(&self)
{
if (self.bytes >= 32)
{
self.state[2] ^= (((self.state[0] + self.state[3]) * K[0]) + self.state[1]).rotr(37) * K[1];
self.state[3] ^= (((self.state[1] + self.state[2]) * K[1]) + self.state[0]).rotr(37) * K[0];
self.state[0] ^= (((self.state[0] + self.state[2]) * K[0]) + self.state[3]).rotr(37) * K[1];
self.state[1] ^= (((self.state[1] + self.state[3]) * K[1]) + self.state[2]).rotr(37) * K[0];
self.state[0] = self.vseed + (self.state[0] ^ self.state[1]);
}
char[] final_data = self.stomach[:(self.bytes % 32)];
if (final_data.len >= 16)
{
self.state[1] = self.state[0] + @unaligned_load(((ulong*)final_data.ptr)[0], 1) * K[2]; self.state[1] = self.state[1].rotr(29) * K[3];
self.state[2] = self.state[0] + @unaligned_load(((ulong*)final_data.ptr)[1], 1) * K[2]; self.state[2] = self.state[2].rotr(29) * K[3];
self.state[1] ^= (self.state[1] * K[0]).rotr(21) + self.state[2];
self.state[2] ^= (self.state[2] * K[3]).rotr(21) + self.state[1];
self.state[0] += self.state[2];
final_data = final_data[16:^16];
}
if (final_data.len >= 8)
{
self.state[0] += @unaligned_load(((ulong*)final_data.ptr)[0], 1) * K[3];
self.state[0] ^= self.state[0].rotr(55) * K[1];
final_data = final_data[8:^8];
}
if (final_data.len >= 4)
{
self.state[0] += @unaligned_load(((uint*)final_data.ptr)[0], 1) * K[3];
self.state[0] ^= self.state[0].rotr(26) * K[1];
final_data = final_data[4:^4];
}
if (final_data.len >= 2)
{
self.state[0] += @unaligned_load(((ushort*)final_data.ptr)[0], 1) * K[3];
self.state[0] ^= self.state[0].rotr(48) * K[1];
final_data = final_data[2:^2];
}
if (final_data.len >= 1)
{
self.state[0] += ((char*)final_data.ptr)[0] * K[3];
self.state[0] ^= self.state[0].rotr(37) * K[1];
}
self.state[0] ^= self.state[0].rotr(28);
self.state[0] *= K[0];
self.state[0] ^= self.state[0].rotr(29);
return self.result;
}

164
lib/std/hash/siphash.c3 Normal file
View File

@@ -0,0 +1,164 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// SipHash is a secure pseudorandom function (PRF) which digests a 128-bit key
// and a variable-length message to produce a 64- or 128-bit hash value.
//
// SipHash can be employed in numerous useful ways and structures, e.g.:
// - Hash Tables
// - Message Authentication Codes
// - Denial of Service (hash flooding) resistance
// - Bloom filters
// - Keyed runtime identifier derivation
//
// Read more: https://en.wikipedia.org/wiki/SipHash
//
//
// COMMON HASH VARIANTS.
// These two forms of SipHash (24 and 48) are the most widely
// used by many implementations.
// These provide typical 64-bit hash results.
// -- Best for performance-critical applications.
module std::hash::siphash24;
import std::hash::siphash;
alias SipHash24 = SipHash { ulong, 2, 4 };
alias hash = siphash::hash { ulong, 2, 4 };
// -- Best for conservative security applications.
module std::hash::siphash48;
import std::hash::siphash;
alias SipHash48 = SipHash { ulong, 4, 8 };
alias hash = siphash::hash { ulong, 4, 8 };
// Exact same as above, but for 128-bit outputs. Algorithm internally changes slightly.
module std::hash::siphash24_128;
import std::hash::siphash;
alias SipHash24_128 = SipHash { uint128, 2, 4 };
alias hash = siphash::hash { uint128, 2, 4 };
module std::hash::siphash48_128;
import std::hash::siphash;
alias SipHash48_128 = SipHash { uint128, 4, 8 };
alias hash = siphash::hash { uint128, 4, 8 };
<*
@require OutType.typeid == uint128.typeid || OutType.typeid == ulong.typeid : "Module OutType must be either uint128 or ulong."
*>
module std::hash::siphash { OutType, BLOCK_ROUNDS, FINALIZE_ROUNDS };
struct SipHash
{
ulong[4] v;
long m;
int m_idx;
usz len;
}
fn OutType hash(char[] data, uint128 key)
{
SipHash s;
s.init(key);
s.update(data);
return s.final();
}
fn void SipHash.init(&self, uint128 key)
{
ulong[2] key_64 = bitcast(key, ulong[2]);
self.v = {
0x736f_6d65_7073_6575 ^ key_64[0],
0x646f_7261_6e64_6f6d ^ key_64[1],
0x6c79_6765_6e65_7261 ^ key_64[0],
0x7465_6462_7974_6573 ^ key_64[1],
};
$if OutType.typeid == uint128.typeid :
self.v[1] ^= 0xEE;
$endif
}
<*
@param [in] data : "Bytes to hash"
*>
fn void SipHash.update(&self, char[] data)
{
self.len += data.len;
foreach (byte : data)
{
self.m |= (long)byte << (self.m_idx++ << 3);
if (self.m_idx < 8) continue;
// This runs every time the m_idx is 8, then it resets to 0.
self.v[3] ^= self.m;
$for var $i = 0; $i < BLOCK_ROUNDS; ++$i : // unrolled loop
self.round();
$endfor
self.v[0] ^= self.m;
self.m_idx = 0;
self.m = 0;
}
}
fn OutType SipHash.final(&self)
{
char[8] last = { [7] = (char)self.len };
self.update(last[(self.m_idx < 7 ? self.m_idx : 7)..]);
$if OutType.typeid == uint128.typeid :
self.v[2] ^= 0xEE;
$else
self.v[2] ^= 0xFF;
$endif
$for var $i = 0; $i < FINALIZE_ROUNDS; ++$i : // unrolled loop
self.round();
$endfor
$if OutType.typeid == ulong.typeid :
return self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3];
$else
ulong lo = self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3];
self.v[1] ^= 0xDD;
$for var $i = 0; $i < FINALIZE_ROUNDS; ++$i : // unrolled loop
self.round();
$endfor
return lo | ((uint128)(self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3]) << 64);
$endif
}
fn void SipHash.round(&self) @local
{
self.v[0] += self.v[1];
self.v[1] = self.v[1].rotl(13);
self.v[1] ^= self.v[0];
self.v[0] = self.v[0].rotl(32);
self.v[2] += self.v[3];
self.v[3] = self.v[3].rotl(16);
self.v[3] ^= self.v[2];
self.v[0] += self.v[3];
self.v[3] = self.v[3].rotl(21);
self.v[3] ^= self.v[0];
self.v[2] += self.v[1];
self.v[1] = self.v[1].rotl(17);
self.v[1] ^= self.v[2];
self.v[2] = self.v[2].rotl(32);
}

View File

@@ -0,0 +1,185 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// Dedicated from repo: https://github.com/NotsoanoNimus/whirlpool.c3l
module std::hash::whirlpool;
import std::hash::hmac;
const BLOCK_SIZE = 64;
const HASH_SIZE = 64;
const BLOCK_128 = 64 / int128.sizeof;
struct Whirlpool
{
ulong[8] hash;
union
{
char[BLOCK_SIZE] block;
int128[BLOCK_128] block_128;
}
// Using these two integers with a funnel shift permits the original WHIRLPOOL 2^256 length limit.
uint128 counter_high;
uint128 counter_low;
}
alias HmacWhirlpool = Hmac { Whirlpool, HASH_SIZE, BLOCK_SIZE };
alias hmac = hmac::hash { Whirlpool, HASH_SIZE, BLOCK_SIZE };
alias pbkdf2 = hmac::pbkdf2 { Whirlpool, HASH_SIZE, BLOCK_SIZE };
fn char[HASH_SIZE] hash(char[] data)
{
Whirlpool w;
w.update(data);
return w.final();
}
// Added to satisfy HMAC
macro void Whirlpool.init(&self) => *self = { };
<*
@require data.len <= isz.max : "Update with smaller slices"
*>
fn void Whirlpool.update(&self, char[] data)
{
char remainder = (char)self.counter_low & 0x3F; // if not at an even BLOCK_SIZE, how many bytes are over it
uint to_pad = BLOCK_SIZE - remainder; // how many bytes must be filled to get the BLOCK_SIZE
uint128 prev_ctr = self.counter_low;
self.counter_low += data.len;
// Handle counter rollover by just adding 1 onto 'high'. The overflow amount in 'low' remains valid.
// Since 'data' is limited to INT64_MAX, there are no edge cases where 'high' must be incremented by more than 1.
// This is so much data (> 2^128 bytes) that this will likely NEVER happen in practice...
if (@unlikely(self.counter_low < prev_ctr)) ++self.counter_high;
// Pad partial blocks of input data.
if (remainder > 0)
{
usz span = to_pad < data.len ? to_pad : data.len;
self.block[remainder:span] = data[:span];
// When there wasn't enough data ingested to fill a block, just return to allow more incoming data to fill in.
// If the algorithm is finalized during a 'partial' block, it has its own padding to fill the remaining gap.
if (data.len < to_pad) return;
self.process_block(&self.block);
data = data[to_pad..];
}
// Digest blocks wholesale.
while (data.len >= BLOCK_SIZE)
{
self.process_block(data);
data = (data.len > BLOCK_SIZE) ? data[BLOCK_SIZE..] : {};
}
// Any leftovers should be swept into the 'block' buffer in the context for the next update or for 'final'.
if (data.len)
{
usz leftover_span = (BLOCK_SIZE < data.len ? BLOCK_SIZE : data.len);
self.block[:leftover_span] = data[:leftover_span];
}
}
fn char[HASH_SIZE] Whirlpool.final(&self)
{
char remainder = (char)self.counter_low & 0x3F; // if not at an even BLOCK_SIZE, how many bytes are over it
// Terminating byte gets added to the block.
self.block[remainder++] = 0x80;
// When fewer than 256 bits are available to store the counter into, pad the block with zeroes and process it.
if (remainder > 32)
{
if (remainder < 64) self.block[remainder..63] = 0x00;
self.process_block(&self.block);
remainder = 0;
}
// Zero out the rest of the chunk (or a new chunk if the above 'if' was true), up to byte 32.
if (remainder < 32) self.block[remainder..31] = 0x00;
// Insert the big-endian values of the counter as a suffix of the block.
// This 'counter' value is actually the number of BITS processed, hence the << 3 (or x8).
self.block_128[2] = $$bswap(self.counter_high << 3 | (self.counter_low >> 125 & 0x07));
self.block_128[3] = $$bswap(self.counter_low << 3);
// Process the final block.
self.process_block(&self.block);
// Each ulong in the resultant hash should be bit-swapped before the final return.
char[HASH_SIZE] hash @align(ulong.alignof);
ulong* hash_ref = (ulong*)&hash;
$for var $i = 0; $i < 8; ++$i :
hash_ref[$i] = $$bswap(self.hash[$i]);
$endfor
// All done!
return hash;
}
macro ulong @w_op(#src, $shift) @private
=> S_BOX[(0 * 256) + (int)(#src[($shift + 0) & 7] >> 56) ]
^ S_BOX[(1 * 256) + (int)(#src[($shift + 7) & 7] >> 48) & 0xFF]
^ S_BOX[(2 * 256) + (int)(#src[($shift + 6) & 7] >> 40) & 0xFF]
^ S_BOX[(3 * 256) + (int)(#src[($shift + 5) & 7] >> 32) & 0xFF]
^ S_BOX[(4 * 256) + (int)(#src[($shift + 4) & 7] >> 24) & 0xFF]
^ S_BOX[(5 * 256) + (int)(#src[($shift + 3) & 7] >> 16) & 0xFF]
^ S_BOX[(6 * 256) + (int)(#src[($shift + 2) & 7] >> 8) & 0xFF]
^ S_BOX[(7 * 256) + (int)(#src[($shift + 1) & 7] >> 0) & 0xFF];
const ulong[10] RC @private = {
0x1823c6e887b8014f,
0x36a6d2f5796f9152,
0x60bc9b8ea30c7b35,
0x1de0d7c22e4bfe57,
0x157737e59ff04ada,
0x58c9290ab1a06b85,
0xbd5d10f4cb3e0567,
0xe427418ba77d95d8,
0xfbee7c66dd17479e,
0xca2dbf07ad5a8333
};
const ROUNDS = 10;
fn void Whirlpool.process_block(&self, char* block) @local
{
ulong[2 * 8] k; // key
ulong[2 * 8] state; // state
// NOTE: These loops are unrolled with C3's Chad-tier compile-time evaluation.
$for var $round = 0; $round < 8; $round++:
k[$round] = self.hash[$round];
state[$round] = $$bswap(@unaligned_load(((ulong*)block)[$round], 1)) ^ self.hash[$round];
self.hash[$round] = state[$round];
$endfor
$for var $round = 0; $round < ROUNDS; ++$round :
var $m = $round % 2;
k[(($m ^ 1) * 8) + 0] = @w_op((&k[$m * 8]), 0) ^ RC[$round];
$for var $i = 1; $i < 8; $i++ :
k[(($m ^ 1) * 8) + $i] = @w_op((&k[$m * 8]), $i);
$endfor
$for var $i = 0; $i < 8; $i++ :
state[(($m ^ 1) * 8) + $i] = @w_op(&(state[$m * 8]), $i) ^ k[(($m ^ 1) * 8) + $i];
$endfor
$endfor
$for var $x = 0; $x < 8; $x++:
self.hash[$x] ^= state[$x];
$endfor
}

View File

@@ -0,0 +1,539 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// Dedicated from repo: https://github.com/NotsoanoNimus/whirlpool.c3l
module std::hash::whirlpool @private;
// =========================================================================
// WHIRLPOOL s-box data.
const ulong[8 * 256] S_BOX = {
// C0
0x18186018c07830d8, 0x23238c2305af4626, 0xc6c63fc67ef991b8, 0xe8e887e8136fcdfb,
0x878726874ca113cb, 0xb8b8dab8a9626d11, 0x0101040108050209, 0x4f4f214f426e9e0d,
0x3636d836adee6c9b, 0xa6a6a2a6590451ff, 0xd2d26fd2debdb90c, 0xf5f5f3f5fb06f70e,
0x7979f979ef80f296, 0x6f6fa16f5fcede30, 0x91917e91fcef3f6d, 0x52525552aa07a4f8,
0x60609d6027fdc047, 0xbcbccabc89766535, 0x9b9b569baccd2b37, 0x8e8e028e048c018a,
0xa3a3b6a371155bd2, 0x0c0c300c603c186c, 0x7b7bf17bff8af684, 0x3535d435b5e16a80,
0x1d1d741de8693af5, 0xe0e0a7e05347ddb3, 0xd7d77bd7f6acb321, 0xc2c22fc25eed999c,
0x2e2eb82e6d965c43, 0x4b4b314b627a9629, 0xfefedffea321e15d, 0x575741578216aed5,
0x15155415a8412abd, 0x7777c1779fb6eee8, 0x3737dc37a5eb6e92, 0xe5e5b3e57b56d79e,
0x9f9f469f8cd92313, 0xf0f0e7f0d317fd23, 0x4a4a354a6a7f9420, 0xdada4fda9e95a944,
0x58587d58fa25b0a2, 0xc9c903c906ca8fcf, 0x2929a429558d527c, 0x0a0a280a5022145a,
0xb1b1feb1e14f7f50, 0xa0a0baa0691a5dc9, 0x6b6bb16b7fdad614, 0x85852e855cab17d9,
0xbdbdcebd8173673c, 0x5d5d695dd234ba8f, 0x1010401080502090, 0xf4f4f7f4f303f507,
0xcbcb0bcb16c08bdd, 0x3e3ef83eedc67cd3, 0x0505140528110a2d, 0x676781671fe6ce78,
0xe4e4b7e47353d597, 0x27279c2725bb4e02, 0x4141194132588273, 0x8b8b168b2c9d0ba7,
0xa7a7a6a7510153f6, 0x7d7de97dcf94fab2, 0x95956e95dcfb3749, 0xd8d847d88e9fad56,
0xfbfbcbfb8b30eb70, 0xeeee9fee2371c1cd, 0x7c7ced7cc791f8bb, 0x6666856617e3cc71,
0xdddd53dda68ea77b, 0x17175c17b84b2eaf, 0x4747014702468e45, 0x9e9e429e84dc211a,
0xcaca0fca1ec589d4, 0x2d2db42d75995a58, 0xbfbfc6bf9179632e, 0x07071c07381b0e3f,
0xadad8ead012347ac, 0x5a5a755aea2fb4b0, 0x838336836cb51bef, 0x3333cc3385ff66b6,
0x636391633ff2c65c, 0x02020802100a0412, 0xaaaa92aa39384993, 0x7171d971afa8e2de,
0xc8c807c80ecf8dc6, 0x19196419c87d32d1, 0x494939497270923b, 0xd9d943d9869aaf5f,
0xf2f2eff2c31df931, 0xe3e3abe34b48dba8, 0x5b5b715be22ab6b9, 0x88881a8834920dbc,
0x9a9a529aa4c8293e, 0x262698262dbe4c0b, 0x3232c8328dfa64bf, 0xb0b0fab0e94a7d59,
0xe9e983e91b6acff2, 0x0f0f3c0f78331e77, 0xd5d573d5e6a6b733, 0x80803a8074ba1df4,
0xbebec2be997c6127, 0xcdcd13cd26de87eb, 0x3434d034bde46889, 0x48483d487a759032,
0xffffdbffab24e354, 0x7a7af57af78ff48d, 0x90907a90f4ea3d64, 0x5f5f615fc23ebe9d,
0x202080201da0403d, 0x6868bd6867d5d00f, 0x1a1a681ad07234ca, 0xaeae82ae192c41b7,
0xb4b4eab4c95e757d, 0x54544d549a19a8ce, 0x93937693ece53b7f, 0x222288220daa442f,
0x64648d6407e9c863, 0xf1f1e3f1db12ff2a, 0x7373d173bfa2e6cc, 0x12124812905a2482,
0x40401d403a5d807a, 0x0808200840281048, 0xc3c32bc356e89b95, 0xecec97ec337bc5df,
0xdbdb4bdb9690ab4d, 0xa1a1bea1611f5fc0, 0x8d8d0e8d1c830791, 0x3d3df43df5c97ac8,
0x97976697ccf1335b, 0x0000000000000000, 0xcfcf1bcf36d483f9, 0x2b2bac2b4587566e,
0x7676c57697b3ece1, 0x8282328264b019e6, 0xd6d67fd6fea9b128, 0x1b1b6c1bd87736c3,
0xb5b5eeb5c15b7774, 0xafaf86af112943be, 0x6a6ab56a77dfd41d, 0x50505d50ba0da0ea,
0x45450945124c8a57, 0xf3f3ebf3cb18fb38, 0x3030c0309df060ad, 0xefef9bef2b74c3c4,
0x3f3ffc3fe5c37eda, 0x55554955921caac7, 0xa2a2b2a2791059db, 0xeaea8fea0365c9e9,
0x656589650fecca6a, 0xbabad2bab9686903, 0x2f2fbc2f65935e4a, 0xc0c027c04ee79d8e,
0xdede5fdebe81a160, 0x1c1c701ce06c38fc, 0xfdfdd3fdbb2ee746, 0x4d4d294d52649a1f,
0x92927292e4e03976, 0x7575c9758fbceafa, 0x06061806301e0c36, 0x8a8a128a249809ae,
0xb2b2f2b2f940794b, 0xe6e6bfe66359d185, 0x0e0e380e70361c7e, 0x1f1f7c1ff8633ee7,
0x6262956237f7c455, 0xd4d477d4eea3b53a, 0xa8a89aa829324d81, 0x96966296c4f43152,
0xf9f9c3f99b3aef62, 0xc5c533c566f697a3, 0x2525942535b14a10, 0x59597959f220b2ab,
0x84842a8454ae15d0, 0x7272d572b7a7e4c5, 0x3939e439d5dd72ec, 0x4c4c2d4c5a619816,
0x5e5e655eca3bbc94, 0x7878fd78e785f09f, 0x3838e038ddd870e5, 0x8c8c0a8c14860598,
0xd1d163d1c6b2bf17, 0xa5a5aea5410b57e4, 0xe2e2afe2434dd9a1, 0x616199612ff8c24e,
0xb3b3f6b3f1457b42, 0x2121842115a54234, 0x9c9c4a9c94d62508, 0x1e1e781ef0663cee,
0x4343114322528661, 0xc7c73bc776fc93b1, 0xfcfcd7fcb32be54f, 0x0404100420140824,
0x51515951b208a2e3, 0x99995e99bcc72f25, 0x6d6da96d4fc4da22, 0x0d0d340d68391a65,
0xfafacffa8335e979, 0xdfdf5bdfb684a369, 0x7e7ee57ed79bfca9, 0x242490243db44819,
0x3b3bec3bc5d776fe, 0xabab96ab313d4b9a, 0xcece1fce3ed181f0, 0x1111441188552299,
0x8f8f068f0c890383, 0x4e4e254e4a6b9c04, 0xb7b7e6b7d1517366, 0xebeb8beb0b60cbe0,
0x3c3cf03cfdcc78c1, 0x81813e817cbf1ffd, 0x94946a94d4fe3540, 0xf7f7fbf7eb0cf31c,
0xb9b9deb9a1676f18, 0x13134c13985f268b, 0x2c2cb02c7d9c5851, 0xd3d36bd3d6b8bb05,
0xe7e7bbe76b5cd38c, 0x6e6ea56e57cbdc39, 0xc4c437c46ef395aa, 0x03030c03180f061b,
0x565645568a13acdc, 0x44440d441a49885e, 0x7f7fe17fdf9efea0, 0xa9a99ea921374f88,
0x2a2aa82a4d825467, 0xbbbbd6bbb16d6b0a, 0xc1c123c146e29f87, 0x53535153a202a6f1,
0xdcdc57dcae8ba572, 0x0b0b2c0b58271653, 0x9d9d4e9d9cd32701, 0x6c6cad6c47c1d82b,
0x3131c43195f562a4, 0x7474cd7487b9e8f3, 0xf6f6fff6e309f115, 0x464605460a438c4c,
0xacac8aac092645a5, 0x89891e893c970fb5, 0x14145014a04428b4, 0xe1e1a3e15b42dfba,
0x16165816b04e2ca6, 0x3a3ae83acdd274f7, 0x6969b9696fd0d206, 0x09092409482d1241,
0x7070dd70a7ade0d7, 0xb6b6e2b6d954716f, 0xd0d067d0ceb7bd1e, 0xeded93ed3b7ec7d6,
0xcccc17cc2edb85e2, 0x424215422a578468, 0x98985a98b4c22d2c, 0xa4a4aaa4490e55ed,
0x2828a0285d885075, 0x5c5c6d5cda31b886, 0xf8f8c7f8933fed6b, 0x8686228644a411c2,
// C1
0xd818186018c07830, 0x2623238c2305af46, 0xb8c6c63fc67ef991, 0xfbe8e887e8136fcd,
0xcb878726874ca113, 0x11b8b8dab8a9626d, 0x0901010401080502, 0x0d4f4f214f426e9e,
0x9b3636d836adee6c, 0xffa6a6a2a6590451, 0x0cd2d26fd2debdb9, 0x0ef5f5f3f5fb06f7,
0x967979f979ef80f2, 0x306f6fa16f5fcede, 0x6d91917e91fcef3f, 0xf852525552aa07a4,
0x4760609d6027fdc0, 0x35bcbccabc897665, 0x379b9b569baccd2b, 0x8a8e8e028e048c01,
0xd2a3a3b6a371155b, 0x6c0c0c300c603c18, 0x847b7bf17bff8af6, 0x803535d435b5e16a,
0xf51d1d741de8693a, 0xb3e0e0a7e05347dd, 0x21d7d77bd7f6acb3, 0x9cc2c22fc25eed99,
0x432e2eb82e6d965c, 0x294b4b314b627a96, 0x5dfefedffea321e1, 0xd5575741578216ae,
0xbd15155415a8412a, 0xe87777c1779fb6ee, 0x923737dc37a5eb6e, 0x9ee5e5b3e57b56d7,
0x139f9f469f8cd923, 0x23f0f0e7f0d317fd, 0x204a4a354a6a7f94, 0x44dada4fda9e95a9,
0xa258587d58fa25b0, 0xcfc9c903c906ca8f, 0x7c2929a429558d52, 0x5a0a0a280a502214,
0x50b1b1feb1e14f7f, 0xc9a0a0baa0691a5d, 0x146b6bb16b7fdad6, 0xd985852e855cab17,
0x3cbdbdcebd817367, 0x8f5d5d695dd234ba, 0x9010104010805020, 0x07f4f4f7f4f303f5,
0xddcbcb0bcb16c08b, 0xd33e3ef83eedc67c, 0x2d0505140528110a, 0x78676781671fe6ce,
0x97e4e4b7e47353d5, 0x0227279c2725bb4e, 0x7341411941325882, 0xa78b8b168b2c9d0b,
0xf6a7a7a6a7510153, 0xb27d7de97dcf94fa, 0x4995956e95dcfb37, 0x56d8d847d88e9fad,
0x70fbfbcbfb8b30eb, 0xcdeeee9fee2371c1, 0xbb7c7ced7cc791f8, 0x716666856617e3cc,
0x7bdddd53dda68ea7, 0xaf17175c17b84b2e, 0x454747014702468e, 0x1a9e9e429e84dc21,
0xd4caca0fca1ec589, 0x582d2db42d75995a, 0x2ebfbfc6bf917963, 0x3f07071c07381b0e,
0xacadad8ead012347, 0xb05a5a755aea2fb4, 0xef838336836cb51b, 0xb63333cc3385ff66,
0x5c636391633ff2c6, 0x1202020802100a04, 0x93aaaa92aa393849, 0xde7171d971afa8e2,
0xc6c8c807c80ecf8d, 0xd119196419c87d32, 0x3b49493949727092, 0x5fd9d943d9869aaf,
0x31f2f2eff2c31df9, 0xa8e3e3abe34b48db, 0xb95b5b715be22ab6, 0xbc88881a8834920d,
0x3e9a9a529aa4c829, 0x0b262698262dbe4c, 0xbf3232c8328dfa64, 0x59b0b0fab0e94a7d,
0xf2e9e983e91b6acf, 0x770f0f3c0f78331e, 0x33d5d573d5e6a6b7, 0xf480803a8074ba1d,
0x27bebec2be997c61, 0xebcdcd13cd26de87, 0x893434d034bde468, 0x3248483d487a7590,
0x54ffffdbffab24e3, 0x8d7a7af57af78ff4, 0x6490907a90f4ea3d, 0x9d5f5f615fc23ebe,
0x3d202080201da040, 0x0f6868bd6867d5d0, 0xca1a1a681ad07234, 0xb7aeae82ae192c41,
0x7db4b4eab4c95e75, 0xce54544d549a19a8, 0x7f93937693ece53b, 0x2f222288220daa44,
0x6364648d6407e9c8, 0x2af1f1e3f1db12ff, 0xcc7373d173bfa2e6, 0x8212124812905a24,
0x7a40401d403a5d80, 0x4808082008402810, 0x95c3c32bc356e89b, 0xdfecec97ec337bc5,
0x4ddbdb4bdb9690ab, 0xc0a1a1bea1611f5f, 0x918d8d0e8d1c8307, 0xc83d3df43df5c97a,
0x5b97976697ccf133, 0x0000000000000000, 0xf9cfcf1bcf36d483, 0x6e2b2bac2b458756,
0xe17676c57697b3ec, 0xe68282328264b019, 0x28d6d67fd6fea9b1, 0xc31b1b6c1bd87736,
0x74b5b5eeb5c15b77, 0xbeafaf86af112943, 0x1d6a6ab56a77dfd4, 0xea50505d50ba0da0,
0x5745450945124c8a, 0x38f3f3ebf3cb18fb, 0xad3030c0309df060, 0xc4efef9bef2b74c3,
0xda3f3ffc3fe5c37e, 0xc755554955921caa, 0xdba2a2b2a2791059, 0xe9eaea8fea0365c9,
0x6a656589650fecca, 0x03babad2bab96869, 0x4a2f2fbc2f65935e, 0x8ec0c027c04ee79d,
0x60dede5fdebe81a1, 0xfc1c1c701ce06c38, 0x46fdfdd3fdbb2ee7, 0x1f4d4d294d52649a,
0x7692927292e4e039, 0xfa7575c9758fbcea, 0x3606061806301e0c, 0xae8a8a128a249809,
0x4bb2b2f2b2f94079, 0x85e6e6bfe66359d1, 0x7e0e0e380e70361c, 0xe71f1f7c1ff8633e,
0x556262956237f7c4, 0x3ad4d477d4eea3b5, 0x81a8a89aa829324d, 0x5296966296c4f431,
0x62f9f9c3f99b3aef, 0xa3c5c533c566f697, 0x102525942535b14a, 0xab59597959f220b2,
0xd084842a8454ae15, 0xc57272d572b7a7e4, 0xec3939e439d5dd72, 0x164c4c2d4c5a6198,
0x945e5e655eca3bbc, 0x9f7878fd78e785f0, 0xe53838e038ddd870, 0x988c8c0a8c148605,
0x17d1d163d1c6b2bf, 0xe4a5a5aea5410b57, 0xa1e2e2afe2434dd9, 0x4e616199612ff8c2,
0x42b3b3f6b3f1457b, 0x342121842115a542, 0x089c9c4a9c94d625, 0xee1e1e781ef0663c,
0x6143431143225286, 0xb1c7c73bc776fc93, 0x4ffcfcd7fcb32be5, 0x2404041004201408,
0xe351515951b208a2, 0x2599995e99bcc72f, 0x226d6da96d4fc4da, 0x650d0d340d68391a,
0x79fafacffa8335e9, 0x69dfdf5bdfb684a3, 0xa97e7ee57ed79bfc, 0x19242490243db448,
0xfe3b3bec3bc5d776, 0x9aabab96ab313d4b, 0xf0cece1fce3ed181, 0x9911114411885522,
0x838f8f068f0c8903, 0x044e4e254e4a6b9c, 0x66b7b7e6b7d15173, 0xe0ebeb8beb0b60cb,
0xc13c3cf03cfdcc78, 0xfd81813e817cbf1f, 0x4094946a94d4fe35, 0x1cf7f7fbf7eb0cf3,
0x18b9b9deb9a1676f, 0x8b13134c13985f26, 0x512c2cb02c7d9c58, 0x05d3d36bd3d6b8bb,
0x8ce7e7bbe76b5cd3, 0x396e6ea56e57cbdc, 0xaac4c437c46ef395, 0x1b03030c03180f06,
0xdc565645568a13ac, 0x5e44440d441a4988, 0xa07f7fe17fdf9efe, 0x88a9a99ea921374f,
0x672a2aa82a4d8254, 0x0abbbbd6bbb16d6b, 0x87c1c123c146e29f, 0xf153535153a202a6,
0x72dcdc57dcae8ba5, 0x530b0b2c0b582716, 0x019d9d4e9d9cd327, 0x2b6c6cad6c47c1d8,
0xa43131c43195f562, 0xf37474cd7487b9e8, 0x15f6f6fff6e309f1, 0x4c464605460a438c,
0xa5acac8aac092645, 0xb589891e893c970f, 0xb414145014a04428, 0xbae1e1a3e15b42df,
0xa616165816b04e2c, 0xf73a3ae83acdd274, 0x066969b9696fd0d2, 0x4109092409482d12,
0xd77070dd70a7ade0, 0x6fb6b6e2b6d95471, 0x1ed0d067d0ceb7bd, 0xd6eded93ed3b7ec7,
0xe2cccc17cc2edb85, 0x68424215422a5784, 0x2c98985a98b4c22d, 0xeda4a4aaa4490e55,
0x752828a0285d8850, 0x865c5c6d5cda31b8, 0x6bf8f8c7f8933fed, 0xc28686228644a411,
// C2
0x30d818186018c078, 0x462623238c2305af, 0x91b8c6c63fc67ef9, 0xcdfbe8e887e8136f,
0x13cb878726874ca1, 0x6d11b8b8dab8a962, 0x0209010104010805, 0x9e0d4f4f214f426e,
0x6c9b3636d836adee, 0x51ffa6a6a2a65904, 0xb90cd2d26fd2debd, 0xf70ef5f5f3f5fb06,
0xf2967979f979ef80, 0xde306f6fa16f5fce, 0x3f6d91917e91fcef, 0xa4f852525552aa07,
0xc04760609d6027fd, 0x6535bcbccabc8976, 0x2b379b9b569baccd, 0x018a8e8e028e048c,
0x5bd2a3a3b6a37115, 0x186c0c0c300c603c, 0xf6847b7bf17bff8a, 0x6a803535d435b5e1,
0x3af51d1d741de869, 0xddb3e0e0a7e05347, 0xb321d7d77bd7f6ac, 0x999cc2c22fc25eed,
0x5c432e2eb82e6d96, 0x96294b4b314b627a, 0xe15dfefedffea321, 0xaed5575741578216,
0x2abd15155415a841, 0xeee87777c1779fb6, 0x6e923737dc37a5eb, 0xd79ee5e5b3e57b56,
0x23139f9f469f8cd9, 0xfd23f0f0e7f0d317, 0x94204a4a354a6a7f, 0xa944dada4fda9e95,
0xb0a258587d58fa25, 0x8fcfc9c903c906ca, 0x527c2929a429558d, 0x145a0a0a280a5022,
0x7f50b1b1feb1e14f, 0x5dc9a0a0baa0691a, 0xd6146b6bb16b7fda, 0x17d985852e855cab,
0x673cbdbdcebd8173, 0xba8f5d5d695dd234, 0x2090101040108050, 0xf507f4f4f7f4f303,
0x8bddcbcb0bcb16c0, 0x7cd33e3ef83eedc6, 0x0a2d050514052811, 0xce78676781671fe6,
0xd597e4e4b7e47353, 0x4e0227279c2725bb, 0x8273414119413258, 0x0ba78b8b168b2c9d,
0x53f6a7a7a6a75101, 0xfab27d7de97dcf94, 0x374995956e95dcfb, 0xad56d8d847d88e9f,
0xeb70fbfbcbfb8b30, 0xc1cdeeee9fee2371, 0xf8bb7c7ced7cc791, 0xcc716666856617e3,
0xa77bdddd53dda68e, 0x2eaf17175c17b84b, 0x8e45474701470246, 0x211a9e9e429e84dc,
0x89d4caca0fca1ec5, 0x5a582d2db42d7599, 0x632ebfbfc6bf9179, 0x0e3f07071c07381b,
0x47acadad8ead0123, 0xb4b05a5a755aea2f, 0x1bef838336836cb5, 0x66b63333cc3385ff,
0xc65c636391633ff2, 0x041202020802100a, 0x4993aaaa92aa3938, 0xe2de7171d971afa8,
0x8dc6c8c807c80ecf, 0x32d119196419c87d, 0x923b494939497270, 0xaf5fd9d943d9869a,
0xf931f2f2eff2c31d, 0xdba8e3e3abe34b48, 0xb6b95b5b715be22a, 0x0dbc88881a883492,
0x293e9a9a529aa4c8, 0x4c0b262698262dbe, 0x64bf3232c8328dfa, 0x7d59b0b0fab0e94a,
0xcff2e9e983e91b6a, 0x1e770f0f3c0f7833, 0xb733d5d573d5e6a6, 0x1df480803a8074ba,
0x6127bebec2be997c, 0x87ebcdcd13cd26de, 0x68893434d034bde4, 0x903248483d487a75,
0xe354ffffdbffab24, 0xf48d7a7af57af78f, 0x3d6490907a90f4ea, 0xbe9d5f5f615fc23e,
0x403d202080201da0, 0xd00f6868bd6867d5, 0x34ca1a1a681ad072, 0x41b7aeae82ae192c,
0x757db4b4eab4c95e, 0xa8ce54544d549a19, 0x3b7f93937693ece5, 0x442f222288220daa,
0xc86364648d6407e9, 0xff2af1f1e3f1db12, 0xe6cc7373d173bfa2, 0x248212124812905a,
0x807a40401d403a5d, 0x1048080820084028, 0x9b95c3c32bc356e8, 0xc5dfecec97ec337b,
0xab4ddbdb4bdb9690, 0x5fc0a1a1bea1611f, 0x07918d8d0e8d1c83, 0x7ac83d3df43df5c9,
0x335b97976697ccf1, 0x0000000000000000, 0x83f9cfcf1bcf36d4, 0x566e2b2bac2b4587,
0xece17676c57697b3, 0x19e68282328264b0, 0xb128d6d67fd6fea9, 0x36c31b1b6c1bd877,
0x7774b5b5eeb5c15b, 0x43beafaf86af1129, 0xd41d6a6ab56a77df, 0xa0ea50505d50ba0d,
0x8a5745450945124c, 0xfb38f3f3ebf3cb18, 0x60ad3030c0309df0, 0xc3c4efef9bef2b74,
0x7eda3f3ffc3fe5c3, 0xaac755554955921c, 0x59dba2a2b2a27910, 0xc9e9eaea8fea0365,
0xca6a656589650fec, 0x6903babad2bab968, 0x5e4a2f2fbc2f6593, 0x9d8ec0c027c04ee7,
0xa160dede5fdebe81, 0x38fc1c1c701ce06c, 0xe746fdfdd3fdbb2e, 0x9a1f4d4d294d5264,
0x397692927292e4e0, 0xeafa7575c9758fbc, 0x0c3606061806301e, 0x09ae8a8a128a2498,
0x794bb2b2f2b2f940, 0xd185e6e6bfe66359, 0x1c7e0e0e380e7036, 0x3ee71f1f7c1ff863,
0xc4556262956237f7, 0xb53ad4d477d4eea3, 0x4d81a8a89aa82932, 0x315296966296c4f4,
0xef62f9f9c3f99b3a, 0x97a3c5c533c566f6, 0x4a102525942535b1, 0xb2ab59597959f220,
0x15d084842a8454ae, 0xe4c57272d572b7a7, 0x72ec3939e439d5dd, 0x98164c4c2d4c5a61,
0xbc945e5e655eca3b, 0xf09f7878fd78e785, 0x70e53838e038ddd8, 0x05988c8c0a8c1486,
0xbf17d1d163d1c6b2, 0x57e4a5a5aea5410b, 0xd9a1e2e2afe2434d, 0xc24e616199612ff8,
0x7b42b3b3f6b3f145, 0x42342121842115a5, 0x25089c9c4a9c94d6, 0x3cee1e1e781ef066,
0x8661434311432252, 0x93b1c7c73bc776fc, 0xe54ffcfcd7fcb32b, 0x0824040410042014,
0xa2e351515951b208, 0x2f2599995e99bcc7, 0xda226d6da96d4fc4, 0x1a650d0d340d6839,
0xe979fafacffa8335, 0xa369dfdf5bdfb684, 0xfca97e7ee57ed79b, 0x4819242490243db4,
0x76fe3b3bec3bc5d7, 0x4b9aabab96ab313d, 0x81f0cece1fce3ed1, 0x2299111144118855,
0x03838f8f068f0c89, 0x9c044e4e254e4a6b, 0x7366b7b7e6b7d151, 0xcbe0ebeb8beb0b60,
0x78c13c3cf03cfdcc, 0x1ffd81813e817cbf, 0x354094946a94d4fe, 0xf31cf7f7fbf7eb0c,
0x6f18b9b9deb9a167, 0x268b13134c13985f, 0x58512c2cb02c7d9c, 0xbb05d3d36bd3d6b8,
0xd38ce7e7bbe76b5c, 0xdc396e6ea56e57cb, 0x95aac4c437c46ef3, 0x061b03030c03180f,
0xacdc565645568a13, 0x885e44440d441a49, 0xfea07f7fe17fdf9e, 0x4f88a9a99ea92137,
0x54672a2aa82a4d82, 0x6b0abbbbd6bbb16d, 0x9f87c1c123c146e2, 0xa6f153535153a202,
0xa572dcdc57dcae8b, 0x16530b0b2c0b5827, 0x27019d9d4e9d9cd3, 0xd82b6c6cad6c47c1,
0x62a43131c43195f5, 0xe8f37474cd7487b9, 0xf115f6f6fff6e309, 0x8c4c464605460a43,
0x45a5acac8aac0926, 0x0fb589891e893c97, 0x28b414145014a044, 0xdfbae1e1a3e15b42,
0x2ca616165816b04e, 0x74f73a3ae83acdd2, 0xd2066969b9696fd0, 0x124109092409482d,
0xe0d77070dd70a7ad, 0x716fb6b6e2b6d954, 0xbd1ed0d067d0ceb7, 0xc7d6eded93ed3b7e,
0x85e2cccc17cc2edb, 0x8468424215422a57, 0x2d2c98985a98b4c2, 0x55eda4a4aaa4490e,
0x50752828a0285d88, 0xb8865c5c6d5cda31, 0xed6bf8f8c7f8933f, 0x11c28686228644a4,
// C3
0x7830d818186018c0, 0xaf462623238c2305, 0xf991b8c6c63fc67e, 0x6fcdfbe8e887e813,
0xa113cb878726874c, 0x626d11b8b8dab8a9, 0x0502090101040108, 0x6e9e0d4f4f214f42,
0xee6c9b3636d836ad, 0x0451ffa6a6a2a659, 0xbdb90cd2d26fd2de, 0x06f70ef5f5f3f5fb,
0x80f2967979f979ef, 0xcede306f6fa16f5f, 0xef3f6d91917e91fc, 0x07a4f852525552aa,
0xfdc04760609d6027, 0x766535bcbccabc89, 0xcd2b379b9b569bac, 0x8c018a8e8e028e04,
0x155bd2a3a3b6a371, 0x3c186c0c0c300c60, 0x8af6847b7bf17bff, 0xe16a803535d435b5,
0x693af51d1d741de8, 0x47ddb3e0e0a7e053, 0xacb321d7d77bd7f6, 0xed999cc2c22fc25e,
0x965c432e2eb82e6d, 0x7a96294b4b314b62, 0x21e15dfefedffea3, 0x16aed55757415782,
0x412abd15155415a8, 0xb6eee87777c1779f, 0xeb6e923737dc37a5, 0x56d79ee5e5b3e57b,
0xd923139f9f469f8c, 0x17fd23f0f0e7f0d3, 0x7f94204a4a354a6a, 0x95a944dada4fda9e,
0x25b0a258587d58fa, 0xca8fcfc9c903c906, 0x8d527c2929a42955, 0x22145a0a0a280a50,
0x4f7f50b1b1feb1e1, 0x1a5dc9a0a0baa069, 0xdad6146b6bb16b7f, 0xab17d985852e855c,
0x73673cbdbdcebd81, 0x34ba8f5d5d695dd2, 0x5020901010401080, 0x03f507f4f4f7f4f3,
0xc08bddcbcb0bcb16, 0xc67cd33e3ef83eed, 0x110a2d0505140528, 0xe6ce78676781671f,
0x53d597e4e4b7e473, 0xbb4e0227279c2725, 0x5882734141194132, 0x9d0ba78b8b168b2c,
0x0153f6a7a7a6a751, 0x94fab27d7de97dcf, 0xfb374995956e95dc, 0x9fad56d8d847d88e,
0x30eb70fbfbcbfb8b, 0x71c1cdeeee9fee23, 0x91f8bb7c7ced7cc7, 0xe3cc716666856617,
0x8ea77bdddd53dda6, 0x4b2eaf17175c17b8, 0x468e454747014702, 0xdc211a9e9e429e84,
0xc589d4caca0fca1e, 0x995a582d2db42d75, 0x79632ebfbfc6bf91, 0x1b0e3f07071c0738,
0x2347acadad8ead01, 0x2fb4b05a5a755aea, 0xb51bef838336836c, 0xff66b63333cc3385,
0xf2c65c636391633f, 0x0a04120202080210, 0x384993aaaa92aa39, 0xa8e2de7171d971af,
0xcf8dc6c8c807c80e, 0x7d32d119196419c8, 0x70923b4949394972, 0x9aaf5fd9d943d986,
0x1df931f2f2eff2c3, 0x48dba8e3e3abe34b, 0x2ab6b95b5b715be2, 0x920dbc88881a8834,
0xc8293e9a9a529aa4, 0xbe4c0b262698262d, 0xfa64bf3232c8328d, 0x4a7d59b0b0fab0e9,
0x6acff2e9e983e91b, 0x331e770f0f3c0f78, 0xa6b733d5d573d5e6, 0xba1df480803a8074,
0x7c6127bebec2be99, 0xde87ebcdcd13cd26, 0xe468893434d034bd, 0x75903248483d487a,
0x24e354ffffdbffab, 0x8ff48d7a7af57af7, 0xea3d6490907a90f4, 0x3ebe9d5f5f615fc2,
0xa0403d202080201d, 0xd5d00f6868bd6867, 0x7234ca1a1a681ad0, 0x2c41b7aeae82ae19,
0x5e757db4b4eab4c9, 0x19a8ce54544d549a, 0xe53b7f93937693ec, 0xaa442f222288220d,
0xe9c86364648d6407, 0x12ff2af1f1e3f1db, 0xa2e6cc7373d173bf, 0x5a24821212481290,
0x5d807a40401d403a, 0x2810480808200840, 0xe89b95c3c32bc356, 0x7bc5dfecec97ec33,
0x90ab4ddbdb4bdb96, 0x1f5fc0a1a1bea161, 0x8307918d8d0e8d1c, 0xc97ac83d3df43df5,
0xf1335b97976697cc, 0x0000000000000000, 0xd483f9cfcf1bcf36, 0x87566e2b2bac2b45,
0xb3ece17676c57697, 0xb019e68282328264, 0xa9b128d6d67fd6fe, 0x7736c31b1b6c1bd8,
0x5b7774b5b5eeb5c1, 0x2943beafaf86af11, 0xdfd41d6a6ab56a77, 0x0da0ea50505d50ba,
0x4c8a574545094512, 0x18fb38f3f3ebf3cb, 0xf060ad3030c0309d, 0x74c3c4efef9bef2b,
0xc37eda3f3ffc3fe5, 0x1caac75555495592, 0x1059dba2a2b2a279, 0x65c9e9eaea8fea03,
0xecca6a656589650f, 0x686903babad2bab9, 0x935e4a2f2fbc2f65, 0xe79d8ec0c027c04e,
0x81a160dede5fdebe, 0x6c38fc1c1c701ce0, 0x2ee746fdfdd3fdbb, 0x649a1f4d4d294d52,
0xe0397692927292e4, 0xbceafa7575c9758f, 0x1e0c360606180630, 0x9809ae8a8a128a24,
0x40794bb2b2f2b2f9, 0x59d185e6e6bfe663, 0x361c7e0e0e380e70, 0x633ee71f1f7c1ff8,
0xf7c4556262956237, 0xa3b53ad4d477d4ee, 0x324d81a8a89aa829, 0xf4315296966296c4,
0x3aef62f9f9c3f99b, 0xf697a3c5c533c566, 0xb14a102525942535, 0x20b2ab59597959f2,
0xae15d084842a8454, 0xa7e4c57272d572b7, 0xdd72ec3939e439d5, 0x6198164c4c2d4c5a,
0x3bbc945e5e655eca, 0x85f09f7878fd78e7, 0xd870e53838e038dd, 0x8605988c8c0a8c14,
0xb2bf17d1d163d1c6, 0x0b57e4a5a5aea541, 0x4dd9a1e2e2afe243, 0xf8c24e616199612f,
0x457b42b3b3f6b3f1, 0xa542342121842115, 0xd625089c9c4a9c94, 0x663cee1e1e781ef0,
0x5286614343114322, 0xfc93b1c7c73bc776, 0x2be54ffcfcd7fcb3, 0x1408240404100420,
0x08a2e351515951b2, 0xc72f2599995e99bc, 0xc4da226d6da96d4f, 0x391a650d0d340d68,
0x35e979fafacffa83, 0x84a369dfdf5bdfb6, 0x9bfca97e7ee57ed7, 0xb44819242490243d,
0xd776fe3b3bec3bc5, 0x3d4b9aabab96ab31, 0xd181f0cece1fce3e, 0x5522991111441188,
0x8903838f8f068f0c, 0x6b9c044e4e254e4a, 0x517366b7b7e6b7d1, 0x60cbe0ebeb8beb0b,
0xcc78c13c3cf03cfd, 0xbf1ffd81813e817c, 0xfe354094946a94d4, 0x0cf31cf7f7fbf7eb,
0x676f18b9b9deb9a1, 0x5f268b13134c1398, 0x9c58512c2cb02c7d, 0xb8bb05d3d36bd3d6,
0x5cd38ce7e7bbe76b, 0xcbdc396e6ea56e57, 0xf395aac4c437c46e, 0x0f061b03030c0318,
0x13acdc565645568a, 0x49885e44440d441a, 0x9efea07f7fe17fdf, 0x374f88a9a99ea921,
0x8254672a2aa82a4d, 0x6d6b0abbbbd6bbb1, 0xe29f87c1c123c146, 0x02a6f153535153a2,
0x8ba572dcdc57dcae, 0x2716530b0b2c0b58, 0xd327019d9d4e9d9c, 0xc1d82b6c6cad6c47,
0xf562a43131c43195, 0xb9e8f37474cd7487, 0x09f115f6f6fff6e3, 0x438c4c464605460a,
0x2645a5acac8aac09, 0x970fb589891e893c, 0x4428b414145014a0, 0x42dfbae1e1a3e15b,
0x4e2ca616165816b0, 0xd274f73a3ae83acd, 0xd0d2066969b9696f, 0x2d12410909240948,
0xade0d77070dd70a7, 0x54716fb6b6e2b6d9, 0xb7bd1ed0d067d0ce, 0x7ec7d6eded93ed3b,
0xdb85e2cccc17cc2e, 0x578468424215422a, 0xc22d2c98985a98b4, 0x0e55eda4a4aaa449,
0x8850752828a0285d, 0x31b8865c5c6d5cda, 0x3fed6bf8f8c7f893, 0xa411c28686228644,
// C4
0xc07830d818186018, 0x05af462623238c23, 0x7ef991b8c6c63fc6, 0x136fcdfbe8e887e8,
0x4ca113cb87872687, 0xa9626d11b8b8dab8, 0x0805020901010401, 0x426e9e0d4f4f214f,
0xadee6c9b3636d836, 0x590451ffa6a6a2a6, 0xdebdb90cd2d26fd2, 0xfb06f70ef5f5f3f5,
0xef80f2967979f979, 0x5fcede306f6fa16f, 0xfcef3f6d91917e91, 0xaa07a4f852525552,
0x27fdc04760609d60, 0x89766535bcbccabc, 0xaccd2b379b9b569b, 0x048c018a8e8e028e,
0x71155bd2a3a3b6a3, 0x603c186c0c0c300c, 0xff8af6847b7bf17b, 0xb5e16a803535d435,
0xe8693af51d1d741d, 0x5347ddb3e0e0a7e0, 0xf6acb321d7d77bd7, 0x5eed999cc2c22fc2,
0x6d965c432e2eb82e, 0x627a96294b4b314b, 0xa321e15dfefedffe, 0x8216aed557574157,
0xa8412abd15155415, 0x9fb6eee87777c177, 0xa5eb6e923737dc37, 0x7b56d79ee5e5b3e5,
0x8cd923139f9f469f, 0xd317fd23f0f0e7f0, 0x6a7f94204a4a354a, 0x9e95a944dada4fda,
0xfa25b0a258587d58, 0x06ca8fcfc9c903c9, 0x558d527c2929a429, 0x5022145a0a0a280a,
0xe14f7f50b1b1feb1, 0x691a5dc9a0a0baa0, 0x7fdad6146b6bb16b, 0x5cab17d985852e85,
0x8173673cbdbdcebd, 0xd234ba8f5d5d695d, 0x8050209010104010, 0xf303f507f4f4f7f4,
0x16c08bddcbcb0bcb, 0xedc67cd33e3ef83e, 0x28110a2d05051405, 0x1fe6ce7867678167,
0x7353d597e4e4b7e4, 0x25bb4e0227279c27, 0x3258827341411941, 0x2c9d0ba78b8b168b,
0x510153f6a7a7a6a7, 0xcf94fab27d7de97d, 0xdcfb374995956e95, 0x8e9fad56d8d847d8,
0x8b30eb70fbfbcbfb, 0x2371c1cdeeee9fee, 0xc791f8bb7c7ced7c, 0x17e3cc7166668566,
0xa68ea77bdddd53dd, 0xb84b2eaf17175c17, 0x02468e4547470147, 0x84dc211a9e9e429e,
0x1ec589d4caca0fca, 0x75995a582d2db42d, 0x9179632ebfbfc6bf, 0x381b0e3f07071c07,
0x012347acadad8ead, 0xea2fb4b05a5a755a, 0x6cb51bef83833683, 0x85ff66b63333cc33,
0x3ff2c65c63639163, 0x100a041202020802, 0x39384993aaaa92aa, 0xafa8e2de7171d971,
0x0ecf8dc6c8c807c8, 0xc87d32d119196419, 0x7270923b49493949, 0x869aaf5fd9d943d9,
0xc31df931f2f2eff2, 0x4b48dba8e3e3abe3, 0xe22ab6b95b5b715b, 0x34920dbc88881a88,
0xa4c8293e9a9a529a, 0x2dbe4c0b26269826, 0x8dfa64bf3232c832, 0xe94a7d59b0b0fab0,
0x1b6acff2e9e983e9, 0x78331e770f0f3c0f, 0xe6a6b733d5d573d5, 0x74ba1df480803a80,
0x997c6127bebec2be, 0x26de87ebcdcd13cd, 0xbde468893434d034, 0x7a75903248483d48,
0xab24e354ffffdbff, 0xf78ff48d7a7af57a, 0xf4ea3d6490907a90, 0xc23ebe9d5f5f615f,
0x1da0403d20208020, 0x67d5d00f6868bd68, 0xd07234ca1a1a681a, 0x192c41b7aeae82ae,
0xc95e757db4b4eab4, 0x9a19a8ce54544d54, 0xece53b7f93937693, 0x0daa442f22228822,
0x07e9c86364648d64, 0xdb12ff2af1f1e3f1, 0xbfa2e6cc7373d173, 0x905a248212124812,
0x3a5d807a40401d40, 0x4028104808082008, 0x56e89b95c3c32bc3, 0x337bc5dfecec97ec,
0x9690ab4ddbdb4bdb, 0x611f5fc0a1a1bea1, 0x1c8307918d8d0e8d, 0xf5c97ac83d3df43d,
0xccf1335b97976697, 0x0000000000000000, 0x36d483f9cfcf1bcf, 0x4587566e2b2bac2b,
0x97b3ece17676c576, 0x64b019e682823282, 0xfea9b128d6d67fd6, 0xd87736c31b1b6c1b,
0xc15b7774b5b5eeb5, 0x112943beafaf86af, 0x77dfd41d6a6ab56a, 0xba0da0ea50505d50,
0x124c8a5745450945, 0xcb18fb38f3f3ebf3, 0x9df060ad3030c030, 0x2b74c3c4efef9bef,
0xe5c37eda3f3ffc3f, 0x921caac755554955, 0x791059dba2a2b2a2, 0x0365c9e9eaea8fea,
0x0fecca6a65658965, 0xb9686903babad2ba, 0x65935e4a2f2fbc2f, 0x4ee79d8ec0c027c0,
0xbe81a160dede5fde, 0xe06c38fc1c1c701c, 0xbb2ee746fdfdd3fd, 0x52649a1f4d4d294d,
0xe4e0397692927292, 0x8fbceafa7575c975, 0x301e0c3606061806, 0x249809ae8a8a128a,
0xf940794bb2b2f2b2, 0x6359d185e6e6bfe6, 0x70361c7e0e0e380e, 0xf8633ee71f1f7c1f,
0x37f7c45562629562, 0xeea3b53ad4d477d4, 0x29324d81a8a89aa8, 0xc4f4315296966296,
0x9b3aef62f9f9c3f9, 0x66f697a3c5c533c5, 0x35b14a1025259425, 0xf220b2ab59597959,
0x54ae15d084842a84, 0xb7a7e4c57272d572, 0xd5dd72ec3939e439, 0x5a6198164c4c2d4c,
0xca3bbc945e5e655e, 0xe785f09f7878fd78, 0xddd870e53838e038, 0x148605988c8c0a8c,
0xc6b2bf17d1d163d1, 0x410b57e4a5a5aea5, 0x434dd9a1e2e2afe2, 0x2ff8c24e61619961,
0xf1457b42b3b3f6b3, 0x15a5423421218421, 0x94d625089c9c4a9c, 0xf0663cee1e1e781e,
0x2252866143431143, 0x76fc93b1c7c73bc7, 0xb32be54ffcfcd7fc, 0x2014082404041004,
0xb208a2e351515951, 0xbcc72f2599995e99, 0x4fc4da226d6da96d, 0x68391a650d0d340d,
0x8335e979fafacffa, 0xb684a369dfdf5bdf, 0xd79bfca97e7ee57e, 0x3db4481924249024,
0xc5d776fe3b3bec3b, 0x313d4b9aabab96ab, 0x3ed181f0cece1fce, 0x8855229911114411,
0x0c8903838f8f068f, 0x4a6b9c044e4e254e, 0xd1517366b7b7e6b7, 0x0b60cbe0ebeb8beb,
0xfdcc78c13c3cf03c, 0x7cbf1ffd81813e81, 0xd4fe354094946a94, 0xeb0cf31cf7f7fbf7,
0xa1676f18b9b9deb9, 0x985f268b13134c13, 0x7d9c58512c2cb02c, 0xd6b8bb05d3d36bd3,
0x6b5cd38ce7e7bbe7, 0x57cbdc396e6ea56e, 0x6ef395aac4c437c4, 0x180f061b03030c03,
0x8a13acdc56564556, 0x1a49885e44440d44, 0xdf9efea07f7fe17f, 0x21374f88a9a99ea9,
0x4d8254672a2aa82a, 0xb16d6b0abbbbd6bb, 0x46e29f87c1c123c1, 0xa202a6f153535153,
0xae8ba572dcdc57dc, 0x582716530b0b2c0b, 0x9cd327019d9d4e9d, 0x47c1d82b6c6cad6c,
0x95f562a43131c431, 0x87b9e8f37474cd74, 0xe309f115f6f6fff6, 0x0a438c4c46460546,
0x092645a5acac8aac, 0x3c970fb589891e89, 0xa04428b414145014, 0x5b42dfbae1e1a3e1,
0xb04e2ca616165816, 0xcdd274f73a3ae83a, 0x6fd0d2066969b969, 0x482d124109092409,
0xa7ade0d77070dd70, 0xd954716fb6b6e2b6, 0xceb7bd1ed0d067d0, 0x3b7ec7d6eded93ed,
0x2edb85e2cccc17cc, 0x2a57846842421542, 0xb4c22d2c98985a98, 0x490e55eda4a4aaa4,
0x5d8850752828a028, 0xda31b8865c5c6d5c, 0x933fed6bf8f8c7f8, 0x44a411c286862286,
// C5
0x18c07830d8181860, 0x2305af462623238c, 0xc67ef991b8c6c63f, 0xe8136fcdfbe8e887,
0x874ca113cb878726, 0xb8a9626d11b8b8da, 0x0108050209010104, 0x4f426e9e0d4f4f21,
0x36adee6c9b3636d8, 0xa6590451ffa6a6a2, 0xd2debdb90cd2d26f, 0xf5fb06f70ef5f5f3,
0x79ef80f2967979f9, 0x6f5fcede306f6fa1, 0x91fcef3f6d91917e, 0x52aa07a4f8525255,
0x6027fdc04760609d, 0xbc89766535bcbcca, 0x9baccd2b379b9b56, 0x8e048c018a8e8e02,
0xa371155bd2a3a3b6, 0x0c603c186c0c0c30, 0x7bff8af6847b7bf1, 0x35b5e16a803535d4,
0x1de8693af51d1d74, 0xe05347ddb3e0e0a7, 0xd7f6acb321d7d77b, 0xc25eed999cc2c22f,
0x2e6d965c432e2eb8, 0x4b627a96294b4b31, 0xfea321e15dfefedf, 0x578216aed5575741,
0x15a8412abd151554, 0x779fb6eee87777c1, 0x37a5eb6e923737dc, 0xe57b56d79ee5e5b3,
0x9f8cd923139f9f46, 0xf0d317fd23f0f0e7, 0x4a6a7f94204a4a35, 0xda9e95a944dada4f,
0x58fa25b0a258587d, 0xc906ca8fcfc9c903, 0x29558d527c2929a4, 0x0a5022145a0a0a28,
0xb1e14f7f50b1b1fe, 0xa0691a5dc9a0a0ba, 0x6b7fdad6146b6bb1, 0x855cab17d985852e,
0xbd8173673cbdbdce, 0x5dd234ba8f5d5d69, 0x1080502090101040, 0xf4f303f507f4f4f7,
0xcb16c08bddcbcb0b, 0x3eedc67cd33e3ef8, 0x0528110a2d050514, 0x671fe6ce78676781,
0xe47353d597e4e4b7, 0x2725bb4e0227279c, 0x4132588273414119, 0x8b2c9d0ba78b8b16,
0xa7510153f6a7a7a6, 0x7dcf94fab27d7de9, 0x95dcfb374995956e, 0xd88e9fad56d8d847,
0xfb8b30eb70fbfbcb, 0xee2371c1cdeeee9f, 0x7cc791f8bb7c7ced, 0x6617e3cc71666685,
0xdda68ea77bdddd53, 0x17b84b2eaf17175c, 0x4702468e45474701, 0x9e84dc211a9e9e42,
0xca1ec589d4caca0f, 0x2d75995a582d2db4, 0xbf9179632ebfbfc6, 0x07381b0e3f07071c,
0xad012347acadad8e, 0x5aea2fb4b05a5a75, 0x836cb51bef838336, 0x3385ff66b63333cc,
0x633ff2c65c636391, 0x02100a0412020208, 0xaa39384993aaaa92, 0x71afa8e2de7171d9,
0xc80ecf8dc6c8c807, 0x19c87d32d1191964, 0x497270923b494939, 0xd9869aaf5fd9d943,
0xf2c31df931f2f2ef, 0xe34b48dba8e3e3ab, 0x5be22ab6b95b5b71, 0x8834920dbc88881a,
0x9aa4c8293e9a9a52, 0x262dbe4c0b262698, 0x328dfa64bf3232c8, 0xb0e94a7d59b0b0fa,
0xe91b6acff2e9e983, 0x0f78331e770f0f3c, 0xd5e6a6b733d5d573, 0x8074ba1df480803a,
0xbe997c6127bebec2, 0xcd26de87ebcdcd13, 0x34bde468893434d0, 0x487a75903248483d,
0xffab24e354ffffdb, 0x7af78ff48d7a7af5, 0x90f4ea3d6490907a, 0x5fc23ebe9d5f5f61,
0x201da0403d202080, 0x6867d5d00f6868bd, 0x1ad07234ca1a1a68, 0xae192c41b7aeae82,
0xb4c95e757db4b4ea, 0x549a19a8ce54544d, 0x93ece53b7f939376, 0x220daa442f222288,
0x6407e9c86364648d, 0xf1db12ff2af1f1e3, 0x73bfa2e6cc7373d1, 0x12905a2482121248,
0x403a5d807a40401d, 0x0840281048080820, 0xc356e89b95c3c32b, 0xec337bc5dfecec97,
0xdb9690ab4ddbdb4b, 0xa1611f5fc0a1a1be, 0x8d1c8307918d8d0e, 0x3df5c97ac83d3df4,
0x97ccf1335b979766, 0x0000000000000000, 0xcf36d483f9cfcf1b, 0x2b4587566e2b2bac,
0x7697b3ece17676c5, 0x8264b019e6828232, 0xd6fea9b128d6d67f, 0x1bd87736c31b1b6c,
0xb5c15b7774b5b5ee, 0xaf112943beafaf86, 0x6a77dfd41d6a6ab5, 0x50ba0da0ea50505d,
0x45124c8a57454509, 0xf3cb18fb38f3f3eb, 0x309df060ad3030c0, 0xef2b74c3c4efef9b,
0x3fe5c37eda3f3ffc, 0x55921caac7555549, 0xa2791059dba2a2b2, 0xea0365c9e9eaea8f,
0x650fecca6a656589, 0xbab9686903babad2, 0x2f65935e4a2f2fbc, 0xc04ee79d8ec0c027,
0xdebe81a160dede5f, 0x1ce06c38fc1c1c70, 0xfdbb2ee746fdfdd3, 0x4d52649a1f4d4d29,
0x92e4e03976929272, 0x758fbceafa7575c9, 0x06301e0c36060618, 0x8a249809ae8a8a12,
0xb2f940794bb2b2f2, 0xe66359d185e6e6bf, 0x0e70361c7e0e0e38, 0x1ff8633ee71f1f7c,
0x6237f7c455626295, 0xd4eea3b53ad4d477, 0xa829324d81a8a89a, 0x96c4f43152969662,
0xf99b3aef62f9f9c3, 0xc566f697a3c5c533, 0x2535b14a10252594, 0x59f220b2ab595979,
0x8454ae15d084842a, 0x72b7a7e4c57272d5, 0x39d5dd72ec3939e4, 0x4c5a6198164c4c2d,
0x5eca3bbc945e5e65, 0x78e785f09f7878fd, 0x38ddd870e53838e0, 0x8c148605988c8c0a,
0xd1c6b2bf17d1d163, 0xa5410b57e4a5a5ae, 0xe2434dd9a1e2e2af, 0x612ff8c24e616199,
0xb3f1457b42b3b3f6, 0x2115a54234212184, 0x9c94d625089c9c4a, 0x1ef0663cee1e1e78,
0x4322528661434311, 0xc776fc93b1c7c73b, 0xfcb32be54ffcfcd7, 0x0420140824040410,
0x51b208a2e3515159, 0x99bcc72f2599995e, 0x6d4fc4da226d6da9, 0x0d68391a650d0d34,
0xfa8335e979fafacf, 0xdfb684a369dfdf5b, 0x7ed79bfca97e7ee5, 0x243db44819242490,
0x3bc5d776fe3b3bec, 0xab313d4b9aabab96, 0xce3ed181f0cece1f, 0x1188552299111144,
0x8f0c8903838f8f06, 0x4e4a6b9c044e4e25, 0xb7d1517366b7b7e6, 0xeb0b60cbe0ebeb8b,
0x3cfdcc78c13c3cf0, 0x817cbf1ffd81813e, 0x94d4fe354094946a, 0xf7eb0cf31cf7f7fb,
0xb9a1676f18b9b9de, 0x13985f268b13134c, 0x2c7d9c58512c2cb0, 0xd3d6b8bb05d3d36b,
0xe76b5cd38ce7e7bb, 0x6e57cbdc396e6ea5, 0xc46ef395aac4c437, 0x03180f061b03030c,
0x568a13acdc565645, 0x441a49885e44440d, 0x7fdf9efea07f7fe1, 0xa921374f88a9a99e,
0x2a4d8254672a2aa8, 0xbbb16d6b0abbbbd6, 0xc146e29f87c1c123, 0x53a202a6f1535351,
0xdcae8ba572dcdc57, 0x0b582716530b0b2c, 0x9d9cd327019d9d4e, 0x6c47c1d82b6c6cad,
0x3195f562a43131c4, 0x7487b9e8f37474cd, 0xf6e309f115f6f6ff, 0x460a438c4c464605,
0xac092645a5acac8a, 0x893c970fb589891e, 0x14a04428b4141450, 0xe15b42dfbae1e1a3,
0x16b04e2ca6161658, 0x3acdd274f73a3ae8, 0x696fd0d2066969b9, 0x09482d1241090924,
0x70a7ade0d77070dd, 0xb6d954716fb6b6e2, 0xd0ceb7bd1ed0d067, 0xed3b7ec7d6eded93,
0xcc2edb85e2cccc17, 0x422a578468424215, 0x98b4c22d2c98985a, 0xa4490e55eda4a4aa,
0x285d8850752828a0, 0x5cda31b8865c5c6d, 0xf8933fed6bf8f8c7, 0x8644a411c2868622,
// C6
0x6018c07830d81818, 0x8c2305af46262323, 0x3fc67ef991b8c6c6, 0x87e8136fcdfbe8e8,
0x26874ca113cb8787, 0xdab8a9626d11b8b8, 0x0401080502090101, 0x214f426e9e0d4f4f,
0xd836adee6c9b3636, 0xa2a6590451ffa6a6, 0x6fd2debdb90cd2d2, 0xf3f5fb06f70ef5f5,
0xf979ef80f2967979, 0xa16f5fcede306f6f, 0x7e91fcef3f6d9191, 0x5552aa07a4f85252,
0x9d6027fdc0476060, 0xcabc89766535bcbc, 0x569baccd2b379b9b, 0x028e048c018a8e8e,
0xb6a371155bd2a3a3, 0x300c603c186c0c0c, 0xf17bff8af6847b7b, 0xd435b5e16a803535,
0x741de8693af51d1d, 0xa7e05347ddb3e0e0, 0x7bd7f6acb321d7d7, 0x2fc25eed999cc2c2,
0xb82e6d965c432e2e, 0x314b627a96294b4b, 0xdffea321e15dfefe, 0x41578216aed55757,
0x5415a8412abd1515, 0xc1779fb6eee87777, 0xdc37a5eb6e923737, 0xb3e57b56d79ee5e5,
0x469f8cd923139f9f, 0xe7f0d317fd23f0f0, 0x354a6a7f94204a4a, 0x4fda9e95a944dada,
0x7d58fa25b0a25858, 0x03c906ca8fcfc9c9, 0xa429558d527c2929, 0x280a5022145a0a0a,
0xfeb1e14f7f50b1b1, 0xbaa0691a5dc9a0a0, 0xb16b7fdad6146b6b, 0x2e855cab17d98585,
0xcebd8173673cbdbd, 0x695dd234ba8f5d5d, 0x4010805020901010, 0xf7f4f303f507f4f4,
0x0bcb16c08bddcbcb, 0xf83eedc67cd33e3e, 0x140528110a2d0505, 0x81671fe6ce786767,
0xb7e47353d597e4e4, 0x9c2725bb4e022727, 0x1941325882734141, 0x168b2c9d0ba78b8b,
0xa6a7510153f6a7a7, 0xe97dcf94fab27d7d, 0x6e95dcfb37499595, 0x47d88e9fad56d8d8,
0xcbfb8b30eb70fbfb, 0x9fee2371c1cdeeee, 0xed7cc791f8bb7c7c, 0x856617e3cc716666,
0x53dda68ea77bdddd, 0x5c17b84b2eaf1717, 0x014702468e454747, 0x429e84dc211a9e9e,
0x0fca1ec589d4caca, 0xb42d75995a582d2d, 0xc6bf9179632ebfbf, 0x1c07381b0e3f0707,
0x8ead012347acadad, 0x755aea2fb4b05a5a, 0x36836cb51bef8383, 0xcc3385ff66b63333,
0x91633ff2c65c6363, 0x0802100a04120202, 0x92aa39384993aaaa, 0xd971afa8e2de7171,
0x07c80ecf8dc6c8c8, 0x6419c87d32d11919, 0x39497270923b4949, 0x43d9869aaf5fd9d9,
0xeff2c31df931f2f2, 0xabe34b48dba8e3e3, 0x715be22ab6b95b5b, 0x1a8834920dbc8888,
0x529aa4c8293e9a9a, 0x98262dbe4c0b2626, 0xc8328dfa64bf3232, 0xfab0e94a7d59b0b0,
0x83e91b6acff2e9e9, 0x3c0f78331e770f0f, 0x73d5e6a6b733d5d5, 0x3a8074ba1df48080,
0xc2be997c6127bebe, 0x13cd26de87ebcdcd, 0xd034bde468893434, 0x3d487a7590324848,
0xdbffab24e354ffff, 0xf57af78ff48d7a7a, 0x7a90f4ea3d649090, 0x615fc23ebe9d5f5f,
0x80201da0403d2020, 0xbd6867d5d00f6868, 0x681ad07234ca1a1a, 0x82ae192c41b7aeae,
0xeab4c95e757db4b4, 0x4d549a19a8ce5454, 0x7693ece53b7f9393, 0x88220daa442f2222,
0x8d6407e9c8636464, 0xe3f1db12ff2af1f1, 0xd173bfa2e6cc7373, 0x4812905a24821212,
0x1d403a5d807a4040, 0x2008402810480808, 0x2bc356e89b95c3c3, 0x97ec337bc5dfecec,
0x4bdb9690ab4ddbdb, 0xbea1611f5fc0a1a1, 0x0e8d1c8307918d8d, 0xf43df5c97ac83d3d,
0x6697ccf1335b9797, 0x0000000000000000, 0x1bcf36d483f9cfcf, 0xac2b4587566e2b2b,
0xc57697b3ece17676, 0x328264b019e68282, 0x7fd6fea9b128d6d6, 0x6c1bd87736c31b1b,
0xeeb5c15b7774b5b5, 0x86af112943beafaf, 0xb56a77dfd41d6a6a, 0x5d50ba0da0ea5050,
0x0945124c8a574545, 0xebf3cb18fb38f3f3, 0xc0309df060ad3030, 0x9bef2b74c3c4efef,
0xfc3fe5c37eda3f3f, 0x4955921caac75555, 0xb2a2791059dba2a2, 0x8fea0365c9e9eaea,
0x89650fecca6a6565, 0xd2bab9686903baba, 0xbc2f65935e4a2f2f, 0x27c04ee79d8ec0c0,
0x5fdebe81a160dede, 0x701ce06c38fc1c1c, 0xd3fdbb2ee746fdfd, 0x294d52649a1f4d4d,
0x7292e4e039769292, 0xc9758fbceafa7575, 0x1806301e0c360606, 0x128a249809ae8a8a,
0xf2b2f940794bb2b2, 0xbfe66359d185e6e6, 0x380e70361c7e0e0e, 0x7c1ff8633ee71f1f,
0x956237f7c4556262, 0x77d4eea3b53ad4d4, 0x9aa829324d81a8a8, 0x6296c4f431529696,
0xc3f99b3aef62f9f9, 0x33c566f697a3c5c5, 0x942535b14a102525, 0x7959f220b2ab5959,
0x2a8454ae15d08484, 0xd572b7a7e4c57272, 0xe439d5dd72ec3939, 0x2d4c5a6198164c4c,
0x655eca3bbc945e5e, 0xfd78e785f09f7878, 0xe038ddd870e53838, 0x0a8c148605988c8c,
0x63d1c6b2bf17d1d1, 0xaea5410b57e4a5a5, 0xafe2434dd9a1e2e2, 0x99612ff8c24e6161,
0xf6b3f1457b42b3b3, 0x842115a542342121, 0x4a9c94d625089c9c, 0x781ef0663cee1e1e,
0x1143225286614343, 0x3bc776fc93b1c7c7, 0xd7fcb32be54ffcfc, 0x1004201408240404,
0x5951b208a2e35151, 0x5e99bcc72f259999, 0xa96d4fc4da226d6d, 0x340d68391a650d0d,
0xcffa8335e979fafa, 0x5bdfb684a369dfdf, 0xe57ed79bfca97e7e, 0x90243db448192424,
0xec3bc5d776fe3b3b, 0x96ab313d4b9aabab, 0x1fce3ed181f0cece, 0x4411885522991111,
0x068f0c8903838f8f, 0x254e4a6b9c044e4e, 0xe6b7d1517366b7b7, 0x8beb0b60cbe0ebeb,
0xf03cfdcc78c13c3c, 0x3e817cbf1ffd8181, 0x6a94d4fe35409494, 0xfbf7eb0cf31cf7f7,
0xdeb9a1676f18b9b9, 0x4c13985f268b1313, 0xb02c7d9c58512c2c, 0x6bd3d6b8bb05d3d3,
0xbbe76b5cd38ce7e7, 0xa56e57cbdc396e6e, 0x37c46ef395aac4c4, 0x0c03180f061b0303,
0x45568a13acdc5656, 0x0d441a49885e4444, 0xe17fdf9efea07f7f, 0x9ea921374f88a9a9,
0xa82a4d8254672a2a, 0xd6bbb16d6b0abbbb, 0x23c146e29f87c1c1, 0x5153a202a6f15353,
0x57dcae8ba572dcdc, 0x2c0b582716530b0b, 0x4e9d9cd327019d9d, 0xad6c47c1d82b6c6c,
0xc43195f562a43131, 0xcd7487b9e8f37474, 0xfff6e309f115f6f6, 0x05460a438c4c4646,
0x8aac092645a5acac, 0x1e893c970fb58989, 0x5014a04428b41414, 0xa3e15b42dfbae1e1,
0x5816b04e2ca61616, 0xe83acdd274f73a3a, 0xb9696fd0d2066969, 0x2409482d12410909,
0xdd70a7ade0d77070, 0xe2b6d954716fb6b6, 0x67d0ceb7bd1ed0d0, 0x93ed3b7ec7d6eded,
0x17cc2edb85e2cccc, 0x15422a5784684242, 0x5a98b4c22d2c9898, 0xaaa4490e55eda4a4,
0xa0285d8850752828, 0x6d5cda31b8865c5c, 0xc7f8933fed6bf8f8, 0x228644a411c28686,
// C7
0x186018c07830d818, 0x238c2305af462623, 0xc63fc67ef991b8c6, 0xe887e8136fcdfbe8,
0x8726874ca113cb87, 0xb8dab8a9626d11b8, 0x0104010805020901, 0x4f214f426e9e0d4f,
0x36d836adee6c9b36, 0xa6a2a6590451ffa6, 0xd26fd2debdb90cd2, 0xf5f3f5fb06f70ef5,
0x79f979ef80f29679, 0x6fa16f5fcede306f, 0x917e91fcef3f6d91, 0x525552aa07a4f852,
0x609d6027fdc04760, 0xbccabc89766535bc, 0x9b569baccd2b379b, 0x8e028e048c018a8e,
0xa3b6a371155bd2a3, 0x0c300c603c186c0c, 0x7bf17bff8af6847b, 0x35d435b5e16a8035,
0x1d741de8693af51d, 0xe0a7e05347ddb3e0, 0xd77bd7f6acb321d7, 0xc22fc25eed999cc2,
0x2eb82e6d965c432e, 0x4b314b627a96294b, 0xfedffea321e15dfe, 0x5741578216aed557,
0x155415a8412abd15, 0x77c1779fb6eee877, 0x37dc37a5eb6e9237, 0xe5b3e57b56d79ee5,
0x9f469f8cd923139f, 0xf0e7f0d317fd23f0, 0x4a354a6a7f94204a, 0xda4fda9e95a944da,
0x587d58fa25b0a258, 0xc903c906ca8fcfc9, 0x29a429558d527c29, 0x0a280a5022145a0a,
0xb1feb1e14f7f50b1, 0xa0baa0691a5dc9a0, 0x6bb16b7fdad6146b, 0x852e855cab17d985,
0xbdcebd8173673cbd, 0x5d695dd234ba8f5d, 0x1040108050209010, 0xf4f7f4f303f507f4,
0xcb0bcb16c08bddcb, 0x3ef83eedc67cd33e, 0x05140528110a2d05, 0x6781671fe6ce7867,
0xe4b7e47353d597e4, 0x279c2725bb4e0227, 0x4119413258827341, 0x8b168b2c9d0ba78b,
0xa7a6a7510153f6a7, 0x7de97dcf94fab27d, 0x956e95dcfb374995, 0xd847d88e9fad56d8,
0xfbcbfb8b30eb70fb, 0xee9fee2371c1cdee, 0x7ced7cc791f8bb7c, 0x66856617e3cc7166,
0xdd53dda68ea77bdd, 0x175c17b84b2eaf17, 0x47014702468e4547, 0x9e429e84dc211a9e,
0xca0fca1ec589d4ca, 0x2db42d75995a582d, 0xbfc6bf9179632ebf, 0x071c07381b0e3f07,
0xad8ead012347acad, 0x5a755aea2fb4b05a, 0x8336836cb51bef83, 0x33cc3385ff66b633,
0x6391633ff2c65c63, 0x020802100a041202, 0xaa92aa39384993aa, 0x71d971afa8e2de71,
0xc807c80ecf8dc6c8, 0x196419c87d32d119, 0x4939497270923b49, 0xd943d9869aaf5fd9,
0xf2eff2c31df931f2, 0xe3abe34b48dba8e3, 0x5b715be22ab6b95b, 0x881a8834920dbc88,
0x9a529aa4c8293e9a, 0x2698262dbe4c0b26, 0x32c8328dfa64bf32, 0xb0fab0e94a7d59b0,
0xe983e91b6acff2e9, 0x0f3c0f78331e770f, 0xd573d5e6a6b733d5, 0x803a8074ba1df480,
0xbec2be997c6127be, 0xcd13cd26de87ebcd, 0x34d034bde4688934, 0x483d487a75903248,
0xffdbffab24e354ff, 0x7af57af78ff48d7a, 0x907a90f4ea3d6490, 0x5f615fc23ebe9d5f,
0x2080201da0403d20, 0x68bd6867d5d00f68, 0x1a681ad07234ca1a, 0xae82ae192c41b7ae,
0xb4eab4c95e757db4, 0x544d549a19a8ce54, 0x937693ece53b7f93, 0x2288220daa442f22,
0x648d6407e9c86364, 0xf1e3f1db12ff2af1, 0x73d173bfa2e6cc73, 0x124812905a248212,
0x401d403a5d807a40, 0x0820084028104808, 0xc32bc356e89b95c3, 0xec97ec337bc5dfec,
0xdb4bdb9690ab4ddb, 0xa1bea1611f5fc0a1, 0x8d0e8d1c8307918d, 0x3df43df5c97ac83d,
0x976697ccf1335b97, 0x0000000000000000, 0xcf1bcf36d483f9cf, 0x2bac2b4587566e2b,
0x76c57697b3ece176, 0x82328264b019e682, 0xd67fd6fea9b128d6, 0x1b6c1bd87736c31b,
0xb5eeb5c15b7774b5, 0xaf86af112943beaf, 0x6ab56a77dfd41d6a, 0x505d50ba0da0ea50,
0x450945124c8a5745, 0xf3ebf3cb18fb38f3, 0x30c0309df060ad30, 0xef9bef2b74c3c4ef,
0x3ffc3fe5c37eda3f, 0x554955921caac755, 0xa2b2a2791059dba2, 0xea8fea0365c9e9ea,
0x6589650fecca6a65, 0xbad2bab9686903ba, 0x2fbc2f65935e4a2f, 0xc027c04ee79d8ec0,
0xde5fdebe81a160de, 0x1c701ce06c38fc1c, 0xfdd3fdbb2ee746fd, 0x4d294d52649a1f4d,
0x927292e4e0397692, 0x75c9758fbceafa75, 0x061806301e0c3606, 0x8a128a249809ae8a,
0xb2f2b2f940794bb2, 0xe6bfe66359d185e6, 0x0e380e70361c7e0e, 0x1f7c1ff8633ee71f,
0x62956237f7c45562, 0xd477d4eea3b53ad4, 0xa89aa829324d81a8, 0x966296c4f4315296,
0xf9c3f99b3aef62f9, 0xc533c566f697a3c5, 0x25942535b14a1025, 0x597959f220b2ab59,
0x842a8454ae15d084, 0x72d572b7a7e4c572, 0x39e439d5dd72ec39, 0x4c2d4c5a6198164c,
0x5e655eca3bbc945e, 0x78fd78e785f09f78, 0x38e038ddd870e538, 0x8c0a8c148605988c,
0xd163d1c6b2bf17d1, 0xa5aea5410b57e4a5, 0xe2afe2434dd9a1e2, 0x6199612ff8c24e61,
0xb3f6b3f1457b42b3, 0x21842115a5423421, 0x9c4a9c94d625089c, 0x1e781ef0663cee1e,
0x4311432252866143, 0xc73bc776fc93b1c7, 0xfcd7fcb32be54ffc, 0x0410042014082404,
0x515951b208a2e351, 0x995e99bcc72f2599, 0x6da96d4fc4da226d, 0x0d340d68391a650d,
0xfacffa8335e979fa, 0xdf5bdfb684a369df, 0x7ee57ed79bfca97e, 0x2490243db4481924,
0x3bec3bc5d776fe3b, 0xab96ab313d4b9aab, 0xce1fce3ed181f0ce, 0x1144118855229911,
0x8f068f0c8903838f, 0x4e254e4a6b9c044e, 0xb7e6b7d1517366b7, 0xeb8beb0b60cbe0eb,
0x3cf03cfdcc78c13c, 0x813e817cbf1ffd81, 0x946a94d4fe354094, 0xf7fbf7eb0cf31cf7,
0xb9deb9a1676f18b9, 0x134c13985f268b13, 0x2cb02c7d9c58512c, 0xd36bd3d6b8bb05d3,
0xe7bbe76b5cd38ce7, 0x6ea56e57cbdc396e, 0xc437c46ef395aac4, 0x030c03180f061b03,
0x5645568a13acdc56, 0x440d441a49885e44, 0x7fe17fdf9efea07f, 0xa99ea921374f88a9,
0x2aa82a4d8254672a, 0xbbd6bbb16d6b0abb, 0xc123c146e29f87c1, 0x535153a202a6f153,
0xdc57dcae8ba572dc, 0x0b2c0b582716530b, 0x9d4e9d9cd327019d, 0x6cad6c47c1d82b6c,
0x31c43195f562a431, 0x74cd7487b9e8f374, 0xf6fff6e309f115f6, 0x4605460a438c4c46,
0xac8aac092645a5ac, 0x891e893c970fb589, 0x145014a04428b414, 0xe1a3e15b42dfbae1,
0x165816b04e2ca616, 0x3ae83acdd274f73a, 0x69b9696fd0d20669, 0x092409482d124109,
0x70dd70a7ade0d770, 0xb6e2b6d954716fb6, 0xd067d0ceb7bd1ed0, 0xed93ed3b7ec7d6ed,
0xcc17cc2edb85e2cc, 0x4215422a57846842, 0x985a98b4c22d2c98, 0xa4aaa4490e55eda4,
0x28a0285d88507528, 0x5c6d5cda31b8865c, 0xf8c7f8933fed6bf8, 0x86228644a411c286,
};

56
lib/std/hash/wyhash2.c3 Normal file
View File

@@ -0,0 +1,56 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// An implementation of Wang Yi's wyhash(2) algorithm in C3:
// https://github.com/wangyi-fudan/wyhash
//
module std::hash::wyhash2;
fn ulong wyr3(char* in, usz len) @inline
=> ((ulong)in[0] << 16) | ((ulong)in[len >> 1] << 8) | (ulong)in[len - 1];
// See: https://docs.google.com/spreadsheets/d/1HmqDj-suH4wBFNg7etwE8WVBlfCufvD5-gAnIENs94k/edit?gid=1915335726#gid=1915335726
// Credit to article:
// https://medium.com/@tprodanov/benchmarking-non-cryptographic-hash-functions-in-rust-2e6091077d11
//
// wyhash2 has a >90% chance of collisions when its input data is above 16 bytes in length.
// However, it is the fastest performing and most evenly randomized hash for very low-length inputs,
// making it an ideal candidate for hashing primitive data types quickly and making things like hash
// tables even faster. Therefore, a 16-byte input limit is imposed on all calls to the hash function.
//
<*
@require input.len <= 16 : `wyhash2 is not useable for inputs over 16 bytes in length.`
*>
fn ulong hash(char[] input, ulong seed = 0)
{
seed ^= 0xa076_1d64_78bd_642f;
ulong a, b;
if (@likely(input.len <= 8)) // more likely to encounter 8-byte or lower type here
{
if (@likely(input.len >= 4))
{
a = (ulong)@unaligned_load(*(uint*)input.ptr, 1); // first 4 bytes widened to a u64
b = (ulong)@unaligned_load(*(uint*)&input[^4], 1); // a walking 4-byte window based on input.len
}
else if (input.len > 0)
{
a = wyr3(input, input.len);
}
}
else
{
a = @unaligned_load(*(ulong*)input.ptr, 1); // first 8 bytes
b = @unaligned_load(*(ulong*)&input[^8], 1); // a walking 8-byte window based on input.len
}
uint128 r = ((uint128)a ^ 0xe703_7ed1_a0b4_28db) * ((uint128)b ^ seed);
ulong pre_res = (ulong)r ^ (ulong)(r >> 64);
r = ((uint128)0xe703_7ed1_a0b4_28db ^ input.len) * (uint128)pre_res;
return (ulong)r ^ (ulong)(r >> 64);
}

View File

@@ -69,7 +69,6 @@ struct Formatter
PrintFlags flags;
uint width;
uint prec;
usz idx;
fault first_fault;
}
}
@@ -495,6 +494,11 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
out = ((char*)current.ptr)[:current.type.sizeof];
break;
}
if (current.type.kindof == POINTER)
{
out = ((*(char**)current.ptr))[:current.type.inner.sizeof];
break;
}
total_len += self.out_substr("<INVALID>")!;
continue;
}
@@ -566,5 +570,5 @@ fn usz? Formatter.print(&self, String str)
self.out_fn = &out_null_fn;
}
foreach (c : str) self.out(c)!;
return self.idx;
return str.len;
}

View File

@@ -660,7 +660,6 @@ fn usz? Formatter.out_char(&self, any arg) @private
fn usz? Formatter.out_reverse(&self, char[] buf) @private
{
usz n;
usz buffer_start_idx = self.idx;
usz len = buf.len;
// pad spaces up to given width
if (!self.flags.zeropad && !self.flags.left)

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2025 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::io;
@@ -16,6 +16,7 @@ faultdef
BUSY,
CANNOT_READ_DIR,
DIR_NOT_EMPTY,
PARENT_DIR_MISSING,
EOF,
FILE_CANNOT_DELETE,
FILE_IS_DIR,
@@ -49,41 +50,23 @@ faultdef
"\r" will be filtered from the String.
@param stream : `The stream to read from.`
@require @is_instream(stream) : `The stream must implement InStream.`
@require !($defined(&stream) &&& @is_instream(&stream)) : "The value for 'stream' should have been passed as a pointer and not as a value, please add '&'."
@require @is_instream(stream) : `Make sure that the stream is actually an InStream.`
@param [inout] allocator : `the allocator to use.`
@return `The string containing the data read.`
*>
macro String? readline(Allocator allocator, stream = io::stdin())
{
bool $is_stream = @typeis(stream, InStream);
$if $is_stream:
$typeof(&stream.read_byte) func = &stream.read_byte;
char val = func((void*)stream)!;
$else
char val = stream.read_byte()!;
$endif
if (val == '\n') return "";
if (allocator == tmem)
{
DString str = dstring::temp_with_capacity(256);
readline_to_stream(&str, stream)!;
return str.str_view();
}
@pool()
{
DString str = dstring::temp_with_capacity(256);
if (val != '\r') str.append(val);
while (1)
{
$if $is_stream:
char? c = func((void*)stream);
$else
char? c = stream.read_byte();
$endif
if (catch err = c)
{
if (err == io::EOF) break;
return err?;
}
if (c == '\r') continue;
if (c == '\n') break;
str.append_char(c);
}
readline_to_stream(&str, stream)!;
return str.copy_str(allocator);
};
}
@@ -93,6 +76,7 @@ macro String? readline(Allocator allocator, stream = io::stdin())
on the temporary allocator and does not need to be freed.
@param stream : `The stream to read from.`
@require !($defined(&stream) &&& @is_instream(&stream)) : "The value for 'stream' should have been passed as a pointer and not as a value, please add '&'."
@require @is_instream(stream) : `The stream must implement InStream.`
@return `The temporary string containing the data read.`
*>
@@ -101,6 +85,65 @@ macro String? treadline(stream = io::stdin())
return readline(tmem, stream) @inline;
}
<*
Reads a string, see `readline`, the data is passed to an outstream
@param out_stream : `The stream to write to`
@param in_stream : `The stream to read from.`
@require !($defined(&in_stream) &&& @is_instream(&in_stream)) : "The value for 'in_stream' should have been passed as a pointer and not as a value, please add '&'."
@require !($defined(&out_stream) &&& @is_outstream(&out_stream)) : "The value for 'out_stream' should have been passed as a pointer and not as a value, please add '&'."
@require @is_instream(in_stream) : `The in_stream must implement InStream.`
@require @is_outstream(out_stream) : `The out_stream must implement OutStream.`
@return `The number of bytes written`
*>
macro usz? readline_to_stream(out_stream, in_stream = io::stdin())
{
bool $is_stream = @typeis(in_stream, InStream);
$if $is_stream:
var func = &in_stream.read_byte;
char val = func((void*)in_stream)!;
$else
char val = in_stream.read_byte()!;
$endif
bool $is_out_stream = @typeis(out_stream, OutStream);
$if $is_out_stream:
var out_func = &out_stream.write_byte;
$endif
if (val == '\n') return 0;
usz len;
if (val != '\r')
{
$if $is_out_stream:
out_func((void*)out_stream.ptr, val)!;
$else
out_stream.write_byte(val)!;
$endif
len++;
}
while (1)
{
$if $is_stream:
char? c = func((void*)in_stream);
$else
char? c = in_stream.read_byte();
$endif
if (catch err = c)
{
if (err == io::EOF) break;
return err?;
}
if (c == '\r') continue;
if (c == '\n') break;
$if $is_out_stream:
out_func((void*)out_stream.ptr, c)!;
$else
out_stream.write_byte(c)!;
$endif
len++;
}
return len;
}
<*
Print a value to a stream.
@@ -117,7 +160,7 @@ macro usz? fprint(out, x)
$case ZString: return out.write(x.str_view());
$case DString: return out.write(x.str_view());
$default:
$if $assignable(x, String):
$if @assignable_to(x, String):
return out.write((String)x);
$else
$if is_struct_with_default_print($Type):

View File

@@ -3,28 +3,28 @@ import std::io::path, libc, std::os;
macro void? native_chdir(Path path)
{
$switch:
$case env::POSIX:
if (posix::chdir(path.as_zstr()))
{
switch (libc::errno())
@pool()
{
$switch:
$case env::POSIX:
if (posix::chdir(path.str_view().zstr_tcopy()))
{
case errno::EACCES: return io::NO_PERMISSION?;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG?;
case errno::ENOTDIR: return io::FILE_NOT_DIR?;
case errno::ENOENT: return io::FILE_NOT_FOUND?;
case errno::ELOOP: return io::SYMLINK_FAILED?;
default: return io::GENERAL_ERROR?;
switch (libc::errno())
{
case errno::EACCES: return io::NO_PERMISSION?;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG?;
case errno::ENOTDIR: return io::FILE_NOT_DIR?;
case errno::ENOENT: return io::FILE_NOT_FOUND?;
case errno::ELOOP: return io::SYMLINK_FAILED?;
default: return io::GENERAL_ERROR?;
}
}
}
$case env::WIN32:
@pool()
{
$case env::WIN32:
// TODO improve with better error handling.
if (win32::setCurrentDirectoryW(path.str_view().to_temp_utf16()!!)) return;
};
return io::GENERAL_ERROR?;
$default:
return io::UNSUPPORTED_OPERATION?;
$endswitch
return io::GENERAL_ERROR?;
$default:
return io::UNSUPPORTED_OPERATION?;
$endswitch
};
}

View File

@@ -5,7 +5,11 @@ fn PathList? native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
{
PathList list;
list.init(allocator);
DIRPtr directory = posix::opendir(dir.str_view() ? dir.as_zstr() : (ZString)".");
DIRPtr directory @noinit;
@pool()
{
directory = posix::opendir(dir.str_view() ? dir.str_view().zstr_tcopy() : (ZString)".");
};
defer if (directory) posix::closedir(directory);
if (!directory) return (path::is_dir(dir) ? io::CANNOT_READ_DIR : io::FILE_NOT_DIR)?;
Posix_dirent* entry;

View File

@@ -7,9 +7,11 @@ import std::os::posix;
macro bool? native_mkdir(Path path, MkdirPermissions permissions)
{
@pool()
{
$switch:
$case env::POSIX:
if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true;
if (!posix::mkdir(path.str_view().zstr_tcopy(), permissions == NORMAL ? 0o777 : 0o700)) return true;
switch (libc::errno())
{
case errno::EACCES:
@@ -23,11 +25,11 @@ macro bool? native_mkdir(Path path, MkdirPermissions permissions)
case errno::EEXIST: return false;
case errno::ELOOP: return io::SYMLINK_FAILED?;
case errno::ENOTDIR: return io::FILE_NOT_FOUND?;
default: return io::GENERAL_ERROR?;
case errno::ENOENT: return io::PARENT_DIR_MISSING?;
default:
return io::GENERAL_ERROR?;
}
$case env::WIN32:
@pool()
{
// TODO security attributes
if (win32::createDirectoryW(path.str_view().to_temp_utf16()!!, null)) return true;
switch (win32::getLastError())
@@ -43,8 +45,8 @@ macro bool? native_mkdir(Path path, MkdirPermissions permissions)
default:
return io::GENERAL_ERROR?;
}
};
$default:
return io::UNSUPPORTED_OPERATION?;
$endswitch
};
}

View File

@@ -6,9 +6,11 @@ import std::os::posix;
macro bool? native_rmdir(Path path)
{
@pool()
{
$switch:
$case env::POSIX:
if (!posix::rmdir(path.as_zstr())) return true;
if (!posix::rmdir(path.str_view().zstr_tcopy())) return true;
switch (libc::errno())
{
case errno::EBUSY: return io::BUSY?;
@@ -24,25 +26,23 @@ macro bool? native_rmdir(Path path)
default: return io::GENERAL_ERROR?;
}
$case env::WIN32:
@pool()
if (win32::removeDirectoryW(path.str_view().to_temp_utf16()!!)) return true;
switch (win32::getLastError())
{
if (win32::removeDirectoryW(path.str_view().to_temp_utf16()!!)) return true;
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:
return io::NO_PERMISSION?;
case win32::ERROR_CURRENT_DIRECTORY:
return io::BUSY?;
case win32::ERROR_DIR_NOT_EMPTY:
return io::DIR_NOT_EMPTY?;
case win32::ERROR_DIRECTORY:
case win32::ERROR_PATH_NOT_FOUND:
return false;
default:
return io::GENERAL_ERROR?;
}
};
case win32::ERROR_ACCESS_DENIED:
return io::NO_PERMISSION?;
case win32::ERROR_CURRENT_DIRECTORY:
return io::BUSY?;
case win32::ERROR_DIR_NOT_EMPTY:
return io::DIR_NOT_EMPTY?;
case win32::ERROR_DIRECTORY:
case win32::ERROR_PATH_NOT_FOUND:
return false;
default:
return io::GENERAL_ERROR?;
}
$default:
return io::UNSUPPORTED_OPERATION?;
$endswitch
};
}

View File

@@ -6,30 +6,33 @@ import std::io, std::os, libc;
*>
fn void? native_rmtree(Path dir)
{
DIRPtr directory = posix::opendir(dir.as_zstr());
defer if (directory) posix::closedir(directory);
if (!directory) return path::is_dir(dir) ? io::CANNOT_READ_DIR? : io::FILE_NOT_DIR?;
Posix_dirent* entry;
while ((entry = posix::readdir(directory)))
@pool()
{
@pool()
DIRPtr directory = posix::opendir(dir.str_view().zstr_tcopy());
defer if (directory) posix::closedir(directory);
if (!directory) return path::is_dir(dir) ? io::CANNOT_READ_DIR? : io::FILE_NOT_DIR?;
Posix_dirent* entry;
while ((entry = posix::readdir(directory)))
{
String name = ((ZString)&entry.name).str_view();
if (!name || name == "." || name == "..") continue;
Path new_path = dir.tappend(name)!;
if (entry.d_type == posix::DT_DIR)
@pool()
{
native_rmtree(new_path)!;
continue;
}
if (libc::remove(new_path.as_zstr()))
{
// TODO improve
return io::GENERAL_ERROR?;
}
};
}
os::native_rmdir(dir)!;
String name = ((ZString)&entry.name).str_view();
if (!name || name == "." || name == "..") continue;
Path new_path = dir.tappend(name)!;
if (entry.d_type == posix::DT_DIR)
{
native_rmtree(new_path)!;
continue;
}
if (libc::remove(new_path.str_view().zstr_tcopy()))
{
// TODO improve
return io::GENERAL_ERROR?;
}
};
}
os::native_rmdir(dir)!;
};
}
module std::io::os @if(env::WIN32);

View File

@@ -93,7 +93,7 @@ enum MkdirPermissions
@param recursive : `If directories in between should be created if they're missing, defaults to false`
@param permissions : `The permissions to set on the directory`
*>
macro bool? mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
macro bool? mkdir(path, bool recursive = false, MkdirPermissions permissions = NORMAL)
{
$if @typeis(path, String):
@pool() { return _mkdir(temp(path), recursive, permissions); };
@@ -148,27 +148,20 @@ fn Path? new(Allocator allocator, String path, PathEnv path_env = DEFAULT_ENV)
@return? INVALID_PATH : `if the path was invalid`
*>
fn Path? temp(String path, PathEnv path_env = DEFAULT_ENV)
{
return new(tmem, path, path_env);
}
fn Path? temp(String path, PathEnv path_env = DEFAULT_ENV) => new(tmem, path, path_env);
fn Path? from_win32_wstring(Allocator allocator, WString path) => @pool()
fn Path? from_wstring(Allocator allocator, WString path) => @pool()
{
return path::new(allocator, string::tfrom_wstring(path)!);
}
fn Path? for_windows(Allocator allocator, String path)
{
return new(allocator, path, WIN32);
}
fn Path? from_win32_wstring(Allocator allocator, WString path) @deprecated("Use 'from_wstring' instead") => from_wstring(allocator, path);
fn Path? for_posix(Allocator allocator, String path)
{
return new(allocator, path, POSIX);
}
fn Path? for_windows(Allocator allocator, String path) => new(allocator, path, WIN32);
fn bool Path.equals(self, Path p2)
fn Path? for_posix(Allocator allocator, String path) => new(allocator, path, POSIX);
fn bool Path.equals(self, Path p2) @operator(==)
{
return self.env == p2.env && self.path_string == p2.path_string;
}
@@ -340,15 +333,9 @@ fn String Path.volume_name(self)
return self.path_string[:len];
}
fn Path? String.to_path(self, Allocator allocator)
{
return new(allocator, self);
}
fn Path? String.to_path(self, Allocator allocator) => new(allocator, self);
fn Path? String.to_tpath(self)
{
return new(tmem, self);
}
fn Path? String.to_tpath(self) => new(tmem, self);
fn usz? volume_name_len(String path, PathEnv path_env) @local
{
@@ -527,7 +514,7 @@ fn String? normalize(String path_str, PathEnv path_env = DEFAULT_ENV)
return path_str[:len];
}
fn ZString Path.as_zstr(self) => (ZString)self.path_string.ptr;
fn ZString Path.as_zstr(self) @deprecated => (ZString)self.path_string.ptr;
fn String Path.root_directory(self)
{
@@ -607,29 +594,17 @@ fn bool? traverse(Path path, TraverseCallback callback, any data)
return false;
}
fn String Path.str_view(self) @inline
{
return self.path_string;
}
fn String Path.str_view(self) @inline => self.path_string;
fn bool Path.has_suffix(self, String str)
{
return self.str_view().ends_with(str);
}
fn bool Path.has_suffix(self, String str) => self.str_view().ends_with(str);
<*
@require self.allocator != null : "This Path should never be freed"
*>
fn void Path.free(self)
{
allocator::free(self.allocator, self.path_string.ptr);
}
fn void Path.free(self) => allocator::free(self.allocator, self.path_string.ptr);
fn usz? Path.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.print(self.str_view());
}
fn usz? Path.to_format(&self, Formatter* formatter) @dynamic => formatter.print(self.str_view());
const bool[256] RESERVED_PATH_CHAR_POSIX = {
@@ -649,10 +624,7 @@ const bool[256] RESERVED_PATH_CHAR_WIN32 = {
['*'] = true,
};
macro bool is_reserved_win32_path_char(char c)
{
return RESERVED_PATH_CHAR_WIN32[c];
}
macro bool is_reserved_win32_path_char(char c) => RESERVED_PATH_CHAR_WIN32[c];
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_ENV)
{

View File

@@ -39,12 +39,12 @@ fn usz? available(InStream s)
macro bool @is_instream(#expr)
{
return $assignable(#expr, InStream);
return @assignable_to(#expr, InStream);
}
macro bool @is_outstream(#expr)
{
return $assignable(#expr, OutStream);
return @assignable_to(#expr, OutStream);
}
<*

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2025 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module libc;
@@ -8,6 +8,9 @@ const int EXIT_FAILURE = 1;
const int EXIT_SUCCESS = 0;
const int RAND_MAX = 0x7fffffff;
alias WChar @if(env::WIN32) = Char16;
alias WChar @if(!env::WIN32) = Char32;
struct DivResult
{
CInt quot;
@@ -148,6 +151,7 @@ extern fn void qsort(void* base, usz items, usz size, CompareFunction compare);
extern fn CInt raise(CInt signal);
extern fn CInt rand();
extern fn isz read(Fd fd, void* buf, usz nbyte) @if(!env::WIN32);
extern fn isz readlink(ZString pathname, char* buf, int bufsize) @if(!env::WIN32);
extern fn void* realloc(void* ptr, usz size);
extern fn CInt remove(ZString filename);
extern fn CInt rename(ZString old_name, ZString new_name);

View File

@@ -4,7 +4,7 @@ import std::time;
<*
Return a "timespec" from a duration.
@require self >= 0
@require self >= time::NANO_DURATION_ZERO
*>
fn TimeSpec NanoDuration.to_timespec(self) @inline
{
@@ -16,7 +16,7 @@ fn TimeSpec NanoDuration.to_timespec(self) @inline
<*
Convert a duration to a timespec.
@require self >= 0
@require self >= time::DURATION_ZERO
*>
fn TimeSpec Duration.to_timespec(self) @inline
{
@@ -24,3 +24,11 @@ fn TimeSpec Duration.to_timespec(self) @inline
Time_t sec = (Time_t)(self / time::SEC);
return { .s = sec, .ns = ns };
}
<*
Convert a timestamp to a timespec.
*>
fn TimeSpec Time.to_timespec(self) @inline
{
return ((Duration)self).to_timespec();
}

View File

@@ -11,6 +11,11 @@ extern fn int* __errno() @if(env::ANDROID);
macro int errno() @if(env::ANDROID) => *__errno();
macro void errno_set(int err) @if(env::ANDROID) => *(__errno()) = err;
// OpenBSD
extern fn int* __errno() @if(env::OPENBSD);
macro int errno() @if(env::OPENBSD) => *__errno();
macro void errno_set(int err) @if(env::OPENBSD) => *(__errno()) = err;
// Darwin
extern fn int* __error() @if(env::DARWIN);
macro int errno() @if(env::DARWIN) => *__error();

View File

@@ -0,0 +1,60 @@
module libc @if(env::OPENBSD);
// Checked for x86_64
alias Blksize_t = int;
alias Nlink_t = $typefrom(env::X86_64 ? uint.typeid : CUInt.typeid);
alias Dev_t = int;
alias Ino_t = ulong;
alias Mode_t = uint;
alias Blkcnt_t = long;
alias Fflags_t = uint;
struct Stat @if(env::X86_64)
{
Mode_t st_mode;
Dev_t st_dev;
Ino_t st_ino;
Nlink_t st_nlink;
Uid_t st_uid;
Gid_t st_gid;
Dev_t st_rdev;
TimeSpec st_atime;
TimeSpec st_mtime;
TimeSpec st_ctime;
Off_t st_size;
Blkcnt_t st_blocks;
Blksize_t st_blksize;
Fflags_t st_flags;
uint st_gen;
ulong[10] st_spare;
}
// TODO: Investigate if this needs to be fixed
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

@@ -43,7 +43,7 @@ macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b };
macro Complex Complex.scale(self, Real r) @operator_s(*) => { .v = self.v * r };
macro Complex Complex.mul(self, Complex b)@operator(*) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
macro Complex Complex.div_real(self, Real r) @operator(/) => { .v = self.v / r };
macro Complex Complex.div_real_inverse(Complex c, Real r) @operator_r(/) => ((Complex) { .r = self }).div(c);
macro Complex Complex.div_real_inverse(Complex c, Real r) @operator_r(/) => ((Complex) { .r = r }).div(c);
macro Complex Complex.div(self, Complex b) @operator(/)
{
Real div = b.v.dot(b.v);

View File

@@ -6,6 +6,8 @@ import std::math::complex;
import std::math::matrix;
import std::math::quaternion;
attrdef @MathLibc(name) = @extern(name), @link(env::POSIX, "m");
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
const LOG10E = 0.434294481903251827651128918916605082; // log10(e)
@@ -128,7 +130,7 @@ macro atan2(x, y)
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
@require @typekind(sinp) == POINTER : "Expected sinp to be a pointer"
@require values::@is_same_type(sinp, cosp) : "Expected sinp and cosp to have the same type"
@require $assignable(x, $typeof(*sinp)) : "Expected x and sinp/cosp to have the same type"
@require @assignable_to(x, $typeof(*sinp)) : "Expected x and sinp/cosp to have the same type"
*>
macro sincos_ref(x, sinp, cosp)
{
@@ -394,7 +396,7 @@ macro nearbyint(x) => $$nearbyint(x);
<*
@require values::@is_promotable_to_floatlike(x) : `The input must be a number or a float vector`
@require $assignable(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) : `The input must be an integer, castable to the type of x`
@require @assignable_to(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) : `The input must be an integer, castable to the type of x`
*>
macro pow(x, exp)
{
@@ -1012,33 +1014,33 @@ macro void float.set_word(float* f, uint u) => *f = bitcast(u, float);
macro double scalbn(double x, int n) => _scalbn(x, n);
extern fn double _atan(double x) @extern("atan");
extern fn float _atanf(float x) @extern("atanf");
extern fn double _atan2(double, double) @extern("atan2");
extern fn float _atan2f(float, float) @extern("atan2f");
extern fn double _atan(double x) @MathLibc("atan");
extern fn float _atanf(float x) @MathLibc("atanf");
extern fn double _atan2(double, double) @MathLibc("atan2");
extern fn float _atan2f(float, float) @MathLibc("atan2f");
extern fn void _sincos(double, double*, double*) @extern("__sincos") @link("m") @if(env::DARWIN);
extern fn void _sincosf(float, float*, float*) @extern("__sincosf") @link("m") @if(env::DARWIN);
extern fn void _sincos(double, double*, double*) @MathLibc("__sincos") @if(env::DARWIN);
extern fn void _sincosf(float, float*, float*) @MathLibc("__sincosf") @if(env::DARWIN);
extern fn void _sincos(double, double*, double*) @extern("sincos") @link("m") @if(!env::DARWIN && !env::WIN32);
extern fn void _sincosf(float, float*, float*) @extern("sincosf") @link("m") @if(!env::DARWIN && !env::WIN32);
extern fn void _sincos(double, double*, double*) @MathLibc("sincos") @if(!env::DARWIN && !env::WIN32);
extern fn void _sincosf(float, float*, float*) @MathLibc("sincosf") @if(!env::DARWIN && !env::WIN32);
fn void _sincos(double a, double* s, double* c) @extern("sincos") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
fn void _sincosf(float a, float* s, float* c) @extern("sincosf") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
extern fn double _tan(double x) @extern("tan");
extern fn float _tanf(float x) @extern("tanf");
extern fn double _scalbn(double x, int n) @extern("scalbn");
extern fn double _acos(double x) @extern("acos");
extern fn double _asin(double x) @extern("asin");
extern fn double _acosh(double x) @extern("acosh");
extern fn double _asinh(double x) @extern("asinh");
extern fn double _atanh(double x) @extern("atanh");
extern fn float _acosf(float x) @extern("acosf");
extern fn float _asinf(float x) @extern("asinf");
extern fn float _acoshf(float x) @extern("acoshf");
extern fn float _asinhf(float x) @extern("asinhf");
extern fn float _atanhf(float x) @extern("atanhf");
extern fn double _tan(double x) @MathLibc("tan");
extern fn float _tanf(float x) @MathLibc("tanf");
extern fn double _scalbn(double x, int n) @MathLibc("scalbn");
extern fn double _acos(double x) @MathLibc("acos");
extern fn double _asin(double x) @MathLibc("asin");
extern fn double _acosh(double x) @MathLibc("acosh");
extern fn double _asinh(double x) @MathLibc("asinh");
extern fn double _atanh(double x) @MathLibc("atanh");
extern fn float _acosf(float x) @MathLibc("acosf");
extern fn float _asinf(float x) @MathLibc("asinf");
extern fn float _acoshf(float x) @MathLibc("acoshf");
extern fn float _asinhf(float x) @MathLibc("asinhf");
extern fn float _atanhf(float x) @MathLibc("atanhf");
fn double _frexp(double x, int* e)

View File

@@ -117,7 +117,7 @@ macro bool next_bool(random)
*>
macro float next_float(random)
{
uint val = random.next_int() & (1 << 24 - 1);
uint val = random.next_int() & (1U << 24 - 1);
return val * 0x1.0p-24f;
}
@@ -133,7 +133,7 @@ macro double next_double(random)
}
// True if the value is a Random.
macro bool is_random(random) => $assignable(random, Random);
macro bool is_random(random) => @assignable_to(random, Random);
macro uint128 @long_to_int128(#function) => (uint128)#function << 64 + #function;
macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function;

View File

@@ -23,7 +23,7 @@ macro void @loop_over_ai(AddrInfo* ai; @body(NativeSocket fd, AddrInfo* ai))
}
}
const Duration POLL_FOREVER = -1;
const Duration POLL_FOREVER = (Duration)-1;
typedef PollSubscribes = ushort;
typedef PollEvents = ushort;

View File

@@ -5,11 +5,11 @@ import std::time, libc;
typedef TcpSocket = inline Socket;
typedef TcpServerSocket = inline Socket;
fn TcpSocket? connect(String host, uint port, Duration timeout = 0, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED)
fn TcpSocket? connect(String host, uint port, Duration timeout = time::DURATION_ZERO, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED)
{
AddrInfo* ai = net::addrinfo(host, port, ip_protocol.ai_family, os::SOCK_STREAM)!;
defer os::freeaddrinfo(ai);
if (timeout > 0)
if (timeout > time::DURATION_ZERO)
{
return (TcpSocket)net::connect_with_timeout_from_addrinfo(ai, options, timeout)!;
}

View File

@@ -87,11 +87,12 @@ fn void*[] capture_current(void*[] buffer)
alias BacktraceList = List{Backtrace};
alias symbolize_backtrace @if(env::LINUX) = linux::symbolize_backtrace;
alias symbolize_backtrace @if(env::WIN32) = win32::symbolize_backtrace;
alias symbolize_backtrace @if(env::DARWIN) = darwin::symbolize_backtrace;
alias symbolize_backtrace @if(env::LINUX) = linux::symbolize_backtrace;
alias symbolize_backtrace @if(env::WIN32) = win32::symbolize_backtrace;
alias symbolize_backtrace @if(env::DARWIN) = darwin::symbolize_backtrace;
alias symbolize_backtrace @if(env::OPENBSD) = openbsd::symbolize_backtrace;
fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace) @if(!env::NATIVE_STACKTRACE)
{
return {};
}
}

View File

@@ -79,7 +79,7 @@ fn String? get_home_dir(Allocator allocator)
<*
Returns the current user's config directory.
Returns the current user's config directory.
*>
fn Path? get_config_dir(Allocator allocator) => @pool()
{
@@ -87,13 +87,15 @@ fn Path? get_config_dir(Allocator allocator) => @pool()
return path::new(allocator, tget_var("AppData"));
$else
$if env::DARWIN:
String s = tget_var("HOME")!;
String home_dir = tget_var("HOME")!;
const DIR = "Library/Application Support";
$else
String s = tget_var("XDG_CONFIG_HOME") ?? tget_var("HOME")!;
String? config_path = tget_var("XDG_CONFIG_HOME");
if (try config_path && config_path.len > 0) return path::new(allocator, config_path);
String home_dir = tget_var("HOME")!;
const DIR = ".config";
$endif
return path::temp(s).append(allocator, DIR);
return path::temp(home_dir).append(allocator, DIR);
$endif
}
@@ -115,10 +117,10 @@ fn bool clear_var(String name) => @pool()
$endswitch
}
fn String? executable_path(Allocator allocator)
fn String? executable_path()
{
$if env::DARWIN:
return darwin::executable_path(allocator);
return darwin::executable_path();
$else
return NOT_FOUND?;
$endif

View File

@@ -130,70 +130,76 @@ fn ulong? elf_module_image_base(String path) @local
fn void? backtrace_add_from_exec(Allocator allocator, BacktraceList* list, void* addr) @local
{
char[] buf = mem::talloc_array(char, 1024);
String exec_path = process::execute_stdout_to_buffer(buf, {"realpath", "-e", string::tformat("/proc/%d/exe", posix::getpid())})!;
char[1024] buf @noinit;
String exec_path = process::execute_stdout_to_buffer(&buf, {"realpath", "-e", string::bformat(&&(char[64]){}, "/proc/%d/exe", posix::getpid())})!;
String obj_name = exec_path.copy(allocator);
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::tformat("0x%x", addr)})!;
String addr2line = process::execute_stdout_to_buffer(&buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::bformat(&&(char[64]){}, "0x%x", addr)})!;
return backtrace_add_addr2line(allocator, list, addr, addr2line, obj_name, "???");
}
fn void? backtrace_add_from_dlinfo(Allocator allocator, BacktraceList* list, void* addr, Linux_Dl_info* info) @local
{
char[] buf = mem::talloc_array(char, 1024);
char[1024] buf @noinit;
void* obj_addr = addr - (uptr)info.dli_fbase + (uptr)elf_module_image_base(info.dli_fname.str_view())!;
ZString obj_path = info.dli_fname;
String sname = info.dli_sname ? info.dli_sname.str_view() : "???";
String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_addr - 1)})!;
String addr2line = process::execute_stdout_to_buffer(&buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::bformat(&&(char[64]){}, "0x%x", obj_addr - 1)})!;
return backtrace_add_addr2line(allocator, list, addr, addr2line, info.dli_fname.str_view(), sname);
}
fn Backtrace? backtrace_line_parse(Allocator allocator, String string, String obj_name, String func_name, bool is_inlined)
{
String[] parts = string.trim().tsplit(" at ");
if (parts.len != 2) return NOT_FOUND?;
uint line = 0;
String source = "";
if (!parts[1].contains("?") && parts[1].contains(":"))
@stack_mem(256; Allocator mem)
{
usz index = parts[1].rindex_of_char(':')!;
source = parts[1][:index];
line = parts[1][index + 1..].to_uint()!;
}
return {
.function = parts[0].copy(allocator),
.object_file = obj_name.copy(allocator),
.file = source.copy(allocator),
.line = line,
.allocator = allocator,
.is_inline = is_inlined
String[] parts = string.trim().split(mem, " at ");
if (parts.len != 2) return NOT_FOUND?;
uint line = 0;
String source = "";
if (!parts[1].contains("?") && parts[1].contains(":"))
{
usz index = parts[1].rindex_of_char(':')!;
source = parts[1][:index];
line = parts[1][index + 1..].to_uint()!;
}
return {
.function = parts[0].copy(allocator),
.object_file = obj_name.copy(allocator),
.file = source.copy(allocator),
.line = line,
.allocator = allocator,
.is_inline = is_inlined
};
};
}
fn void? backtrace_add_addr2line(Allocator allocator, BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name) @local
{
String[] inline_parts = addr2line.tsplit("(inlined by)");
usz last = inline_parts.len - 1;
foreach (i, part : inline_parts)
@stack_mem(1024; Allocator mem)
{
bool is_inline = i != last;
Backtrace? trace = backtrace_line_parse(allocator, part, obj_name, func_name, is_inline);
if (catch trace)
String[] inline_parts = addr2line.split(mem, "(inlined by)");
usz last = inline_parts.len - 1;
foreach (i, part : inline_parts)
{
list.push({
.function = func_name.copy(allocator),
.object_file = obj_name.copy(allocator),
.offset = (uptr)addr,
.file = "".copy(allocator),
.line = 0,
.allocator = allocator,
.is_inline = is_inline
});
continue;
bool is_inline = i != last;
Backtrace? trace = backtrace_line_parse(allocator, part, obj_name, func_name, is_inline);
if (catch trace)
{
list.push({
.function = func_name.copy(allocator),
.object_file = obj_name.copy(allocator),
.offset = (uptr)addr,
.file = "".copy(allocator),
.line = 0,
.allocator = allocator,
.is_inline = is_inline
});
continue;
}
list.push(trace);
}
list.push(trace);
}
};
}
fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, void* addr) @local
@@ -204,15 +210,12 @@ fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, void* a
return;
}
@pool()
Linux_Dl_info info;
if (dladdr(addr, &info) == 0)
{
Linux_Dl_info info;
if (dladdr(addr, &info) == 0)
{
return backtrace_add_from_exec(allocator, list, addr);
}
return backtrace_add_from_dlinfo(allocator, list, addr, &info);
};
return backtrace_add_from_exec(allocator, list, addr);
}
return backtrace_add_from_dlinfo(allocator, list, addr, &info);
}
fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
@@ -227,12 +230,9 @@ fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
}
list.free();
}
@pool()
foreach (addr : backtrace)
{
foreach (addr : backtrace)
{
backtrace_add_element(allocator, &list, addr)!;
}
};
backtrace_add_element(allocator, &list, addr)!;
}
return list;
}

View File

@@ -79,19 +79,26 @@ alias Darwin_mach_timebase_info_data_t = Darwin_mach_timebase_info;
extern fn void mach_timebase_info(Darwin_mach_timebase_info_data_t* timebase);
extern fn ulong mach_absolute_time();
fn String? executable_path(Allocator allocator)
fn String? executable_path()
{
char[4096] path;
uint len = path.len;
if (darwin_NSGetExecutablePath(&path, &len) < 0) return NOT_FOUND?;
return ((ZString)&path).copy(allocator);
static char[4096] path;
static uint len = 0;
if (!len)
{
char[4096] buf;
uint temp_len = buf.len;
if (darwin_NSGetExecutablePath(&buf, &temp_len) < 0) return NOT_FOUND?;
path[:len] = buf[:len];
len = (int)((ZString)&buf).len();
}
return (String)path[:len];
}
fn uptr? load_address() @local
{
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
if (!cmd) return backtrace::SEGMENT_NOT_FOUND?;
String path = env::executable_path(tmem) ?? backtrace::EXECUTABLE_PATH_NOT_FOUND?!;
String path = env::executable_path() ?? backtrace::EXECUTABLE_PATH_NOT_FOUND?!;
uint dyld_count = darwin::_dyld_image_count();
for (uint i = 0; i < dyld_count; i++)
{
@@ -103,23 +110,22 @@ fn uptr? load_address() @local
return backtrace::IMAGE_NOT_FOUND?;
}
fn Backtrace? backtrace_load_element(Allocator allocator, String execpath, void* buffer, void* load_address) @local
{
@pool()
if (buffer)
{
if (buffer)
{
char* buf = tmalloc(1024);
String s = process::execute_stdout_to_buffer(buf[:1024],
{ "atos", "-o", execpath, "-arch", env::AARCH64 ? "arm64" : "x86_64", "-l",
string::tformat("%p", load_address),
string::tformat("%p", buffer - 1),
"-fullPath" })!;
String[] parts = s.tsplit(" ");
char[1024] buf;
String s = process::execute_stdout_to_buffer(&buf,
{ "atos", "-o", execpath, "-arch", env::AARCH64 ? "arm64" : "x86_64", "-l",
string::bformat(&&(char[64]){}, "%p", load_address),
string::bformat(&&(char[64]){}, "%p", buffer - 1),
"-fullPath" })!;
@stack_mem(512; Allocator mem)
{
String[] parts = s.split(mem, " ", 5);
if (parts.len == 4)
{
String[] path_parts = parts[3].tsplit(":");
String[] path_parts = parts[3].split(mem, ":");
return {
.offset = (uptr)buffer,
.function = parts[0].copy(allocator),
@@ -129,17 +135,17 @@ fn Backtrace? backtrace_load_element(Allocator allocator, String execpath, void*
.allocator = allocator
};
}
}
Darwin_Dl_info info;
if (!buffer || !darwin::dladdr(buffer, &info)) return backtrace::BACKTRACE_UNKNOWN;
return {
.offset = (uptr)buffer,
.function = info.dli_sname ? info.dli_sname.copy(allocator) : "???".copy(allocator),
.object_file = info.dli_fname.copy(allocator),
.file = "".copy(allocator),
.line = 0,
.allocator = allocator
};
}
Darwin_Dl_info info;
if (!buffer || !darwin::dladdr(buffer, &info)) return backtrace::BACKTRACE_UNKNOWN;
return {
.offset = (uptr)buffer,
.function = info.dli_sname ? info.dli_sname.copy(allocator) : "???".copy(allocator),
.object_file = info.dli_fname.copy(allocator),
.file = "".copy(allocator),
.line = 0,
.allocator = allocator
};
}
@@ -156,14 +162,11 @@ fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
}
list.free();
}
@pool()
String execpath = executable_path()!;
foreach (addr : backtrace)
{
String execpath = executable_path(tmem)!;
foreach (addr : backtrace)
{
list.push(backtrace_load_element(allocator, execpath, addr, load_addr) ?? backtrace::BACKTRACE_UNKNOWN);
}
};
list.push(backtrace_load_element(allocator, execpath, addr, load_addr) ?? backtrace::BACKTRACE_UNKNOWN);
}
return list;
}

View File

@@ -1,2 +1,74 @@
module std::os::openbsd @if(env::OPENBSD);
import libc, std::os, std::collections::list;
extern fn ZString* backtrace_symbols_fmt(void **addrlist, usz len, ZString fmt);
fn Backtrace? backtrace_line_parse(Allocator allocator, String obj, String addr2line) @local
{
@stack_mem(256; Allocator mem)
{
String[] parts = addr2line.trim().split(mem, " at ");
if (parts.len != 2) return NOT_FOUND?;
uint line = 0;
String source = "";
if (!parts[1].contains("?") && parts[1].contains(":"))
{
usz index = parts[1].rindex_of_char(':')!;
source = parts[1][:index];
line = parts[1][index + 1..].to_uint()!;
}
return {
.function = parts[0].copy(allocator),
.object_file = obj.copy(allocator),
.file = source.copy(allocator),
.line = line,
.allocator = allocator,
.is_inline = false,
};
};
}
fn void? backtrace_add_addr2line(Allocator allocator, BacktraceList* list, String obj, String addr2line) @local
{
list.push(backtrace_line_parse(allocator, obj, addr2line)!);
}
fn void? backtrace_add_from_exec(Allocator allocator, BacktraceList* list, String fun, String obj) @local
{
char[1024] buf @noinit;
String addr2line = process::execute_stdout_to_buffer(&buf, {"llvm-addr2line", "-fpe", obj, fun})!;
return backtrace_add_addr2line(allocator, list, obj, addr2line);
}
fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, String fun, String obj) @local
{
return backtrace_add_from_exec(allocator, list, fun, obj);
}
fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
{
BacktraceList list;
list.init(allocator, backtrace.len);
defer catch
{
foreach (trace : list)
{
trace.free();
}
list.free();
}
ZString *strings = backtrace_symbols_fmt(backtrace.ptr, backtrace.len, "%n%D %f");
for (int i = 0; i < backtrace.len; i++) {
String full = strings[i].str_view();
Splitter iter = full.tokenize(" ");
String fun = iter.next()!;
String obj = iter.next()!;
backtrace_add_element(allocator, &list, fun, obj)!;
}
free(strings);
return list;
}

View File

@@ -1,3 +1,8 @@
module std::os::posix @if(env::POSIX);
extern ZString* environ;
extern fn CLong sysconf(CInt name);
const CInt _SC_PAGESIZE @if(env::LINUX) = 30;
const CInt _SC_PAGESIZE @if(env::DARWIN) = 29;

28
lib/std/os/posix/mman.c3 Normal file
View File

@@ -0,0 +1,28 @@
module std::os::posix @if(env::POSIX);
import libc;
const PROT_NONE = 0x00; // no permissions
const PROT_READ = 0x01; // pages can be read
const PROT_WRITE = 0x02; // pages can be written
const PROT_EXEC = 0x04; // pages can be executed
const MAP_SHARED = 0x0001; // share changes
const MAP_PRIVATE = 0x0002; // changes are private
const MAP_FILE = 0x0000; // map from file (default)
const MAP_ANONYMOUS = 0x1000; // allocated from memory, swap space
const void* MAP_FAILED = (void *)(uptr)-1; // mmap failed
const MADV_NORMAL = 0; // no further special treatment
const MADV_RANDOM = 1; // expect random page refs
const MADV_SEQUENTIAL = 2; // expect sequential page refs
const MADV_WILLNEED = 3; // will need these pages
const MADV_DONTNEED = 4; // dont need these pages
extern fn void* mmap(void*, usz, CInt, CInt, CInt, Off_t);
extern fn CInt munmap(void*, usz);
extern fn CInt mprotect(void*, usz, CInt);
extern fn int madvise(void*, usz, CInt);
extern fn CInt getpagesize();

View File

@@ -58,7 +58,9 @@ const CInt WUNTRACES = 2;
JmpBuf backtrace_jmpbuf @local;
alias BacktraceFn = fn CInt(void** buffer, CInt size);
fn CInt backtrace(void** buffer, CInt size)
extern fn CInt backtrace(void** buffer, CInt size) @if(env::OPENBSD);
fn CInt backtrace(void** buffer, CInt size) @if(!env::OPENBSD)
{
if (size < 1) return 0;
void* handle = libc::dlopen("libc.so.6", libc::RTLD_LAZY|libc::RTLD_NODELETE);

View File

@@ -55,21 +55,18 @@ fn void? create_named_pipe_helper(void** rd, void **wr) @local @if(env::WIN32)
tlocal long index = 0;
long unique = index++;
@pool()
{
String s = string::tformat(`\\.\pipe\c3_subprocess.%08x.%08x.%d`, win32::getCurrentProcessId(), win32::getCurrentThreadId(), unique);
Win32_LPCSTR str = (Win32_LPCSTR)s.ptr;
*rd = win32::createNamedPipeA(
str,
win32::PIPE_ACCESS_INBOUND | win32::FILE_FLAG_OVERLAPPED,
win32::PIPE_TYPE_BYTE | win32::PIPE_WAIT,
1, 4096, 4096, 0, &sa_attr);
if (win32::INVALID_HANDLE_VALUE == *rd) return FAILED_TO_CREATE_PIPE?;
*wr = win32::createFileA(
str, win32::GENERIC_WRITE, 0, &sa_attr,
win32::OPEN_EXISTING, win32::FILE_ATTRIBUTE_NORMAL, null);
if (win32::INVALID_HANDLE_VALUE == *wr) return FAILED_TO_CREATE_PIPE?;
};
String s = string::bformat(&&(char[128]){}, `\\.\pipe\c3_subprocess.%08x.%08x.%d`, win32::getCurrentProcessId(), win32::getCurrentThreadId(), unique);
Win32_LPCSTR str = (Win32_LPCSTR)s.ptr;
*rd = win32::createNamedPipeA(
str,
win32::PIPE_ACCESS_INBOUND | win32::FILE_FLAG_OVERLAPPED,
win32::PIPE_TYPE_BYTE | win32::PIPE_WAIT,
1, 4096, 4096, 0, &sa_attr);
if (win32::INVALID_HANDLE_VALUE == *rd) return FAILED_TO_CREATE_PIPE?;
*wr = win32::createFileA(
str, win32::GENERIC_WRITE, 0, &sa_attr,
win32::OPEN_EXISTING, win32::FILE_ATTRIBUTE_NORMAL, null);
if (win32::INVALID_HANDLE_VALUE == *wr) return FAILED_TO_CREATE_PIPE?;
}
fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WIN32) @local
@@ -143,12 +140,12 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
}
@pool()
@stack_mem(2048; Allocator mem)
{
WString used_environment = null;
if (!options.inherit_environment)
{
DString env = dstring::temp_with_capacity(64);
DString env = dstring::new_with_capacity(mem, 64);
if (!environment.len)
{
env.append("\0");
@@ -159,7 +156,7 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
env.append("\0");
}
env.append("\0");
used_environment = env.str_view().to_temp_wstring()!;
used_environment = env.str_view().to_wstring(mem)!;
}
// Handle stdin pipe if not inheriting
@@ -266,26 +263,26 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
<*
@require command_line.len > 0
*>
fn ZString* tcopy_command_line(String[] command_line) @local @inline @if(env::POSIX)
fn ZString* copy_command_line(Allocator mem, String[] command_line) @local @inline @if(env::POSIX)
{
ZString* copy = mem::talloc_array(ZString, command_line.len + 1);
ZString* copy = allocator::alloc_array(mem, ZString, command_line.len + 1);
foreach (i, str : command_line)
{
copy[i] = str.zstr_tcopy();
copy[i] = str.zstr_copy(mem);
}
copy[command_line.len] = null;
return copy;
}
const ZString[1] EMPTY_ENVIRONMENT @if(env::POSIX) = { null };
fn ZString* tcopy_env(String[] environment) @local @inline @if(env::POSIX)
fn ZString* copy_env(Allocator mem, String[] environment) @local @inline @if(env::POSIX)
{
if (!environment.len) return &EMPTY_ENVIRONMENT;
ZString* copy = mem::talloc_array(ZString, environment.len + 1);
ZString* copy = allocator::alloc_array(mem, ZString, environment.len + 1);
copy[environment.len] = null;
foreach (i, str : environment)
{
copy[i] = str.zstr_tcopy();
copy[i] = str.zstr_copy(mem);
}
return copy;
}
@@ -338,10 +335,10 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
}
Pid_t child;
@pool()
@stack_mem(2048; Allocator mem)
{
ZString* command_line_copy = tcopy_command_line(command_line);
ZString* used_environment = options.inherit_environment ? posix::environ : tcopy_env(environment);
ZString* command_line_copy = copy_command_line(mem, command_line);
ZString* used_environment = options.inherit_environment ? posix::environ : copy_env(mem, environment);
if (options.search_user_path)
{
if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return FAILED_TO_START_PROCESS?;

View File

@@ -221,6 +221,8 @@ const Win32_DWORD ERROR_DEVICE_FEATURE_NOT_SUPPORTED = 0x13C;
const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D;
const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E;
const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F;
const Win32_DWORD ERROR_INVALID_ADDRESS = 0x1E7;
const Win32_DWORD ERROR_IO_INCOMPLETE = 0x3E4;
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;
const Win32_DWORD ERROR_TIMEOUT = 0x5B4;
const Win32_DWORD ERROR_TIMEOUT = 0x5B4;
const Win32_DWORD ERROR_COMMITMENT_LIMIT = 0x5AF;

View File

@@ -0,0 +1,52 @@
module std::os::win32 @if(env::WIN32);
enum Win32_AllocationType : const Win32_DWORD
{
MEM_COMMIT = 0x00001000,
MEM_RESERVE = 0x00002000,
MEM_RESET = 0x00080000,
MEM_RESET_UNDO = 0x01000000,
MEM_LARGE_PAGES = 0x20000000,
MEM_PHYSICAL = 0x00400000,
MEM_TOP_DOWN = 0x00100000,
MEM_WRITE_WATCH = 0x00200000
}
enum Win32_Protect : const Win32_DWORD
{
PAGE_EXECUTE = 0x10,
PAGE_EXECUTE_READ = 0x20,
PAGE_EXECUTE_READWRITE = 0x40,
PAGE_EXECUTE_WRITECOPY = 0x80,
PAGE_NOACCESS = 0x01,
PAGE_READONLY = 0x02,
PAGE_READWRITE = 0x04,
PAGE_WRITECOPY = 0x08,
PAGE_TARGETS_INVALID = 0x40000000,
PAGE_TARGETS_NO_UPDATE = 0x40000000,
PAGE_GUARD = 0x100,
PAGE_NOCACHE = 0x200,
PAGE_WRITECOMBINE = 0x400,
}
enum Win32_FreeType : const Win32_DWORD
{
MEM_DECOMMIT = 0x00004000,
MEM_RELEASE = 0x00008000,
MEM_COALESCE_PLACEHOLDERS = 0x00000001,
MEM_PRESERVE_PLACEHOLDER = 0x00000002,
}
extern fn Win32_LPVOID virtualAlloc(Win32_LPVOID lpAddres, Win32_SIZE_T dwSize, Win32_AllocationType flAllocationType, Win32_Protect flProtect) @extern("VirtualAlloc");
extern fn Win32_PVOID virtualAlloc2(Win32_HANDLE process, Win32_PVOID baseAddress, Win32_SIZE_T size, Win32_AllocationType allocationType, Win32_ULONG pageProtection, Win32_MEM_EXTENDED_PARAMETER* extendedParameters, Win32_ULONG parameterCount) @extern("VirtualAlloc2");
extern fn Win32_BOOL virtualFree(Win32_LPVOID lpAddress, Win32_SIZE_T dwSize, Win32_FreeType dwFreeType) @extern("VirtualFree");
extern fn Win32_BOOL virtualProtect(Win32_LPVOID lpAddress, Win32_SIZE_T dwSize, Win32_Protect flNewProtect, Win32_Protect* lpflOldProtect) @extern("VirtualProtect");
fn usz allocation_granularity()
{
static usz granularity;
if (granularity) return granularity;
Win32_SYSTEM_INFO info;
win32::getSystemInfo(&info);
return granularity = (usz)info.dwAllocationGranularity;
}

37
lib/std/os/win32/winnt.c3 Normal file
View File

@@ -0,0 +1,37 @@
module std::os::win32 @if(env::WIN32);
enum Win32_MEM_EXTENDED_PARAMETER_TYPE : CInt
{
INVALID_TYPE,
ADDRESS_REQUIREMENTS,
NUMA_NODE,
PARTITION_HANDLE,
USER_PHYSICAL_HANDLE,
ATTRIBUTE_FLAGS,
IMAGE_MACHINE,
MAX
}
alias Win32_PMEM_EXTENDED_PARAMETER_TYPE = Win32_MEM_EXTENDED_PARAMETER_TYPE;
enum Win32_MEM_EXTENDED_PARAMETER_ATTRIBUTE : const Win32_DWORD64
{
NONPAGED = 0x02, // The allocation is non-pageable.
NONPAGED_LARGE = 0x08, // The allocation is mapped using large pages.
NONPAGED_HUGE = 0x10, // The allocation is mapped using huge pages.
EC_CODE = 0x40, // The allocation will contain emulation-compatible (EC) code.
}
struct Win32_MEM_EXTENDED_PARAMETER
{
Win32_MEM_EXTENDED_PARAMETER_TYPE type;
union
{
Win32_MEM_EXTENDED_PARAMETER_ATTRIBUTE attribute; // If type is ATTRIBUTE_FLAGS
Win32_DWORD64 nodeNumber; // If type is NUMA_NODE
Win32_PVOID pointer; // If type is ADDRESS_REQUIREMENTS
Win32_SIZE_T size;
Win32_HANDLE handle;
Win32_DWORD uLong;
}
}
alias Win32_PMEM_EXTENDED_PARAMETER = Win32_MEM_EXTENDED_PARAMETER*;

View File

@@ -32,7 +32,7 @@ alias Indexs @private = char[256];
alias 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], KeyFn.paramsof[0].type);
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| @assignable_to((ElementType){}, KeyFn.paramsof[0].type);
const bool LIST_HAS_REF @private = $defined(&(Type){}[0]);
alias KeyFnReturnType @if(!NO_KEY_FN) = $typefrom(KeyFn.returns) ;

View File

@@ -25,7 +25,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], CmpFn.paramsof[0].type);
var $cmp_by_value = $has_cmp &&& @assignable_to(list[0], CmpFn.paramsof[0].type);
var $has_get_ref = $defined(&list[0]);
for (usz i = low; i < high; ++i)
{

View File

@@ -115,7 +115,7 @@ macro @partition(Type list, isz l, isz h, 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], CmpFn.paramsof[0].type);
var $cmp_by_value = $has_cmp &&& @assignable_to(list[0], CmpFn.paramsof[0].type);
ElementType pivot = list[l];
while (l < h)

View File

@@ -39,7 +39,7 @@ macro int @sort_cmp(list, pos, cmp, ctx) @local
{
var $has_cmp = @is_valid_macro_slot(cmp);
var $has_context = @is_valid_macro_slot(ctx);
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typeof(cmp).paramsof[0].type);
var $cmp_by_value = $has_cmp &&& @assignable_to(list[0], $typeof(cmp).paramsof[0].type);
var a = list[pos];
var b = list[pos+1];

View File

@@ -142,12 +142,26 @@ fn void? NativeConditionVariable.wait(&cond, NativeMutex* mtx)
*>
fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms)
{
TimeSpec now;
if (libc::timespec_get(&now, libc::TIME_UTC) != libc::TIME_UTC) return thread::WAIT_FAILED?;
now.ns += (CLong)((ms % 1000) * 1000_000);
now.s += (Time_t)(ms / 1000 + now.ns / 1000_000_000);
now.ns = now.ns % 1000_000_000;
switch (posix::pthread_cond_timedwait(cond, &mtx.mutex, &now))
Time time = time::now() + time::ms(ms);
return cond.wait_until(mtx, time) @inline;
}
<*
@require mtx.is_initialized()
*>
fn void? NativeConditionVariable.wait_timeout_duration(&cond, NativeMutex* mtx, Duration duration)
{
if (duration < time::DURATION_ZERO) return thread::WAIT_TIMEOUT?;
Time time = time::now() + duration;
return cond.wait_until(mtx, time) @inline;
}
<*
@require mtx.is_initialized()
*>
fn void? NativeConditionVariable.wait_until(&cond, NativeMutex* mtx, Time time)
{
switch (posix::pthread_cond_timedwait(cond, &mtx.mutex, &&time.to_timespec()))
{
case errno::ETIMEDOUT:
return thread::WAIT_TIMEOUT?;
@@ -216,7 +230,7 @@ fn void native_thread_yield()
fn void? native_sleep_nano(NanoDuration nano)
{
if (nano <= 0) return;
if (nano <= time::NANO_DURATION_ZERO) return;
if (libc::nanosleep(&&nano.to_timespec(), null)) return thread::INTERRUPTED?;
}

View File

@@ -198,7 +198,7 @@ fn void? NativeTimedMutex.lock_timeout(&mtx, ulong ms)
NanoDuration duration = time::ms(ms).to_nano();
Clock start = clock::now();
for (NanoDuration remaining = duration; remaining > 0; remaining = duration - start.to_now())
for (NanoDuration remaining = duration; remaining > time::NANO_DURATION_ZERO; remaining = duration - start.to_now())
{
ulong remaining_ms = remaining.to_ms();
if (remaining_ms > uint.max) remaining_ms = uint.max;
@@ -318,6 +318,26 @@ fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms)
return timedwait(cond, mtx, (uint)ms) @inline;
}
<*
@require mtx.initialized : "Mutex was not initialized"
*>
fn void? NativeConditionVariable.wait_timeout_duration(&cond, NativeMutex* mtx, Duration duration) @inline
{
if (duration < time::DURATION_ZERO) return thread::WAIT_TIMEOUT?;
long ms = duration.to_ms();
if (ms > uint.max) ms = uint.max;
return timedwait(cond, mtx, (uint)ms) @inline;
}
<*
@require mtx.initialized : "Mutex was not initialized"
*>
fn void? NativeConditionVariable.wait_until(&cond, NativeMutex* mtx, Time time) @inline
{
Duration duration = time - time::now();
return cond.wait_timeout_duration(mtx, duration);
}
fn void? NativeThread.create(&thread, ThreadFn func, void* args)
{
if (!(*thread = (NativeThread)win32::createThread(null, 0, func, args, 0, null))) return thread::INIT_FAILED?;

View File

@@ -48,6 +48,11 @@ macro void? TimedMutex.lock_timeout(&mutex, ulong ms) => NativeTimedMutex.lock_t
macro bool TimedMutex.try_lock(&mutex) => NativeTimedMutex.try_lock((NativeTimedMutex*)mutex);
macro void? TimedMutex.unlock(&mutex) => NativeTimedMutex.unlock((NativeTimedMutex*)mutex);
macro void fence(AtomicOrdering $ordering) @safemacro
{
$$fence($ordering.ordinal);
}
macro void Mutex.@in_lock(&mutex; @body)
{
(void)mutex.lock();
@@ -63,12 +68,33 @@ macro void? ConditionVariable.wait(&cond, Mutex* mutex)
{
return NativeConditionVariable.wait((NativeConditionVariable*)cond, (NativeMutex*)mutex);
}
macro void? ConditionVariable.wait_timeout(&cond, Mutex* mutex, ulong ms)
<*
@require @assignable_to(#ms_or_duration, Duration) || @assignable_to(#ms_or_duration, ulong)
*>
macro void? ConditionVariable.wait_timeout(&cond, Mutex* mutex, #ms_or_duration) @safemacro
{
return NativeConditionVariable.wait_timeout((NativeConditionVariable*)cond, (NativeMutex*)mutex, ms);
$if @assignable_to(#ms_or_duration, Duration):
return NativeConditionVariable.wait_timeout_duration((NativeConditionVariable*)cond, (NativeMutex*)mutex, #ms_or_duration);
$else
return NativeConditionVariable.wait_timeout((NativeConditionVariable*)cond, (NativeMutex*)mutex, #ms_or_duration);
$endif
}
macro void? ConditionVariable.wait_until(&cond, Mutex* mutex, Time time)
{
return NativeConditionVariable.wait_until((NativeConditionVariable*)cond, (NativeMutex*)mutex, time);
}
<*
Create and start a thread.
@require thread_fn != null : "A non null thread function is required"
*>
macro void? Thread.create(&thread, ThreadFn thread_fn, void* arg)
{
return NativeThread.create(thread, thread_fn, arg);
}
macro void? Thread.create(&thread, ThreadFn thread_fn, void* arg) => NativeThread.create(thread, thread_fn, arg);
macro void? Thread.detach(thread) => NativeThread.detach(thread);
macro int? Thread.join(thread) => NativeThread.join(thread);
macro bool Thread.equals(thread, Thread other) => NativeThread.equals(thread, other);

View File

@@ -12,10 +12,10 @@ fn Clock native_clock()
ulong mult = 0;
if (!freq.quadPart)
{
if (!win32::queryPerformanceFrequency(&freq)) return 0;
if (!win32::queryPerformanceFrequency(&freq)) return (Clock)0;
}
Win32_LARGE_INTEGER counter @noinit;
if (!win32::queryPerformanceCounter(&counter)) return 0;
if (!win32::queryPerformanceCounter(&counter)) return (Clock)0;
return (Clock)counter.quadPart.muldiv(1_000_000_000, freq.quadPart);
}

View File

@@ -1,13 +1,15 @@
module std::time;
import std::io, std::time::os;
typedef Time = long;
typedef Duration = long;
typedef Clock = ulong;
typedef NanoDuration (Printable) = long;
typedef Time @structlike = long;
typedef Duration @structlike = long;
typedef Clock @structlike = ulong;
typedef NanoDuration (Printable) @structlike = long;
const Time FAR_FUTURE = long.max;
const Time FAR_PAST = long.min;
const NanoDuration NANO_DURATION_ZERO = 0;
const Duration US = 1;
const Duration MS = 1_000;
const Duration SEC = 1_000_000;
@@ -18,6 +20,7 @@ const Duration WEEK = 7 * DAY;
const Duration MONTH = 30 * DAY;
const Duration YEAR = 36525 * DAY / 100;
const Duration FOREVER = long.max;
const Duration DURATION_ZERO = 0;
fn Duration us(long l) @inline => l * US;
fn Duration ms(long l) @inline => l * MS;
@@ -78,7 +81,7 @@ fn Time now()
$if $defined(native_timestamp):
return os::native_timestamp();
$else
return 0;
return (Time)0;
$endif
}
@@ -114,31 +117,31 @@ macro Duration Duration.mult(#td, long #val) @operator_s(*) @safemacro => (Durat
fn usz? NanoDuration.to_format(&self, Formatter* formatter) @dynamic
{
NanoDuration nd = *self;
if (nd == 0)
if (nd == NANO_DURATION_ZERO)
{
return formatter.printf("0s")!;
}
bool neg = nd < 0;
bool neg = nd < NANO_DURATION_ZERO;
if (neg) nd = -nd;
DString str = dstring::temp_with_capacity(64);
if (nd < 1_000_000_000)
if (nd < (NanoDuration)1_000_000_000)
{
// Less than 1s: print milliseconds, microseconds and nanoseconds.
NanoDuration ms = nd / 1_000_000;
if (ms > 0)
if (ms > NANO_DURATION_ZERO)
{
str.appendf("%dms", ms);
nd -= ms * 1_000_000;
}
NanoDuration us = nd / 1000;
if (us > 0)
if (us > NANO_DURATION_ZERO)
{
str.appendf("%dµs", us);
nd -= us * 1000;
}
if (nd > 0)
if (nd > NANO_DURATION_ZERO)
{
str.appendf("%dns", nd);
}
@@ -150,19 +153,19 @@ fn usz? NanoDuration.to_format(&self, Formatter* formatter) @dynamic
nd /= 1_000_000_000;
NanoDuration hour = nd / 3600;
if (hour > 0)
if (hour > NANO_DURATION_ZERO)
{
str.appendf("%dh", hour);
nd -= hour * 3600;
}
NanoDuration min = nd / 60;
if (min > 0)
if (min > NANO_DURATION_ZERO)
{
str.appendf("%dm", min);
nd -= min * 60;
}
NanoDuration sec = nd;
if (ms > 0)
if (ms > NANO_DURATION_ZERO)
{
// Ignore trailing zeroes.
while (ms / 10 * 10 == ms) ms /= 10;

View File

@@ -141,7 +141,7 @@ resource = first(tools["localizedResources"], lambda x: x["language"] == "en-us"
license = resource["license"]
if not args.accept_license:
accept = input(f"Do you accept Visual Studio license at {license}, and also confirm that you have a valid license Visual Studio license allowing you to download the VS Build Tools [Y/N] ?")
accept = input(f"Do you accept Visual Studio license at {license} [Y/N] ?")
if not accept or accept[0].lower() != "y":
exit(0)

View File

@@ -1,5 +1,112 @@
# C3C Release Notes
## 0.7.4 Change list
### Changes / improvements
- Added const enums: `enum Foo : const`. Behaves like C enums but may be any type.
- Casting to / from an enum is now possible again. No need to use `.ordinal` and `.from_ordinal`.
- Inline associated enum values are deprecated, use `--use-old-enums` to re-enable them.
- `$typeof` may return a compile time type.
- Improved error messages on missing qualifier on enum value. #2260
- Add `--echo-prefix` to edit the prefix with `$echo` statements. Supports {FILE} and {LINE}
- Catch accidental `foo == BAR;` where `foo = BAR;` was most likely intended. #2274
- Improve error message when doing a rethrow in a function that doesn't return an optional.
- Add `--list-asm` to view all supported `asm` instructions.
- Formatting option "%h" now supports pointers.
- Improve error on unsigned implicit conversion to signed.
- Update error message for struct initialization #2286
- Add SipHash family of keyed PRFs. #2287
- `$is_const` is deprecated in favour of `@is_const` based on `$defined`.
- Multiline contract comments #2113
- Removed the use of temp allocator in backtrace printing.
- `env::AUTHORS` and `env::AUTHOR_EMAILS` added.
- Suppress codegen of panic printing with when panic messages are set to "off".
- Implicit linking of libc math when libc math functions are used.
- Allow even smaller memory limits.
- Check unaligned array access.
- Add "@structlike" for typedefs.
- "poison" the current function early when a declaration can't be correctly resolved.
- Add komihash, a5hash, metrohash64, metrohash128, and wyhash2 variants with tests/benchmark. #2293
- '$assignable' is deprecated.
- Deprecate allocator::heap() and allocator::temp()
- Add `thread::fence` providing a thread fence.
- Place output in `out` by default for projects. Use temp folder for building at the command line.
- Allow absolute paths for `$embed`.
- Add `@try` and `@try_catch`.
- Assignment evaluation order now right->left, following C++17 and possibly C23.
### Fixes
- mkdir/rmdir would not work properly with substring paths on non-windows platforms.
- Hex string formatter check incorrectly rejected slices.
- Correctly reject interface methods `type` and `ptr`.
- Comparing a null ZString with a non-null ZString would crash.
- Switch case with const non-int / enum would be treated as ints and crash. #2263
- Missing bounds check on upper bound with const ranges `foo[1:3]`.
- Check up the hierarchy when considering if an interface cast is valid #2267.
- Fix issue with labelled break inside of a $switch.
- Non-const macros may not return untyped lists.
- `$for` ct-state not properly popped.
- Inline `r / complex` for complex numbers fixed.
- Const slice lengths were not always detected as constant.
- Const slice indexing was not bounds checked.
- Initialize pool correctly in print_backtrace.
- `--max-mem` now works correctly again.
- Casting a fault to a pointer would trigger an assert.
- Make `to_float` more tolerant to spaces.
- Fixes to thread local pointer handling.
- Fixes to JSON parsing and Object.
- Array indices are now using int64 internally.
- Bit shift operation fails with inline uint enum despite matching underlying type #2279.
- Fix to codegen when using a bitstruct constant defined using a cast with an operator #2248.
- Function pointers are now compile time constants.
- Splat 8 arguments can sometimes cause incorrect behaviour in the compiler. #2283
- Correctly poison the analysis after a failed $assert or $error. #2284
- `$foo` variables could be assigned non-compile time values.
- `$foo[0] = ...` was incorrectly requiring that the assigned values were compile time constants.
- "Inlined at" would sometimes show the current location.
- Fixed bug splatting constants into constants.
- Resize bug when resizing memory down in ArenaAllocator, DynamicArenaAllocator, BackedArenaAllocator.
- Error message for missing arg incorrect for methods with zero args #2296.
- Fix stringify of $vaexpr #2301.
- Segfault when failing to cast subexpression to 'isz' in pointer subtraction #2305.
- Fix unexpected display of macro definition when passing a poisoned expression #2305.
- `@links` on macros would not be added to calling functions.
- Fix `Formatter.print` returning incorrect size.
- A distinct type based on an array would yield .len == 0
- Overloading addition with a pointer would not work.
- Copying const enums and regular enums incorrect #2313.
- Regression: Chaining an optional together with contracts could in some cases lose the optional.
- `char[*] b = *(char[*]*)&a;` would crash the compiler if `a` was a slice. #2320
- Implicitly cast const int expressions would sometimes not be detected as compile time const.
- Using @noreturn in a short body macro would not work properly #2326.
- Bug when reporting error in a macro return would crash the compiler #2326.
- Short body return expression would not have the correct span.
- Fix issue where recursively creating a dir would be incorrectly marked as a failure the first time.
- `@format` did not work correctly with macros #2341.
- Crash when parsing recursive type declaration #2345.
- Remove unnecessary "ret" in naked functions #2344.
- Lambdas now properly follow its attributes #2346.
- Not setting android-ndk resulted in a "set ndk-path" error.
- Lambda deduplication would be incorrect when generated at the global scope.
- Disallow accessing parameters in a naked function, as well as `return`, this fixes #1955.
- Assigning string literal to char[<*>] stores pointer rather than characters. #2357
### Stdlib changes
- Improve contract for readline. #2280
- Added Whirlpool hash.
- Added Ed25519.
- Added string::bformat.
- Virtual memory library.
- New virtual emory arena allocator.
- Added `WString.len`.
- Added `@addr` macro.
- Add `ConditionVariable.wait_until` and `ConditionVariable.wait_for`
- Added readline_to_stream that takes a stream.
- Added `Ref` and `RefCounted` experimental functionality.
- Added `Volatile` generic type.
- Added `UnalignedRef` generic type.
- Add String conversion functions snake_case -> PascalCase and vice versa.
## 0.7.3 Change list
### Changes / improvements
@@ -30,6 +137,7 @@
- Compile-time comparison of constant vectors. #1575.
- $member.get supports bitstructs.
- $member.set for setting members without the *& trick.
- Initial support for #1925, does not affect C compilation yet, and doesn't try to link etc. Using "--emit-only"
### Fixes
- `-2147483648`, MIN literals work correctly.
@@ -79,6 +187,7 @@
- `is_array_or_slice_of_char` and `is_arrayptr_or_slice_of_char` are replaced by constant `@` variants.
- `@pool` now has an optional `reserve` parameter, some minor changes to the temp_allocator API
- io::struct_to_format now supports bitstructs.
- Add `String.escape`, `String.unescape` for escaping and unescaping a string.
## 0.7.2 Change list
@@ -106,6 +215,7 @@
- Add deprecation for `@param foo "abc"`.
- Add `--header-output` and `header-output` options for controlling header output folder.
- Generic faults is disallowed.
- Detect when a slice on the stack is accidentally returned from a function.
### Fixes
- Assert triggered when casting from `int[2]` to `uint[2]` #2115

View File

@@ -3,6 +3,8 @@ import std;
import bar;
import clib;
import clib2;
import std::core::env;
fn int test_doubler(int x) @if(env::WIN32) => x * x;
extern fn int test_doubler(int) @if(!env::WIN32);
@@ -11,6 +13,11 @@ extern fn void printf(char *, ...);
fn int main()
{
printf("Hello World!\n");
printf("Authors:");
io::printn(env::AUTHORS);
printf("Author emails:");
io::printn(env::AUTHOR_EMAILS);
bar::test();
printf("Hello double: %d\n", test_doubler(11));
if ($feature(ABCD)) io::printn("ABCD");

View File

@@ -484,6 +484,7 @@ typedef struct BuildOptions_
int api_version;
} android;
int build_threads;
const char *echo_prefix;
const char **libraries_to_fetch;
const char **files;
const char *test_filter;
@@ -566,6 +567,7 @@ typedef struct BuildOptions_
const char *header_out;
const char *obj_out;
const char *script_dir;
const char **emit_only;
RelocModel reloc_model;
X86VectorCapability x86_vector_capability;
X86CpuSet x86_cpu_set;
@@ -592,10 +594,17 @@ typedef struct BuildOptions_
bool print_precedence;
bool print_linking;
bool print_env;
bool print_asm;
bool benchmarking;
bool testing;
} BuildOptions;
typedef struct
{
const char *author;
const char *email;
} AuthorEntry;
typedef struct
{
struct Library__ *parent;
@@ -648,6 +657,7 @@ typedef struct
const char **linker_libdirs;
const char **linker_libs;
const char *cpu;
const char *echo_prefix;
const char **link_args;
const char *build_dir;
const char *object_file_dir;
@@ -657,6 +667,7 @@ typedef struct
const char *header_file_dir;
const char *script_dir;
const char *run_dir;
const char **emit_only;
bool is_non_project;
bool run_after_compile;
bool delete_after_run;
@@ -687,6 +698,7 @@ typedef struct
bool print_stats;
bool old_slice_copy;
bool old_enums;
bool single_threaded;
int build_threads;
TrustLevel trust_level;
OptimizationSetting optsetting;
@@ -723,6 +735,7 @@ typedef struct
const char **csources;
const char **cinclude_dirs;
const char **exec;
AuthorEntry *authors;
const char **feature_list;
const char *custom_linker_path;
struct

View File

@@ -50,7 +50,7 @@ static const char *optsizes[3] = {
[SIZE_OPTIMIZATION_TINY] = "tiny",
};
static const char *linker[3] = {
static const char *linker_kind[3] = {
[LINKER_TYPE_BUILTIN] = "builtin",
[LINKER_TYPE_CC] = "cc",
[LINKER_TYPE_CUSTOM] = "custom"

View File

@@ -106,6 +106,7 @@ static void usage(bool full)
print_opt("--path <dir>", "Use this as the base directory for the current command.");
print_opt("--template <template>", "Select template for 'init': \"exe\", \"static-lib\", \"dynamic-lib\" or a path.");
print_opt("--symtab <value>", "Sets the preferred symtab size.");
print_opt("--max-mem <value>", "Set the maximum memory size (in MB).");
print_opt("--run-once", "After running the output file, delete it immediately.");
print_opt("--suppress-run", "Build but do not run on test/benchmark options.");
print_opt("--trust=<option>", "Trust level: none (default), include ($include allowed), full ($exec / exec allowed).");
@@ -140,6 +141,7 @@ static void usage(bool full)
{
PRINTF("");
print_opt("--ansi=<yes|no>", "Set colour output using ansi on/off, default is to try to detect it.");
print_opt("--echo-prefix <arg>", "Sets the prefix for any output using '$echo', defaults to 'c3c:'.");
print_opt("--test-filter <arg>", "Set a filter when running tests, running only matching tests.");
print_opt("--test-breakpoint", "When running tests, trigger a breakpoint on failure.");
print_opt("--test-nosort", "Do not sort tests.");
@@ -159,6 +161,7 @@ static void usage(bool full)
print_opt("--use-stdlib=<yes|no>", "Include the standard library (default: yes).");
print_opt("--link-libc=<yes|no>", "Link libc other default libraries (default: yes).");
print_opt("--emit-stdlib=<yes|no>", "Output files for the standard library. (default: yes)");
print_opt("--emit-only <file>", "Output only the file matching <file>.");
print_opt("--panicfn <name>", "Override the panic function name.");
print_opt("--testfn <name>", "Override the test runner function name.");
print_opt("--benchfn <name>", "Override the benchmark runner function name.");
@@ -188,6 +191,7 @@ static void usage(bool full)
print_opt("--list-manifest-properties", "List all available keys used in manifest.json files.");
print_opt("--list-targets", "List all architectures the compiler supports.");
print_opt("--list-type-properties", "List all type properties.");
print_opt("--list-asm", "List all asm instructions for the current target.");
PRINTF("");
print_opt("--print-output", "Print the object files created to stdout.");
print_opt("--print-input", "Print inputted C3 files to stdout.");
@@ -724,6 +728,18 @@ static void parse_option(BuildOptions *options)
options->ansi = parse_opt_select(Ansi, argopt, on_off);
return;
}
if (match_longopt("max-mem"))
{
if (at_end() || next_is_opt())
{
FAIL_WITH_ERR_LONG("'--max-mem' expected a max memory.");
}
if (atoll(next_arg()) < 1)
{
FAIL_WITH_ERR_LONG("'--max-mem' expected a positive integer value.");
}
return;
}
if (match_longopt("sources"))
{
if (at_end() || next_is_opt())
@@ -743,7 +759,7 @@ static void parse_option(BuildOptions *options)
options->old_slice_copy = true;
return;
}
if (match_longopt("use-new-enums"))
if (match_longopt("use-old-enums"))
{
options->old_enums = true;
return;
@@ -862,7 +878,7 @@ static void parse_option(BuildOptions *options)
if ((argopt = match_argopt("linker")))
{
options->custom_linker_path = NULL;
options->linker_type = parse_opt_select(LinkerType, argopt, linker);
options->linker_type = parse_opt_select(LinkerType, argopt, linker_kind);
if (options->linker_type == LINKER_TYPE_CUSTOM)
{
if (at_end() || next_is_opt()) error_exit("error: --linker=custom expects a valid linker name.");
@@ -885,6 +901,12 @@ static void parse_option(BuildOptions *options)
options->emit_stdlib = parse_opt_select(EmitStdlib, argopt, on_off);
return;
}
if (match_longopt("emit-only"))
{
if (at_end() || next_is_opt()) error_exit("error: --emit-only expects an output name, e.g. 'foo', to only output 'foo.o'.");
vec_add(options->emit_only, next_arg());
return;
}
if ((argopt = match_argopt("use-stdlib")))
{
options->use_stdlib = parse_opt_select(UseStdlib, argopt, on_off);
@@ -915,10 +937,10 @@ static void parse_option(BuildOptions *options)
options->riscv_float_capability = parse_opt_select(RiscvFloatCapability, argopt, riscv_capability);
return;
}
if ((argopt = match_argopt("max-vector-size")))
if (match_longopt("max-vector-size"))
{
int size = atoi(next_arg());
if (size < 128) error_exit("Expected a valid positive integer >= 128 or --max-vector-size.");
int size = (at_end() || next_is_opt()) ? 0 : atoi(next_arg());
if (size < 128) error_exit("Expected a valid positive integer >= 128 for --max-vector-size.");
if (size > MAX_VECTOR_WIDTH) error_exit("Expected a valid positive integer <= %u for --max-vector-size.", (unsigned)MAX_VECTOR_WIDTH);
if (size != next_highest_power_of_2(size))
{
@@ -986,6 +1008,11 @@ static void parse_option(BuildOptions *options)
options->command = COMMAND_PRINT_SYNTAX;
return;
}
if (match_longopt("list-asm"))
{
options->print_asm = true;
return;
}
if (match_longopt("list-project-properties"))
{
options->print_project_properties = true;
@@ -1020,6 +1047,12 @@ static void parse_option(BuildOptions *options)
options->build_threads = threads;
return;
}
if (match_longopt("echo-prefix"))
{
if (at_end() || next_is_opt()) error_exit("error: --echo-prefix needs a prefix.");
options->echo_prefix = next_arg();
return;
}
if (match_longopt("target"))
{
if (at_end() || next_is_opt()) error_exit("error: --target needs a arch+os definition.");
@@ -1329,7 +1362,7 @@ static void parse_option(BuildOptions *options)
break;
}
FAIL_WITH_ERR("Cannot process the unknown option \"%s\".", current_arg);
FAIL_WITH_ERR_LONG("Cannot process the unknown option \"%s\".", current_arg);
}
BuildOptions parse_arguments(int argc, const char *argv[])
@@ -1440,7 +1473,7 @@ BuildOptions parse_arguments(int argc, const char *argv[])
const char *ndk_path = getenv("ANDROID_NDK");
if (!ndk_path)
{
FAIL_WITH_ERR("Can't find Android NDK, please set --ndk-path.");
FAIL_WITH_ERR("Can't find Android NDK, please set --android-ndk.");
}
build_options.android.ndk_path = strdup(ndk_path);
}

View File

@@ -271,6 +271,28 @@ static LinkLibc libc_from_arch_os(ArchOsTarget target)
#define OVERRIDE_IF_SET(prop_) do { if (options->prop_) target->prop_ = options->prop_; } while (0)
#define set_if_updated(target_, original_) do { if ((int)original_ != -1) target_ = original_; } while (0)
static void set_dir_with_default(const char **setting, const char *option, const char *default_value)
{
if (option)
{
*setting = option;
return;
}
if (!*setting) *setting = default_value;
}
static void set_output_dir_from_options(const char **setting, const char *option, const char *default_value, const char *target_name, const char *out_dir)
{
if (option)
{
*setting = option;
}
if (!*setting)
{
*setting = file_append_path(file_append_path(out_dir, default_value), target_name);
}
}
static void update_build_target_from_options(BuildTarget *target, BuildOptions *options)
{
switch (options->command)
@@ -410,6 +432,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
OVERRIDE_IF_SET(max_vector_size);
OVERRIDE_IF_SET(win.def);
OVERRIDE_IF_SET(no_entry);
OVERRIDE_IF_SET(echo_prefix);
OVERRIDE_IF_SET(macos.sysroot);
OVERRIDE_IF_SET(win.sdk);
@@ -477,50 +500,44 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
error_exit("Unable to detect the default target, please set an explicit --target value.");
}
target->emit_only = options->emit_only;
const char *target_name = arch_os_target[target->arch_os_target];
OVERRIDE_IF_SET(run_dir);
if (command_accepts_files(options->command))
{
target->build_dir = options->build_dir ? options->build_dir : ".build";
if (!target->script_dir) target->script_dir = ".";
set_dir_with_default(&target->output_dir, options->output_dir, ".");
if (!target->build_dir)
{
if (!options->build_dir)
{
options->build_dir = dir_make_temp_dir();
if (!options->build_dir)
{
error_exit("Unable to create temporary directory for build.");
}
}
target->build_dir = options->build_dir;
}
set_dir_with_default(&target->script_dir, options->script_dir, ".");
}
else
{
if (!target->build_dir) target->build_dir = "build";
if (options->build_dir)
{
target->build_dir = options->build_dir;
}
else
{
options->build_dir = target->build_dir;
}
if (!target->script_dir) target->script_dir = "scripts";
set_dir_with_default(&target->output_dir, options->output_dir, "out");
set_dir_with_default(&target->build_dir, options->build_dir, "build");
set_dir_with_default(&target->script_dir, options->script_dir, "scripts");
}
if (!options->run_dir) options->run_dir = target->run_dir;
target->ir_file_dir = options->llvm_out;
target->asm_file_dir = options->asm_out;
target->header_file_dir = options->header_out;
target->object_file_dir = options->obj_out;
if (!target->ir_file_dir)
set_output_dir_from_options(&target->ir_file_dir, options->llvm_out, "llvm", target_name, target->output_dir);
set_output_dir_from_options(&target->asm_file_dir, options->asm_out, "asm", target_name, target->output_dir);
set_output_dir_from_options(&target->header_file_dir, options->header_out, "headers", target_name, target->output_dir);
if (target->type == TARGET_TYPE_OBJECT_FILES)
{
target->ir_file_dir = options->build_dir
? file_append_path(file_append_path(options->build_dir, "llvm"), target_name)
: file_append_path("llvm", target_name);
}
if (!target->asm_file_dir)
{
target->asm_file_dir = options->build_dir
? file_append_path(file_append_path(options->build_dir, "asm"), target_name)
: file_append_path("asm", target_name);
set_output_dir_from_options(&target->object_file_dir, options->obj_out, "obj", target_name, target->output_dir);
}
if (!target->object_file_dir)
else
{
target->object_file_dir = file_append_path(file_append_path(target->build_dir, "obj"), target_name);
}
if (!target->header_file_dir)
{
target->header_file_dir = target->output_dir ? target->output_dir : target->build_dir;
set_output_dir_from_options(&target->object_file_dir, options->obj_out, "obj", target_name, target->build_dir);
}
if (options->files)
@@ -573,7 +590,6 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
target->emit_asm = false;
target->emit_object_files = false;
}
target->run_dir = options->run_dir;
if (options->no_obj)
{
target->emit_object_files = false;

View File

@@ -1,5 +1,5 @@
#include "build_internal.h"
#include "compiler/compiler.h"
#include "compiler/compiler_internal.h"
#define MANIFEST_FILE "manifest.json"
@@ -206,7 +206,7 @@ static inline JSONObject *resolve_zip_library(BuildTarget *build_target, const c
scratch_buffer_clear();
assert(build_target->build_dir);
scratch_buffer_append(build_target->build_dir);
scratch_buffer_printf("/_c3l/%s/", lib_name);
scratch_buffer_printf("/unpacked_c3l/%s/", lib_name);
char *lib_dir = scratch_buffer_copy();
char *lib_dir_copy = scratch_buffer_copy();
scratch_buffer_append("checksum.txt");
@@ -329,12 +329,12 @@ void resolve_libraries(BuildTarget *build_target)
}
FOREACH(const char *, exec, library->execs)
{
printf("] Execute '%s' for library '%s':", exec, library->provides);
OUTF("Execute '%s' for library '%s':", exec, library->provides);
puts(execute_cmd(exec, false, NULL, 2048));
}
FOREACH(const char *, exec, target->execs)
{
printf("] Execute '%s' for library '%s':", exec, library->provides);
OUTF("Execute '%s' for library '%s':", exec, library->provides);
puts(execute_cmd(exec, false, NULL, 2048));
}
}

View File

@@ -184,7 +184,29 @@ static void load_into_build_target(BuildParseContext context, JSONObject *json,
error_exit("Error reading %s: output extension '%s' must start with a '.'", context.file, target->extension);
}
}
else
{
const char **authors = get_optional_string_array(context, json, "authors");
AuthorEntry *author_list = NULL;
FOREACH(const char *, author, authors)
{
const char *email_start = strstr(author, "<");
if (email_start)
{
const char *end = strstr(email_start + 1, ">");
if (!end || end[1] != 0 || email_start + 1 == end) error_exit("Error reading %s: invalid author format '%s'", author);
const char *email = str_trim(str_copy(email_start + 1, end - email_start - 1));
AuthorEntry entry = { str_trim(str_copy(author, email_start - author)), email };
vec_add(author_list, entry);
}
else
{
AuthorEntry entry = { str_trim(str_dup(author)), NULL };
vec_add(author_list, entry);
}
}
target->authors = author_list;
}
// "Before compilation" execution
APPEND_STRING_LIST(&target->exec, "exec");

View File

@@ -164,6 +164,10 @@ const char *MAIN_TEMPLATE =
"\treturn 0;\n"
"}\n";
const char *GITIGNORE_TEMPLATE =
"build/\n"
"out/\n";
const char* MAIN_INTERFACE_TEMPLATE =
"module %s;\n"
"\n"
@@ -290,6 +294,7 @@ void create_project(BuildOptions *build_options)
chdir_or_fail(build_options, build_options->project_name);
CREATE:
create_file_or_fail(build_options, ".gitignore", GITIGNORE_TEMPLATE);
create_file_or_fail(build_options, "LICENSE", NULL);
create_file_or_fail(build_options, "README.md", NULL);
create_file_or_fail(build_options, "project.json", template, build_options->project_name);

View File

@@ -702,13 +702,17 @@ static void init_asm_x86(PlatformTarget* target)
reg_instr(target, "iretl", NULL);
reg_instr(target, "iretw", NULL);
reg_instr(target, "iretq", NULL);
reg_instr(target, "rdtsc", NULL);
reg_instr(target, "rdtscp", NULL);
reg_instr_clob(target, "rdtsc", clobbers_make_from(rax_mask, X86_RDX, -1), NULL);
reg_instr_clob(target, "rdtscp", clobbers_make_from(rax_mask, X86_RDX, X86_RCX, -1), NULL);
reg_instr(target, "ret", NULL);
reg_instr(target, "push", "imm8");
reg_instr(target, "pushw", "r16/mem/imm16");
reg_instr(target, "popw", "w:r16/mem");
reg_instr_clob(target, "popcntw", cc_flag_mask, "w:r16, r16/mem");
reg_instr_clob(target, "popcntl", cc_flag_mask, "w:r32, r32/mem");
reg_instr_clob(target, "popcntq", cc_flag_mask, "w:r64, r64/mem");
target->clobber_name_list = X86ClobberNames;
target->extra_clobbers = "~{flags},~{dirflag},~{fspr}";
if (target->arch == ARCH_TYPE_X86)
@@ -747,6 +751,187 @@ bool asm_is_supported(ArchType arch)
}
}
static void scratch_append_bit(const char *name, const char *string, bool *has_print_reg)
{
if (*has_print_reg)
{
scratch_buffer_append("/");
}
else
{
*has_print_reg = true;
}
scratch_buffer_append(name);
scratch_buffer_append(string);
}
static void scratch_append_bits(const char *name, AsmArgBits bits)
{
bool has_print = false;
if (bits & ARG_BITS_512) scratch_append_bit(name, "512", &has_print);
if (bits & ARG_BITS_256) scratch_append_bit(name, "256", &has_print);
if (bits & ARG_BITS_128) scratch_append_bit(name, "128", &has_print);
if (bits & ARG_BITS_80) scratch_append_bit(name, "80", &has_print);
if (bits & ARG_BITS_64) scratch_append_bit(name, "64", &has_print);
if (bits & ARG_BITS_32) scratch_append_bit(name, "32", &has_print);
if (bits & ARG_BITS_20) scratch_append_bit(name, "20", &has_print);
if (bits & ARG_BITS_16) scratch_append_bit(name, "16", &has_print);
if (bits & ARG_BITS_12) scratch_append_bit(name, "12", &has_print);
if (bits & ARG_BITS_8) scratch_append_bit(name, "8", &has_print);
if (bits & ARG_BITS_5) scratch_append_bit(name, "5", &has_print);
}
static void arm_arg_to_scratch(AsmArgType type)
{
scratch_buffer_clear();
if (type.is_write && !type.is_readwrite) scratch_buffer_append("w");
if (type.is_readwrite) scratch_buffer_append("rw");
if (scratch_buffer.len) scratch_buffer_append(":");
bool has_print = false;
if (type.imm_arg_ubits)
{
scratch_append_bits("immu", type.imm_arg_ubits);
has_print = true;
}
if (type.imm_arg_ibits)
{
if (has_print) scratch_buffer_append("/");
scratch_append_bits("imm", type.imm_arg_ibits);
has_print = true;
}
if (type.ireg_bits)
{
if (has_print) scratch_buffer_append("/");
scratch_append_bits("r", type.ireg_bits);
has_print = true;
}
if (type.float_bits)
{
if (has_print) scratch_buffer_append("/");
scratch_append_bits("f", type.float_bits);
has_print = true;
}
if (type.vec_bits)
{
if (has_print) scratch_buffer_append("/");
scratch_append_bits("v", type.vec_bits);
has_print = true;
}
if (type.is_address)
{
if (has_print) scratch_buffer_append("/");
scratch_buffer_append("mem");
}
}
static int comp(const void *a_ptr, const void *b_ptr)
{
const AsmInstruction *a = a_ptr;
const AsmInstruction *b = b_ptr;
if (a->name == b->name) return 0;
if (!a->name) return -1;
if (!b->name) return 1;
return strcmp(a->name, b->name);
}
static void print_arch_asm(PlatformTarget *target)
{
AsmInstruction instructions[ASM_INSTRUCTION_MAX];
memcpy(instructions, target->instructions, sizeof(target->instructions));
qsort(instructions, ASM_INSTRUCTION_MAX, sizeof(instructions[0]), comp);
printf("+------------+--------------------------------+-----------------------------------------------------------------------+\n");
printf("| instr | clobbers | args |\n");
printf("+------------+--------------------------------+-----------------------------------------------------------------------+\n");
for (int i = 0; i < ASM_INSTRUCTION_MAX; i++)
{
AsmInstruction *instruction = &instructions[i];
if (!instruction->name) continue;
printf("| %-10s | ", instruction->name);
scratch_buffer_clear();
Clobbers clobbers = instruction->mask;
switch (target->arch)
{
case ARCH_TYPE_RISCV32:
case ARCH_TYPE_RISCV64:
{
for (RISCVClobbers cl = 0; cl <= RISCV_MSIP; cl++)
{
if (clobbers.mask[cl / 64] & (1ULL << (cl % 64)))
{
scratch_buffer_append(RISCVClobberNames[cl]);
scratch_buffer_append(", ");
}
}
break;
}
case ARCH_TYPE_X86_64:
case ARCH_TYPE_X86:
{
for (X86Clobbers cl = 0; cl <= X86_TMM7; cl++)
{
if (clobbers.mask[cl / 64] & (1ULL << (cl % 64)))
{
scratch_buffer_append(X86ClobberNames[cl]);
scratch_buffer_append(", ");
}
}
break;
}
case ARCH_TYPE_AARCH64:
case ARCH_TYPE_AARCH64_32:
case ARCH_TYPE_AARCH64_BE:
{
for (Aarch64Clobbers cl = 0; cl <= AARCH64_V31; cl++)
{
if (clobbers.mask[cl / 64] & (1ULL << (cl % 64)))
{
scratch_buffer_append(Aarch64ClobberNames[cl]);
scratch_buffer_append(", ");
}
}
break;
}
default:
UNREACHABLE
}
if (scratch_buffer.len) scratch_buffer.len -= 2;
printf("%-30s | ", scratch_buffer_to_string());
int len = 0;
for (unsigned j = 0; j < instruction->param_count; j++)
{
if (j != 0)
{
len += 2;
printf(", ");
}
arm_arg_to_scratch(instruction->param[j]);
printf("%s", scratch_buffer_to_string());
len += scratch_buffer.len;
}
for (int j = 0; j < 70 - len; j++) printf(" ");
printf("|\n");
}
printf("+------------+--------------------------------+-----------------------------------------------------------------------+\n");
}
void print_asm(PlatformTarget *target)
{
init_asm(target);
switch (target->arch)
{
case ARCH_TYPE_X86_64:
case ARCH_TYPE_X86:
case ARCH_TYPE_AARCH64:
case ARCH_TYPE_AARCH64_32:
case ARCH_TYPE_AARCH64_BE:
case ARCH_TYPE_RISCV32:
case ARCH_TYPE_RISCV64:
print_arch_asm(target);
return;
default:
printf("Printing ASM for target is not yet supported.");
return;
}
}
void init_asm(PlatformTarget *target)
{
if (target->asm_initialized) return;

View File

@@ -257,7 +257,7 @@ static bool c_emit_type_decl(GenContext *c, Type *type)
if (prev) return false;
c_emit_type_decl(c, type->array.base);
int id = ++c->typename;
PRINTF("typedef struct { %s ptr[%u]; } __c3_array%d;\n", c_type_name(c, type->array.base), type->array.len, id);
PRINTF("typedef struct { %s ptr[%llu]; } __c3_array%d;\n", c_type_name(c, type->array.base), (unsigned long long)type->array.len, id);
scratch_buffer_clear();
scratch_buffer_printf(" __c3_array%d", id);
htable_set(&c->gen_decl, type, scratch_buffer_copy());
@@ -637,9 +637,50 @@ static void c_emit_local_decl(GenContext *c, Decl *decl, CValue *value)
value->kind = CV_VALUE;
}
PRINT("/* TODO ZERO INIT */\n");
//llvm_store_zero(c, value);
//llvm_value_set(value, llvm_get_zero(c, var_type), var_type);
switch (var_type->type_kind)
{
case TYPE_BOOL:
PRINTF("___var_%d = false;\n", value->var);
break;
case ALL_INTS:
case ALL_FLOATS:
PRINTF("___var_%d = 0;\n", value->var);
break;
case TYPE_POISONED:
case TYPE_VOID:
case TYPE_DISTINCT:
case TYPE_CONST_ENUM:
case TYPE_FUNC_RAW:
case TYPE_BITSTRUCT:
case TYPE_TYPEDEF:
case TYPE_UNTYPED_LIST:
case TYPE_FLEXIBLE_ARRAY:
case TYPE_INFERRED_ARRAY:
case TYPE_INFERRED_VECTOR:
case TYPE_OPTIONAL:
case TYPE_WILDCARD:
case TYPE_TYPEINFO:
case TYPE_MEMBER:
UNREACHABLE
case TYPE_ANY:
case TYPE_INTERFACE:
PRINTF("___var_%d = (c3_any_t){ NULL, NULL };\n", value->var);
break;
case TYPE_ANYFAULT:
case TYPE_TYPEID:
case TYPE_FUNC_PTR:
case TYPE_POINTER:
case TYPE_ENUM:
PRINTF("___var_%d = 0;\n", value->var);
break;
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_SLICE:
case TYPE_ARRAY:
case TYPE_VECTOR:
PRINT("/* TODO ZERO INIT */\n");
}
}
static void c_emit_return(GenContext *c, Ast *stmt)
@@ -758,9 +799,9 @@ static void c_emit_stmt(GenContext *c, Ast *stmt)
case AST_CASE_STMT:
break;
case AST_COMPOUND_STMT:
PRINT(" {\n");
PRINT("{\n");
c_emit_stmt_chain(c, stmt->compound_stmt.first_stmt);
PRINT(" }\n");
PRINT("}\n");
return;
case AST_CONTINUE_STMT:
break;

View File

@@ -295,9 +295,10 @@ bool codegen_single_obj_output()
return compiler.build.single_module == SINGLE_MODULE_ON;
}
void codegen_setup_object_names(Module *module, const char **ir_filename, const char **asm_filename, const char **object_filename)
void codegen_setup_object_names(Module *module, const char **base_name, const char **ir_filename, const char **asm_filename, const char **object_filename)
{
const char *result = module_create_object_file_name(module);
*base_name = scratch_buffer_copy();
assert(compiler.build.object_file_dir);
if (codegen_single_obj_output())
{
@@ -309,7 +310,7 @@ void codegen_setup_object_names(Module *module, const char **ir_filename, const
{
res = str_printf("%s%s", result, ext);
}
compiler.obj_output = *object_filename = file_append_path(compiler.build.output_dir ? compiler.build.output_dir : ".", res);
compiler.obj_output = *object_filename = (file_path_is_relative(res) ? file_append_path(compiler.build.output_dir, res) : res);
char *dir_path = NULL;
char *filename = NULL;
file_get_dir_and_filename_from_full(compiler.obj_output, &filename, &dir_path);
@@ -323,8 +324,7 @@ void codegen_setup_object_names(Module *module, const char **ir_filename, const
*object_filename = file_append_path(compiler.build.object_file_dir, str_printf("%s%s", result, get_object_extension()));
}
*ir_filename = str_printf(compiler.build.backend == BACKEND_LLVM ? "%s.ll" : "%s.ir", result);
if (compiler.build.ir_file_dir) *ir_filename = file_append_path(compiler.build.ir_file_dir, *ir_filename);
*ir_filename = file_append_path(compiler.build.ir_file_dir, str_printf(compiler.build.backend == BACKEND_LLVM ? "%s.ll" : "%s.ir", result));
if (compiler.build.emit_asm)
{
*asm_filename = str_printf("%s.s", result);

View File

@@ -76,13 +76,13 @@ void compiler_init(BuildOptions *build_options)
compiler.context.module_list = NULL;
compiler.context.generic_module_list = NULL;
compiler.context.method_extensions = NULL;
vmem_init(&ast_arena, 512);
vmem_init(&ast_arena, 4096);
ast_calloc();
vmem_init(&expr_arena, 512);
vmem_init(&expr_arena, 4096);
expr_calloc();
vmem_init(&decl_arena, 256);
vmem_init(&decl_arena, 4096);
decl_calloc();
vmem_init(&type_info_arena, 256);
vmem_init(&type_info_arena, 4096);
type_info_calloc();
// Create zero index value.
if (build_options->std_lib_dir)
@@ -99,6 +99,10 @@ void compiler_init(BuildOptions *build_options)
{
compiler.context.should_print_environment = true;
}
if (build_options->print_asm)
{
compiler.context.should_print_asm = true;
}
}
static void compiler_lex(void)
@@ -405,9 +409,20 @@ void compiler_parse(void)
compiler_parsing_time = bench_mark();
}
bool compiler_should_ouput_file(const char *file)
{
if (!vec_size(compiler.build.emit_only)) return true;
FOREACH(const char *, f, compiler.build.emit_only)
{
if (str_eq(file, f)) return true;
}
return false;
}
static void create_output_dir(const char *dir)
{
if (!dir) return;
if (strlen(dir) == 0) return;
if (file_exists(dir))
{
if (!file_is_dir(dir)) error_exit("Output directory is not a directory %s.", dir);
@@ -415,7 +430,8 @@ static void create_output_dir(const char *dir)
}
scratch_buffer_clear();
scratch_buffer_append(dir);
if (!dir_make_recursive(scratch_buffer_to_string()))
dir_make_recursive(scratch_buffer_to_string());
if (!file_exists(dir))
{
error_exit("Failed to create directory '%s'.", dir);
}
@@ -463,20 +479,15 @@ void compiler_compile(void)
void **gen_contexts;
void (*task)(void *);
if (compiler.build.asm_file_dir || compiler.build.ir_file_dir || compiler.build.emit_object_files)
{
create_output_dir(compiler.build.build_dir);
}
if (compiler.build.ir_file_dir && (compiler.build.emit_llvm || compiler.build.test_output || compiler.build.lsp_output))
if ((compiler.build.emit_llvm || compiler.build.test_output || compiler.build.lsp_output))
{
create_output_dir(compiler.build.ir_file_dir);
}
if (compiler.build.asm_file_dir && compiler.build.emit_asm)
if (compiler.build.emit_asm)
{
create_output_dir(compiler.build.asm_file_dir);
}
if (compiler.build.object_file_dir && compiler.build.emit_object_files)
if (compiler.build.emit_object_files)
{
create_output_dir(compiler.build.object_file_dir);
}
@@ -492,6 +503,18 @@ void compiler_compile(void)
{
compiler.build.single_module = SINGLE_MODULE_ON;
}
if (compiler.build.emit_asm)
{
scratch_buffer_clear();
scratch_buffer_append(compiler.build.asm_file_dir);
dir_make_recursive(scratch_buffer_to_string());
}
if (compiler.build.emit_object_files)
{
scratch_buffer_clear();
scratch_buffer_append(compiler.build.object_file_dir);
dir_make_recursive(scratch_buffer_to_string());
}
switch (compiler.build.backend)
{
case BACKEND_C:
@@ -669,13 +692,14 @@ void compiler_compile(void)
}
error_exit("Compilation produced no object files, maybe there was no code?");
}
if (vec_size(compiler.build.emit_only)) goto SKIP;
if (output_exe)
{
if (compiler.build.output_dir)
if (file_path_is_relative(output_exe))
{
create_output_dir(compiler.build.output_dir);
output_exe = file_append_path(compiler.build.output_dir, output_exe);
}
;
file_create_folders(output_exe);
bool system_linker_available = link_libc() && compiler.platform.os != OS_TYPE_WIN32;
bool use_system_linker = system_linker_available && compiler.build.arch_os_target == default_target;
@@ -771,9 +795,8 @@ void compiler_compile(void)
}
else if (output_static)
{
if (compiler.build.output_dir)
if (file_path_is_relative(output_static))
{
create_output_dir(compiler.build.output_dir);
output_static = file_append_path(compiler.build.output_dir, output_static);
}
file_create_folders(output_static);
@@ -788,9 +811,8 @@ void compiler_compile(void)
}
else if (output_dynamic)
{
if (compiler.build.output_dir)
if (file_path_is_relative(output_dynamic))
{
create_output_dir(compiler.build.output_dir);
output_dynamic = file_append_path(compiler.build.output_dir, output_dynamic);
}
file_create_folders(output_dynamic);
@@ -805,6 +827,7 @@ void compiler_compile(void)
}
else
{
SKIP:
compiler_print_bench();
}
free(obj_files);
@@ -855,9 +878,9 @@ void compile_file_list(BuildOptions *options)
{
error_exit("The target is a 'prepare' target, and only 'build' can be used with it.");
}
OUTF("] Running prepare target '%s'.\n", options->target_select);
OUTF("Running prepare target '%s'.\n", options->target_select);
execute_scripts();
OUTF("] Completed.\n.");
OUTN("Completed.\n.");
return;
}
if (options->command == COMMAND_CLEAN_RUN)
@@ -900,16 +923,7 @@ static void setup_bool_define(const char *id, bool value)
setup_define(id, expr_new_const_bool(INVALID_SPAN, type_bool, value));
}
#if FETCH_AVAILABLE
const char * vendor_fetch_single(const char* lib, const char* path)
{
const char *resource = str_printf("/c3lang/vendor/releases/download/latest/%s.c3l", lib);
const char *destination = file_append_path(path, str_printf("%s.c3l", lib));
const char *error = download_file("https://github.com", resource, destination);
return error;
}
static bool use_ansi(void)
bool use_ansi(void)
{
switch (compiler.context.ansi)
{
@@ -927,6 +941,15 @@ static bool use_ansi(void)
#endif
}
#if FETCH_AVAILABLE
const char * vendor_fetch_single(const char* lib, const char* path)
{
const char *resource = str_printf("/c3lang/vendor/releases/download/latest/%s.c3l", lib);
const char *destination = file_append_path(path, str_printf("%s.c3l", lib));
const char *error = download_file("https://github.com", resource, destination);
return error;
}
#define PROGRESS_BAR_LENGTH 35
void update_progress_bar(const char* lib, int current_step, int total_steps)
@@ -1419,6 +1442,11 @@ void compile()
print_build_env();
exit_compiler(COMPILER_SUCCESS_EXIT);
}
if (compiler.context.should_print_asm)
{
print_asm(&compiler.platform);
exit_compiler(COMPILER_SUCCESS_EXIT);
}
check_sanitizer_options(&compiler.build);
resolve_libraries(&compiler.build);
compiler.context.sources = compiler.build.sources;
@@ -1452,6 +1480,7 @@ void compile()
setup_int_define("COMPILER_SIZE_OPT_LEVEL", (uint64_t)compiler.build.optsize, type_int);
setup_bool_define("COMPILER_SAFE_MODE", safe_mode_enabled());
setup_bool_define("DEBUG_SYMBOLS", compiler.build.debug_info == DEBUG_INFO_FULL);
setup_bool_define("PANIC_MSG", compiler.build.feature.panic_level != PANIC_OFF);
setup_bool_define("BACKTRACE", compiler.build.show_backtrace != SHOW_BACKTRACE_OFF);
#if LLVM_AVAILABLE
setup_int_define("LLVM_VERSION", llvm_version_major, type_int);
@@ -1468,7 +1497,22 @@ void compile()
setup_bool_define("THREAD_SANITIZER", compiler.build.feature.sanitize_thread);
setup_string_define("BUILD_HASH", GIT_HASH);
setup_string_define("BUILD_DATE", compiler_date_to_iso());
Expr *expr_names = expr_new(EXPR_CONST, INVALID_SPAN);
Expr *expr_emails = expr_new(EXPR_CONST, INVALID_SPAN);
expr_names->const_expr.const_kind = CONST_UNTYPED_LIST;
expr_emails->const_expr.const_kind = CONST_UNTYPED_LIST;
expr_names->type = type_untypedlist;
expr_emails->type = type_untypedlist;
expr_names->resolve_status = expr_emails->resolve_status = RESOLVE_DONE;
FOREACH(AuthorEntry, entry, compiler.build.authors)
{
Expr *const_name = expr_new_const_string(INVALID_SPAN, entry.author); // NOLINT
Expr *const_email = expr_new_const_string(INVALID_SPAN, entry.email ? entry.email : ""); // NOLINT
vec_add(expr_names->const_expr.untyped_list, const_name);
vec_add(expr_emails->const_expr.untyped_list, const_email);
}
setup_define("AUTHORS", expr_names);
setup_define("AUTHOR_EMAILS", expr_emails);
type_init_cint();
compiler_init_time = bench_mark();
@@ -1720,7 +1764,8 @@ static bool is_posix(OsType os)
case OS_TYPE_NETBSD:
case OS_TYPE_LINUX:
case OS_TYPE_KFREEBSD:
case OS_TYPE_FREE_BSD:
case OS_TYPE_FREEBSD:
case OS_TYPE_OPENBSD:
case OS_TYPE_SOLARIS:
return true;
case OS_TYPE_WIN32:

View File

@@ -16,11 +16,11 @@ typedef double Real;
typedef uint64_t ByteSize;
typedef uint32_t TypeSize;
typedef int32_t IndexDiff;
typedef int32_t ArrayIndex;
typedef int64_t ArrayIndex;
typedef uint16_t StructIndex;
typedef uint32_t AlignSize;
typedef int32_t ScopeId;
typedef uint32_t ArraySize;
typedef uint64_t ArraySize;
typedef uint64_t BitSize;
typedef uint16_t FileId;
@@ -34,7 +34,7 @@ typedef uint16_t FileId;
#define UINT12_MAX 4095
#define UINT20_MAX 1048575U
#define MAX_ARRAYINDEX INT32_MAX
#define MAX_ARRAYINDEX INT64_MAX
#define MAX_FIXUPS 0xFFFFF
#define MAX_HASH_SIZE (512 * 1024 * 1024)
#define INVALID_SPAN ((SourceSpan){ .row = 0 })
@@ -67,11 +67,22 @@ typedef uint16_t FileId;
#define SEMA_NOTE(_node, ...) sema_note_prev_at((_node)->span, __VA_ARGS__)
#define SEMA_DEPRECATED(_node, ...) do { if (compiler.build.test_output && !compiler.build.silence_deprecation) print_error_at((_node)->span, __VA_ARGS__); if (!compiler.build.silence_deprecation) \
sema_note_prev_at((_node)->span, __VA_ARGS__); } while (0)
#define PRINT_DEPRECATED_AT(span__, ...) do { if (compiler.build.test_output && !compiler.build.silence_deprecation) print_error_at(span__, __VA_ARGS__); if (!compiler.build.silence_deprecation) \
sema_note_prev_at(span__, __VA_ARGS__); } while (0)
#define EXPAND_EXPR_STRING(str_) (str_)->const_expr.bytes.len, (str_)->const_expr.bytes.ptr
#define TABLE_MAX_LOAD 0.5
#define OUTF(...) do { if (!compiler.build.silent) printf(__VA_ARGS__); } while(0)
#define OUTN(str__) do { if (!compiler.build.silent) puts(str__); } while(0)
#ifdef NDEBUG
#define ASSERT_SPANF(node__, check__, format__, ...) do { (void)(check__); } while(0)
#define ASSERT_SPAN(node__, check__) do { (void)(check__); } while(0)
#define ASSERT_AT(span__, check__) do { (void)(check__);} while(0)
#else
#define ASSERT_SPANF(node__, check__, format__, ...) do { if (!(check__)) { assert_print_line((node__)->span); eprintf(format__, __VA_ARGS__); ASSERT(check__); } } while(0)
#define ASSERT_SPAN(node__, check__) do { if (!(check__)) { assert_print_line((node__)->span); ASSERT(check__); } } while(0)
#define ASSERT_AT(span__, check__) do { if (!(check__)) { assert_print_line(span__); ASSERT(check__); } } while(0)
#endif
#define INVALID_PTR ((void*)(uintptr_t)0xAAAAAAAAAAAAAAAA)
@@ -635,6 +646,7 @@ typedef struct Decl_
bool attr_compact : 1;
bool resolved_attributes : 1;
bool allow_deprecated : 1;
bool attr_structlike : 1;
union
{
void *backend_ref;
@@ -699,16 +711,17 @@ typedef enum RangeType
RANGE_CONST_END,
RANGE_CONST_LEN,
RANGE_CONST_RANGE,
RANGE_SINGLE_ELEMENT,
} RangeType;
typedef struct
{
ResolveStatus status : 3;
RangeType range_type;
RangeType range_type : 4;
bool start_from_end : 1;
bool end_from_end : 1;
bool is_len : 1;
bool is_range : 1;
bool is_optional : 1;
union
{
@@ -1701,6 +1714,7 @@ typedef struct
bool ensures : 1;
bool pure : 1;
bool in_no_eval : 1;
bool is_naked_fn : 1;
bool ignore_deprecation : 1;
SourceSpan in_if_resolution;
Decl **opt_returns;
@@ -1863,11 +1877,13 @@ typedef struct CopyStruct_
typedef struct
{
const char **links;
bool link_math;
} Linking;
typedef struct
{
bool should_print_environment;
bool should_print_asm;
Ansi ansi;
HTable modules;
Module *core_module;
@@ -2021,6 +2037,8 @@ INLINE bool compile_asserts(void)
return safe_mode_enabled() || compiler.build.testing;
}
void assert_print_line(SourceSpan span);
bool ast_is_not_empty(Ast *ast);
bool ast_is_compile_time(Ast *ast);
@@ -2158,6 +2176,7 @@ Ast *copy_ast_defer(Ast *source_ast);
TypeInfo *copy_type_info_single(TypeInfo *type_info);
void init_asm(PlatformTarget *target);
void print_asm_list(PlatformTarget *target);
AsmRegister *asm_reg_by_name(PlatformTarget *target, const char *name);
AsmInstruction *asm_instr_by_name(const char *name);
INLINE const char *asm_clobber_by_index(unsigned index);
@@ -2406,6 +2425,7 @@ bool sema_resolve_array_like_len(SemaContext *context, TypeInfo *type_info, Arra
bool sema_resolve_type_info(SemaContext *context, TypeInfo *type_info, ResolveTypeKind kind);
bool sema_unresolved_type_is_generic(SemaContext *context, TypeInfo *type_info);
bool use_ansi(void);
void print_error_at(SourceSpan loc, const char *message, ...);
void print_error_after(SourceSpan loc, const char *message, ...);
void sema_note_prev_at(SourceSpan loc, const char *message, ...);
@@ -2425,6 +2445,7 @@ File *source_file_text_load(const char *filename, char *content);
File *compile_and_invoke(const char *file, const char *args, const char *stdin_data, size_t limit);
void compiler_parse(void);
bool compiler_should_ouput_file(const char *file);
void emit_json(void);
void stable_init(STable *table, uint32_t initial_size);
@@ -2452,7 +2473,7 @@ const char *symtab_preset(const char *data, TokenType type);
const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type);
void *llvm_target_machine_create(void);
void codegen_setup_object_names(Module *module, const char **ir_filename, const char **asm_filename, const char **object_filename);
void codegen_setup_object_names(Module *module, const char **base_name, const char **ir_filename, const char **asm_filename, const char **object_filename);
void target_setup(BuildTarget *build_target);
int target_alloca_addr_space();
bool os_is_apple(OsType os_type);
@@ -2571,7 +2592,6 @@ INLINE Type *type_vector_type(Type *type);
static inline CanonicalType *type_pointer_type(Type *type);
static inline CanonicalType *type_flatten(Type *type);
static inline bool type_flat_is_char_array(Type *type);
static inline Type *type_base(Type *type);
INLINE TypeInfo *type_info_new(TypeInfoKind kind, SourceSpan span);
@@ -2592,7 +2612,6 @@ BinaryOp binaryop_from_token(TokenType type);
BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op);
TokenType binaryop_to_token(BinaryOp type);
// ---- static inline function implementations.
INLINE Type *type_no_optional(Type *type)
@@ -2945,6 +2964,7 @@ INLINE TypeInfoId type_info_id_new_base(Type *type, SourceSpan span)
return type_infoid(type_info_new_base(type, span));
}
INLINE Type *type_new(TypeKind kind, const char *name)
{
Type *type = CALLOCS(Type);
@@ -2968,7 +2988,7 @@ INLINE bool type_convert_will_trunc(Type *destination, Type *source)
// Useful sanity check function.
INLINE void advance_and_verify(ParseContext *context, TokenType token_type)
{
ASSERT(context->tok == token_type);
ASSERT_SPAN(context, context->tok == token_type);
advance(context);
}
@@ -3045,6 +3065,44 @@ static inline Type *type_flat_distinct_inline(Type *type)
}
}
static inline CanonicalType *type_vector_base(CanonicalType *type)
{
return type->type_kind == TYPE_VECTOR ? type->array.base->canonical : type;
}
static inline Type *type_flatten_and_inline(Type *type)
{
while (1)
{
type = type->canonical;
Decl *decl;
switch (type->type_kind)
{
case TYPE_OPTIONAL:
type = type->optional;
continue;
case TYPE_DISTINCT:
type = type->decl->distinct->type;
continue;
case TYPE_CONST_ENUM:
type = type->decl->enums.type_info->type;
continue;
case TYPE_ENUM:
decl = type->decl;
if (!decl->is_substruct) return type;
if (!compiler.build.old_enums || decl->enums.inline_value)
{
type = decl->enums.type_info->type;
continue;
}
type = decl->enums.parameters[decl->enums.inline_index]->type;
continue;
default:
return type;
}
}
}
static inline Type *type_flat_distinct_enum_inline(Type *type)
{
while (1)
@@ -3112,6 +3170,32 @@ static inline Type *type_flatten_to_int(Type *type)
}
}
static inline CanonicalType *type_distinct_inline(Type *type)
{
while (1)
{
type = type->canonical;
switch (type->type_kind)
{
case TYPE_ENUM:
if (!type->decl->is_substruct) return type;
FALLTHROUGH;
case TYPE_CONST_ENUM:
type = enum_inner_type(type);
break;
case TYPE_DISTINCT:
type = type->decl->distinct->type;
break;
case TYPE_OPTIONAL:
type = type->optional;
break;
case TYPE_TYPEDEF:
UNREACHABLE
default:
return type;
}
}
}
static inline CanonicalType *type_flatten(Type *type)
{
while (1)
@@ -3163,10 +3247,11 @@ static inline Type *type_flatten_no_export(Type *type)
}
}
static inline bool type_flat_is_char_array(Type *type)
static inline bool type_flat_is_valid_for_arg_h(Type *type)
{
type = type_flatten(type);
if (type->type_kind != TYPE_ARRAY) return false;
if (type->type_kind == TYPE_POINTER) return true;
if (type->type_kind != TYPE_ARRAY && type->type_kind != TYPE_SLICE) return false;
switch (type->array.base->type_kind)
{
case TYPE_I8:
@@ -3177,6 +3262,7 @@ static inline bool type_flat_is_char_array(Type *type)
}
}
INLINE Type *type_vector_type(Type *type)
{
Type *flatten = type_flatten(type);
@@ -3496,8 +3582,6 @@ static inline void expr_set_span(Expr *expr, SourceSpan loc)
default:
return;
}
expr_list_set_span(expr->expression_list, loc);
return;
case EXPR_CAST:
exprid_set_span(expr->cast_expr.expr, loc);
return;
@@ -3901,7 +3985,6 @@ INLINE void expr_rewrite_enum_from_ord(Expr *expr, Type *type)
expr->resolve_status = RESOLVE_DONE;
}
INLINE void expr_rewrite_slice_len(Expr *expr, Expr *inner, Type *type)
{
ASSERT(inner->resolve_status == RESOLVE_DONE);
@@ -4238,18 +4321,15 @@ INLINE bool check_module_name(Path *path)
return true;
}
#ifdef NDEBUG
#define ASSERT_SPANF(node__, check__, format__, ...) do { } while(0)
#define ASSERT_SPAN(node__, check__) do { } while(0)
#define ASSERT_AT(span__, check__) do { } while(0)
#else
#define ASSERT_SPANF(node__, check__, format__, ...) do { if (!(check__)) { assert_print_line((node__)->span); eprintf(format__, __VA_ARGS__); ASSERT(check__); } } while(0)
#define ASSERT_SPAN(node__, check__) do { if (!(check__)) { assert_print_line((node__)->span); ASSERT(check__); } } while(0)
#define ASSERT_AT(span__, check__) do { if (!(check__)) { assert_print_line(span__); ASSERT(check__); } } while(0)
#endif
void assert_print_line(SourceSpan span);
INLINE bool expr_is_valid_index(Expr *expr)
{
ASSERT_SPAN(expr, expr_is_const_int(expr));
return int_fits(expr->const_expr.ixx, TYPE_I64);
}
const char *default_c_compiler(void);
void print_build_env(void);
void print_asm(PlatformTarget *target);
const char *os_type_to_string(OsType os);

View File

@@ -195,6 +195,8 @@ void copy_range(CopyStruct *c, Range *range)
{
switch (range->range_type)
{
case RANGE_SINGLE_ELEMENT:
UNREACHABLE
case RANGE_CONST_LEN:
case RANGE_CONST_END:
MACRO_COPY_EXPRID(range->start);
@@ -860,13 +862,17 @@ static ResolvedAttrData *copy_attrs_resolved(CopyStruct *c, ResolvedAttrData *da
{
if (!data) return NULL;
ResolvedAttrData *copy = MALLOCS(ResolvedAttrData);
const char **new_links = NULL;
FOREACH(const char *, link, data->links) vec_add(new_links, link);
*copy = (ResolvedAttrData) {
.tags = copy_attributes(c, data->tags),
.deprecated = data->deprecated,
.links = data->links,
.links = new_links,
.section = data->section,
.wasm_module = data->wasm_module
};
const char **new = NULL;
return copy;
}
@@ -1083,16 +1089,16 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
return copy;
case DECL_ENUM_CONSTANT:
if (copy->enum_constant.is_raw)
{
MACRO_COPY_EXPR_LIST(copy->enum_constant.associated);
}
else
{
if (copy->resolve_status != RESOLVE_DONE)
{
MACRO_COPY_EXPR(copy->enum_constant.value);
}
}
else
{
MACRO_COPY_EXPR_LIST(copy->enum_constant.associated);
}
break;
case DECL_TYPEDEF:
copy_decl_type(copy);

View File

@@ -162,18 +162,40 @@ static void print_error_type_at(SourceSpan location, const char *message, PrintT
}
eprintf("\n");
bool ansi = use_ansi();
if (col_location)
{
switch (print_type)
{
case PRINT_TYPE_ERROR:
eprintf("(%s:%d:%d) Error: %s\n\n", file->full_path, location.row, col_location, message);
if (ansi)
{
eprintf("(%s:%d:%d) \x1b[31;1mError\x1b[0m: %s\n\n", file->full_path, location.row, col_location, message);
}
else
{
eprintf("(%s:%d:%d) Error: %s\n\n", file->full_path, location.row, col_location, message);
}
break;
case PRINT_TYPE_NOTE:
eprintf("(%s:%d:%d) Note: %s\n\n", file->full_path, location.row, col_location, message);
if (ansi)
{
eprintf("(%s:%d:%d) \x1b[1mNote\x1b[0m: %s\n\n", file->full_path, location.row, col_location, message);
}
else
{
eprintf("(%s:%d:%d) Note: %s\n\n", file->full_path, location.row, col_location, message);
}
break;
case PRINT_TYPE_WARN:
eprintf("(%s:%d:%d) Warning: %s\n\n", file->full_path, location.row, col_location, message);
if (ansi)
{
eprintf("(%s:%d:%d) \x1b[33;1mWarning\x1b[0m: %s\n\n", file->full_path, location.row, col_location, message);
}
else
{
eprintf("(%s:%d:%d) Warning: %s\n\n", file->full_path, location.row, col_location, message);
}
break;
default:
UNREACHABLE
@@ -184,13 +206,34 @@ static void print_error_type_at(SourceSpan location, const char *message, PrintT
switch (print_type)
{
case PRINT_TYPE_ERROR:
eprintf("(%s:%d) Error: %s\n\n", file->full_path, location.row, message);
if (ansi)
{
eprintf("(%s:%d) \x1b[31;1mError\x1b[0m: %s\n\n", file->full_path, location.row, message);
}
else
{
eprintf("(%s:%d) Error: %s\n\n", file->full_path, location.row, message);
}
break;
case PRINT_TYPE_NOTE:
eprintf("(%s:%d) Note: %s\n\n", file->full_path, location.row, message);
if (ansi)
{
eprintf("(%s:%d) \x1b[1mNote\x1b[0m: %s\n\n", file->full_path, location.row, message);
}
else
{
eprintf("(%s:%d) Note: %s\n\n", file->full_path, location.row, message);
}
break;
case PRINT_TYPE_WARN:
eprintf("(%s:%d) Warning: %s\n\n", file->full_path, location.row, message);
if (ansi)
{
eprintf("(%s:%d) \x1b[33;1mWarning\x1b[0m: %s\n\n", file->full_path, location.row, message);
}
else
{
eprintf("(%s:%d) Warning: %s\n\n", file->full_path, location.row, message);
}
break;
default:
UNREACHABLE

View File

@@ -260,7 +260,7 @@ typedef enum FLAG_ATTR
#define EXPORTED_USER_DEFINED_TYPES (ATTR_ENUM | ATTR_UNION | ATTR_STRUCT | ATTR_FAULT)
#define CALLABLE_TYPE (ATTR_FUNC | ATTR_INTERFACE_METHOD | ATTR_MACRO | ATTR_FNTYPE)
#define USER_DEFINED_TYPES EXPORTED_USER_DEFINED_TYPES | ATTR_BITSTRUCT | ATTR_DISTINCT
#define USER_DEFINED_TYPES (EXPORTED_USER_DEFINED_TYPES | ATTR_BITSTRUCT | ATTR_DISTINCT)
typedef enum
{
@@ -309,6 +309,7 @@ typedef enum
ATTRIBUTE_REFLECT,
ATTRIBUTE_SAFEMACRO,
ATTRIBUTE_SECTION,
ATTRIBUTE_STRUCTLIKE,
ATTRIBUTE_TAG,
ATTRIBUTE_TEST,
ATTRIBUTE_UNUSED,
@@ -450,6 +451,7 @@ typedef enum
BUILTIN_EXP2,
BUILTIN_EXPECT,
BUILTIN_EXPECT_WITH_PROBABILITY,
BUILTIN_FENCE,
BUILTIN_FLOOR,
BUILTIN_FMA,
BUILTIN_FMULADD,
@@ -964,6 +966,7 @@ typedef enum
OVERLOADS_COUNT = OVERLOAD_SHR_ASSIGN
} OperatorOverload;
#define OVERLOAD_NONE ((OperatorOverload)0)
typedef enum
{
OS_TYPE_UNKNOWN,
@@ -971,7 +974,7 @@ typedef enum
OS_TYPE_ANANAS,
OS_TYPE_CLOUD_ABI,
OS_TYPE_DRAGON_FLY,
OS_TYPE_FREE_BSD,
OS_TYPE_FREEBSD,
OS_TYPE_FUCHSIA,
OS_TYPE_IOS,
OS_TYPE_KFREEBSD,

View File

@@ -904,6 +904,8 @@ bool expr_is_pure(Expr *expr)
if (!exprid_is_pure(expr->slice_expr.expr)) return false;
switch (expr->slice_expr.range.range_type)
{
case RANGE_SINGLE_ELEMENT:
UNREACHABLE
case RANGE_CONST_RANGE:
return true;
case RANGE_CONST_END:

View File

@@ -203,7 +203,7 @@ static void header_print_type(HeaderContext *c, Type *type)
case TYPE_ARRAY:
PRINTF("struct { ");
header_print_type(c, type->array.base);
PRINTF(" arr[%d]; }", type->array.len);
PRINTF(" arr[%llu]; }", (unsigned long long)type->array.len);
return;
case TYPE_ANY:
case TYPE_INTERFACE:
@@ -227,7 +227,7 @@ static void header_print_type(HeaderContext *c, Type *type)
default:
UNREACHABLE;
}
PRINTF("%dx%d", (int)type_bit_size(type->array.base), type->array.len);
PRINTF("%dx%llu", (int)type_bit_size(type->array.base), (unsigned long long)type->array.len);
return;
}
}
@@ -345,7 +345,7 @@ static void header_gen_members(HeaderContext *c, int indent, Decl **members)
{
case TYPE_ARRAY:
header_print_type(c, type->array.base);
PRINTF(" %s[%d];\n", member->name, type->array.len);
PRINTF(" %s[%llu];\n", member->name, (unsigned long long)type->array.len);
break;
case TYPE_FLEXIBLE_ARRAY:
header_print_type(c, type->array.base);
@@ -556,7 +556,6 @@ RETRY:
TODO;
case TYPE_FUNC_RAW:
UNREACHABLE
return;
case TYPE_STRUCT:
case TYPE_UNION:
header_gen_struct_union_top(c, type->decl, is_pointer ? GEN_POINTER : GEN_FULL);
@@ -575,7 +574,7 @@ RETRY:
header_print_type(c, flat_type);
PRINTF(" ");
header_print_type(c, type);
PRINTF(" __attribute__((vector_size(%d)));\n", (int)type_size(flat_type) * type->array.len);
PRINTF(" __attribute__((vector_size(%llu)));\n", (int)type_size(flat_type) * (unsigned long long)type->array.len);
return;
}
}

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