Compare commits

...

268 Commits

Author SHA1 Message Date
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
Christoffer Lerno
9285dfefad - $member.get supports bitstructs.
- $member.set for setting members without the *& trick.
- io::struct_to_format now supports bitstructs.
2025-06-29 01:19:09 +02:00
Book-reader
5246ef83e7 Fix typo in libc_allocator.c3 2025-06-28 14:33:18 +02:00
Zack Puhl
0448e50b3d Fix Incorrect SHA256 Hashes for Large Inputs (#2247)
* fix >256MiB sha256 bitcount computation overflow
2025-06-28 03:20:33 +02:00
Christoffer Lerno
2d535aaa25 Default assert() message drops parens #2249. 2025-06-28 00:07:26 +02:00
Christoffer Lerno
dc1e5323ab Segfault in the compiler when using a bitstruct constant defined using a cast with an operator #2248. 2025-06-27 23:00:14 +02:00
Christoffer Lerno
63abf1c2f8 - Compiler null pointer when building a static-lib with -o somedir/... #2246 2025-06-27 15:46:17 +02:00
Christoffer Lerno
df8904909b Fix bugs relating to optional interface addr-of #2244. 2025-06-27 15:02:12 +02:00
Christoffer Lerno
e986e3a8c0 Refactoring. 2025-06-25 15:08:57 +02:00
Christoffer Lerno
f67da4f315 Refactoring, optimize negation in if statement. 2025-06-25 12:33:17 +02:00
Christoffer Lerno
8a4e7b6ce8 Fix regression causing text output. 2025-06-25 02:48:22 +02:00
Christoffer Lerno
2b0fb52f65 Fix regression: Enum inference with compare operators #2241 2025-06-25 00:55:29 +02:00
Christoffer Lerno
faf073885f Updated fix of #2218 2025-06-24 22:28:14 +02:00
Christoffer Lerno
11b8a9808d Fix overeager constant folding. 2025-06-24 16:17:04 +02:00
Christoffer Lerno
1ef9c73342 Compile-time comparison of constant vectors. #1575. 2025-06-24 16:04:24 +02:00
Christoffer Lerno
92d56b7a35 Updated project suggestions 2025-06-24 14:19:41 +02:00
Christoffer Lerno
a894adbdd6 Assert comparing untyped lists #2240. 2025-06-24 12:47:08 +02:00
Christoffer Lerno
605a7c4091 Improve error message on pointer diff #2239. 2025-06-23 23:47:26 +02:00
Zack Puhl
adabae2a24 add 'strdup' reference to libc 2025-06-23 15:37:25 +02:00
Christoffer Lerno
affb722b23 @tag didn't work with members #2236. 2025-06-23 14:45:16 +02:00
Christoffer Lerno
1b2f5989e1 Assert casting bitstruct to short/char #2237 2025-06-23 14:12:34 +02:00
Christoffer Lerno
e0b6c83a62 Aaaand let's get that last test. 2025-06-23 02:05:31 +02:00
Christoffer Lerno
839f835845 Last test fix. 2025-06-23 00:58:16 +02:00
Christoffer Lerno
2636a855c4 Forgotten test directives. 2025-06-23 00:38:12 +02:00
Christoffer Lerno
0d147a48b2 Another fix to #2226 2025-06-23 00:36:15 +02:00
Christoffer Lerno
aff3a3f746 Compiler segfault when using distinct type in attribute imported from other module #2234. 2025-06-22 23:58:38 +02:00
Christoffer Lerno
c95204c3f7 Fix inc/dec vector ptr. 2025-06-22 18:09:08 +02:00
Christoffer Lerno
794e8371c8 Rename test. 2025-06-21 23:47:49 +02:00
Christoffer Lerno
2bbc6cbbca Further #2226 fixes. 2025-06-21 23:18:17 +02:00
Christoffer Lerno
07bd37da43 Further #2226 fixes. 2025-06-21 16:50:52 +02:00
Christoffer Lerno
a0497e9274 math::overflow_* wrappers incorrectly don't allow distinct integers #2221. 2025-06-21 13:28:45 +02:00
Christoffer Lerno
fa730e7ec2 Overload resolution fixes to inline typedef #2226. 2025-06-21 13:03:16 +02:00
Christoffer Lerno
b4a6e3704f Update grammar to interpret $Foo = int as a statement. 2025-06-21 00:30:43 +02:00
Christoffer Lerno
dd80e8b799 Compile time type assignment (eg $Foo = int) is no longer an expression. 2025-06-20 23:31:40 +02:00
Zack Puhl
5efc721b0c Add SHA512 Module to stdlib (#2227)
* add sha512 module to stdlib with passing unit tests

* fix release notes PR ref num for this

* deduplicate const SHA512 hash info
2025-06-20 19:08:45 +02:00
Christoffer Lerno
20c13c0bb4 - Incorrect handling of constant null fault causing compiler crash #2232. 2025-06-20 15:29:52 +02:00
Christoffer Lerno
cd3e924d1e Fix test for LLVM 20 2025-06-20 02:01:32 +02:00
Estanislao Pérez Nartallo
2e42868467 Fix error maybe-uninitialized (#2230)
* Add build instructions for Arch Linux

* Fix error maybe-uninitialized in llvm_codegen_expr.c when compiling with clang 20.1.6
2025-06-20 00:55:06 +02:00
Christoffer Lerno
8d698b5e40 Lambda C-style vaargs were not properly rejected, leading to crash #2229. 2025-06-20 00:52:03 +02:00
Christoffer Lerno
2f45beecbe @pool now has an optional reserve parameter, some minor changes to the temp_allocator API 2025-06-19 01:13:43 +02:00
Christoffer Lerno
1b4b9bca94 Linking fails on operator method imported as @public #2224. 2025-06-18 23:34:39 +02:00
Tanis Pérez Nartallo
40ae9d2e55 Add build instructions for Arch Linux 2025-06-18 23:34:16 +02:00
Christoffer Lerno
0df538d0e2 Test LLVM 20 compatibility 2025-06-18 22:41:35 +02:00
Christoffer Lerno
aa425a0886 Fixes to x += { 1, 1 } for enum and pointer vectors #2222. 2025-06-18 22:27:30 +02:00
Christoffer Lerno
842788e59d x += 1 and x -= 1 works propertly on pointer vectors #2222. 2025-06-18 17:02:56 +02:00
Christoffer Lerno
2b97d7d59c x++ and x-- works on pointer vectors #2222. 2025-06-18 13:17:21 +02:00
Christoffer Lerno
75f78551cf Rename TypdefDecl to TypeAliasDecl 2025-06-18 11:59:44 +02:00
Christoffer Lerno
01ef53a090 Bug when offsetting pointers of large structs using ++ and --. 2025-06-18 10:13:48 +02:00
Christoffer Lerno
a55f56a88f Linker errors when shadowing @local with public function #2198 2025-06-18 02:07:07 +02:00
Christoffer Lerno
eb75d8f82a Method ambiguity when importing parent module publicly in private submodule. #2208 2025-06-18 00:18:56 +02:00
Christoffer Lerno
f07bd3cbc6 $defined(#expr) broken with binary. #2219 2025-06-17 18:00:16 +02:00
Christoffer Lerno
93640699be Support distrinct types as the base type of bitstructs. #2218 2025-06-17 16:49:46 +02:00
Christoffer Lerno
99e29bff8d Bug in AST copying would make operator overloading like += compile incorrectly #2217 2025-06-17 16:02:43 +02:00
Ero Mrinin
95137db64b 'next_float' macro patch (#2213)
More optimized implementation.
2025-06-17 09:54:27 +02:00
Christoffer Lerno
e7ce79e731 Fix error for named arguments-order with compile-time arguments #2212 2025-06-16 23:56:03 +02:00
Christoffer Lerno
779f548a00 Allow generics over distinct types #2216. 2025-06-16 23:16:35 +02:00
Christoffer Lerno
f0bd93d1f0 Additional #2210 fixes. 2025-06-16 22:56:34 +02:00
Christoffer Lerno
3ce15bd7af Incorrect codegen if a macro ends with unreachable and is assigned to something. #2210 2025-06-15 22:35:44 +02:00
Christoffer Lerno
07eee04e94 In some cases, the compiler would dereference a compile time null. #2215 2025-06-15 21:58:39 +02:00
Christoffer Lerno
1f7b62b248 Fix to is_array_or_slice_of_char #2214.
`is_array_or_slice_of_char` and `is_arrayptr_or_slice_of_char` are replaced by constant `@` variants.
2025-06-15 21:27:47 +02:00
Christoffer Lerno
b2c994618f Fix to is_array_or_slice_of_char #2214.
`is_array_or_slice_of_char` and `is_arrayptr_or_slice_of_char` are replaced by constant `@` variants.
2025-06-15 16:54:20 +02:00
Christoffer Lerno
2afa544d7d Correctly format '%c' when given a width. #2199 2025-06-15 02:27:36 +02:00
Christoffer Lerno
dda2d2ecbe Show code that caused unreachable code #2207
`$echo` would suppress warning about unreachable code. #2205
2025-06-15 00:37:28 +02:00
Christoffer Lerno
f79f6d4001 - cflags additions for targets was not handed properly. #2209 2025-06-14 23:40:54 +02:00
Christoffer Lerno
cf167c9446 Make unreachable() only panic in safe mode. 2025-06-14 18:37:49 +02:00
Christoffer Lerno
f0201f971e Fix NULL -> false. 2025-06-13 22:51:51 +02:00
Christoffer Lerno
a3abea1a33 Reorder fields. 2025-06-13 22:08:07 +02:00
Christoffer Lerno
5f6f52838c The form-feed character '\f' is no longer valid white space. 2025-06-13 21:37:06 +02:00
Christoffer Lerno
e0237096d6 - Support untyped second argument for operator overloading.
- Distinct versions of builtin types ignore @operator overloads #2204.
- @operator macro using untyped parameter causes compiler segfault #2200.
- Add comparison with `==` for ZString types.
2025-06-13 17:12:39 +02:00
Christoffer Lerno
82491a6f85 - Fixes to @format checking #2199. 2025-06-12 02:26:39 +02:00
Christoffer Lerno
1aacb1fa60 Fixed regression compiler crash when using && for untyped parameters #2197. 2025-06-10 16:26:08 +02:00
Christoffer Lerno
bbd9f6dc96 Add --sources build option to add additional files to compile. #2097 2025-06-10 14:09:15 +02:00
Christoffer Lerno
496d23e93f Fix some @require comments. 2025-06-10 01:54:45 +02:00
Christoffer Lerno
e936b999d2 Update Android ABI lowering 2025-06-08 23:14:07 +02:00
Christoffer Lerno
becda6ea1d Improve Android linking 2025-06-08 15:06:19 +02:00
Christoffer Lerno
2ad17a04d4 Improve android detection. 2025-06-08 12:18:55 +02:00
Christoffer Lerno
1617792a35 Fix Android detection. 2025-06-08 11:53:47 +02:00
Christoffer Lerno
c7b3ae0cf9 Fix Android errno 2025-06-08 11:33:28 +02:00
Christoffer Lerno
1dcd40aa5f --lsp sometimes does not emit end tag #2194. 2025-06-08 00:30:11 +02:00
Christoffer Lerno
40554192b1 - Make accepting arguments for main a bit more liberal, accepting main(int argc, ZString* argv)
- Make `$echo` and `@sprintf` correctly stringify compile time initializers and slices.
- Fixes methodsof to pick up all sorts of extension methods. #2192
- Fix regression accidentally allowing `$assert $foo, $bar`
2025-06-08 00:23:04 +02:00
Book-reader
9bc5e259d2 fix types::may_load_atomic with enums 2025-06-07 13:54:27 +02:00
Christoffer Lerno
f66cadccd2 Add printf format to $assert and $error #2183. 2025-06-06 23:50:55 +02:00
Christoffer Lerno
be511b26cd Additional fixes on $define 2025-06-06 20:11:58 +02:00
Christoffer Lerno
4cfa5441d2 Additional fixes on $define 2025-06-06 15:38:46 +02:00
Christoffer Lerno
5e45c34f21 - Deprecate String.is_zstr and String.quick_zstr #2188. 2025-06-06 15:30:46 +02:00
Awang
d7a11903c7 Add external __errno() function for env::ANDROID (#2182) 2025-06-06 14:47:16 +02:00
Christoffer Lerno
b893697a87 Various fixes for $defined 2025-06-06 14:41:20 +02:00
Christoffer Lerno
f2daf2e11e @sprintf macro (based on the $$sprintf builtin) allows compile time format strings #1874. 2025-06-06 03:18:28 +02:00
Christoffer Lerno
9baeca3a8e $eval now also works with @foo, #foo, $Foo and $foo parameters #2114. 2025-06-06 01:23:23 +02:00
Christian Brendlin
ef649050c4 Add support for custom file extensions in project.json targets (Resolves #1315) (#2169)
* [Feat] add support for custom output file extensions in build process

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-06-05 23:04:09 +02:00
Christoffer Lerno
d6d0e08906 Allow inference across && #2172. 2025-06-05 14:20:40 +02:00
Christoffer Lerno
c9d9127da6 Deprecate foo.#bar. 2025-06-05 12:51:35 +02:00
Christoffer Lerno
7f85534414 - Implicitly convert from constant typeid to Type in $Type assignment, and $assignable.
- Make $Type parameters accept constant typeid values.
2025-06-05 00:37:16 +02:00
Christoffer Lerno
ba1332dc2a Splatting const slices would not be const. #2185 2025-06-04 16:09:15 +02:00
Christoffer Lerno
45a0895c39 Fix fmod regression. 2025-06-04 15:25:18 +02:00
Christoffer Lerno
72cc8e430a -0xFF will now be a signed integer.
- `-2147483648`, MIN literals work correctly.
2025-06-04 15:20:49 +02:00
Christoffer Lerno
9645bd3289 - $typefrom now also accepts a constant string, and so works like $evaltype.
- `$evaltype` is deprecated in favour of `$typefrom`.
2025-06-03 14:51:56 +02:00
Christoffer Lerno
8fc01d4e1a Simplify contract macros. 2025-06-02 22:53:06 +02:00
Bram Windey
a48e2274e5 Update options.run_dir from target.run_dir if options.run_dir is null 2025-06-02 14:09:29 +02:00
Christoffer Lerno
786e47408a Update compiler version 2025-06-02 13:40:16 +02:00
Christoffer Lerno
6e348d1e71 Update compiler version 2025-06-02 10:12:04 +02:00
Christoffer Lerno
d697b910ba Removed the naive check for compile time modification, which fixes #1997 but regresses in detection. 2025-06-01 23:50:13 +02:00
Christoffer Lerno
4d848f1707 Incorrect ensure on String.split. 2025-06-01 20:28:32 +02:00
Christoffer Lerno
6377f0573d Typo 2025-06-01 20:06:34 +02:00
Christoffer Lerno
c3d2b2824c Bug using #foo arguments with $defined #2173 2025-05-31 17:35:29 +02:00
Christoffer Lerno
18e408ead4 Fix example. 2025-05-30 19:22:17 +02:00
Christoffer Lerno
08c63108a1 Release candidate 0.7.2 2025-05-30 19:13:19 +02:00
Christoffer Lerno
da25a411f9 Generic faults is disallowed. 2025-05-30 19:12:26 +02:00
Christian Brendlin
e685414829 Fix #1718: Add --header-output option to specify header file directory (#2161)
* Fix #1718: Add --header-output option to specify header file directory

- Add header_out field to BuildOptions struct
- Add header_file_dir field to BuildTarget struct
- Add --header-output command line option parsing with help text
- Modify header_gen() to use configured output directory instead of hardcoded root
- Add default behavior to use build directory when no custom path specified
- Add directory creation for header output paths
- Resolves issue where generated C headers were always output to root directory

* Fix directory creation timing for header output

- Move header output directory creation before header_gen() call
- Ensures custom header output directories are created before files are written
- Fixes issue where --header-output would fail if directory doesn't exist

* Fix style

* Fix Style

* Add to releasenotes.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-05-30 10:19:46 +02:00
BWindey
ae5a74bc41 [Feat] add quiet to project.json settings (#2166)
* Add quiet to BuildTarget struct and set default to false

* Link BuildTarget.quiet with BuildOptions.verbosity_level (like --quiet flag)

* Parse quiet from project.json, and sort the entries alphabetically

* Add changes to release-notes

* Only set options.verbosity_level if it wasn't set on the commandline

* Formatting

* Added small fix.
2025-05-30 10:16:14 +02:00
Christoffer Lerno
76374d31c4 Spelling 2025-05-30 10:08:04 +02:00
Christoffer Lerno
ffd7a5e483 Fix incorrect percentage 2025-05-30 01:47:05 +02:00
BWindey
d143ec227c Libc ioctl extern function (#2162)
* Add barebones extern ioctl() binding in libc
* Update release notes
2025-05-29 23:00:35 +02:00
Christoffer Lerno
f2703508f2 Fixed test. 2025-05-29 21:14:50 +02:00
Christoffer Lerno
bb96dc931e Add deprecation for @param foo "abc". 2025-05-29 00:45:11 +02:00
Christoffer Lerno
a5a2b00ec8 Too strict project view #2163. 2025-05-28 22:44:00 +02:00
Christoffer Lerno
00f1206f3c Compiler didn't check foreach over flexible array member, and folding a flexible array member was allowed #2164. 2025-05-28 22:21:06 +02:00
Christoffer Lerno
349d9ef3cf Allow recursive generic modules. 2025-05-28 15:39:35 +02:00
Christoffer Lerno
9f30b56e13 Deprecate f32, f64 and f128 suffixes. 2025-05-28 13:01:49 +02:00
Christoffer Lerno
83d6b35afe Add d as floating point suffix for double types. 2025-05-28 12:02:24 +02:00
Christoffer Lerno
f4b9f375e0 Add run-dir to project.json 2025-05-27 23:22:32 +02:00
Christoffer Lerno
be3f9007c9 Check pointer/slice/etc on [out] and & params. #2156. 2025-05-27 23:03:43 +02:00
Christian Brendlin
b665e2cbe5 change releasenotes entry to reflect to correct issue (#2159)
I changed the reference #2138 to point to the issue #2012 instead of the pull request.
2025-05-27 14:15:36 +02:00
Christoffer Lerno
0ed68f94cf Update matrix pass. 2025-05-27 13:59:12 +02:00
Christoffer Lerno
966e8107f8 Add $$matrix_mul and $$matrix_transpose builtins. 2025-05-27 00:50:21 +02:00
Book-reader
61a4dcc807 add macro wrappers for $$overflow_add, $$overflow_sub, and $$overflow_mul builtins 2025-05-26 20:58:16 +02:00
Christoffer Lerno
52541a03eb @jump now included in --list-attributes #2155. 2025-05-26 16:18:08 +02:00
Christoffer Lerno
972c84b65b for with incorrect var declaration caused crash #2154. 2025-05-26 15:56:51 +02:00
Christoffer Lerno
f668b96cc9 $$sat_mul was missing. 2025-05-26 12:23:19 +02:00
Christoffer Lerno
9461873b4c Distinct types could not be used with tagof #2152 2025-05-26 00:57:20 +02:00
Christoffer Lerno
8d563eba7a Implicit casting from struct to interface failure for inheriting interfaces #2151. Fix second bug in #2148 2025-05-24 17:10:11 +02:00
Christoffer Lerno
fe98225f0a Remove superfluous cleanup parameter in os::exit and os::fastexit. 2025-05-23 22:04:54 +02:00
Christoffer Lerno
bae3e59217 Add missing @noreturn to os::exit 2025-05-23 21:41:33 +02:00
Christoffer Lerno
b5ddc36d7f Limit vector max size, default is 4096 bits, but may be increased using --max-vector-size. 2025-05-23 21:40:14 +02:00
Christoffer Lerno
c2c0ecded8 - --path does not interact correctly with relative path arguments #2149. 2025-05-23 19:17:04 +02:00
Christoffer Lerno
9d5b31dad5 Missing error on default values for body with default arguments #2148. 2025-05-23 18:57:21 +02:00
Christoffer Lerno
6c0e94cad9 Fix indent 2025-05-23 16:45:57 +02:00
Christian Brendlin
84aee6a25b Feature: Add inherit_stdio Option for SubProcess (#2138)
* add inherit_stdio option
2025-05-22 11:06:23 +02:00
Matthew Brush
71a765c66e Update CODESTYLE.md
Fix a couple typos and wording.
2025-05-22 11:03:50 +02:00
Gregory Oakes
5c3b637cf6 Add Maybe.equals when inner type is equatable. 2025-05-22 00:06:11 +02:00
Christoffer Lerno
bd1de1e7dc &&& was accidentally available as a valid prefix operator. 2025-05-21 23:36:33 +02:00
Christoffer Lerno
3cd2267b0a Update error message. 2025-05-20 23:00:31 +02:00
Christoffer Lerno
7fcc91edc8 Improve error message when encountering recursively defined structs. #2146 2025-05-19 21:36:47 +02:00
Christoffer Lerno
9052f07c19 Empty default case in @jump switch does not fallthrough #2147. 2025-05-19 21:18:23 +02:00
Christoffer Lerno
c7f0d54328 Designated const initializers with {} would overwrite the parent field. 2025-05-18 23:40:52 +02:00
Christoffer Lerno
498803e9ba Error when using named argument on trailing macro body expansion #2139. 2025-05-17 23:50:15 +02:00
Christoffer Lerno
082457c5fb Incorrect parsing of call attributes #2144. 2025-05-17 22:10:03 +02:00
Christoffer Lerno
23897bc9a4 - Incorrect parsing of ad hoc generic types, like Foo{int}**** #2140.
- $define did not correctly handle generic types #2140.
2025-05-17 21:14:10 +02:00
Christoffer Lerno
8ada2a70d9 Using a non-const as the end range for a bitstruct would trigger an assert. 2025-05-17 18:55:58 +02:00
mr6r4y
a91330b7d1 Fix typo causing segmentation fault 2025-05-17 15:41:49 +02:00
Christoffer Lerno
2f3954a7d9 Deprecate SomeFn.params 2025-05-16 21:57:18 +02:00
Christoffer Lerno
b7ae5dce8b Deprecate MyEnum.elements. 2025-05-16 16:12:37 +02:00
Christoffer Lerno
91db6ceeda Defining an enum like ABC = { 1 2 } was accidentally allowed. 2025-05-16 09:56:08 +02:00
Christoffer Lerno
fc2f718d9e Update error message. 2025-05-15 23:34:01 +02:00
Christoffer Lerno
64ef3fc756 Some folding was missing in binary op compile time resolution #2135. 2025-05-15 16:04:55 +02:00
Christoffer Lerno
93dd432b62 Improve error message when using keywords as functions/macros/variables #2133. 2025-05-15 15:27:14 +02:00
Christoffer Lerno
6c822e5aa3 Add math::@ceil() compile time ceil function. #2134 2025-05-15 12:46:46 +02:00
Christoffer Lerno
8c741c617c Variable aliases of aliases would not resolve correctly. #2131
Variable aliases could not be assigned to.
2025-05-15 09:36:16 +02:00
Christoffer Lerno
b83e57b952 Added @rnd() compile time random function (using the $$rnd() builtin). #2078 2025-05-15 00:51:33 +02:00
Christoffer Lerno
24ebe975d8 Allow the right hand side of ||| and &&& be runtime values. 2025-05-14 23:40:36 +02:00
Christoffer Lerno
511ae0da00 Contract on trying to use Object without initializing it. 2025-05-14 23:22:34 +02:00
Christoffer Lerno
36eb650228 Correctly error on @attrdef Foo = ;. 2025-05-14 12:15:48 +02:00
DragonFriend
50b4d7aa35 Add replace and treplace to String (#2127)
* Add replace and treplace functions to String
2025-05-14 11:00:20 +02:00
Christoffer Lerno
abe4727c3a Deprecate uXX and iXX bit suffixes.
Add experimental LL / ULL suffixes for int128 and uint128 literals.
2025-05-13 23:48:59 +02:00
Christoffer Lerno
c528f53d58 - attrdef with any invalid name causes compiler assert #2128. 2025-05-12 01:41:19 +02:00
Christoffer Lerno
83955ea5b5 Add --run-dir, to specify directory for running executable using compile-run and run #2121. 2025-05-12 01:24:51 +02:00
Christoffer Lerno
fc5c70a628 Update links. 2025-05-11 22:46:43 +02:00
Christoffer Lerno
5287640140 Fix link 2025-05-11 22:38:50 +02:00
Christoffer Lerno
634438eb82 Cleanup. 2025-05-08 21:05:43 +02:00
Christoffer Lerno
164c901ae6 More comments on the allocators. 2025-05-07 12:52:19 +02:00
Christoffer Lerno
54e70cae0f Add DateTime + Duration overloads. 2025-05-07 10:49:30 +02:00
Lucian Feroiu
30ec200492 Add support for default Homebrew-installed LLD (#2119) 2025-05-06 22:38:48 +02:00
Christoffer Lerno
584a8a2e60 - Fix regression in Time diff due to operator overloading #2124
- Add `Duration * Int` and `Clock - Clock` overload.
2025-05-06 22:33:39 +02:00
Christoffer Lerno
3f07d1c7b8 Fix No index OOB check for [:^n] #2123 2025-05-06 16:53:14 +02:00
Christoffer Lerno
125436d23e Better default assert messages when no message is specified #2122 2025-05-05 00:01:36 +02:00
Christoffer Lerno
900365c25e Fix stringify for compound initializers #2120. 2025-05-04 15:31:55 +02:00
Christoffer Lerno
d313afa487 Add String.count to count the number of instances of a string. 2025-05-02 21:48:04 +02:00
Christoffer Lerno
a411f20762 Assert when a macro with compile time value is discarded, e.g. foo(); where foo() returns an untyped list. #2117 2025-05-02 21:16:56 +02:00
Christoffer Lerno
8a0907cb70 Add String.tokenize_all to replace the now deprecated String.splitter 2025-05-02 20:51:15 +02:00
Christoffer Lerno
8a09b2e5f7 std::ascii moved into std::core::ascii. Old _m variants are deprecated, as is uint methods. 2025-05-02 18:06:28 +02:00
Christoffer Lerno
bfccc303d1 Added comments. 2025-05-02 13:36:46 +02:00
Christoffer Lerno
0d3299f267 Added String.quick_ztr and String.is_zstr 2025-05-02 13:22:13 +02:00
Christoffer Lerno
11bb8b49da - Assert triggered when casting from int[2] to uint[2] #2115 2025-05-01 18:23:48 +02:00
Christoffer Lerno
f0d2b0eff0 Update links in example code 2025-05-01 02:14:15 +02:00
Christoffer Lerno
005cc08118 0.7.2 bump 2025-04-30 18:03:00 +02:00
Christoffer Lerno
c5494a23ce Update readme 2025-04-30 16:13:59 +02:00
Christoffer Lerno
5dcc67aa1b Release candidate 0.7.1 2025-04-30 14:37:14 +02:00
Christian Buttner
335f53fb64 Rework Win32 mutex, condition variable and once flag (#2111)
* Rework Win32 mutex, condition variable and once flag.
2025-04-29 22:50:01 +02:00
Christoffer Lerno
3636898ac0 Fixed enum regression after 0.7.0 enum change. 2025-04-29 11:53:32 +02:00
Christoffer Lerno
5ba24e05d0 Typo 2025-04-27 14:22:51 +02:00
Christoffer Lerno
0ada5504af Add a file about contributing. 2025-04-27 14:01:22 +02:00
Christoffer Lerno
8ac02a28cc Error message for casting generic to incompatible type does not work properly with nested generics #1953 2025-04-27 00:40:43 +02:00
Christoffer Lerno
246957b8bd Minor refactoring 2025-04-26 23:32:52 +02:00
Christian Buttner
0595270d9a Fix mem::copy_inline compile. 2025-04-25 16:04:41 +02:00
Christoffer Lerno
8b86d1461d "Length mismatch between slices" when there is none #2110 2025-04-25 16:03:36 +02:00
Christoffer Lerno
0129308bf3 c3c build picks first target rather than the first executable #2105. 2025-04-25 15:44:34 +02:00
Christoffer Lerno
05094b4f47 @ensure should be allowed to read "out" variables. #2107 2025-04-25 15:36:07 +02:00
Christoffer Lerno
9a59cd164d Fixed regression slice copy #2106 2025-04-25 15:14:00 +02:00
Christoffer Lerno
3eecaf9e29 Compiler crash when passing an untyped list as an argument to assert #2108. 2025-04-25 15:02:23 +02:00
Christoffer Lerno
8b47673524 Added Enum.lookup and Enum.lookup_field. 2025-04-25 14:44:00 +02:00
Christoffer Lerno
8b29e4780d The %s would not properly print function pointers. 2025-04-25 11:26:28 +02:00
niedlich
fd2a81afb1 Improved CMake integration of LLVM (#2103)
Added the paths for the CMake config files for LLVM 19 and higher to ``CMAKE_PREFIX_PATH``,
so that they will also be searched automatically.
2025-04-23 11:41:26 +02:00
Christoffer Lerno
a0d4df2272 Update README.md
Add showcase link.
2025-04-23 00:52:46 +02:00
Christoffer Lerno
e39c7cae8d Comparing a distinct type with an enum with an inline distinct type failed unexpectedly 2025-04-22 21:51:37 +02:00
Christoffer Lerno
8a2907806b Fixes to tclone and other temp allocations with overaligned data. 2025-04-20 21:30:36 +02:00
Christoffer Lerno
f778e75757 Added missing @clone_aligned and add checks to @tclone 2025-04-20 18:31:52 +02:00
hyperpastel
6ab7953706 Patch false maybe-uninitialized warning 2025-04-20 16:59:52 +02:00
Christoffer Lerno
42e4370994 Fix conditional in slice assign check. 2025-04-18 22:14:33 +02:00
Christoffer Lerno
9a1fdbbca0 Add missing build_options.c commit. 2025-04-18 19:50:04 +02:00
joshringuk@gmail.com
434a0e8e4b array contains 2025-04-18 18:15:01 +02:00
Christoffer Lerno
946c167bf1 Improve error for default args #2096. Deprecated old inference with slice copy. Copying must now ensure a slicing operator at the end of the right hand side: foo[1..2] = bar[..] rather than the old foo[1..2] = bar. The old behaviour can be mostly retained with --use-old-slice-copy). 2025-04-18 17:19:04 +02:00
Christoffer Lerno
ba10c8953d @ensure was not included when the function doesn't return a value #2098. 2025-04-17 20:26:21 +02:00
Christoffer Lerno
72d7813c20 @if was ignored on attrdef, regression 0.7 #2093 2025-04-17 19:07:48 +02:00
Christoffer Lerno
1083de1f81 - Fix broken enum inline -> bool conversions #2094. 2025-04-17 19:00:04 +02:00
Christoffer Lerno
b4b6cba301 - Improved error messages on Foo { 3, abc } #2099. 2025-04-17 18:42:25 +02:00
Christoffer Lerno
3244898610 - @if now does implicit conversion to bool like $if. #2086 2025-04-16 23:49:12 +02:00
Christoffer Lerno
6454856fdb String str = "" is now guaranteed to be null terminated. #2083 2025-04-16 23:19:28 +02:00
Christoffer Lerno
5cf48ad730 Add Ubuntu 22 2025-04-16 20:25:42 +02:00
Boris Barbulovski
b5d0739de0 Add env::ANDROID to std.* 2025-04-16 17:47:49 +02:00
AlexCodesApps
f6e130ad3c Type mismatch fix (#2081)
* Fixed type mismatch in static function 'match_argopt' in file 'src/build/build_options.c',
 where false was returned from the function which has a return type of 'const char *'.
2025-04-16 17:46:54 +02:00
Simone Raimondi
f9e62b80ea Fix for build (#2082) 2025-04-16 17:45:39 +02:00
Christoffer Lerno
debbae594c Remove more Ubuntu 20 2025-04-16 17:11:53 +02:00
Christoffer Lerno
37ffd92f7b - Bug with slice acces as inline struct member #2088. 2025-04-16 17:02:22 +02:00
Christoffer Lerno
a44e932806 ABI bug on x64 Linux / MacOS when passing a union containing a struct of 3 floats. #2087 2025-04-16 15:58:14 +02:00
Christoffer Lerno
668175851b Improved error message #2084 2025-04-16 15:25:05 +02:00
Christoffer Lerno
e7c9ec0938 Added comments. 2025-04-16 01:32:01 +02:00
Christoffer Lerno
d6fa9cd50b Added some comments about the tracking allocator. 2025-04-15 16:03:12 +02:00
Christoffer Lerno
41e173d255 Added some comment about the temp allocator. 2025-04-15 15:53:11 +02:00
Christoffer Lerno
fde2bb2a7e Support @if on locals. 2025-04-15 13:20:10 +02:00
Christoffer Lerno
0a9bb2e8e0 Fix to simple a += b overload fallback. Renaming and reordering in the stdlib. 2025-04-15 12:01:58 +02:00
Christoffer Lerno
b64dcde21d Make aliases able to use @deprecated. Prefer math::I and math::I_F for math::IMAGINARY and math::IMAGINARYF the latter is deprecated. Combination of += and [] overloads now properly handled in most cases. 2025-04-14 20:51:01 +02:00
Christoffer Lerno
eade5fa57a Fix Windows sincos. 2025-04-14 03:36:03 +02:00
Christoffer Lerno
f85198e3ee Added += and related as overloads. Updated tests and docs. Slice2 extracted to its own file. 2025-04-14 00:55:46 +02:00
Christoffer Lerno
dca805bd8a Added tests to sincos. Correctly detect multiple overloads of the same type. Fix regression quaternion overload. Remove "1." style. 2025-04-13 15:46:27 +02:00
Christoffer Lerno
3888fcb182 - Add @operator_r and @operator_s attributes. 2025-04-13 13:43:03 +02:00
Christoffer Lerno
de73265d28 Fix operator overload struct placement. 2025-04-11 21:27:59 +02:00
Christoffer Lerno
cb895754c8 Size to store overload increas for msvc. 2025-04-11 21:17:42 +02:00
Christoffer Lerno
6e42bfef3b Typo 2025-04-11 21:04:46 +02:00
Christoffer Lerno
01357ef6d7 Check before hitting assert. 2025-04-11 21:00:30 +02:00
Christoffer Lerno
89d205258e Fix test. 2025-04-11 19:11:49 +02:00
Christoffer Lerno
0f2d425297 Operator overloading for + - * / % & | ^ << >> ~ == != 2025-04-11 18:46:22 +02:00
Christoffer Lerno
28fc03c376 Do not user finalizer with wasm 2025-04-08 00:03:35 +02:00
Christoffer Lerno
1290906d66 Setup temp allocator by default on Wasm 2025-04-07 23:35:38 +02:00
Christoffer Lerno
25d416aca1 Regression with invalid setup of the WASM temp allocator. 2025-04-07 21:58:21 +02:00
Christoffer Lerno
8cce7f6836 Incorrect rounding at compile time going from double to int. 2025-04-07 02:36:04 +02:00
Christoffer Lerno
4c26adb376 Improved error message when narrowing isn't allowed. 2025-04-07 01:12:23 +02:00
Christoffer Lerno
94b8330ac5 Function @require checks are added to the caller in safe mode. #186 2025-04-06 15:28:10 +02:00
Christoffer Lerno
3cb5df5639 0.7 fixes. Improving the yacc grammar. 2025-04-04 18:14:16 +02:00
Christoffer Lerno
ded5fde2d5 Fix test, fix type name. 2025-04-04 13:48:07 +02:00
Christoffer Lerno
65fb977e89 Clearer errors when using a &ref parameter with type. 2025-04-04 13:21:53 +02:00
Christoffer Lerno
e3f3b6f5f1 Better errors trying to convert an enum to an int and vice versa. Trying to cast an enum to int and back caused the compiler to crash. 2025-04-04 02:38:51 +02:00
Boris Barbulovski
ab4ed9472a Copy paste typo. 2025-04-04 02:29:25 +02:00
Christoffer Lerno
1668999f90 Better errors on some common casting mistakes (pointer->slice, String->ZString, deref pointer->array) #2064. 2025-04-04 00:12:52 +02:00
Christoffer Lerno
e828d9a05a Fix stdlib naming. 2025-04-03 01:47:52 +02:00
Avaxar
47447dc069 Glob crt1.o on Linux depending on architecture 2025-04-03 00:59:51 +02:00
Christoffer Lerno
39a59c929f Add dummy deprecated key. 2025-04-03 00:59:24 +02:00
Christoffer Lerno
f355738dda Project refactoring. Remove deprecated properties. 2025-04-03 00:56:17 +02:00
Christoffer Lerno
87e254e4b1 Added vector hash to release notes and change raylib dependency 2025-03-31 16:42:24 +02:00
Sander van den Bosch
561a683230 Added .hash() functions for vectors (#2043)
* Added .hash() functions for vectors
* Update test to a non-zero sized vector
* Changed vector hash functions to hash the underlying bytes in a char slice, the same approch is used for arrays
* Added test for hashed
* Updated formatting to be consistant with C3 code style
* Formatting, use "self"

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-03-31 16:33:48 +02:00
Alec Larson
63e5aa58c5 Create project_schema.json
By setting the `$schema` field of your `project.json` file to a URL leading to this JSON schema, your IDE may be able to provide auto-completion.
2025-03-31 16:09:52 +02:00
Christoffer Lerno
2be3071bdb 0.7.1 dev 2025-03-31 01:36:58 +02:00
Christoffer Lerno
d3e81b193a Update CI 2025-03-31 01:34:01 +02:00
Christoffer Lerno
586d191585 Fix in stdlib and update readme. 2025-03-30 23:11:29 +02:00
378 changed files with 14233 additions and 4349 deletions

View File

@@ -10,7 +10,7 @@ env:
LLVM_RELEASE_VERSION_WINDOWS: 18
LLVM_RELEASE_VERSION_MAC: 17
LLVM_RELEASE_VERSION_LINUX: 17
LLVM_RELEASE_VERSION_UBUNTU20: 17
LLVM_RELEASE_VERSION_UBUNTU22: 17
LLVM_DEV_VERSION: 21
jobs:
@@ -77,15 +77,15 @@ jobs:
- name: Vendor-fetch
run: |
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55_v7
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55
- name: Try raylib5
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55_v7
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_arkanoid.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_snake.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_tetris.c3
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_arkanoid.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_snake.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_tetris.c3
- name: Compile run unit tests
run: |
@@ -158,7 +158,7 @@ jobs:
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib55_v7
./build/c3c vendor-fetch raylib55
- name: Build testproject lib
run: |
@@ -401,8 +401,8 @@ jobs:
name: c3-linux-${{matrix.build_type}}
path: c3-linux-${{matrix.build_type}}.tar.gz
build-linux-ubuntu20:
runs-on: ubuntu-20.04
build-linux-ubuntu22:
runs-on: ubuntu-22.04
strategy:
# Don't abort runners if a single one fails
fail-fast: false
@@ -507,7 +507,7 @@ jobs:
../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU22
run: |
mkdir c3
cp -r lib c3
@@ -515,15 +515,15 @@ jobs:
cp releasenotes.md c3
cp msvc_build_libraries.py c3
cp build/c3c c3
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz c3
tar czf c3-ubuntu-22-${{matrix.build_type}}.tar.gz c3
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU22
uses: actions/upload-artifact@v4
with:
name: c3-ubuntu-20-${{matrix.build_type}}
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
name: c3-ubuntu-22-${{matrix.build_type}}
path: c3-ubuntu-22-${{matrix.build_type}}.tar.gz
build-with-docker:
runs-on: ubuntu-22.04
strategy:
@@ -642,7 +642,7 @@ jobs:
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib55_v7
./build/c3c vendor-fetch raylib55
- name: Compile and run some examples
run: |
@@ -749,7 +749,7 @@ jobs:
release:
runs-on: ubuntu-22.04
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu20]
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu22]
if: github.ref == 'refs/heads/master'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -769,19 +769,19 @@ 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-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
- run: mv c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
- run: mv c3-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
- run: mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-Debug/c3-macos-debug.zip
- run: gh release delete latest-0.7.0-prerelease --cleanup-tag -y || true
- run: echo "RELEASE_NAME=latest-0.7.0-rc-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
- run: gh release delete latest-prerelease --cleanup-tag -y || true
- run: echo "RELEASE_NAME=latest-prerelease-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
- id: create_release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: latest-0.7.0-prerelease
tag_name: latest-prerelease
name: ${{ env.RELEASE_NAME }}
draft: false
prerelease: true
@@ -790,7 +790,7 @@ jobs:
c3-windows-debug.zip
c3-linux-Release/c3-linux.tar.gz
c3-linux-Debug/c3-linux-debug.tar.gz
c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz
c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
c3-macos-Release/c3-macos.zip
c3-macos-Debug/c3-macos-debug.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,11 +156,26 @@ 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)
find_package(LLD REQUIRED CONFIG)
else()
# Add paths for LLVM CMake files of version 19 and higher as they follow a new installation
# layout and are now in /usr/lib/llvm/*/lib/cmake/llvm/ rather than /usr/lib/cmake/llvm/
#
# Because of CMAKE_FIND_PACKAGE_SORT_ORDER CMAKE_FIND_PACKAGE_SORT_DIRECTION,
# the newest version will always be found first.
c3_print_variables(CMAKE_PREFIX_PATH)
if (DEFINED LLVM_DIR)
message(STATUS "Looking for LLVM CMake files in user-specified directory ${LLVM_DIR}")
else()
file (GLOB LLVM_CMAKE_PATHS "/usr/lib/llvm/*/lib/cmake/llvm/")
list (APPEND CMAKE_PREFIX_PATH ${LLVM_CMAKE_PATHS} "/usr/lib/")
message(STATUS "No LLVM_DIR specified, searching default directories ${CMAKE_PREFIX_PATH}")
endif()
if (NOT C3_LLVM_VERSION STREQUAL "auto")
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
else()
@@ -151,6 +183,10 @@ if(C3_WITH_LLVM)
endif()
endif()
if (EXISTS /opt/homebrew/lib)
list(APPEND LLVM_LIBRARY_DIRS /opt/homebrew/lib)
endif()
if (EXISTS /usr/lib)
# Some systems (such as Alpine Linux) seem to put some of the relevant
# LLVM files in /usr/lib, but this doesn't seem to be included in the
@@ -158,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)
@@ -213,44 +252,35 @@ 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)
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
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)
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
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}
@@ -275,8 +305,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)
@@ -394,6 +424,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)
@@ -485,34 +520,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
@@ -522,13 +550,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)
@@ -539,6 +574,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
@@ -558,3 +599,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

@@ -151,8 +151,9 @@ one with related functions when working on temporary strings.
# C3 Standard library style guide.
When contributing to the standard librairy please to your best to follow the following style requirements
as to ensure a consistent style in the stdlib and also make accepting PRs more quickly.
When contributing to the standard library please try your best to adhere to the
following style requirements to ensure a consistent style in the stdlib and to
facilitate accepting PRs more quickly.
### Braces are placed on the next line
@@ -256,4 +257,4 @@ argument.
## Add tests to your changes
If you add or fix things, then there should always be tests in `test/unit/stdlib` to verify
the functionality.
the functionality.

70
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,70 @@
# How to contribute to C3
The C3 project consists of
1. The C3 language itself.
2. The C3 compiler, called c3c.
3. The C3 standard library
4. Various tools, such as the editor plugins
## 1. How to contribute to the C3 language
The C3 language is essentially the language specification. You can contribute to the language by:
1. Filing enhancement requests for changes to the language.
2. Offering feedback on existing features, on Discord or by filing issues.
3. Help working on the language specification.
4. Help working on the grammar.
## 2. How to contribute to the C3 compiler
The C3 compiler consists for the compiler itself + test suites for testing the compiler.
You can contribute by:
1. File bugs (by far the most important thing).
2. Suggest improved diagnostics / error messages.
3. Refactoring existing code (needs deep understanding of the compiler).
4. Add support for more architectures.
5. Add support for more backends.
## 3. How to contribute to the standard library
The standard library is the library itself + test suites for testing the standard library.
You can contribute by:
1. Filing bugs on the standard library.
2. Write additional unit tests.
3. Suggest new functionality by filing an issue.
4. Work on stdlib additions.
5. Fix bugs in the stdlib
6. Maintain a section of the standard library
### How to work on small stdlib additions
If there is just a matter of adding a function or two to an existing module, a pull request
is sufficient. However, please make sure that:
1. It follows the guidelines for the code to ensure a uniform experience (naming standard, indentation, braces etc).
2. Add a line in the release notes about the change.
3. Make sure it has unit tests.
### How to work on non-trivial additions to the stdlib
Regardless whether an addition is approved for inclusion or not, it needs to incubate:
1. First implement it standalone, showing that its working well and has a solid design. This has the advantage of people being able to contribute or even create competing implementations
2. Once it is considered finished it can be proposed for inclusion.
This will greatly help improving the quality of additions.
Note that any new addition needs a full set of unit tests before being included into the standard library.
### Maintain a part of the standard library
A single maintainer is insufficient for a standard library, instead we need one or more maintainer
for each module. The maintainer(s) will review pull requests and actively work on making the module
pristine with the highest possible quality.
## 4. How to contribute to various tools
In general, file a pull request. Depending on who maintains it, rules may differ.

View File

@@ -8,16 +8,18 @@ for programmers who like C.
Precompiled binaries for the following operating systems are available:
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
- 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).
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
![vkQuake](https://github.com/c3lang/c3c/blob/master/resources/images/vkQuake.png?raw=true)
Thanks to full ABI compatibility with C, it's possible to mix C and C3 in the same project with no effort. As a demonstration, vkQuake was compiled with a small portion of the code converted to C3 and compiled with the c3c compiler. (The fork can be found at https://github.com/c3lang/vkQuake)
Thanks to full ABI compatibility with C, it's possible to mix C and C3 in the same project with no effort. As a demonstration, vkQuake was compiled with a small portion of the code converted to C3 and compiled with the c3c compiler. (The aging fork can be found at https://github.com/c3lang/vkQuake)
A non-curated list of user written projects and other resources can be found [here](https://github.com/c3lang/c3-showcase).
### Design Principles
- Procedural "get things done"-type of language.
@@ -33,7 +35,7 @@ whole new language.
### Example code
The following code shows [generic modules](https://c3-lang.org/references/docs/generics/) (more examples can be found at https://c3-lang.org/references/docs/examples/).
The following code shows [generic modules](https://c3-lang.org/generic-programming/generics/) (more examples can be found at https://c3-lang.org/language-overview/examples/).
```cpp
module stack {Type};
@@ -124,6 +126,7 @@ fn void main()
- New semantic macro system
- Module based name spacing
- Slices
- Operator overloading
- Compile time reflection
- Enhanced compile time execution
- Generics based on generic modules
@@ -138,9 +141,10 @@ fn void main()
### Current status
The current stable version of the compiler is **version 0.6.8**.
The current stable version of the compiler is **version 0.7.3**.
The the next version is 0.7.0 which will be a breaking release.
The upcoming 0.7.x releases will focus on expanding the standard library,
fixing bugs and improving compile time analysis.
Follow the issues [here](https://github.com/c3lang/c3c/issues).
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
@@ -193,29 +197,31 @@ More platforms will be supported in the future.
### Installing
This installs the latest prerelease build, as opposed to the latest released version.
#### Installing on Windows with precompiled binaries
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-windows-debug.zip))
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows-debug.zip))
2. Unzip exe and standard lib.
3. If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows.
4. Run `c3c.exe`.
#### Installing on Debian with precompiled binaries
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-linux-debug.tar.gz))
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux-debug.tar.gz))
2. Unpack executable and standard lib.
3. Run `./c3c`.
#### Installing on Ubuntu with precompiled binaries
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20-debug.tar.gz))
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20-debug.tar.gz))
2. Unpack executable and standard lib.
3. Run `./c3c`.
#### Installing on MacOS with precompiled binaries
1. Make sure you have XCode with command line tools installed.
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-macos-debug.zip))
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos-debug.zip))
3. Unzip executable and standard lib.
4. Run `./c3c`.
@@ -307,17 +313,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
@@ -363,6 +377,23 @@ For a sytem-wide installation, run the following as root: `cmake --install .`
The c3c binary should be created in the build directory. You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
#### Compiling on Arch Linux
1. Install required project dependencies: `sudo pacman -S curl lld llvm-libs clang cmake git libedit llvm`
2. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
- If you only need the latest commit, you may want to make a shallow clone: `git clone https://github.com/c3lang/c3c.git --depth=1`
3. Enter the C3C directory: `cd c3c`
4. Create the CMake build cache:
```bash
cmake -B build \
-D C3_LINK_DYNAMIC=ON \
-D CMAKE_BUILD_TYPE=Release
```
5. Build the project: `make -C build`.
After compilation, the `c3c` binary will be located in the `build` directory. You can test it by compiling an example: `./build/c3c compile resources/examples/ls.c3`.
6. To install the compiler globally: `sudo cmake --install build`
#### Compiling on other Linux / Unix variants
@@ -406,4 +437,4 @@ A special thank you to sponsors [Caleb-o](https://github.com/Caleb-o) and [devda
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=c3lang/c3c&type=Date)](https://www.star-history.com/#c3lang/c3c&Date)
[![Star History Chart](https://api.star-history.com/svg?repos=c3lang/c3c&type=Date)](https://www.star-history.com/#c3lang/c3c&Date)

View File

@@ -1,77 +1,42 @@
<* This module is scheduled for removal, use std::core::ascii *>
module std::ascii;
macro bool in_range_m(c, start, len) => (uint)(c - start) < len;
macro bool is_lower_m(c) => in_range_m(c, 0x61, 26);
macro bool is_upper_m(c) => in_range_m(c, 0x41, 26);
macro bool is_digit_m(c) => in_range_m(c, 0x30, 10);
macro bool is_lower_m(c) => in_range_m(c, 0x61, 26);
macro bool is_upper_m(c) => in_range_m(c, 0x41, 26);
macro bool is_digit_m(c) => in_range_m(c, 0x30, 10);
macro bool is_bdigit_m(c) => in_range_m(c, 0x30, 2);
macro bool is_odigit_m(c) => in_range_m(c, 0x30, 8);
macro bool is_xdigit_m(c) => in_range_m(c | 32, 0x61, 6) || is_digit_m(c);
macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26);
macro bool is_print_m(c) => in_range_m(c, 0x20, 95);
macro bool is_graph_m(c) => in_range_m(c, 0x21, 94);
macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20;
macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c);
macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c);
macro bool is_blank_m(c) => c == 0x20 || c == 0x9;
macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f;
macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26);
macro bool is_print_m(c) => in_range_m(c, 0x20, 95);
macro bool is_graph_m(c) => in_range_m(c, 0x21, 94);
macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20;
macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c);
macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c);
macro bool is_blank_m(c) => c == 0x20 || c == 0x9;
macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f;
macro to_lower_m(c) => is_upper_m(c) ? c + 0x20 : c;
macro to_upper_m(c) => is_lower_m(c) ? c - 0x20 : c;
fn bool in_range(char c, char start, char len) => in_range_m(c, start, len);
fn bool is_lower(char c) => is_lower_m(c);
fn bool is_upper(char c) => is_upper_m(c);
fn bool is_digit(char c) => is_digit_m(c);
fn bool is_bdigit(char c) => is_bdigit_m(c);
fn bool is_odigit(char c) => is_odigit_m(c);
fn bool is_xdigit(char c) => is_xdigit_m(c);
fn bool is_alpha(char c) => is_alpha_m(c);
fn bool is_print(char c) => is_print_m(c);
fn bool is_graph(char c) => is_graph_m(c);
fn bool is_space(char c) => is_space_m(c);
fn bool is_alnum(char c) => is_alnum_m(c);
fn bool is_punct(char c) => is_punct_m(c);
fn bool is_blank(char c) => is_blank_m(c);
fn bool is_cntrl(char c) => is_cntrl_m(c);
fn char to_lower(char c) => (char)to_lower_m(c);
fn char to_upper(char c) => (char)to_upper_m(c);
fn bool char.in_range(char c, char start, char len) => in_range_m(c, start, len);
fn bool char.is_lower(char c) => is_lower_m(c);
fn bool char.is_upper(char c) => is_upper_m(c);
fn bool char.is_digit(char c) => is_digit_m(c);
fn bool char.is_bdigit(char c) => is_bdigit_m(c);
fn bool char.is_odigit(char c) => is_odigit_m(c);
fn bool char.is_xdigit(char c) => is_xdigit_m(c);
fn bool char.is_alpha(char c) => is_alpha_m(c);
fn bool char.is_print(char c) => is_print_m(c);
fn bool char.is_graph(char c) => is_graph_m(c);
fn bool char.is_space(char c) => is_space_m(c);
fn bool char.is_alnum(char c) => is_alnum_m(c);
fn bool char.is_punct(char c) => is_punct_m(c);
fn bool char.is_blank(char c) => is_blank_m(c);
fn bool char.is_cntrl(char c) => is_cntrl_m(c);
fn char char.to_lower(char c) => (char)to_lower_m(c);
fn char char.to_upper(char c) => (char)to_upper_m(c);
<*
@require c.is_xdigit()
*>
fn char char.from_hex(char c) => c.is_digit() ? c - '0' : 10 + (c | 0x20) - 'a';
fn bool uint.in_range(uint c, uint start, uint len) => in_range_m(c, start, len);
fn bool uint.is_lower(uint c) => is_lower_m(c);
fn bool uint.is_upper(uint c) => is_upper_m(c);
fn bool uint.is_digit(uint c) => is_digit_m(c);
fn bool uint.is_bdigit(uint c) => is_bdigit_m(c);
fn bool uint.is_odigit(uint c) => is_odigit_m(c);
fn bool uint.is_xdigit(uint c) => is_xdigit_m(c);
fn bool uint.is_alpha(uint c) => is_alpha_m(c);
fn bool uint.is_print(uint c) => is_print_m(c);
fn bool uint.is_graph(uint c) => is_graph_m(c);
fn bool uint.is_space(uint c) => is_space_m(c);
fn bool uint.is_alnum(uint c) => is_alnum_m(c);
fn bool uint.is_punct(uint c) => is_punct_m(c);
fn bool uint.is_blank(uint c) => is_blank_m(c);
fn bool uint.is_cntrl(uint c) => is_cntrl_m(c);
fn uint uint.to_lower(uint c) => (uint)to_lower_m(c);
fn uint uint.to_upper(uint c) => (uint)to_upper_m(c);
fn bool uint.is_lower(uint c) @deprecated => is_lower_m(c);
fn bool uint.is_upper(uint c) @deprecated => is_upper_m(c);
fn bool uint.is_digit(uint c) @deprecated => is_digit_m(c);
fn bool uint.is_bdigit(uint c) @deprecated => is_bdigit_m(c);
fn bool uint.is_odigit(uint c) @deprecated => is_odigit_m(c);
fn bool uint.is_xdigit(uint c) @deprecated => is_xdigit_m(c);
fn bool uint.is_alpha(uint c) @deprecated => is_alpha_m(c);
fn bool uint.is_print(uint c) @deprecated => is_print_m(c);
fn bool uint.is_graph(uint c) @deprecated => is_graph_m(c);
fn bool uint.is_space(uint c) @deprecated => is_space_m(c);
fn bool uint.is_alnum(uint c) @deprecated => is_alnum_m(c);
fn bool uint.is_punct(uint c) @deprecated => is_punct_m(c);
fn bool uint.is_blank(uint c) @deprecated => is_blank_m(c);
fn bool uint.is_cntrl(uint c) @deprecated => is_cntrl_m(c);
fn uint uint.to_lower(uint c) @deprecated => (uint)to_lower_m(c);
fn uint uint.to_upper(uint c) @deprecated => (uint)to_upper_m(c);

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2023 Eduardo José Gómez Hernández. All rights reserved.
// Copyright (c) 2023-2025 Eduardo José Gómez Hernández. 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::atomic::types{Type};
@@ -175,7 +175,7 @@ macro bool is_native_atomic_type($Type)
$case BOOL:
return true;
$case DISTINCT:
return is_native_atomic_type($typefrom($Type.inner));
return is_native_atomic_type($Type.inner);
$default:
return false;
$endswitch
@@ -183,7 +183,7 @@ macro bool is_native_atomic_type($Type)
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to be added to ptr."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@@ -203,7 +203,7 @@ macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to be subtracted from ptr."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@@ -223,7 +223,7 @@ macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to be multiplied with ptr."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@@ -240,7 +240,7 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
$StorageType* storage_ptr = ($StorageType*)ptr;
@@ -263,7 +263,7 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to divide ptr by."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@@ -280,7 +280,7 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
$StorageType* storage_ptr = ($StorageType*)ptr;
@@ -303,7 +303,7 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to perform a bitwise or with."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@@ -320,7 +320,7 @@ macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to perform a bitwise xor with."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@@ -337,7 +337,7 @@ macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to perform a bitwise and with."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@@ -354,7 +354,7 @@ macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to shift ptr by."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@@ -372,7 +372,7 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
$StorageType* storage_ptr = ($StorageType*)ptr;
@@ -396,7 +396,7 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to shift ptr by."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@@ -414,7 +414,7 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
$StorageType* storage_ptr = ($StorageType*)ptr;
@@ -438,7 +438,7 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@@ -466,7 +466,7 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
}
<*
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"

View File

@@ -7,6 +7,17 @@ import std::io,std::math;
alias AnyPredicate = fn bool(any value);
alias AnyTest = fn bool(any type, any context);
<*
The AnyList contains a heterogenous set of types. Anything placed in the
list will shallowly copied in order to be stored as an `any`. This means
that the list will copy and free its elements.
However, because we're getting `any` values back when we pop, those operations
need to take an allocator, as we can only copy then pop then return the copy.
If we're not doing pop, then things are easier, since we can just hand over
the existing any.
*>
struct AnyList (Printable)
{
usz size;
@@ -17,8 +28,11 @@ struct AnyList (Printable)
<*
Initialize the list. If not initialized then it will use the temp allocator
when something is pushed to it.
@param [&inout] allocator : "The allocator to use"
@param initial_capacity : "The initial capacity to reserve"
@param initial_capacity : "The initial capacity to reserve, defaults to 16"
*>
fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
{
@@ -49,6 +63,361 @@ fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16)
fn bool AnyList.is_initialized(&self) @inline => self.allocator != null;
<*
Push an element on the list by cloning it.
*>
macro void AnyList.push(&self, element)
{
if (!self.allocator) self.allocator = tmem;
self._append(allocator::clone(self.allocator, element));
}
<*
Free a retained element removed using *_retained.
*>
fn void AnyList.free_element(&self, any element) @inline
{
allocator::free(self.allocator, element.ptr);
}
<*
Pop a value who's type is known. If the type is incorrect, this
will still pop the element.
@param $Type : "The type we assume the value has"
@return "The last value as the type given"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
*>
macro AnyList.pop(&self, $Type)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return *anycast(self.entries[--self.size], $Type);
}
<*
Copy the last value, pop it and return the copy of it.
@param [&inout] allocator : "The allocator to use for copying"
@return "A copy of the last value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.copy_pop(&self, Allocator allocator)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return allocator::clone_any(allocator, self.entries[--self.size]);
}
<*
Copy the last value, pop it and return the copy of it.
@return "A temp copy of the last value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.tcopy_pop(&self) => self.copy_pop(tmem);
<*
Pop the last value. It must later be released using `list.free_element()`.
@return "The last value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.pop_retained(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
<*
Remove all elements in the list.
*>
fn void AnyList.clear(&self)
{
for (usz i = 0; i < self.size; i++)
{
self.free_element(self.entries[i]);
}
self.size = 0;
}
<*
Pop a value who's type is known. If the type is incorrect, this
will still pop the element.
@param $Type : "The type we assume the value has"
@return "The first value as the type given"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
*>
macro AnyList.pop_first(&self, $Type)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return *anycast(self.entries[0], $Type);
}
<*
Pop the first value. It must later be released using `list.free_element()`.
@return "The first value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.pop_first_retained(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
<*
Copy the first value, pop it and return the copy of it.
@param [&inout] allocator : "The allocator to use for copying"
@return "A copy of the first value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.copy_pop_first(&self, Allocator allocator)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
defer self.remove_at(0);
return allocator::clone_any(allocator, self.entries[0]);
}
<*
Copy the first value, pop it and return the temp copy of it.
@return "A temp copy of the first value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
<*
Remove the element at the particular index.
@param index : "The index of the element to remove"
@require index < self.size
*>
fn void AnyList.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.free_element(self.entries[index]);
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
<*
Add all the elements in another AnyList.
@param [&in] other_list : "The list to add"
*>
fn void AnyList.add_all(&self, AnyList* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
foreach (value : other_list)
{
self.entries[self.size++] = allocator::clone_any(self.allocator, value);
}
}
<*
Reverse the order of the elements in the list.
*>
fn void AnyList.reverse(&self)
{
if (self.size < 2) return;
usz half = self.size / 2U;
usz end = self.size - 1;
for (usz i = 0; i < half; i++)
{
self.swap(i, end - i);
}
}
<*
Return a view of the data as a slice.
@return "The slice view"
*>
fn any[] AnyList.array_view(&self)
{
return self.entries[:self.size];
}
<*
Push an element to the front of the list.
@param value : "The value to push to the list"
*>
macro void AnyList.push_front(&self, value)
{
self.insert_at(0, value);
}
<*
Insert an element at a particular index.
@param index : "the index where the element should be inserted"
@param type : "the value to insert"
@require index <= self.size : "The index is out of bounds"
*>
macro void AnyList.insert_at(&self, usz index, type)
{
if (index == self.size)
{
self.push(type);
return;
}
any value = allocator::copy(self.allocator, type);
self._insert_at(self, index, value);
}
<*
Remove the last element in the list. The list may not be empty.
@require self.size > 0 : "The list was already empty"
*>
fn void AnyList.remove_last(&self)
{
self.free_element(self.entries[--self.size]);
}
<*
Remove the first element in the list, the list may not be empty.
@require self.size > 0
*>
fn void AnyList.remove_first(&self)
{
self.remove_at(0);
}
<*
Return the first element by value, assuming it is the given type.
@param $Type : "The type of the first element"
@return "The first element"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
*>
macro AnyList.first(&self, $Type)
{
return *anycast(self.first_any(), $Type);
}
<*
Return the first element
@return "The first element"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.first_any(&self) @inline
{
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
}
<*
Return the last element by value, assuming it is the given type.
@param $Type : "The type of the last element"
@return "The last element"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
*>
macro AnyList.last(&self, $Type)
{
return *anycast(self.last_any(), $Type);
}
<*
Return the last element
@return "The last element"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.last_any(&self) @inline
{
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
}
<*
Return whether the list is empty.
@return "True if the list is empty"
*>
fn bool AnyList.is_empty(&self) @inline
{
return !self.size;
}
<*
Return the length of the list.
@return "The number of elements in the list"
*>
fn usz AnyList.len(&self) @operator(len) @inline
{
return self.size;
}
<*
Return an element in the list by value, assuming it is the given type.
@param index : "The index of the element to retrieve"
@param $Type : "The type of the element"
@return "The element at the index"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
@require index < self.size : "Index out of range"
*>
macro AnyList.get(&self, usz index, $Type)
{
return *anycast(self.entries[index], $Type);
}
<*
Return an element in the list.
@param index : "The index of the element to retrieve"
@return "The element at the index"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
@require index < self.size : "Index out of range"
*>
fn any AnyList.get_any(&self, usz index) @inline @operator([])
{
return self.entries[index];
}
<*
Completely free and clear a list.
*>
fn void AnyList.free(&self)
{
if (!self.allocator) return;
self.clear();
allocator::free(self.allocator, self.entries);
self.capacity = 0;
self.entries = null;
}
<*
Swap two elements in a list.
@param i : "Index of one of the elements"
@param j : "Index of the other element"
@require i < self.size : "The first index is out of range"
@require j < self.size : "The second index is out of range"
*>
fn void AnyList.swap(&self, usz i, usz j)
{
any temp = self.entries[i];
self.entries[i] = self.entries[j];
self.entries[j] = temp;
}
<*
Print the list to a formatter.
*>
fn usz? AnyList.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
@@ -70,265 +439,8 @@ fn usz? AnyList.to_format(&self, Formatter* formatter) @dynamic
}
<*
Push an element on the list by cloning it.
*>
macro void AnyList.push(&self, element)
{
if (!self.allocator) self.allocator = tmem;
self.append_internal(allocator::clone(self.allocator, element));
}
Remove any elements matching the predicate.
fn void AnyList.append_internal(&self, any element) @local
{
self.ensure_capacity();
self.entries[self.size++] = element;
}
<*
Free a retained element removed using *_retained.
*>
fn void AnyList.free_element(&self, any element) @inline
{
allocator::free(self.allocator, element.ptr);
}
<*
Pop a value who's type is known. If the type is incorrect, this
will still pop the element.
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
*>
macro AnyList.pop(&self, $Type)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return *anycast(self.entries[--self.size], $Type);
}
<*
Pop the last value and allocate the copy using the given allocator.
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.copy_pop(&self, Allocator allocator)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return allocator::clone_any(allocator, self.entries[--self.size]);
}
<*
Pop the last value and allocate the copy using the temp allocator
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.tcopy_pop(&self) => self.copy_pop(tmem);
<*
Pop the last value. It must later be released using list.free_element()
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.pop_retained(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
fn void AnyList.clear(&self)
{
for (usz i = 0; i < self.size; i++)
{
self.free_element(self.entries[i]);
}
self.size = 0;
}
<*
Same as pop() but pops the first value instead.
*>
macro AnyList.pop_first(&self, $Type)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return *anycast(self.entries[0], $Type);
}
<*
Same as pop_retained() but pops the first value instead.
*>
fn any? AnyList.pop_first_retained(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
<*
Same as copy_pop() but pops the first value instead.
*>
fn any? AnyList.copy_pop_first(&self, Allocator allocator)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
defer self.remove_at(0);
return allocator::clone_any(allocator, self.entries[0]);
}
<*
Same as temp_pop() but pops the first value instead.
*>
fn any? AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
<*
@require index < self.size
*>
fn void AnyList.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.free_element(self.entries[index]);
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
fn void AnyList.add_all(&self, AnyList* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
foreach (value : other_list)
{
self.entries[self.size++] = allocator::clone_any(self.allocator, value);
}
}
<*
Reverse the elements in a list.
*>
fn void AnyList.reverse(&self)
{
if (self.size < 2) return;
usz half = self.size / 2U;
usz end = self.size - 1;
for (usz i = 0; i < half; i++)
{
self.swap(i, end - i);
}
}
fn any[] AnyList.array_view(&self)
{
return self.entries[:self.size];
}
<*
Push an element to the front of the list.
*>
macro void AnyList.push_front(&self, type)
{
self.insert_at(0, type);
}
<*
@require index < self.size
*>
macro void AnyList.insert_at(&self, usz index, type) @local
{
any value = allocator::copy(self.allocator, type);
self.insert_at_internal(self, index, value);
}
<*
@require index < self.size
*>
fn void AnyList.insert_at_internal(&self, usz index, any value) @local
{
self.ensure_capacity();
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.entries[index] = value;
}
<*
@require self.size > 0
*>
fn void AnyList.remove_last(&self)
{
self.free_element(self.entries[--self.size]);
}
<*
@require self.size > 0
*>
fn void AnyList.remove_first(&self)
{
self.remove_at(0);
}
macro AnyList.first(&self, $Type)
{
return *anycast(self.first_any(), $Type);
}
fn any? AnyList.first_any(&self) @inline
{
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
}
macro AnyList.last(&self, $Type)
{
return *anycast(self.last_any(), $Type);
}
fn any? AnyList.last_any(&self) @inline
{
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
}
fn bool AnyList.is_empty(&self) @inline
{
return !self.size;
}
fn usz AnyList.len(&self) @operator(len) @inline
{
return self.size;
}
<*
@require index < self.size : "Index out of range"
*>
macro AnyList.get(&self, usz index, $Type)
{
return *anycast(self.entries[index], $Type);
}
<*
@require index < self.size : "Index out of range"
*>
fn any AnyList.get_any(&self, usz index) @inline
{
return self.entries[index];
}
fn void AnyList.free(&self)
{
if (!self.allocator) return;
self.clear();
allocator::free(self.allocator, self.entries);
self.capacity = 0;
self.entries = null;
}
fn void AnyList.swap(&self, usz i, usz j)
{
any temp = self.entries[i];
self.entries[i] = self.entries[j];
self.entries[j] = temp;
}
<*
@param filter : "The function to determine if it should be removed or not"
@return "the number of deleted elements"
*>
@@ -338,6 +450,8 @@ fn usz AnyList.remove_if(&self, AnyPredicate filter)
}
<*
Retain the elements matching the predicate.
@param selection : "The function to determine if it should be kept or not"
@return "the number of deleted elements"
*>
@@ -346,40 +460,95 @@ fn usz AnyList.retain_if(&self, AnyPredicate selection)
return self._remove_if(selection, true);
}
macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && filter(&self.entries[i - 1])) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$endif
}
return size - self.size;
}
<*
Remove any elements matching the predicate.
@param filter : "The function to determine if it should be removed or not"
@param context : "The context to the function"
@return "the number of deleted elements"
*>
fn usz AnyList.remove_using_test(&self, AnyTest filter, any context)
{
return self._remove_using_test(filter, false, context);
}
fn usz AnyList.retain_using_test(&self, AnyTest filter, any context)
<*
Retain any elements matching the predicate.
@param selection : "The function to determine if it should be retained or not"
@param context : "The context to the function"
@return "the number of deleted elements"
*>
fn usz AnyList.retain_using_test(&self, AnyTest selection, any context)
{
return self._remove_using_test(filter, true, context);
return self._remove_using_test(selection, true, context);
}
<*
Reserve memory so that at least the `min_capacity` exists.
@param min_capacity : "The min capacity to hold"
*>
fn void AnyList.reserve(&self, usz min_capacity)
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = tmem;
min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
self.capacity = min_capacity;
}
<*
Set the element at any index.
@param index : "The index where to set the value."
@param value : "The value to set"
@require index <= self.size : "Index out of range"
*>
macro void AnyList.set(&self, usz index, value)
{
if (index == self.size)
{
self.push(value);
return;
}
self.free_element(self.entries[index]);
self.entries[index] = allocator::copy(self.allocator, value);
}
// -- private
fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;
assert(new_size < usz.max / 2U);
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
while (new_capacity < new_size) new_capacity *= 2U;
self.reserve(new_capacity);
}
fn void AnyList._append(&self, any element) @local
{
self.ensure_capacity();
self.entries[self.size++] = element;
}
<*
@require index < self.size
*>
fn void AnyList._insert_at(&self, usz index, any value) @local
{
self.ensure_capacity();
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.entries[index] = value;
}
macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @local
@@ -408,45 +577,28 @@ macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @
return size - self.size;
}
<*
Reserve at least min_capacity
*>
fn void AnyList.reserve(&self, usz min_capacity)
macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = tmem;
min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
self.capacity = min_capacity;
}
macro any AnyList.@item_at(&self, usz index) @operator([])
{
return self.entries[index];
}
<*
@require index <= self.size : "Index out of range"
*>
macro void AnyList.set(&self, usz index, value)
{
if (index == self.size)
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
self.push(value);
return;
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && filter(&self.entries[i - 1])) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$endif
}
self.free_element(self.entries[index]);
self.entries[index] = allocator::copy(self.allocator, value);
}
fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;
assert(new_size < usz.max / 2U);
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
while (new_capacity < new_size) new_capacity *= 2U;
self.reserve(new_capacity);
return size - self.size;
}

View File

@@ -1,18 +1,22 @@
// Copyright (c) 2023-2025 C3 team. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
<*
@require SIZE > 0
@require SIZE > 0 : "The size of the bitset in bits must be at least 1"
*>
module std::collections::bitset{SIZE};
module std::collections::bitset {SIZE};
alias Type = uint;
const BITS = Type.sizeof * 8;
const BITS = uint.sizeof * 8;
const SZ = (SIZE + BITS - 1) / BITS;
struct BitSet
{
Type[SZ] data;
uint[SZ] data;
}
<*
@return "The number of bits set"
*>
fn usz BitSet.cardinality(&self)
{
usz n;
@@ -24,7 +28,11 @@ fn usz BitSet.cardinality(&self)
}
<*
@require i < SIZE
Set a bit in the bitset.
@param i : "The index to set"
@require i < SIZE : "Index was out of range"
*>
fn void BitSet.set(&self, usz i)
{
@@ -34,7 +42,86 @@ fn void BitSet.set(&self, usz i)
}
<*
@require i < SIZE
Perform xor over all bits, mutating itself
@param set : "The bit set to xor with"
@return "The resulting bit set"
*>
macro BitSet BitSet.xor_self(&self, BitSet set) @operator(^=)
{
foreach (i, &x : self.data) *x ^= set.data[i];
return *self;
}
<*
Perform xor over all bits, returning a new bit set.
@param set : "The bit set to xor with"
@return "The resulting bit set"
*>
fn BitSet BitSet.xor(&self, BitSet set) @operator(^)
{
BitSet new_set @noinit;
foreach (i, x : self.data) new_set.data[i] = x ^ set.data[i];
return new_set;
}
<*
Perform or over all bits, returning a new bit set.
@param set : "The bit set to xor with"
@return "The resulting bit set"
*>
fn BitSet BitSet.or(&self, BitSet set) @operator(|)
{
BitSet new_set @noinit;
foreach (i, x : self.data) new_set.data[i] = x | set.data[i];
return new_set;
}
<*
Perform or over all bits, mutating itself
@param set : "The bit set to xor with"
@return "The resulting bit set"
*>
macro BitSet BitSet.or_self(&self, BitSet set) @operator(|=)
{
foreach (i, &x : self.data) *x |= set.data[i];
return *self;
}
<*
Perform & over all bits, returning a new bit set.
@param set : "The bit set to xor with"
@return "The resulting bit set"
*>
fn BitSet BitSet.and(&self, BitSet set) @operator(&)
{
BitSet new_set @noinit;
foreach (i, x : self.data) new_set.data[i] = x & set.data[i];
return new_set;
}
<*
Perform & over all bits, mutating itself.
@param set : "The bit set to xor with"
@return "The resulting bit set"
*>
macro BitSet BitSet.and_self(&self, BitSet set) @operator(&=)
{
foreach (i, &x : self.data) *x &= set.data[i];
return *self;
}
<*
Unset (clear) a bit in the bitset.
@param i : "The index to set"
@require i < SIZE : "Index was out of range"
*>
fn void BitSet.unset(&self, usz i)
{
@@ -44,7 +131,11 @@ fn void BitSet.unset(&self, usz i)
}
<*
@require i < SIZE
Get a particular bit in the bitset
@param i : "The index of the bit"
@require i < SIZE : "Index was out of range"
*>
fn bool BitSet.get(&self, usz i) @operator([]) @inline
{
@@ -59,7 +150,12 @@ fn usz BitSet.len(&self) @operator(len) @inline
}
<*
@require i < SIZE
Change a particular bit in the bitset
@param i : "The index of the bit"
@param value : "The value to set the bit to"
@require i < SIZE : "Index was out of range"
*>
fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
{

View File

@@ -3,9 +3,10 @@
*>
module std::collections::enummap{Enum, ValueType};
import std::io;
struct EnumMap (Printable)
{
ValueType[Enum.len] values;
ValueType[Enum.values.len] values;
}
fn void EnumMap.init(&self, ValueType init_value)

View File

@@ -8,9 +8,10 @@
module std::collections::enumset{Enum};
import std::io;
alias EnumSetType @private = $typefrom(type_for_enum_elements(Enum.elements));
const ENUM_COUNT @private = Enum.values.len;
alias EnumSetType @private = $typefrom(type_for_enum_elements(ENUM_COUNT));
const IS_CHAR_ARRAY = Enum.elements > 128;
const IS_CHAR_ARRAY = ENUM_COUNT > 128;
typedef EnumSet (Printable) = EnumSetType;
fn void EnumSet.add(&self, Enum v)

View File

@@ -34,3 +34,12 @@ macro Type? Maybe.get(self)
{
return self.has_value ? self.value : NOT_FOUND?;
}
fn bool Maybe.equals(self, Maybe other) @operator(==) @if(types::is_equatable_type(Type))
{
if (self.has_value)
{
return other.has_value && equals(self.value, other.value);
}
return !other.has_value;
}

View File

@@ -186,6 +186,10 @@ fn void Object.set_object(&self, String key, Object* new_object) @private
self.map.set(key, new_object);
}
<*
@require self.allocator != null : "This object is not properly initialized, was it really created using 'new'"
@require !@typeis(value, void*) ||| value == null : "void pointers cannot be stored in an object"
*>
macro Object* Object.object_from_value(&self, value) @private
{
var $Type = $typeof(value);
@@ -201,7 +205,6 @@ macro Object* Object.object_from_value(&self, value) @private
$case $Type.typeid == Object*.typeid:
return value;
$case $Type.typeid == void*.typeid:
if (value != null) return TYPE_MISMATCH?;
return &NULL_OBJECT;
$case $assignable(value, String):
return new_string(value, self.allocator);

View File

@@ -248,7 +248,7 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
}
// write end of stream
output[pos:END_OF_STREAM.len] = END_OF_STREAM;
output[pos:END_OF_STREAM.len] = END_OF_STREAM[..];
pos += END_OF_STREAM.len;
return output[:pos];
@@ -364,7 +364,7 @@ fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels c
}
// draw the pixel
if (channels == RGBA) { image[loc:4] = p.rgba; } else { image[loc:3] = p.rgb; }
if (channels == RGBA) { image[loc:4] = p.rgba[..]; } else { image[loc:3] = p.rgb[..]; }
}
return image;

View File

@@ -1,9 +1,13 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Copyright (c) 2023-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::core::mem::allocator;
import std::math;
// The arena allocator allocates up to its maximum data
// and then fails to allocate more, returning out of memory.
// It supports mark and reset to mark.
struct ArenaAllocator (Allocator)
{
char[] data;
@@ -12,6 +16,8 @@ struct ArenaAllocator (Allocator)
<*
Initialize a memory arena for use using the provided bytes.
@param [inout] data : "The memory to use for the arena."
*>
fn ArenaAllocator* ArenaAllocator.init(&self, char[] data)
{
@@ -20,23 +26,44 @@ fn ArenaAllocator* ArenaAllocator.init(&self, char[] data)
return self;
}
<*
Reset the usage completely.
*>
fn void ArenaAllocator.clear(&self)
{
self.used = 0;
}
struct ArenaAllocatorHeader @local
{
usz size;
char[*] data;
}
<*
Given some memory, create an arena allocator on the stack for it.
@param [inout] bytes : `The bytes to use, may be empty.`
@return `An arena allocator using the bytes`
*>
macro ArenaAllocator* wrap(char[] bytes)
{
return (ArenaAllocator){}.init(bytes);
}
<*
"Mark" the current state of the arena allocator by returning the use count.
@return `The value to pass to 'reset' in order to reset to the current use.`
*>
fn usz ArenaAllocator.mark(&self) => self.used;
<*
Reset to a previous mark.
@param mark : `The previous mark.`
@require mark <= self.used : "Invalid mark - out of range"
*>
fn void ArenaAllocator.reset(&self, usz mark) => self.used = mark;
<*
Implements the Allocator interface method.
@require ptr != null
*>
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
@@ -50,10 +77,10 @@ fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
}
}
fn usz ArenaAllocator.mark(&self) => self.used;
fn void ArenaAllocator.reset(&self, usz mark) => self.used = mark;
<*
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
@@ -77,6 +104,8 @@ fn void*? ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz a
}
<*
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
@@ -111,4 +140,12 @@ fn void*? ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
void* mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
}
// Internal data
struct ArenaAllocatorHeader @local
{
usz size;
char[*] data;
}

View File

@@ -1,12 +1,15 @@
module std::core::mem::allocator;
import std::io, std::math;
struct AllocChunk @local
{
usz size;
char[*] data;
}
<*
The backed arena allocator provides an allocator that will allocate from a pre-allocated chunk of memory
provided by it's backing allocator. The allocator supports mark / reset operations, so it can be used
as a stack (push-pop) allocator. If the initial memory is used up, it will fall back to regular allocations,
that will be safely freed on `reset`.
While this allocator is similar to the dynamic arena, it supports multiple "save points", which the dynamic arena
doesn't.
*>
struct BackedArenaAllocator (Allocator)
{
Allocator backing_allocator;
@@ -16,6 +19,12 @@ struct BackedArenaAllocator (Allocator)
char[*] data;
}
struct AllocChunk @local
{
usz size;
char[*] data;
}
const usz PAGE_IS_ALIGNED @local = (usz)isz.max + 1u;
struct ExtraPage @local

View File

@@ -4,6 +4,17 @@
module std::core::mem::allocator;
import std::math;
<*
The dynamic arena allocator is an arena allocator that can grow by adding additional arena "pages".
It only supports reset, at which point all pages except the first one is released to the backing
allocator.
If you want multiple save points, use the BackedArenaAllocator instead.
The advantage over the BackedArenaAllocator, is that when allocating beyond the first "page", it will
retain the characteristics of an arena allocator (allocating a large piece of memory then handing off
memory from that memory), wheras the BackedArenaAllocator will have heap allocator characteristics.
*>
struct DynamicArenaAllocator (Allocator)
{
Allocator backing_allocator;

View File

@@ -5,6 +5,13 @@
module std::core::mem::allocator;
import std::math;
<*
The SimpleHeapAllocator implements a simple heap allocator on top of an allocator function.
It uses the given allocator function to allocate memory from some source, but never frees it.
This allocator is intended to be used in environments where there isn't any native libc malloc,
and it has to be emulated from a memory region, or wrapping linear memory as is the case for plain WASM.
*>
struct SimpleHeapAllocator (Allocator)
{
MemoryAllocFn alloc_fn;

View File

@@ -1,13 +1,15 @@
// Copyright (c) 2021-2024 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::core::mem::allocator @if(env::LIBC);
import std::io;
import libc;
const LibcAllocator LIBC_ALLOCATOR = {};
<*
The LibcAllocator is a wrapper around malloc to conform to the Allocator interface.
*>
typedef LibcAllocator (Allocator, Printable) = 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");
@@ -123,7 +125,7 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
if (init_type == ZERO)
{
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
return data ?: mem::OUT_OF_MEMORYY?;
return data ?: mem::OUT_OF_MEMORY?;
}
else
{

View File

@@ -1,5 +1,14 @@
module std::core::mem::allocator;
<*
The OnStackAllocator is similar to the ArenaAllocator: it allocates from a chunk of memory
given to it.
The difference is that when it runs out of memory it will go directly to its backing allocator
rather than failing.
It is utilized by the @stack_mem macro as an alternative to the temp allocator.
*>
struct OnStackAllocator (Allocator)
{
Allocator backing_allocator;
@@ -8,7 +17,6 @@ struct OnStackAllocator (Allocator)
OnStackAllocatorExtraChunk* chunk;
}
struct OnStackAllocatorExtraChunk @local
{
bool is_aligned;

View File

@@ -2,12 +2,32 @@ module std::core::mem::allocator;
import std::io, std::math;
import std::core::sanitizer::asan;
struct TempAllocatorChunk @local
{
usz size;
char[*] data;
}
// This implements the temp allocator.
// The temp allocator is a specialized allocator only intended for use where
// the allocation is strictly stack-like.
//
// It is *not* thread-safe: you cannot safely use the
// temp allocator on a thread and pass it to another thread.
//
// Fundamentally the temp allocator is a thread local arena allocator
// but the stack-like behaviour puts additional constraints to it.
//
// It works great for allocating temporary data in a scope then dropping
// that data, however you should not be storing temporary data in globals
// or locals that have a lifetime outside of the current temp allocator scope.
//
// Furthermore, note that the temp allocator is bounded, with additional
// allocations on top of that causing heap allocations. Such heap allocations
// will be cleaned up but is undesirable from a performance standpoint.
//
// If you want customizable behaviour similar to the temp allocator, consider
// the ArenaAllocator, BackedArenaAllocator or the DynamicArenaAllocator.
//
// Experimenting with the temp allocator to make it work outside of its
// standard usage patterns will invariably end in tears and frustrated
// hair pulling.
//
// Use one of the ArenaAllocators instead.
struct TempAllocator (Allocator)
{
@@ -15,14 +35,22 @@ struct TempAllocator (Allocator)
TempAllocatorPage* last_page;
TempAllocator* derived;
bool allocated;
usz reserve_size;
usz realloc_size;
usz min_size;
usz used;
usz capacity;
usz original_capacity;
char[*] data;
}
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
struct TempAllocatorChunk @local
{
usz size;
char[*] data;
}
const usz PAGE_IS_ALIGNED @local = (usz)isz.max + 1u;
struct TempAllocatorPage
{
@@ -37,15 +65,20 @@ macro usz TempAllocatorPage.pagesize(&self) => self.size & ~PAGE_IS_ALIGNED;
macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED;
<*
@require size >= 16
@require size >= 64
@require realloc_size >= 64
@require allocator.type != TempAllocator.typeid : "You may not create a temp allocator with a TempAllocator as the backing allocator."
@require min_size > TempAllocator.sizeof + 64 : "Min size must meaningfully hold the data + some bytes"
*>
fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size)
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)
{
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
temp.last_page = null;
temp.backing_allocator = allocator;
temp.used = 0;
temp.min_size = min_size;
temp.realloc_size = realloc_size;
temp.reserve_size = reserve;
temp.allocated = true;
temp.derived = null;
temp.original_capacity = temp.capacity = size;
@@ -53,19 +86,18 @@ fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size)
}
<*
@require !self.derived
@require min_size > TempAllocator.sizeof + 64 : "Min size must meaningfully hold the data + some bytes"
@require mult > 0 : "The multiple can never be zero"
*>
fn TempAllocator*? TempAllocator.derive_allocator(&self, usz min_size, usz buffer, usz mult)
fn TempAllocator*? TempAllocator.derive_allocator(&self, usz reserve = 0)
{
if (!reserve) reserve = self.reserve_size;
usz remaining = self.capacity - self.used;
void* mem @noinit;
usz size @noinit;
if (min_size + buffer > remaining)
if (self.min_size + reserve > remaining)
{
return self.derived = new_temp_allocator(self.backing_allocator, min_size * mult)!;
return self.derived = new_temp_allocator(self.backing_allocator, self.realloc_size, self.reserve_size, self.min_size, self.realloc_size)!;
}
usz start = mem::aligned_offset(self.used + buffer, mem::DEFAULT_MEM_ALIGNMENT);
usz start = mem::aligned_offset(self.used + reserve, mem::DEFAULT_MEM_ALIGNMENT);
void* ptr = &self.data[start];
TempAllocator* temp = (TempAllocator*)ptr;
$if env::ADDRESS_SANITIZER:
@@ -74,6 +106,9 @@ fn TempAllocator*? TempAllocator.derive_allocator(&self, usz min_size, usz buffe
temp.last_page = null;
temp.backing_allocator = self.backing_allocator;
temp.used = 0;
temp.min_size = self.min_size;
temp.reserve_size = self.reserve_size;
temp.realloc_size = self.realloc_size;
temp.allocated = false;
temp.derived = null;
temp.original_capacity = temp.capacity = self.capacity - start - TempAllocator.sizeof;
@@ -82,6 +117,9 @@ fn TempAllocator*? TempAllocator.derive_allocator(&self, usz min_size, usz buffe
return temp;
}
<*
Reset the entire temp allocator, which will merge all the children into it.
*>
fn void TempAllocator.reset(&self)
{
TempAllocator* child = self.derived;

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 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.
@@ -18,6 +18,10 @@ alias AllocMap = HashMap { uptr, Allocation };
// A simple tracking allocator.
// It tracks allocations using a hash map but
// is not compatible with allocators that uses mark()
//
// It is also embarassingly single-threaded, so
// do not use it to track allocations that cross threads.
struct TrackingAllocator (Allocator)
{
Allocator inner_allocator;

View File

@@ -2,6 +2,25 @@ module std::core::array;
import std::core::array::slice;
<*
Returns true if the array contains at least one element, else false
@param [in] array
@param [in] element
@require @typekind(array) == SLICE || @typekind(array) == ARRAY
@require @typeis(array[0], $typeof(element)) : "array and element must have the same type"
*>
macro bool contains(array, element)
{
foreach (&item : array)
{
if (*item == element) return true;
}
return false;
}
<*
Return the first index of element found in the array, searching from the start.
@param [in] array
@param [in] element
@return "the first index of the element"
@@ -17,6 +36,14 @@ macro index_of(array, element)
}
<*
Slice a 2d array and create a Slice2d from it.
@param array_ptr : "the pointer to create a slice from"
@param x : "The starting position of the slice x, optional"
@param y : "The starting position of the slice y, optional"
@param xlen : "The length of the slice in x, defaults to the length of the array"
@param ylen : "The length of the slice in y, defaults to the length of the array"
@return "A Slice2d from the array"
@require @typekind(array_ptr) == POINTER
@require @typekind(*array_ptr) == VECTOR || @typekind(*array_ptr) == ARRAY
@require @typekind((*array_ptr)[0]) == VECTOR || @typekind((*array_ptr)[0]) == ARRAY
@@ -26,11 +53,13 @@ macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
var $ElementType = $typeof((*array_ptr)[0][0]);
return Slice2d{$ElementType} { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
return (Slice2d{$ElementType}) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
}
<*
Return the first index of element found in the array, searching in reverse from the end.
@param [in] array
@param [in] element
@return "the last index of the element"
@@ -81,97 +110,4 @@ macro concat(Allocator allocator, arr1, arr2) @nodiscard
@require @typeis(arr1[0], $typeof(arr2[0])) : "Arrays must have the same type"
@ensure return.len == arr1.len + arr2.len
*>
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
module std::core::array::slice{Type};
struct Slice2d
{
Type* ptr;
usz inner_len;
usz ystart;
usz ylen;
usz xstart;
usz xlen;
}
fn usz Slice2d.len(&self) @operator(len)
{
return self.ylen;
}
fn usz Slice2d.count(&self)
{
return self.ylen * self.xlen;
}
macro void Slice2d.@each(&self; @body(usz[<2>], Type))
{
foreach (y, line : *self)
{
foreach (x, val : line)
{
@body({ x, y }, val);
}
}
}
macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
{
foreach (y, line : *self)
{
foreach (x, &val : line)
{
@body({ x, y }, val);
}
}
}
<*
@require idy >= 0 && idy < self.ylen
*>
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
{
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
}
macro Type Slice2d.get_coord(self, usz[<2>] coord)
{
return *self.get_coord_ref(coord);
}
macro Type Slice2d.get_xy(self, x, y)
{
return *self.get_xy_ref(x, y);
}
macro Type* Slice2d.get_xy_ref(self, x, y)
{
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
}
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
{
return self.get_xy_ref(coord.x, coord.y);
}
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
{
*self.get_coord_ref(coord) = value;
}
macro void Slice2d.set_xy(self, x, y, Type value)
{
*self.get_xy_ref(x, y) = value;
}
<*
@require y >= 0 && y < self.ylen
@require x >= 0 && x < self.xlen
*>
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
{
if (xlen < 1) xlen = self.xlen + xlen;
if (ylen < 1) ylen = self.ylen + ylen;
return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen };
}
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);

114
lib/std/core/ascii.c3 Normal file
View File

@@ -0,0 +1,114 @@
<*
This module contains utils for handling ASCII characters. They only operate on
characters corresponding to 0-127.
*>
module std::core::ascii;
macro bool @is_lower(c) => ASCII_LOOKUP[c].lower; // Is a-z
macro bool @is_upper(c) => ASCII_LOOKUP[c].upper; // Is A-Z
macro bool @is_digit(c) => ASCII_LOOKUP[c].digit; // Is 0-9
macro bool @is_bdigit(c) => ASCII_LOOKUP[c].bin_digit; // Is 0-1
macro bool @is_odigit(c) => ASCII_LOOKUP[c].oct_digit; // Is 0-7
macro bool @is_xdigit(c) => ASCII_LOOKUP[c].hex_digit; // Is 0-9 or a-f or A-F
macro bool @is_alpha(c) => ASCII_LOOKUP[c].alpha; // Is a-z or A-Z
macro bool @is_print(c) => ASCII_LOOKUP[c].printable; // Is a printable character (space or higher and < 127
macro bool @is_graph(c) => ASCII_LOOKUP[c].graph; // Does it show any graphics (printable but not space)
macro bool @is_space(c) => ASCII_LOOKUP[c].space; // Is it a space character: space, tab, linefeed etc
macro bool @is_alnum(c) => ASCII_LOOKUP[c].alphanum; // Is it alpha or digit
macro bool @is_punct(c) => ASCII_LOOKUP[c].punct; // Is it "graph" but not digit or letter
macro bool @is_blank(c) => ASCII_LOOKUP[c].blank; // Is it a blank space: space or tab
macro bool @is_cntrl(c) => ASCII_LOOKUP[c].control; // Is it a control character: before space or 127
macro char @to_lower(c) => c + TO_LOWER[c]; // Convert A-Z to a-z if found
macro char @to_upper(c) => c - TO_UPPER[c]; // Convert a-z to A-Z if found
fn bool is_lower(char c) => @is_lower(c); // Is a-z
fn bool is_upper(char c) => @is_upper(c); // Is A-Z
fn bool is_digit(char c) => @is_digit(c); // Is 0-9
fn bool is_bdigit(char c) => @is_bdigit(c); // Is 0-1
fn bool is_odigit(char c) => @is_odigit(c); // Is 0-7
fn bool is_xdigit(char c) => @is_xdigit(c); // Is 0-9 or a-f or A-F
fn bool is_alpha(char c) => @is_alpha(c); // Is a-z or A-Z
fn bool is_print(char c) => @is_print(c); // Is a printable character (space or higher and < 127
fn bool is_graph(char c) => @is_graph(c); // Does it show any graphics (printable but not space)
fn bool is_space(char c) => @is_space(c); // Is it a space character: space, tab, linefeed etc
fn bool is_alnum(char c) => @is_alnum(c); // Is it alpha or digit
fn bool is_punct(char c) => @is_punct(c); // Is it "graph" but not digit or letter
fn bool is_blank(char c) => @is_blank(c); // Is it a blank space: space or tab
fn bool is_cntrl(char c) => @is_cntrl(c); // Is it a control character: before space or 127
fn char to_lower(char c) => @to_lower(c); // Convert A-Z to a-z if found
fn char to_upper(char c) => @to_upper(c); // Convert a-z to A-Z if found
// The following methods are macro methods for the same functions
macro bool char.is_lower(char c) => @is_lower(c);
macro bool char.is_upper(char c) => @is_upper(c);
macro bool char.is_digit(char c) => @is_digit(c);
macro bool char.is_bdigit(char c) => @is_bdigit(c);
macro bool char.is_odigit(char c) => @is_odigit(c);
macro bool char.is_xdigit(char c) => @is_xdigit(c);
macro bool char.is_alpha(char c) => @is_alpha(c);
macro bool char.is_print(char c) => @is_print(c);
macro bool char.is_graph(char c) => @is_graph(c);
macro bool char.is_space(char c) => @is_space(c);
macro bool char.is_alnum(char c) => @is_alnum(c);
macro bool char.is_punct(char c) => @is_punct(c);
macro bool char.is_blank(char c) => @is_blank(c);
macro bool char.is_cntrl(char c) => @is_cntrl(c);
macro char char.to_lower(char c) => @to_lower(c);
macro char char.to_upper(char c) => @to_upper(c);
<*
Convert a-f/A-F/0-9 to the appropriate hex value.
@require c.is_xdigit()
@ensure return >= 0 && return <= 15
*>
macro char char.from_hex(char c) => HEX_VALUE[c];
<*
Bitstruct containing the different properties of a character
*>
bitstruct CharType : ushort @private
{
bool lower;
bool upper;
bool digit;
bool bin_digit;
bool hex_digit;
bool oct_digit;
bool alpha;
bool alphanum;
bool space;
bool printable;
bool blank;
bool punct;
bool control;
bool graph;
}
const CharType[256] ASCII_LOOKUP @private = {
[0..31] = { .control },
[9..13] = { .control, .space },
['\t'] = { .control, .space, .blank },
[' '] = { .space, .printable, .blank },
[33..126] = { .printable, .graph, .punct },
['0'..'9'] = { .printable, .graph, .alphanum, .hex_digit, .digit },
['2'..'7'] = { .printable, .graph, .alphanum, .hex_digit, .digit, .oct_digit },
['0'..'1'] = { .printable, .graph, .alphanum, .hex_digit, .digit, .oct_digit, .bin_digit },
['A'..'Z'] = { .printable, .graph, .alphanum, .alpha, .upper },
['A'..'F'] = { .printable, .graph, .alphanum, .alpha, .upper, .hex_digit },
['a'..'z'] = { .printable, .graph, .alphanum, .alpha, .lower },
['a'..'f'] = { .printable, .graph, .alphanum, .alpha, .lower, .hex_digit },
[127] = { .control },
};
const char[256] HEX_VALUE = {
['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4,
['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9,
['A'] = 10, ['B'] = 11, ['C'] = 12, ['D'] = 13, ['E'] = 14,
['F'] = 15, ['a'] = 10, ['b'] = 11, ['c'] = 12, ['d'] = 13,
['e'] = 14, ['f'] = 15
};
const char[256] TO_UPPER @private = { ['a'..'z'] = 'a' - 'A' };
const char[256] TO_LOWER @private = { ['A'..'Z'] = 'a' - 'A' };

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2023 Christoffer Lerno and contributors. All rights reserved.
// Copyright (c) 2023-2025 Christoffer Lerno and contributors. 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::core::bitorder;
@@ -88,33 +88,33 @@ bitstruct UInt128LE : uint128 @littleendian
}
<*
@require is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
@require @is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
@require is_bitorder($Type) : "type must be a bitorder integer"
*>
macro read(bytes, $Type)
{
char[] s;
$switch @typekind(bytes):
$case POINTER:
s = (*bytes)[:$Type.sizeof];
$default:
s = bytes[:$Type.sizeof];
$case POINTER:
s = (*bytes)[:$Type.sizeof];
$default:
s = bytes[:$Type.sizeof];
$endswitch
return bitcast(*(char[$Type.sizeof]*)s.ptr, $Type).val;
}
<*
@require is_arrayptr_or_slice_of_char(bytes) : "argument must be a pointer to an array or a slice of char"
@require @is_arrayptr_or_slice_of_char(bytes) : "argument must be a pointer to an array or a slice of char"
@require is_bitorder($Type) : "type must be a bitorder integer"
*>
macro write(x, bytes, $Type)
{
char[] s;
$switch @typekind(bytes):
$case POINTER:
s = (*bytes)[:$Type.sizeof];
$default:
s = bytes[:$Type.sizeof];
$case POINTER:
s = (*bytes)[:$Type.sizeof];
$default:
s = bytes[:$Type.sizeof];
$endswitch
*($typeof(x)*)s.ptr = bitcast(x, $Type).val;
}
@@ -122,59 +122,63 @@ macro write(x, bytes, $Type)
macro is_bitorder($Type)
{
$switch $Type:
$case UShortLE:
$case ShortLE:
$case UIntLE:
$case IntLE:
$case ULongLE:
$case LongLE:
$case UInt128LE:
$case Int128LE:
$case UShortBE:
$case ShortBE:
$case UIntBE:
$case IntBE:
$case ULongBE:
$case LongBE:
$case UInt128BE:
$case Int128BE:
return true;
$default:
return false;
$case UShortLE:
$case ShortLE:
$case UIntLE:
$case IntLE:
$case ULongLE:
$case LongLE:
$case UInt128LE:
$case Int128LE:
$case UShortBE:
$case ShortBE:
$case UIntBE:
$case IntBE:
$case ULongBE:
$case LongBE:
$case UInt128BE:
$case Int128BE:
return true;
$default:
return false;
$endswitch
}
macro bool is_array_or_slice_of_char(bytes)
macro bool is_array_or_slice_of_char(bytes) @deprecated("Use @is_array_or_slice_of_char")
{
$switch @typekind(bytes):
$case POINTER:
var $Inner = $typefrom($typeof(bytes).inner);
$if $Inner.kindof == ARRAY:
var $Inner2 = $typefrom($Inner.inner);
return $Inner2.typeid == char.typeid;
$endif
$case ARRAY:
$case SLICE:
var $Inner = $typefrom($typeof(bytes).inner);
return $Inner.typeid == char.typeid;
$default:
return false;
return @is_array_or_slice_of_char(bytes);
}
macro bool @is_array_or_slice_of_char(#bytes) @const
{
var $Type = $typeof(#bytes);
$switch $Type.kindof:
$case POINTER:
typeid $inner = $Type.inner;
return $inner.kindof == ARRAY &&& $inner.inner == char.typeid;
$case ARRAY:
$case SLICE:
return $Type.inner == char.typeid;
$default:
return false;
$endswitch
}
macro bool is_arrayptr_or_slice_of_char(bytes)
macro bool is_arrayptr_or_slice_of_char(bytes) @deprecated("Use @is_arrayptr_or_slice_of_char")
{
$switch @typekind(bytes):
$case POINTER:
var $Inner = $typefrom($typeof(bytes).inner);
$if $Inner.kindof == ARRAY:
var $Inner2 = $typefrom($Inner.inner);
return $Inner2.typeid == char.typeid;
$endif
$case SLICE:
var $Inner = $typefrom($typeof(bytes).inner);
return $Inner.typeid == char.typeid;
$default:
return false;
return @is_arrayptr_or_slice_of_char(bytes);
}
macro bool @is_arrayptr_or_slice_of_char(#bytes) @const
{
var $Type = $typeof(#bytes);
$switch $Type.kindof:
$case POINTER:
typeid $inner = $Type.inner;
return $inner.kindof == ARRAY &&& $inner.inner == char.typeid;
$case SLICE:
return $Type.inner == char.typeid;
$default:
return false;
$endswitch
}

View File

@@ -30,6 +30,14 @@ typedef EmptySlot = void*;
macro @is_empty_macro_slot(#arg) @const @builtin => @typeis(#arg, EmptySlot);
macro @is_valid_macro_slot(#arg) @const @builtin => !@typeis(#arg, EmptySlot);
<*
Returns a random value at compile time.
@ensure return >= 0.0 && return < 1.0
@return "A compile time random"
*>
macro @rnd() @const @builtin => $$rnd();
/*
Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
*/
@@ -196,8 +204,9 @@ macro void unreachable(String string = "Unreachable statement reached.", ...) @b
{
$if env::COMPILER_SAFE_MODE:
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
$endif;
$else
$$unreachable();
$endif
}
<*
@@ -276,9 +285,8 @@ macro enum_by_name($Type, String enum_name) @builtin
@ensure @typeis(return, $Type)
@return? NOT_FOUND
*>
macro @enum_from_value($Type, #value, value) @builtin
macro @enum_from_value($Type, #value, value) @builtin @deprecated("Use Enum.lookup_field and Enum.lookup")
{
usz elements = $Type.elements;
foreach (e : $Type.values)
{
if (e.#value == value) return e;
@@ -431,22 +439,63 @@ macro @generic_hash(value)
return h;
}
macro uint int.hash(int i) => @generic_hash(i);
macro uint uint.hash(uint i) => @generic_hash(i);
macro uint short.hash(short s) => @generic_hash(s);
macro uint ushort.hash(ushort s) => @generic_hash(s);
macro uint char.hash(char c) => @generic_hash(c);
macro uint ichar.hash(ichar c) => @generic_hash(c);
macro uint long.hash(long i) => @generic_hash(i);
macro uint ulong.hash(ulong i) => @generic_hash(i);
macro uint int128.hash(int128 i) => @generic_hash(i);
macro uint uint128.hash(uint128 i) => @generic_hash(i);
macro uint bool.hash(bool b) => @generic_hash(b);
macro uint int128.hash(self) => @generic_hash(self);
macro uint uint128.hash(self) => @generic_hash(self);
macro uint long.hash(self) => @generic_hash(self);
macro uint ulong.hash(self) => @generic_hash(self);
macro uint int.hash(self) => @generic_hash(self);
macro uint uint.hash(self) => @generic_hash(self);
macro uint short.hash(self) => @generic_hash(self);
macro uint ushort.hash(self) => @generic_hash(self);
macro uint ichar.hash(self) => @generic_hash(self);
macro uint char.hash(self) => @generic_hash(self);
macro uint bool.hash(self) => @generic_hash(self);
macro uint int128[*].hash(&self) => hash_array(self);
macro uint uint128[*].hash(&self) => hash_array(self);
macro uint long[*].hash(&self) => hash_array(self);
macro uint ulong[*].hash(&self) => hash_array(self);
macro uint int[*].hash(&self) => hash_array(self);
macro uint uint[*].hash(&self) => hash_array(self);
macro uint short[*].hash(&self) => hash_array(self);
macro uint ushort[*].hash(&self) => hash_array(self);
macro uint char[*].hash(&self) => hash_array(self);
macro uint ichar[*].hash(&self) => hash_array(self);
macro uint bool[*].hash(&self) => hash_array(self);
macro uint int128[<*>].hash(self) => hash_vec(self);
macro uint uint128[<*>].hash(self) => hash_vec(self);
macro uint long[<*>].hash(self) => hash_vec(self);
macro uint ulong[<*>].hash(self) => hash_vec(self);
macro uint int[<*>].hash(self) => hash_vec(self);
macro uint uint[<*>].hash(self) => hash_vec(self);
macro uint short[<*>].hash(self) => hash_vec(self);
macro uint ushort[<*>].hash(self) => hash_vec(self);
macro uint char[<*>].hash(self) => hash_vec(self);
macro uint ichar[<*>].hash(self) => hash_vec(self);
macro uint bool[<*>].hash(self) => hash_vec(self);
macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t));
macro uint String.hash(String c) => (uint)fnv32a::hash(c);
macro uint char[].hash(char[] c) => (uint)fnv32a::hash(c);
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
<*
@require @typekind(array_ptr) == POINTER &&& @typekind(*array_ptr) == ARRAY
*>
macro uint hash_array(array_ptr) @local
{
return (uint)fnv32a::hash(((char*)array_ptr)[:$sizeof(*array_ptr)]);
}
<*
@require @typekind(vec) == VECTOR
*>
macro uint hash_vec(vec) @local
{
return (uint)fnv32a::hash(((char*)&&vec)[:$sizeof(vec.len * $typeof(vec).inner.sizeof)]);
}
const MAX_FRAMEADDRESS = 128;
<*
@@ -731,7 +780,7 @@ macro void* get_returnaddress(int n)
}
}
module std::core::builtin @if((env::LINUX || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
module std::core::builtin @if((env::LINUX || env::ANDROID || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
import libc, std::io;
fn void sig_panic(String message)

View File

@@ -1,12 +1,20 @@
module std::core::dstring;
import std::io;
<*
The DString offers a dynamic string builder.
*>
typedef DString (OutStream) = DStringOpaque*;
typedef DStringOpaque = void;
const usz MIN_CAPACITY @private = 16;
<*
Initialize the DString with a particular allocator.
@param [&inout] allocator : "The allocator to use"
@param capacity : "Starting capacity, defaults to MIN_CAPACITY and cannot be smaller"
@return "Return the DString itself"
@require !self.data() : "String already initialized"
*>
fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
@@ -20,6 +28,11 @@ fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
}
<*
Initialize the DString with the temp allocator. Note that if the dstring is never
initialized, this is the allocator it will default to.
@param capacity : "Starting capacity, defaults to MIN_CAPACITY and cannot be smaller"
@return "Return the DString itself"
@require !self.data() : "String already initialized"
*>
fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY)
@@ -99,7 +112,7 @@ fn void DString.replace(&self, String needle, String replacement)
};
}
fn DString DString.concat(self, Allocator allocator, DString b)
fn DString DString.concat(self, Allocator allocator, DString b) @nodiscard
{
DString string;
string.init(allocator, self.len() + b.len());
@@ -220,7 +233,7 @@ fn usz DString.append_char32(&self, Char32 c)
fn DString DString.tcopy(&self) => self.copy(tmem);
fn DString DString.copy(self, Allocator allocator)
fn DString DString.copy(self, Allocator allocator) @nodiscard
{
if (!self) return new(allocator);
StringData* data = self.data();
@@ -229,7 +242,7 @@ fn DString DString.copy(self, Allocator allocator)
return new_string;
}
fn ZString DString.copy_zstr(self, Allocator allocator)
fn ZString DString.copy_zstr(self, Allocator allocator) @nodiscard
{
usz str_len = self.len();
if (!str_len)
@@ -243,12 +256,12 @@ fn ZString DString.copy_zstr(self, Allocator allocator)
return (ZString)zstr;
}
fn String DString.copy_str(self, Allocator allocator)
fn String DString.copy_str(self, Allocator allocator) @nodiscard
{
return (String)self.copy_zstr(allocator)[:self.len()];
}
fn String DString.tcopy_str(self) => self.copy_str(tmem) @inline;
fn String DString.tcopy_str(self) @nodiscard => self.copy_str(tmem) @inline;
fn bool DString.equals(self, DString other_string)
{
@@ -558,7 +571,7 @@ fn usz? DString.appendfn(&self, String format, args...) @maydiscard
};
}
fn DString join(Allocator allocator, String[] s, String joiner)
fn DString join(Allocator allocator, String[] s, String joiner) @nodiscard
{
if (!s.len) return new(allocator);
usz total_size = joiner.len * s.len;
@@ -568,10 +581,10 @@ fn DString join(Allocator allocator, String[] s, String joiner)
}
DString res = new_with_capacity(allocator, total_size);
res.append(s[0]);
foreach (String* &str : s[1..])
foreach (String str : s[1..])
{
res.append(joiner);
res.append(*str);
res.append(str);
}
return res;
}

View File

@@ -120,6 +120,7 @@ const String COMPILER_BUILD_HASH = $$BUILD_HASH;
const String COMPILER_BUILD_DATE = $$BUILD_DATE;
const OsType OS_TYPE = OsType.from_ordinal($$OS_TYPE);
const ArchType ARCH_TYPE = ArchType.from_ordinal($$ARCH_TYPE);
const usz MAX_VECTOR_SIZE = $$MAX_VECTOR_SIZE;
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
@@ -158,6 +159,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;
macro bool os_is_darwin() @const
{

View File

@@ -342,7 +342,7 @@ macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_alig
@param $src_align : "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile : "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@require src != null || len == 0 : "Copying a null with non-zero length is invalid"
@require src != null || $len == 0 : "Copying a null with non-zero length is invalid"
@require $len == 0 || dst + $len <= src || src + $len <= dst : "Ranges may not overlap"
*>
macro void copy_inline(void* dst, void* src, usz $len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
@@ -576,11 +576,20 @@ fn void temp_pop(PoolState old_state)
allocator::pop_pool(old_state) @inline;
}
macro void @pool_init(Allocator allocator, usz pool_size, usz buffer_size; @body) @builtin
<*
@require pool_size >= 64
@require realloc_size >= 64
@require allocator.type != TempAllocator.typeid : "You may not create a temp allocator with a TempAllocator as the backing allocator."
@require min_size > TempAllocator.sizeof + 64 : "Min size must meaningfully hold the data + some bytes"
*>
macro void @pool_init(Allocator allocator, usz pool_size,
usz reserve_size = allocator::temp_allocator_reserve_size,
usz min_size = allocator::temp_allocator_min_size,
usz realloc_size = allocator::temp_allocator_realloc_size; @body) @builtin
{
Allocator current = allocator::current_temp;
TempAllocator* top = allocator::top_temp;
allocator::create_temp_allocator(allocator, pool_size, buffer_size);
allocator::create_temp_allocator(allocator, pool_size, reserve_size, min_size, realloc_size);
defer
{
allocator::destroy_temp_allocators();
@@ -589,9 +598,19 @@ macro void @pool_init(Allocator allocator, usz pool_size, usz buffer_size; @body
}
@body();
}
macro void @pool(;@body) @builtin
<*
Create a new temporary allocator.
The `reserve` parameter allows you to determine how many bytes should be reserved for
allocations on the current temporary allocator, if allocations are made inside of the pool scope.
It is made available for optimization, and can usually be ignored.
@param reserve : "The amount of bytes to reserve for out-of-order allocations, 0 gives the default."
*>
macro void @pool(usz reserve = 0; @body) @builtin
{
PoolState state = allocator::push_pool() @inline;
PoolState state = allocator::push_pool(reserve) @inline;
defer
{
allocator::pop_pool(state) @inline;
@@ -627,14 +646,36 @@ macro TrackingEnv* get_tracking_env()
$endif
}
<*
@param value : "The value to clone"
@return "A pointer to the cloned value"
@require $alignof(value) <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'clone_aligned' instead"
*>
macro @clone(value) @builtin @nodiscard
{
return allocator::clone(mem, value);
}
<*
@param value : "The value to clone"
@return "A pointer to the cloned value, which must be released using free_aligned"
*>
macro @clone_aligned(value) @builtin @nodiscard
{
return allocator::clone_aligned(mem, value);
}
<*
@param value : "The value to clone"
@return "A pointer to the cloned value"
*>
macro @tclone(value) @builtin @nodiscard
{
return tnew($typeof(value), value);
$if $alignof(value) <= mem::DEFAULT_MEM_ALIGNMENT:
return tnew($typeof(value), value);
$else
return allocator::clone_aligned(tmem, value);
$endif
}
fn void* malloc(usz size) @builtin @inline @nodiscard
@@ -738,9 +779,9 @@ macro alloc_aligned($Type) @nodiscard
macro tnew($Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)tcalloc($Type.sizeof) @inline;
return ($Type*)tcalloc($Type.sizeof, $Type.alignof) @inline;
$else
$Type* val = tmalloc($Type.sizeof) @inline;
$Type* val = tmalloc($Type.sizeof, $Type.alignof) @inline;
*val = $vaexpr[0];
return val;
$endif
@@ -753,9 +794,9 @@ macro tnew($Type, ...) @nodiscard
macro temp_with_padding($Type, usz padding, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)tcalloc($Type.sizeof + padding) @inline;
return ($Type*)tcalloc($Type.sizeof + padding, $Type.alignof) @inline;
$else
$Type* val = tmalloc($Type.sizeof + padding) @inline;
$Type* val = tmalloc($Type.sizeof + padding, $Type.alignof) @inline;
*val = $vaexpr[0];
return val;
$endif
@@ -763,12 +804,12 @@ macro temp_with_padding($Type, usz padding, ...) @nodiscard
macro talloc($Type) @nodiscard
{
return tmalloc($Type.sizeof);
return tmalloc($Type.sizeof, $Type.alignof);
}
macro talloc_with_padding($Type, usz padding) @nodiscard
{
return tmalloc($Type.sizeof + padding);
return tmalloc($Type.sizeof + padding, $Type.alignof);
}
<*

View File

@@ -1,6 +1,18 @@
module std::core::mem::allocator;
import std::math;
// C3 has multiple different allocators available:
//
// Name Arena Uses buffer OOM Fallback? Mark? Reset?
// ArenaAllocator Yes Yes No Yes Yes
// BackedArenaAllocator Yes No Yes Yes Yes
// DynamicArenaAllocator Yes No Yes No Yes
// HeapAllocator No No No No No *Note: Not for normal use
// LibcAllocator No No No No No *Note: Wraps malloc
// 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
const DEFAULT_SIZE_PREFIX = usz.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
@@ -20,13 +32,18 @@ enum AllocInitType
interface Allocator
{
<*
Acquire memory from the allocator, with the given alignment and initialization type.
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
@require size > 0
@require size > 0 : "The size must be 1 or more"
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? acquire(usz size, AllocInitType init_type, usz alignment = 0);
<*
Resize acquired memory from the allocator, with the given new size and alignment.
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
@require ptr != null
@@ -34,8 +51,11 @@ interface Allocator
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? resize(void* ptr, usz new_size, usz alignment = 0);
<*
@require ptr != null
Release memory acquired using `acquire` or `resize`.
@require ptr != null : "Empty pointers should never be released"
*>
fn void release(void* ptr, bool aligned);
}
@@ -283,11 +303,31 @@ macro alloc_array_try(Allocator allocator, $Type, usz elements) @nodiscard
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
}
<*
Clone a value.
@param [&inout] allocator : "The allocator to use to clone"
@param value : "The value to clone"
@return "A pointer to the cloned value"
@require $alignof(value) <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'clone_aligned' instead"
*>
macro clone(Allocator allocator, value) @nodiscard
{
return new(allocator, $typeof(value), value);
}
<*
Clone overaligned values. Must be released using free_aligned.
@param [&inout] allocator : "The allocator to use to clone"
@param value : "The value to clone"
@return "A pointer to the cloned value"
*>
macro clone_aligned(Allocator allocator, value) @nodiscard
{
return new_aligned(allocator, $typeof(value), value)!!;
}
fn any clone_any(Allocator allocator, any value) @nodiscard
{
usz size = value.type.sizeof;
@@ -367,13 +407,13 @@ tlocal TempAllocator* top_temp;
tlocal bool auto_create_temp = false;
usz temp_allocator_min_size = temp_allocator_default_min_size();
usz temp_allocator_buffer_size = temp_allocator_default_buffer_size();
usz temp_allocator_new_mult = 4;
usz temp_allocator_reserve_size = temp_allocator_default_reserve_size();
usz temp_allocator_realloc_size = temp_allocator_default_min_size() * 4;
fn PoolState push_pool()
fn PoolState push_pool(usz reserve = 0)
{
Allocator old = top_temp ? current_temp : create_temp_allocator_on_demand();
current_temp = ((TempAllocator*)old).derive_allocator(temp_allocator_min_size, temp_allocator_buffer_size, temp_allocator_new_mult)!!;
current_temp = ((TempAllocator*)old).derive_allocator(reserve)!!;
return (PoolState)old.ptr;
}
@@ -413,7 +453,7 @@ macro usz temp_allocator_default_min_size() @local
$endswitch
}
macro usz temp_allocator_default_buffer_size() @local
macro usz temp_allocator_default_reserve_size() @local
{
$switch env::MEMORY_ENV:
$case NORMAL: return 1024;
@@ -435,14 +475,15 @@ fn Allocator create_temp_allocator_on_demand() @private
auto_create_temp = true;
abort("Use '@pool_init()' to enable the temp allocator on a new thread. A temp allocator is only implicitly created on the main thread.");
}
return create_temp_allocator(base_allocator(), temp_allocator_size());
return create_temp_allocator(temp_base_allocator, temp_allocator_size(), temp_allocator_reserve_size, temp_allocator_min_size, temp_allocator_realloc_size);
}
<*
@require !top_temp : "This should never be called when temp already exists"
*>
fn Allocator create_temp_allocator(Allocator allocator, usz size, usz buffer = temp_allocator_default_buffer_size()) @private
fn Allocator create_temp_allocator(Allocator allocator, usz size, usz reserve, usz min_size, usz realloc_size) @private
{
return current_temp = top_temp = allocator::new_temp_allocator(allocator, size)!!;
return current_temp = top_temp = allocator::new_temp_allocator(allocator, size, reserve, min_size, realloc_size)!!;
}
macro Allocator temp()
@@ -452,7 +493,7 @@ macro Allocator temp()
alias tmem @builtin = current_temp;
fn void allow_implicit_temp_allocator_on_load_thread() @init(1) @local @if(env::LIBC)
fn void allow_implicit_temp_allocator_on_load_thread() @init(1) @local @if(env::LIBC || env::WASM_NOLIBC)
{
auto_create_temp = true;
}

View File

@@ -143,7 +143,7 @@ uint128 x86_features;
fn void add_feature_if_bit(X86Feature feature, uint register, int bit)
{
if (register & 1U << bit) x86_features |= 1u128 << feature.ordinal;
if (register & 1U << bit) x86_features |= 1ULL << feature.ordinal;
}
fn void x86_initialize_cpu_features()

View File

@@ -22,6 +22,22 @@ struct SliceRaw
usz len;
}
macro @enum_lookup($Type, #value, value)
{
$foreach $val : $Type.values:
if ($val.#value == value) return $val;
$endforeach
return NOT_FOUND?;
}
macro @enum_lookup_new($Type, $name, value)
{
$foreach $val : $Type.values:
if ($val.$eval($name) == value) return $val;
$endforeach
return NOT_FOUND?;
}
module std::core::runtime @if(WASM_NOLIBC);

169
lib/std/core/slice2d.c3 Normal file
View File

@@ -0,0 +1,169 @@
module std::core::array::slice {Type};
<*
A slice2d allows slicing an array like int[10][10] into an arbitrary "int[][]"-like counterpart
Typically you'd use array::slice2d(...) to create one.
*>
struct Slice2d
{
Type* ptr;
usz inner_len;
usz ystart;
usz ylen;
usz xstart;
usz xlen;
}
<*
@return `The length of the "outer" slice`
*>
fn usz Slice2d.len(&self) @operator(len)
{
return self.ylen;
}
<*
@return `The total number of elements.`
*>
fn usz Slice2d.count(&self)
{
return self.ylen * self.xlen;
}
<*
Step through each element of the slice.
*>
macro void Slice2d.@each(&self; @body(usz[<2>], Type))
{
foreach (y, line : *self)
{
foreach (x, val : line)
{
@body({ x, y }, val);
}
}
}
<*
Step through each element of the slice *by reference*
*>
macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
{
foreach (y, line : *self)
{
foreach (x, &val : line)
{
@body({ x, y }, val);
}
}
}
<*
Return a row as a slice.
@param idy : "The row to return"
@return "The slice for the particular row"
@require idy >= 0 && idy < self.ylen
*>
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
{
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
}
<*
Get the value at a particular x/y position in the slice.
@param coord : "The xy coordinate"
@return "The value at that coordinate"
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
*>
macro Type Slice2d.get_coord(self, usz[<2>] coord)
{
return *self.get_coord_ref(coord);
}
<*
Get a pointer to the value at a particular x/y position in the slice.
@param coord : "The xy coordinate"
@return "A pointer to the value at that coordinate"
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
*>
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
{
return self.get_xy_ref(coord.x, coord.y);
}
<*
Get the value at a particular x/y position in the slice.
@param x : "The x coordinate"
@param y : "The x coordinate"
@return "The value at that coordinate"
@require y >= 0 && y < self.ylen : "y value out of range"
@require x >= 0 && x < self.xlen : "x value out of range"
*>
macro Type Slice2d.get_xy(self, x, y)
{
return *self.get_xy_ref(x, y);
}
<*
Get the value at a particular x/y position in the slice by reference.
@param x : "The x coordinate"
@param y : "The y coordinate"
@return "A pointer to the value at that coordinate"
@require y >= 0 && y < self.ylen : "y value out of range"
@require x >= 0 && x < self.xlen : "x value out of range"
*>
macro Type* Slice2d.get_xy_ref(self, x, y)
{
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
}
<*
Set the ´value at a particular x/y position in the slice.
@param coord : "The xy coordinate"
@param value : "The new value"
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
*>
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
{
*self.get_coord_ref(coord) = value;
}
<*
Set the value at a particular x/y position in the slice.
@param x : "The x coordinate"
@param y : "The y coordinate"
@param value : "The new value"
@require y >= 0 && y < self.ylen : "y value out of range"
@require x >= 0 && x < self.xlen : "x value out of range"
*>
macro void Slice2d.set_xy(self, x, y, Type value)
{
*self.get_xy_ref(x, y) = value;
}
<*
Reslice a slice2d returning a new slice.
@param x : "The starting x"
@param xlen : "The length along x"
@param y : "The starting y"
@param ylen : "The length along y"
@require y >= 0 && y < self.ylen
@require x >= 0 && x < self.xlen
*>
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
{
if (xlen < 1) xlen = self.xlen + xlen;
if (ylen < 1) ylen = self.ylen + ylen;
return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen };
}

View File

@@ -1,46 +1,87 @@
module std::core::string;
import std::ascii;
import std::io;
typedef String @if(!$defined(String)) = inline char[];
<*
ZString is a pointer to a zero terminated array of chars.
Use ZString when you need to interop with C zero terminated strings.
*>
typedef ZString = inline char*;
<*
WString is a pointer to a zero terminated array of Char16.
Depending on the platform, this may or may not correspond to wchar_t.
For Windows, wchar_t is generally 16 bits, on MacOS it is 32 bits.
However, for both MacOS and Linux, regular C strings (ZString)
will be UTF-8 encoded, so there is no need to use the wchar_t versions
of functions outside of encoding functions.
*>
typedef WString = inline Char16*;
<*
Char32 is a UTF32 codepoint
*>
alias Char32 = uint;
<*
Char16 is a UTF16 "character"
*>
alias Char16 = ushort;
<*
Common faults used with strings
*>
faultdef INVALID_UTF8, INVALID_UTF16, CONVERSION_FAILED,
EMPTY_STRING, NEGATIVE_VALUE, MALFORMED_INTEGER,
INTEGER_OVERFLOW, MALFORMED_FLOAT, FLOAT_OUT_OF_RANGE;
const uint SURROGATE_OFFSET @private = 0x10000;
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
const uint SURROGATE_MASK @private = 0xFC00;
const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF;
const uint SURROGATE_BITS @private = 10;
const uint SURROGATE_LOW_VALUE @private = 0xDC00;
const uint SURROGATE_HIGH_VALUE @private = 0xD800;
<*
Create a pointer to an UTF32 encoded string at compile time.
@param $string : "The string to encode"
*>
macro Char32* @wstring32(String $string) @builtin
{
return (Char32*)&&$$wstr32($string);
}
<*
Create a slice of an UTF32 encoded string at compile time.
@param $string : "The string to encode"
*>
macro Char32[] @char32(String $string) @builtin
{
return $$wstr32($string)[..^2];
}
<*
Create a WString (an UTF16 encoded string) at compile time.
@param $string : "The string to encode"
*>
macro WString @wstring(String $string) @builtin
{
return (WString)&&$$wstr16($string);
}
<*
Create a slice of an UTF32 encoded string at compile time.
@param $string : "The string to encode"
*>
macro Char16[] @char16(String $string) @builtin
{
return $$wstr16($string)[..^2];
}
macro String @sprintf(String $format, ...) @builtin @const
{
return $$sprintf($format, $vasplat);
}
<*
Return a temporary ZString created using the formatting function.
@@ -117,6 +158,39 @@ fn String join(Allocator allocator, String[] s, String joiner)
};
}
<*
Replace all instances of one substring with a different string.
@param [in] self
@param [in] needle : `The string to be replaced`
@param [in] new_str : `The replacement string`
@param [&inout] allocator : `The allocator to use for the String`
@return "The new string with the elements replaced"
*>
fn String String.replace(self, Allocator allocator, String needle, String new_str) @nodiscard
{
@pool()
{
String[] split = self.tsplit(needle);
return dstring::join(tmem, split, new_str).copy_str(mem);
};
}
<*
Replace all instances of one substring with a different string, allocating the new string on the temp allocator.
@param [in] self
@param [in] needle : `The string to be replaced`
@param [in] new_str : `The replacement string`
@return "The new string with the elements replaced"
*>
fn String String.treplace(self, String needle, String new_str)
{
String[] split = self.tsplit(needle);
return dstring::join(tmem, split, new_str).str_view();
}
<*
Remove characters from the front and end of a string.
@@ -231,7 +305,7 @@ fn String String.strip_end(self, String suffix)
@param [&inout] allocator : "The allocator to use for the String[]"
@require delimiter.len > 0 : "The delimiter must be at least 1 character long"
@ensure return.len > 0
@ensure return.len > 0 || skip_empty
*>
fn String[] String.split(self, Allocator allocator, String delimiter, usz max = 0, bool skip_empty = false)
{
@@ -290,7 +364,7 @@ faultdef BUFFER_EXCEEDED;
@param [inout] buffer
@param max : "Max number of elements, 0 means no limit, defaults to 0"
@require delimiter.len > 0 : "The delimiter must be at least 1 character long"
@ensure return.len > 0
@ensure return.len > 0 || skip_empty
@return? BUFFER_EXCEEDED : `If there are more elements than would fit the buffer`
*>
fn String[]? String.split_to_buffer(s, String delimiter, String[] buffer, usz max = 0, bool skip_empty = false)
@@ -338,6 +412,38 @@ fn bool String.contains(s, String substr)
return @ok(s.index_of(substr));
}
<*
Check how many non-overlapping instances of a substring there is.
If the substring has zero length, the number of matches is zero.
@param [in] self : "The string to search"
@param [in] substr : "The string to look for."
@pure
@return "The number of times matched"
*>
fn usz String.count(self, String substr)
{
usz count = 0;
usz needed = substr.len;
if (needed == 0) return 0;
char first = substr[0];
while OUTER: (self.len >= needed)
{
foreach (i, c: self[..^needed])
{
if (c == first && self[i : needed] == substr)
{
count++;
self = self[i + needed..];
continue OUTER;
}
}
break;
}
return count;
}
<*
Find the index of the first incidence of a string.
@@ -471,6 +577,19 @@ fn usz? String.rindex_of(self, String substr)
return NOT_FOUND?;
}
fn bool ZString.eq(self, ZString other) @operator(==)
{
char* a = self;
char* b = other;
if (a == b) return true;
for (;; a++, b++)
{
char c = *a;
if (c != *b) return false;
if (!c) return true;
}
}
fn String ZString.str_view(self)
{
return (String)(self[:self.len()]);
@@ -692,16 +811,63 @@ fn usz String.utf8_codepoints(s)
return len;
}
<*
Determine whether the current string actually points to a ZString-like string.
This is done by looking at the byte one step after the end of the string. If this
is zero, it is considered zero terminated.
This function can safely be used with data pointing to null. However, it will not
work correctly if the pointer is invalid, for example it is already freed.
*>
fn bool String.is_zstr(self) @deprecated("Unsafe, use copy instead")
{
return self.ptr && *(self.ptr + self.len) == 0;
}
<*
@require (base <= 10 && base > 1) || base == 16 : "Unsupported base"
Return a pointer to the string *iff* it is a pointer
to a zero terminated string, otherwise return a temp allocated zstring copy.
This function is suitable if you are converting strings to ZString on the temp
allocator, but suspect that the String might actually already point to zero
terminated data.
The function looks one step beyond the end of the slice to determine this,
which means that if that data is then modified after this call, this function
might behave incorrectly.
For this reason, try to ensure that the resulting ZString is immediately used.
@ensure return[self.len] == 0
*>
fn ZString String.quick_zstr(self) @deprecated("Unsafe, use zstr_tcopy instead")
{
return self.zstr_tcopy();
}
<*
Convert a number to a given base. If the base is not given, then
it will be inferred from the number if the string starts with 0x 0o or 0b and the
base is given as 10.
Furthermore it will skip any spaces before and after the number.
@param $Type : "The type to convert to"
@param base : "The base to convert to"
@require base > 0 && base <= 16 : "Unsupported base"
@return? MALFORMED_INTEGER : "When the value has some illegal character"
@return? INTEGER_OVERFLOW : "If the value does not fit in the given type"
@return? EMPTY_STRING : "If the string was empty"
@return? NEGATIVE_VALUE : "If the type was unsigned, and the value had a - prefix"
*>
macro String.to_integer(self, $Type, int base = 10)
{
usz len = self.len;
usz index = 0;
char* ptr = self.ptr;
while (index < len && ascii::is_blank_m(ptr[index])) index++;
while (index < len && ptr[index].is_blank()) index++;
if (len == index) return EMPTY_STRING?;
bool is_negative;
switch (self[index])
@@ -746,7 +912,7 @@ macro String.to_integer(self, $Type, int base = 10)
char c = self[index++];
switch
{
case base_used != 16 || c < 'A': c -= '0';
case base_used < 10 || c < 'A': c -= '0';
case c <= 'F': c -= 'A' - 10;
case c < 'a' || c > 'f': return MALFORMED_INTEGER?;
default: c -= 'a' - 10;
@@ -784,22 +950,91 @@ fn char? String.to_uchar(self, int base = 10) => self.to_integer(char, base);
fn double? String.to_double(self) => self.to_real(double);
fn float? String.to_float(self) => self.to_real(float);
fn Splitter String.splitter(self, String split)
{
return { .string = self, .split = split };
}
<*
Create a Splitter to track tokenizing of a string. Tokenize will turn "foo:bar::baz" into
"foo", "bar" and "baz", if you want the empty string to be present, use `tokenize_all`
instead.
@param [in] split : "The string to use for splitting"
@return "A Splitter to track the state"
*>
fn Splitter String.tokenize(self, String split)
{
return { .string = self, .split = split, .tokenize = true };
return { .string = self, .split = split, .type = TOKENIZE };
}
<*
Create a Splitter to track tokenizing of a string. Tokenize will turn "foo:bar::baz" into
"foo", "bar" and "baz", if you want the empty string to be present, use `tokenize_all`
instead.
@param [in] split : "The string to use for splitting"
@param skip_last : "Set to true to not include the last empty token if present (default: false)"
@return "A Splitter to track the state"
*>
fn Splitter String.tokenize_all(self, String split, bool skip_last = false)
{
return {
.string = self,
.split = split,
.type = skip_last ? TOKENIZE_ALL_SKIP_LAST : TOKENIZE_ALL
};
}
fn Splitter String.splitter(self, String split) @deprecated("Use tokenize_all instead")
{
return self.tokenize_all(split, skip_last: true);
}
<*
This macro will create a string description of a struct.
@param [&inout] allocator : "The allocator to use"
@param x : "The struct to create a description of"
*>
macro String from_struct(Allocator allocator, x)
{
DString s;
@stack_mem(512; Allocator mem)
{
s.init(allocator: mem);
io::fprint(&s, x)!!;
return s.copy_str(allocator);
};
}
<*
This macro will create a temporary string description of a struct.
@param x : "The struct to create a description of"
*>
macro String tfrom_struct(x) => from_struct(tmem, x);
const uint SURROGATE_OFFSET @private = 0x10000;
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
const uint SURROGATE_MASK @private = 0xFC00;
const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF;
const uint SURROGATE_BITS @private = 10;
const uint SURROGATE_LOW_VALUE @private = 0xDC00;
const uint SURROGATE_HIGH_VALUE @private = 0xD800;
enum SplitterType
{
TOKENIZE,
TOKENIZE_ALL,
TOKENIZE_ALL_SKIP_LAST
}
<*
Splitter is handles tokenizing strings.
*>
struct Splitter
{
String string;
String split;
usz current;
bool tokenize;
SplitterType type;
int last_index;
}
@@ -814,29 +1049,22 @@ fn String? Splitter.next(&self)
{
usz len = self.string.len;
usz current = self.current;
if (current >= len) return NO_MORE_ELEMENT?;
if (current > len) return NO_MORE_ELEMENT?;
if (current == len)
{
if (self.type != TOKENIZE_ALL) return NO_MORE_ELEMENT?;
self.current++;
return self.string[current - 1:0];
}
String remaining = self.string[current..];
usz? next = remaining.index_of(self.split);
if (try next)
{
self.current = current + next + self.split.len;
if (!next && self.tokenize) continue;
if (!next && self.type == TOKENIZE) continue;
return remaining[:next];
}
self.current = len;
return remaining;
}
}
macro String from_struct(Allocator allocator, x)
{
DString s;
@stack_mem(512; Allocator mem)
{
s.init(allocator: mem);
io::fprint(&s, x)!!;
return s.copy_str(allocator);
};
}
macro String tfrom_struct(x) => from_struct(tmem, x);

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

@@ -98,7 +98,7 @@ macro bool is_numerical($Type)
{
var $kind = $Type.kindof;
$if $kind == TypeKind.DISTINCT:
return is_numerical($typefrom($Type.inner));
return is_numerical($Type.inner);
$else
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|| $kind == TypeKind.VECTOR;
@@ -135,7 +135,7 @@ macro bool is_signed($Type) @const
$case FLOAT:
return true;
$case VECTOR:
return is_signed($typefrom($Type.inner));
return is_signed($Type.inner);
$default:
return false;
$endswitch
@@ -150,7 +150,7 @@ macro bool is_unsigned($Type) @const
$case UNSIGNED_INT:
return true;
$case VECTOR:
return is_unsigned($typefrom($Type.inner));
return is_unsigned($Type.inner);
$default:
return false;
$endswitch
@@ -159,7 +159,7 @@ macro bool is_unsigned($Type) @const
macro typeid flat_type($Type) @const
{
$if $Type.kindof == DISTINCT:
return flat_type($typefrom($Type.inner));
return flat_type($Type.inner);
$else
return $Type.typeid;
$endif
@@ -168,7 +168,7 @@ macro typeid flat_type($Type) @const
macro TypeKind flat_kind($Type) @const
{
$if $Type.kindof == DISTINCT:
return flat_type($typefrom($Type.inner));
return flat_type($Type.inner);
$else
return $Type.kindof;
$endif
@@ -184,6 +184,21 @@ macro bool is_ref_indexable($Type) @const
return $defined(&($Type){}[0]);
}
macro bool is_flat_intlike($Type) @const
{
$switch $Type.kindof:
$case SIGNED_INT:
$case UNSIGNED_INT:
return true;
$case VECTOR:
return is_flat_intlike($Type.inner);
$case DISTINCT:
return is_flat_intlike($Type.inner);
$default:
return false;
$endswitch
}
macro bool is_intlike($Type) @const
{
$switch $Type.kindof:
@@ -204,7 +219,7 @@ macro bool is_underlying_int($Type) @const
$case UNSIGNED_INT:
return true;
$case DISTINCT:
return is_underlying_int($typefrom($Type.inner));
return is_underlying_int($Type.inner);
$default:
return false;
$endswitch
@@ -232,7 +247,7 @@ macro bool is_vector($Type) @const
macro typeid inner_type($Type) @const
{
$if $Type.kindof == TypeKind.DISTINCT:
return inner_type($typefrom($Type.inner));
return inner_type($Type.inner);
$else
return $Type.typeid;
$endif
@@ -272,6 +287,7 @@ macro bool may_load_atomic($Type) @const
$case FLOAT:
return true;
$case DISTINCT:
$case ENUM:
return may_load_atomic($Type.inner);
$default:
return false;

View File

@@ -1,4 +1,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;
@@ -9,6 +11,7 @@ macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $
macro bool @is_same_type(#value1, #value2) @const => $typeof(#value1).typeid == $typeof(#value2).typeid;
macro bool @is_bool(#value) @const => types::is_bool($typeof(#value));
macro bool @is_int(#value) @const => types::is_int($typeof(#value));
macro bool @is_flat_intlike(#value) @const => types::is_flat_intlike($typeof(#value));
macro bool @is_floatlike(#value) @const => types::is_floatlike($typeof(#value));
macro bool @is_float(#value) @const => types::is_float($typeof(#value));
macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to_floatlike($typeof(#value));

View File

@@ -3,7 +3,6 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::encoding::json;
import std::io;
import std::ascii;
import std::collections::object;
faultdef UNEXPECTED_CHARACTER, INVALID_ESCAPE_SEQUENCE, DUPLICATE_MEMBERS, INVALID_NUMBER;

View File

@@ -93,7 +93,7 @@ macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[]
UIntBE be = { (uint)index };
hmac.update(&&bitcast(be, char[4]));
tmp = hmac.final();
out[..] = tmp;
out[..] = tmp[..];
for (int it = 1; it < iterations; it++)
{
hmac = *hmac_start;

View File

@@ -89,10 +89,10 @@ fn char[HASH_BYTES] Md5.final(&ctx)
body(ctx, &ctx.buffer, 64);
char[16] res @noinit;
res[0:4] = bitcast(ctx.a, char[4]);
res[4:4] = bitcast(ctx.b, char[4]);
res[8:4] = bitcast(ctx.c, char[4]);
res[12:4] = bitcast(ctx.d, char[4]);
res[0:4] = bitcast(ctx.a, char[4])[..];
res[4:4] = bitcast(ctx.b, char[4])[..];
res[8:4] = bitcast(ctx.c, char[4])[..];
res[12:4] = bitcast(ctx.d, char[4])[..];
*ctx = {};
return res;
}

View File

@@ -71,7 +71,7 @@ fn void Sha256.update(&self, char[] data) {
uint i = 0;
uint len = data.len;
uint buffer_pos = (uint)(self.bitcount / 8) % BLOCK_SIZE;
self.bitcount += (ulong)(len * 8);
self.bitcount += ((ulong)len * 8);
while (len--) {
self.buffer[buffer_pos++] = data[i++];
@@ -173,4 +173,5 @@ fn void sha256_transform(uint* state, char* buffer) @local {
state[7] += h;
a = b = c = d = e = f = g = h = t1 = t2 = i = 0;
m[:64] = buffer[:64] = 0;
}
}

281
lib/std/hash/sha512.c3 Normal file
View File

@@ -0,0 +1,281 @@
/**
* SHA-512 implementation for the C3 stdlib.
*
* The core components are based almost exclusively on the musl libc sha512 implementation:
* https://git.musl-libc.org/cgit/musl/commit/src/misc/crypt_sha512.c?id=88bf5a8a8d7d796f63cca8589f4de67aa8345f1a
*
* SHA-384, 512/224, and 512/256 are very simple addenda to this module.
*
*/
module std::hash::sha512;
import std::hash::hmac;
const BLOCK_SIZE = 128;
const HASH_SIZE = 64;
struct Sha512
{
ulong length;
ulong[8] hash_state;
char[BLOCK_SIZE] buffer;
}
alias HmacSha512 = Hmac{Sha512, HASH_SIZE, BLOCK_SIZE};
alias hmac = hmac::hash{Sha512, HASH_SIZE, BLOCK_SIZE};
alias pbkdf2 = hmac::pbkdf2{Sha512, HASH_SIZE, BLOCK_SIZE};
macro ulong ror(ulong n, int k) @local => ((n >> k) | (n << (64 - k)));
macro ulong ch(ulong x, ulong y, ulong z) @local => (z ^ (x & (y ^ z)));
macro ulong maj(ulong x, ulong y, ulong z) @local => ((x & y) | (z & (x | y)));
macro ulong s0(ulong x) @local => (ror(x, 28) ^ ror(x, 34) ^ ror(x, 39));
macro ulong s1(ulong x) @local => (ror(x, 14) ^ ror(x, 18) ^ ror(x, 41));
macro ulong r0(ulong x) @local => (ror(x, 1) ^ ror(x, 8) ^ (x >> 7));
macro ulong r1(ulong x) @local => (ror(x, 19) ^ ror(x, 61) ^ (x >> 6));
const ulong[80] K @local = {
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
};
// See: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
// All truncation types are simple to add onto the base SHA512 implementation at a (near) future time.
enum HashTruncationType : uint (inline uint truncation_width, ulong[8] initial_state)
{
SHA512 = {
512,
{
0x6a09e667f3bcc908,
0xbb67ae8584caa73b,
0x3c6ef372fe94f82b,
0xa54ff53a5f1d36f1,
0x510e527fade682d1,
0x9b05688c2b3e6c1f,
0x1f83d9abfb41bd6b,
0x5be0cd19137e2179
}
},
SHA384 = {
384,
{
0xcbbb9d5dc1059ed8,
0x629a292a367cd507,
0x9159015a3070dd17,
0x152fecd8f70e5939,
0x67332667ffc00b31,
0x8eb44a8768581511,
0xdb0c2e0d64f98fa7,
0x47b5481dbefa4fa4
}
},
SHA512_224 = {
224,
{
0x8C3D37C819544DA2,
0x73E1996689DCD4D6,
0x1DFAB7AE32FF9C82,
0x679DD514582F9FCF,
0x0F6D2B697BD44DA8,
0x77E36F7304C48942,
0x3F9D85A86A1D36C8,
0x1112E6AD91D692A1
}
},
SHA512_256 = {
256,
{
0x22312194FC2BF72C,
0x9F555FA3C84C64C2,
0x2393B86B6F53B151,
0x963877195940EABD,
0x96283EE2A88EFFE3,
0xBE5E1E2553863992,
0x2B0199FC2C85B8AA,
0x0EB72DDC81C52CA2
}
},
}
<*
@param [in] data
*>
fn char[HASH_SIZE] hash(char[] data)
{
Sha512 s @noinit;
s.init();
s.update(data);
return s.final();
}
fn void Sha512.init(&self)
{
*self = {
.hash_state = HashTruncationType.SHA512.initial_state
};
}
<*
@param [in] data
@require data.len <= ulong.max
*>
fn void Sha512.update(&self, char[] data)
{
char* p = data.ptr;
ulong len = data.len;
ulong l;
ulong r = self.length % 128;
self.length += len;
if (r)
{
if (len < (128 - r))
{
for (l = 0; l < len; ++l) self.buffer[r+l] = p[l];
return;
}
for (l = 0; l < 128 - r; ++l) self.buffer[r+l] = p[l];
len -= (128 - r);
p = &p[128 - r];
sha512_transform(&self.hash_state, &self.buffer);
}
for (; len >= 128; len -= 128, p = &p[128]) sha512_transform(&self.hash_state, p);
for (l = 0; l < len; ++l) self.buffer[l] = p[l];
}
fn char[HASH_SIZE] Sha512.final(&self)
{
char[HASH_SIZE] hash;
int i;
ulong r = self.length % 128;
self.buffer[r++] = 0x80;
if (r > 112)
{
for (i = 0; i < 128 - r; ++i) self.buffer[r+i] = 0;
r = 0;
sha512_transform(&self.hash_state, &self.buffer);
}
for (i = 0; i < 120 - r; ++i) self.buffer[r+i] = 0;
self.length *= 8;
self.buffer[120] = (char)(self.length >> 56);
self.buffer[121] = (char)(self.length >> 48);
self.buffer[122] = (char)(self.length >> 40);
self.buffer[123] = (char)(self.length >> 32);
self.buffer[124] = (char)(self.length >> 24);
self.buffer[125] = (char)(self.length >> 16);
self.buffer[126] = (char)(self.length >> 8);
self.buffer[127] = (char)(self.length);
sha512_transform(&self.hash_state, &self.buffer);
for (i = 0; i < 8; ++i)
{
hash[(8 * i)] = (char)(self.hash_state[i] >> 56);
hash[(8 * i) + 1] = (char)(self.hash_state[i] >> 48);
hash[(8 * i) + 2] = (char)(self.hash_state[i] >> 40);
hash[(8 * i) + 3] = (char)(self.hash_state[i] >> 32);
hash[(8 * i) + 4] = (char)(self.hash_state[i] >> 24);
hash[(8 * i) + 5] = (char)(self.hash_state[i] >> 16);
hash[(8 * i) + 6] = (char)(self.hash_state[i] >> 8);
hash[(8 * i) + 7] = (char)(self.hash_state[i]);
}
return hash;
}
<*
@param [&inout] state
@param [&in] buf
*>
fn void sha512_transform(ulong *state, char *buf) @local
{
ulong t1, t2, a, b, c, d, e, f, g, h;
ulong[80] w;
int i;
for (i = 0; i < 16; ++i)
{
w[i] = (ulong)buf[(8 * i)] << 56;
w[i] |= (ulong)buf[(8 * i) + 1] << 48;
w[i] |= (ulong)buf[(8 * i) + 2] << 40;
w[i] |= (ulong)buf[(8 * i) + 3] << 32;
w[i] |= (ulong)buf[(8 * i) + 4] << 24;
w[i] |= (ulong)buf[(8 * i) + 5] << 16;
w[i] |= (ulong)buf[(8 * i) + 6] << 8;
w[i] |= buf[(8 * i) + 7];
}
for (; i < 80; ++i) w[i] = r1(w[i - 2]) + w[i - 7] + r0(w[i - 15]) + w[i - 16];
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
f = state[5];
g = state[6];
h = state[7];
for (i = 0; i < 80; ++i)
{
t1 = h + s1(e) + ch(e, f, g) + K[i] + w[i];
t2 = s0(a) + maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;
}

View File

@@ -19,7 +19,7 @@ alias FloatType = double;
macro bool is_struct_with_default_print($Type)
{
return $Type.kindof == STRUCT
return ($Type.kindof == STRUCT ||| $Type.kindof == BITSTRUCT)
&&& !$defined($Type.to_format)
&&& !$defined($Type.to_constant_string);
}
@@ -27,7 +27,7 @@ macro bool is_struct_with_default_print($Type)
<*
Introspect a struct and print it to a formatter
@require @typekind(value) == STRUCT : `This macro is only valid on macros`
@require @typekind(value) == STRUCT || @typekind(value) == BITSTRUCT : `This macro is only valid on macros`
*>
macro usz? struct_to_format(value, Formatter* f, bool $force_dump)
{
@@ -40,8 +40,8 @@ macro usz? struct_to_format(value, Formatter* f, bool $force_dump)
$if $member.nameof != "":
total += f.printf("%s: ", $member.nameof)!;
$endif
$if ($force_dump &&& $member.typeid.kindof == STRUCT) |||
is_struct_with_default_print($typefrom($member.typeid)):
$if ($force_dump &&& ($member.typeid.kindof == STRUCT || $member.typeid.kindof == BITSTRUCT)) |||
is_struct_with_default_print($member.typeid):
total += struct_to_format($member.get(value), f, $force_dump)!;
$else
total += f.printf("%s", $member.get(value))!;
@@ -136,7 +136,7 @@ fn usz? Formatter.print_with_function(&self, Printable arg)
fn usz? Formatter.out_unknown(&self, String category, any arg) @private
{
return self.out_substr("[") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr("]");
return self.out_substr("<") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr(">");
}
fn usz? Formatter.out_str(&self, any arg) @private
{
@@ -196,7 +196,15 @@ fn usz? Formatter.out_str(&self, any arg) @private
case BITSTRUCT:
return self.out_unknown("bitstruct", arg);
case FUNC:
return self.out_unknown("function", arg);
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.width = 0;
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
case DISTINCT:
if (arg.type == String.typeid)
{

View File

@@ -39,10 +39,11 @@ fn uint128? int_from_any(any arg, bool *is_neg) @private
{
switch (arg.type.kindof)
{
case TypeKind.POINTER:
case FUNC:
case POINTER:
*is_neg = false;
return (uint128)(uptr)*(void**)arg.ptr;
case TypeKind.DISTINCT:
case DISTINCT:
return int_from_any(arg.as_inner(), is_neg);
default:
break;
@@ -622,9 +623,12 @@ fn usz? Formatter.out_char(&self, any arg) @private
return self.out_substr("<NOT CHAR>");
}
usz len = 1;
uint l = 1;
// pre padding
len += self.adjust(l)!;
if (!self.flags.left)
{
len += self.pad(' ', self.width, len)!;
}
// char output
Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD;
switch (true)
@@ -644,7 +648,10 @@ fn usz? Formatter.out_char(&self, any arg) @private
self.out((char)(0x80 | (c >> 6 & 0x3F)))!;
self.out((char)(0x80 | (c & 0x3F)))!;
}
len += self.adjust(l)!;
if (self.flags.left)
{
len += self.pad(' ', self.width, len)!;
}
return len;
}

View File

@@ -1,9 +1,9 @@
module std::io::os;
import libc, std::os, std::io;
fn void? native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::BSD_FAMILY) => @pool()
fn void? native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY) => @pool()
{
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
$if env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY:
int res = libc::stat(path.zstr_tcopy(), stat);
$else
unreachable("Stat unimplemented");
@@ -69,6 +69,7 @@ fn bool native_file_or_dir_exists(String path)
$case env::NETBSD:
$case env::OPENBSD:
$case env::LINUX:
$case env::ANDROID:
Stat stat;
return @ok(native_stat(&stat, path));
$case env::WIN32:
@@ -94,6 +95,7 @@ fn bool native_is_file(String path)
$case env::NETBSD:
$case env::OPENBSD:
$case env::LINUX:
$case env::ANDROID:
Stat stat;
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFREG);
$default:
@@ -105,7 +107,7 @@ fn bool native_is_file(String path)
fn bool native_is_dir(String path)
{
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
$if env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY:
Stat stat;
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFDIR);
$else

View File

@@ -49,7 +49,7 @@ macro bool @is_outstream(#expr)
<*
@param [&out] ref
@require @is_instream(stream)
@require @is_instream(stream) : "Expected a stream"
*>
macro usz? read_any(stream, any ref)
{
@@ -191,7 +191,7 @@ const char[*] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 };
*>
macro usz? read_varint(stream, x_ptr)
{
var $Type = $typefrom($typeof(x_ptr).inner);
var $Type = $typeof(x_ptr).inner;
const MAX = MAX_VARS[$Type.sizeof];
$Type x;
uint shift;

View File

@@ -125,6 +125,7 @@ extern fn ZString getenv(ZString name);
extern fn ZString gets(char* buffer);
extern fn Tm* gmtime(Time_t* timer);
extern fn Tm* gmtime_r(Time_t* timer, Tm* buf) @if(!env::WIN32);
extern fn CInt ioctl(CInt fd, ulong request, ...);
extern fn CInt isatty(Fd fd) @if(!env::WIN32);
extern fn CLong labs(CLong x);
extern fn LongDivResult ldiv(CLong number, CLong denom);
@@ -167,6 +168,7 @@ extern fn CInt strcmp(ZString str1, ZString str2);
extern fn CInt strcoll(ZString str1, ZString str2);
extern fn usz strcspn(ZString str1, ZString str2);
extern fn ZString strcpy(ZString dst, ZString src);
extern fn ZString strdup(ZString s);
extern fn ZString strerror(CInt errn);
extern fn usz strftime(char* dest, usz maxsize, ZString format, Tm* timeptr);
extern fn usz strlen(ZString str);
@@ -373,7 +375,7 @@ module libc;
alias CFile = void*;
const HAS_MALLOC_SIZE = env::LINUX || env::WIN32 || env::DARWIN;
const HAS_MALLOC_SIZE = env::LINUX || env::ANDROID || env::WIN32 || env::DARWIN;
// The following needs to be set per arch+os
// For now I have simply pulled the defaults from MacOS

View File

@@ -1,10 +1,16 @@
module libc::os @if(env::LIBC);
import std::core::env;
// Linux
extern fn int* __errno_location() @if(env::LINUX);
macro int errno() @if(env::LINUX) => *__errno_location();
macro void errno_set(int err) @if(env::LINUX) => *(__errno_location()) = err;
// Android 9
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;
// Darwin
extern fn int* __error() @if(env::DARWIN);
macro int errno() @if(env::DARWIN) => *__error();
@@ -22,7 +28,7 @@ extern fn void _get_errno(int* result) @if(env::WIN32);
extern fn void _set_errno(int err) @if(env::WIN32);
// Default
module libc::os @if(!env::LIBC || !(env::LINUX || env::DARWIN || env::WIN32));
module libc::os @if(!env::LIBC || !env::HAS_NATIVE_ERRNO);
tlocal int _errno_c3 = 0;
fn void errno_set(int err) => _errno_c3 = err;
fn int errno() => _errno_c3;

View File

@@ -127,13 +127,13 @@ fn bool BigInt.is_negative(&self)
return self.data[MAX_LEN - 1] & 0x80000000 != 0;
}
fn BigInt BigInt.add(self, BigInt other)
fn BigInt BigInt.add(self, BigInt other) @operator(+)
{
self.add_this(other);
return self;
}
fn void BigInt.add_this(&self, BigInt other)
fn void BigInt.add_this(&self, BigInt other) @operator(+=)
{
bool sign = self.is_negative();
bool sign_arg = other.is_negative();
@@ -172,13 +172,13 @@ macro uint find_length(uint* data, uint length)
return length;
}
fn BigInt BigInt.mult(self, BigInt bi2)
fn BigInt BigInt.mult(self, BigInt bi2) @operator(*)
{
self.mult_this(bi2);
return self;
}
fn void BigInt.mult_this(&self, BigInt bi2)
fn void BigInt.mult_this(&self, BigInt bi2) @operator(*=)
{
if (bi2.is_zero())
{
@@ -270,13 +270,13 @@ fn void BigInt.negate(&self)
macro bool BigInt.is_zero(&self) => self.len == 1 && self.data[0] == 0;
fn BigInt BigInt.sub(self, BigInt other)
fn BigInt BigInt.sub(self, BigInt other) @operator(-)
{
self.sub_this(other);
return self;
}
fn BigInt* BigInt.sub_this(&self, BigInt other)
fn BigInt* BigInt.sub_this(&self, BigInt other) @operator(-=)
{
self.len = max(self.len, other.len);
@@ -325,7 +325,7 @@ fn int BigInt.bitcount(&self)
return bits;
}
fn BigInt BigInt.unary_minus(&self)
fn BigInt BigInt.unary_minus(&self) @operator(-)
{
if (self.is_zero()) return *self;
BigInt result = *self;
@@ -334,13 +334,13 @@ fn BigInt BigInt.unary_minus(&self)
}
macro BigInt BigInt.div(self, BigInt other)
macro BigInt BigInt.div(self, BigInt other) @operator(/)
{
self.div_this(other);
return self;
}
fn void BigInt.div_this(&self, BigInt other)
fn void BigInt.div_this(&self, BigInt other) @operator(/=)
{
bool negate_answer = self.is_negative();
@@ -377,13 +377,13 @@ fn void BigInt.div_this(&self, BigInt other)
*self = quotient;
}
fn BigInt BigInt.mod(self, BigInt bi2)
fn BigInt BigInt.mod(self, BigInt bi2) @operator(%)
{
self.mod_this(bi2);
return self;
}
fn void BigInt.mod_this(&self, BigInt bi2)
fn void BigInt.mod_this(&self, BigInt bi2) @operator(%=)
{
if (bi2.is_negative())
{
@@ -428,30 +428,30 @@ fn void BigInt.bit_negate_this(&self)
self.reduce_len();
}
fn BigInt BigInt.bit_negate(self)
fn BigInt BigInt.bit_negate(self) @operator(~)
{
self.bit_negate_this();
return self;
}
fn BigInt BigInt.shr(self, int shift)
fn BigInt BigInt.shr(self, int shift) @operator(>>)
{
self.shr_this(shift);
return self;
}
fn void BigInt.shr_this(self, int shift)
fn void BigInt.shr_this(self, int shift) @operator(>>=)
{
self.len = shift_right(&self.data, self.len, shift);
}
fn BigInt BigInt.shl(self, int shift)
fn BigInt BigInt.shl(self, int shift) @operator(<<)
{
self.shl_this(shift);
return self;
}
macro bool BigInt.equals(&self, BigInt other)
macro bool BigInt.equals(&self, BigInt other) @operator(==)
{
if (self.len != other.len) return false;
return self.data[:self.len] == other.data[:self.len];
@@ -764,7 +764,7 @@ fn BigInt BigInt.sqrt(&self)
return result;
}
fn BigInt BigInt.bit_and(self, BigInt bi2)
fn BigInt BigInt.bit_and(self, BigInt bi2) @operator(&)
{
self.bit_and_this(bi2);
return self;
@@ -782,7 +782,7 @@ fn void BigInt.bit_and_this(&self, BigInt bi2)
self.reduce_len();
}
fn BigInt BigInt.bit_or(self, BigInt bi2)
fn BigInt BigInt.bit_or(self, BigInt bi2) @operator(|)
{
self.bit_or_this(bi2);
return self;
@@ -800,7 +800,7 @@ fn void BigInt.bit_or_this(&self, BigInt bi2)
self.reduce_len();
}
fn BigInt BigInt.bit_xor(self, BigInt bi2)
fn BigInt BigInt.bit_xor(self, BigInt bi2) @operator(^)
{
self.bit_xor_this(bi2);
return self;
@@ -818,7 +818,7 @@ fn void BigInt.bit_xor_this(&self, BigInt bi2)
self.reduce_len();
}
fn void BigInt.shl_this(&self, int shift)
fn void BigInt.shl_this(&self, int shift) @operator(<<=)
{
self.len = shift_left(&self.data, self.len, shift);
}

66
lib/std/math/complex.c3 Normal file
View File

@@ -0,0 +1,66 @@
module std::math;
// Complex number aliases.
alias Complexf = Complex {float};
alias Complex = Complex {double};
alias COMPLEX_IDENTITY @builtin = complex::IDENTITY {double};
alias COMPLEXF_IDENTITY @builtin = complex::IDENTITY {float};
alias IMAGINARY @builtin @deprecated("Use I") = complex::IMAGINARY { double };
alias IMAGINARYF @builtin @deprecated("Use I_F") = complex::IMAGINARY { float };
alias I @builtin = complex::IMAGINARY { double };
alias I_F @builtin = complex::IMAGINARY { float };
<*
The generic complex number module, for float or double based complex number definitions.
@require Real.kindof == FLOAT : "A complex number must use a floating type"
*>
module std::math::complex {Real};
import std::io;
union Complex (Printable)
{
struct
{
Real r, c;
}
Real[<2>] v;
}
const Complex IDENTITY = { 1, 0 };
const Complex IMAGINARY = { 0, 1 };
macro Complex Complex.add(self, Complex b) @operator(+) => { .v = self.v + b.v };
macro Complex Complex.add_this(&self, Complex b) @operator(+=) => { .v = self.v += b.v };
macro Complex Complex.add_real(self, Real r) @operator_s(+) => { .v = self.v + (Real[<2>]) { r, 0 } };
macro Complex Complex.add_each(self, Real b) => { .v = self.v + b };
macro Complex Complex.sub(self, Complex b) @operator(-) => { .v = self.v - b.v };
macro Complex Complex.sub_this(&self, Complex b) @operator(-=) => { .v = self.v -= b.v };
macro Complex Complex.sub_real(self, Real r) @operator(-) => { .v = self.v - (Real[<2>]) { r, 0 } };
macro Complex Complex.sub_real_inverse(self, Real r) @operator_r(-) => { .v = (Real[<2>]) { r, 0 } - self.v };
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(self, Complex b) @operator(/)
{
Real div = b.v.dot(b.v);
return { (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
}
macro Complex Complex.inverse(self)
{
Real sqr = self.v.dot(self.v);
return { self.r / sqr, -self.c / sqr };
}
macro Complex Complex.conjugate(self) => { .r = self.r, .c = -self.c };
macro Complex Complex.negate(self) @operator(-) => { .v = -self.v };
macro bool Complex.equals(self, Complex b) @operator(==) => self.v == b.v;
macro bool Complex.equals_real(self, Real r) @operator_s(==) => self.v == { r, 0 };
macro bool Complex.not_equals(self, Complex b) @operator(!=) => self.v != b.v;
fn usz? Complex.to_format(&self, Formatter* f) @dynamic
{
return f.printf("%g%+gi", self.r, self.c);
}

View File

@@ -6,7 +6,6 @@ import std::math::complex;
import std::math::matrix;
import std::math::quaternion;
// TODO Define these using quad precision.
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
const LOG10E = 0.434294481903251827651128918916605082; // log10(e)
@@ -57,21 +56,6 @@ const DOUBLE_MAX_EXP = 1024;
const DOUBLE_MIN_EXP = -1021;
const DOUBLE_EPSILON = 2.22044604925031308085e-16;
const QUAD_MANT_DIG = 113;
/*
const QUAD_MAX = 1.18973149535723176508575932662800702e+4932;
const QUAD_MIN = 3.36210314311209350626267781732175260e-4932;
const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966;
const QUAD_DIG = 33;
const QUAD_DEC_DIGITS = 36;
const QUAD_MAX_10_EXP = 4932;
const QUAD_MIN_10_EXP = -4931;
const QUAD_MAX_EXP = 16384;
const QUAD_MIN_EXP = -16481;
const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34;
*/
enum RoundingMode : int
{
TOWARD_ZERO,
@@ -82,35 +66,6 @@ enum RoundingMode : int
faultdef OVERFLOW, MATRIX_INVERSE_DOESNT_EXIST;
alias Complexf = Complex {float};
alias Complex = Complex {double};
alias COMPLEX_IDENTITY @builtin = complex::IDENTITY {double};
alias COMPLEXF_IDENTITY @builtin = complex::IDENTITY {float};
alias Quaternionf = Quaternion {float};
alias Quaternion = Quaternion {double};
alias QUATERNION_IDENTITY @builtin = quaternion::IDENTITY {double};
alias QUATERNIONF_IDENTITY @builtin = quaternion::IDENTITY {float};
alias Matrix2f = Matrix2x2 {float};
alias Matrix2 = Matrix2x2 {double};
alias Matrix3f = Matrix3x3 {float};
alias Matrix3 = Matrix3x3 {double};
alias Matrix4f = Matrix4x4 {float};
alias Matrix4 = Matrix4x4 {double};
alias matrix4_ortho @builtin = matrix::ortho {double};
alias matrix4f_ortho @builtin = matrix::ortho {float};
alias matrix4_perspective @builtin = matrix::perspective {double};
alias matrix4f_perspective @builtin = matrix::perspective {float};
alias MATRIX2_IDENTITY @builtin = matrix::IDENTITY2 {double};
alias MATRIX2F_IDENTITY @builtin = matrix::IDENTITY2 {float};
alias MATRIX3_IDENTITY @builtin = matrix::IDENTITY3 {double};
alias MATRIX3F_IDENTITY @builtin = matrix::IDENTITY3 {float};
alias MATRIX4_IDENTITY @builtin = matrix::IDENTITY4 {double};
alias MATRIX4F_IDENTITY @builtin = matrix::IDENTITY4 {float};
<*
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
*>
@@ -129,7 +84,7 @@ macro is_approx(x, y, eps)
{
if (x == y) return true;
if (is_nan(x) || is_nan(y)) return false;
return abs(x-y) <= eps;
return abs(x - y) <= eps;
}
<*
@@ -140,7 +95,7 @@ macro is_approx_rel(x, y, eps)
{
if (x == y) return true;
if (is_nan(x) || is_nan(y)) return false;
return abs(x-y) <= eps * max(abs(x), abs(y));
return abs(x - y) <= eps * max(abs(x), abs(y));
}
<*
@@ -149,7 +104,7 @@ macro is_approx_rel(x, y, eps)
macro sign(x)
{
var $Type = $typeof(x);
$if $Type.kindof == TypeKind.UNSIGNED_INT:
$if $Type.kindof == UNSIGNED_INT:
return ($Type)(x > 0);
$else
return ($Type)(x > 0) - ($Type)(x < 0);
@@ -177,7 +132,7 @@ macro atan2(x, y)
*>
macro sincos_ref(x, sinp, cosp)
{
$if @typeid(*sinp) == float.typeid:
$if @typeis(sinp, float*.typeid):
return _sincosf(x, sinp, cosp);
$else
return _sincos(x, sinp, cosp);
@@ -192,7 +147,7 @@ macro sincos_ref(x, sinp, cosp)
*>
macro sincos(x)
{
$if @typeid(x) == float.typeid:
$if @typeis(x, float):
float[<2>] v @noinit;
_sincosf(x, &v[0], &v[1]);
$else
@@ -279,6 +234,13 @@ macro asinh(x)
*>
macro ceil(x) => $$ceil(x);
<*
Ceil for compile time evaluation.
@require @typeis($input, double) || @typeis($input, float) : "Only float and double may be used"
*>
macro @ceil($input) @const => $$ceil($input);
<*
Constrain the value to lie within the given interval.
@@ -552,7 +514,7 @@ macro bool is_finite(x)
$case float16:
return bitcast((float)x, uint) & 0x7fffffff < 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) < 0x7ffu64 << 52;
return bitcast((double)x, ulong) & (~0UL >> 1) < 0x7ffUL << 52;
$endswitch
}
@@ -566,7 +528,7 @@ macro is_nan(x)
$case float16:
return bitcast((float)x, uint) & 0x7fffffff > 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ffu64 << 52;
return bitcast((double)x, ulong) & (~0UL >> 1) > 0x7ffUL << 52;
$endswitch
}
@@ -580,7 +542,7 @@ macro is_inf(x)
$case float16:
return bitcast((float)x, uint) & 0x7fffffff == 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ffu64 << 52;
return bitcast((double)x, ulong) & (~0UL >> 1) == 0x7ffUL << 52;
$endswitch
}
@@ -1054,10 +1016,16 @@ 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 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*) @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*) @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);
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");
@@ -1092,8 +1060,8 @@ fn double _frexp(double x, int* e)
return x;
default:
*e = ee - 0x3fe;
i &= 0x800fffffffffffffu64;
i |= 0x3fe0000000000000u64;
i &= 0x800fffffffffffffUL;
i |= 0x3fe0000000000000UL;
return bitcast(i, double);
}
}
@@ -1118,8 +1086,8 @@ fn float _frexpf(float x, int* e)
return x;
default:
*e = ee - 0x7e;
i &= 0x807fffffu32;
i |= 0x3f000000u32;
i &= 0x807fffffU;
i |= 0x3f000000U;
return bitcast(i, float);
}
}
@@ -1145,6 +1113,33 @@ macro overflow_mul_helper(x, y) @local
return res;
}
<*
@param [&out] out : "Where the result of the addition is stored"
@return "Whether the addition resulted in an integer overflow"
@require values::@is_same_type(a, b) : "a and b must be the same type"
@require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based"
@require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
*>
macro bool overflow_add(a, b, out) => $$overflow_add(a, b, out);
<*
@param [&out] out : "Where the result of the subtraction is stored"
@return "Whether the subtraction resulted in an integer overflow"
@require values::@is_same_type(a, b) : "a and b must be the same type"
@require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based"
@require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
*>
macro bool overflow_sub(a, b, out) => $$overflow_sub(a, b, out);
<*
@param [&out] out : "Where the result of the multiplication is stored"
@return "Whether the multiplication resulted in an integer overflow"
@require values::@is_same_type(a, b) : "a and b must be the same type"
@require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based"
@require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
*>
macro bool overflow_mul(a, b, out) => $$overflow_mul(a, b, out);
<*
@require types::is_vector($Type) || ($Type.kindof == ARRAY &&& types::is_numerical($typefrom($Type.inner)))
*>
@@ -1202,31 +1197,31 @@ macro ushort[<*>] ushort[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul
<*
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro int[<*>] int[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
<*
@require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro uint[<*>] uint[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
<*
@require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
<*
@require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
*>
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
<*
@require types::is_int($typeof(a)) `The input must be an integer`
@require types::is_int($typeof(b)) `The input must be an integer`
@require types::is_int($typeof(a)) : `The input must be an integer`
@require types::is_int($typeof(b)) : `The input must be an integer`
*>
macro _gcd(a, b) @private
{
@@ -1247,9 +1242,9 @@ macro _gcd(a, b) @private
}
<*
Calculate the least common multiple for the provided arguments.
Calculate the least common multiple for the provided arguments.
@require $vacount >= 2 `At least two arguments are required.`
@require $vacount >= 2 : "At least two arguments are required."
*>
macro lcm(...)
{
@@ -1265,9 +1260,9 @@ macro lcm(...)
}
<*
Calculate the greatest common divisor for the provided arguments.
Calculate the greatest common divisor for the provided arguments.
@require $vacount >= 2 `At least two arguments are required.`
@require $vacount >= 2 : "At least two arguments are required."
*>
macro gcd(...)
{

View File

@@ -1,31 +0,0 @@
module std::math::complex{Real};
union Complex
{
struct
{
Real r, c;
}
Real[<2>] v;
}
const Complex IDENTITY = { 1, 0 };
const Complex IMAGINARY = { 0, 1 };
macro Complex Complex.add(self, Complex b) => { .v = self.v + b.v };
macro Complex Complex.add_each(self, Real b) => { .v = self.v + b };
macro Complex Complex.sub(self, Complex b) => { .v = self.v - b.v };
macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b };
macro Complex Complex.scale(self, Real s) => { .v = self.v * s };
macro Complex Complex.mul(self, Complex b) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
macro Complex Complex.div(self, Complex b)
{
Real div = b.v.dot(b.v);
return { (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
}
macro Complex Complex.inverse(self)
{
Real sqr = self.v.dot(self.v);
return { self.r / sqr, -self.c / sqr };
}
macro Complex Complex.conjugate(self) => { .r = self.r, .c = -self.c };
macro bool Complex.equals(self, Complex b) => self.v == b.v;

View File

@@ -1,47 +0,0 @@
/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
/* atan(x)
* Method
* 1. Reduce x to positive by atan(x) = -atan(-x).
* 2. According to the integer k=4t+0.25 chopped, t=x, the argument
* is further reduced to one of the following intervals and the
* arctangent of t is evaluated by the corresponding formula:
*
* [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...)
* [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) )
* [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) )
* [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) )
* [39/16,INF] atan(x) = atan(INF) + atan( -1/t )
*
* Constants:
* The hexadecimal values are the intended ones for the following
* constants. The decimal values may be used, provided that the
* compiler will convert from decimal to binary accurately enough
* to produce the hexadecimal values shown.
*/
/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
*/
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
module std::math;

View File

@@ -35,7 +35,7 @@ fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
}
else
{
uxi &= -1UL >> 12;
uxi &= (ulong)-1 >> 12;
uxi |= 1UL << 52;
}
if (!ey)
@@ -45,7 +45,7 @@ fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
}
else
{
uy.i &= -1UL >> 12;
uy.i &= (ulong)-1 >> 12;
uy.i |= 1UL << 52;
}
@@ -105,7 +105,7 @@ fn float fmodf(float x, float y) @extern("fmodf") @weak @nostrip
}
else
{
uxi &= -1U >> 9;
uxi &= (uint)-1 >> 9;
uxi |= 1U << 23;
}
if (!ey)
@@ -115,7 +115,7 @@ fn float fmodf(float x, float y) @extern("fmodf") @weak @nostrip
}
else
{
uy.i &= -1U >> 9;
uy.i &= (uint)-1 >> 9;
uy.i |= 1U << 23;
}

View File

@@ -29,7 +29,7 @@ fn double _atanh(double x) @weak @extern("atanh") @nostrip
}
return double.nan;
/* x<2**-28 */
case ix < 0x3e300000 && (1e300 + x) > 0.:
case ix < 0x3e300000 && (1e300 + x) > 0.0:
return x;
}
x.set_high_word(ix);
@@ -37,11 +37,11 @@ fn double _atanh(double x) @weak @extern("atanh") @nostrip
if (ix < 0x3fe00000)
{
t = x + x;
t = 0.5 * _log1p(t + t * x / (1. - x));
t = 0.5 * _log1p(t + t * x / (1.0 - x));
}
else
{
t = 0.5 * _log1p((x + x) / (1. - x));
t = 0.5 * _log1p((x + x) / (1.0 - x));
}
return sign ? -t : t;
}

View File

@@ -47,13 +47,13 @@ fn double _exp2_specialcase(double tmp, ulong sbits, ulong ki) @private
if (ki & 0x80000000 == 0)
{
// k > 0, the exponent of scale might have overflowed by 1.
sbits -= 1u64 << 52;
sbits -= 1UL << 52;
double scale = bitcast(sbits, double);
double y = 2 * (scale + scale * tmp);
return y;
}
// k < 0, need special care in the subnormal range.
sbits += 1022u64 << 52;
sbits += 1022UL << 52;
double scale = bitcast(sbits, double);
double y = scale + scale * tmp;
if (y >= 1.0)

View File

@@ -90,7 +90,7 @@ fn double _log1p(double x) @weak @extern("log1p") @nostrip
/* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
if (k < 54)
{
c = (k >= 2) ? 1. - (u - x) : x - (u - 1.);
c = (k >= 2) ? 1.0 - (u - x) : x - (u - 1.0);
c /= u;
}
else
@@ -100,10 +100,10 @@ fn double _log1p(double x) @weak @extern("log1p") @nostrip
/* reduce u into [sqrt(2)/2, sqrt(2)] */
hu = (hu & 0x000fffff) + 0x3fe6a09e;
u = bitcast(((ulong)hu << 32) | (bitcast(u, ulong) & 0xffffffff) , double);
f = u - 1.;
f = u - 1.0;
}
double hfsq = 0.5 * f * f;
double s = f / (2. + f);
double s = f / (2.0 + f);
double z = s * s;
double w = z * z;
double t1 = w * (LG2 + w * (LG4 + w * LG6));

View File

@@ -109,7 +109,7 @@ const int[*] IPIO2 = {
0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880,
0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, };
const double[*] PIO2 = {
const double[*] PIO2 @local = {
1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */
7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */
5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */

View File

@@ -1,85 +0,0 @@
module std::math::quaternion{Real};
import std::math::vector;
union Quaternion
{
struct
{
Real i, j, k, l;
}
Real[<4>] v;
}
const Quaternion IDENTITY = { 0, 0, 0, 1 };
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => { .v = a.v + b.v };
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => { .v = a.v + b };
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => { .v = a.v - b.v };
macro Quaternion Quaternion.sub_each(Quaternion a, Real b) => { .v = a.v - b };
macro Quaternion Quaternion.scale(Quaternion a, Real s) => { .v = a.v * s };
macro Quaternion Quaternion.normalize(Quaternion q) => { .v = q.v.normalize() };
macro Real Quaternion.length(Quaternion q) => q.v.length();
macro Quaternion Quaternion.lerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount) };
macro Matrix4f Quaternion.to_matrixf(Quaternion* q) => into_matrix(q, Matrix4f);
macro Matrix4 Quaternion.to_matrix(Quaternion* q) => into_matrix(q, Matrix4);
fn Quaternion Quaternion.nlerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount).normalize() };
fn Quaternion Quaternion.invert(q)
{
Real length_sq = q.v.dot(q.v);
if (length_sq <= 0) return q;
Real inv_length = 1 / length_sq;
return { q.v[0] * -inv_length, q.v[1] * -inv_length, q.v[2] * -inv_length, q.v[3] * inv_length };
}
fn Quaternion Quaternion.slerp(q1, Quaternion q2, Real amount)
{
Quaternion result = {};
Real[<4>] q2v = q2.v;
Real cos_half_theta = q1.v.dot(q2v);
if (cos_half_theta < 0)
{
q2v = -q2v;
cos_half_theta = -cos_half_theta;
}
if (cos_half_theta >= 1) return q1;
Real[<4>] q1v = q1.v;
if (cos_half_theta > 0.95f) return { .v = q1v.lerp(q2v, amount) };
Real half_theta = math::cos(cos_half_theta);
Real sin_half_theta = math::sqrt(1 - cos_half_theta * cos_half_theta);
if (math::abs(sin_half_theta) < 0.001f)
{
return { .v = (q1v + q2v) * 0.5f };
}
Real ratio_a = math::sin((1 - amount) * half_theta) / sin_half_theta;
Real ratio_b = math::sin(amount * half_theta) / sin_half_theta;
return { .v = q1v * ratio_a + q2v * ratio_b };
}
fn Quaternion Quaternion.mul(a, Quaternion b)
{
return { a.i * b.l + a.l * b.i + a.j * b.k - a.k * b.j,
a.j * b.l + a.l * b.j + a.k * b.i - a.i * b.k,
a.k * b.l + a.l * b.k + a.i * b.j - a.j * b.i,
a.l * b.l - a.i * b.i - a.j * a.j - a.k * a.k };
}
macro into_matrix(Quaternion* q, $Type) @private
{
Quaternion rotation = q.normalize();
var x = rotation.i;
var y = rotation.j;
var z = rotation.k;
var w = rotation.l;
return ($Type) {
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0,
0.0, 0.0, 0.0, 1.0,
};
}

View File

@@ -1,4 +1,32 @@
module std::math::matrix{Real};
module std::math;
// Predefined matrix types
alias Matrix2f = Matrix2x2 {float};
alias Matrix2 = Matrix2x2 {double};
alias Matrix3f = Matrix3x3 {float};
alias Matrix3 = Matrix3x3 {double};
alias Matrix4f = Matrix4x4 {float};
alias Matrix4 = Matrix4x4 {double};
// Predefined matrix functions
alias matrix4_ortho @builtin = matrix::ortho {double};
alias matrix4f_ortho @builtin = matrix::ortho {float};
alias matrix4_perspective @builtin = matrix::perspective {double};
alias matrix4f_perspective @builtin = matrix::perspective {float};
alias MATRIX2_IDENTITY @builtin = matrix::IDENTITY2 {double};
alias MATRIX2F_IDENTITY @builtin = matrix::IDENTITY2 {float};
alias MATRIX3_IDENTITY @builtin = matrix::IDENTITY3 {double};
alias MATRIX3F_IDENTITY @builtin = matrix::IDENTITY3 {float};
alias MATRIX4_IDENTITY @builtin = matrix::IDENTITY4 {double};
alias MATRIX4F_IDENTITY @builtin = matrix::IDENTITY4 {float};
<*
The generic matrix module, for float or double based matrix definitions.
@require Real.kindof == FLOAT : "A matrix must use a floating type"
*>
module std::math::matrix {Real};
import std::math::vector;
struct Matrix2x2
@@ -43,7 +71,7 @@ struct Matrix4x4
}
}
fn Real[<2>] Matrix2x2.apply(&self, Real[<2>] vec)
fn Real[<2>] Matrix2x2.apply(&self, Real[<2>] vec) @operator(*)
{
return {
self.m00 * vec[0] + self.m01 * vec[1],
@@ -51,7 +79,7 @@ fn Real[<2>] Matrix2x2.apply(&self, Real[<2>] vec)
};
}
fn Real[<3>] Matrix3x3.apply(&self, Real[<3>] vec)
fn Real[<3>] Matrix3x3.apply(&self, Real[<3>] vec) @operator(*)
{
return {
self.m00 * vec[0] + self.m01 * vec[1] + self.m02 * vec[2],
@@ -60,7 +88,7 @@ fn Real[<3>] Matrix3x3.apply(&self, Real[<3>] vec)
};
}
fn Real[<4>] Matrix4x4.apply(&self, Real[<4>] vec)
fn Real[<4>] Matrix4x4.apply(&self, Real[<4>] vec) @operator(*)
{
return {
self.m00 * vec[0] + self.m01 * vec[1] + self.m02 * vec[2] + self.m03 * vec[3],
@@ -71,7 +99,7 @@ fn Real[<4>] Matrix4x4.apply(&self, Real[<4>] vec)
}
fn Matrix2x2 Matrix2x2.mul(&self, Matrix2x2 b)
fn Matrix2x2 Matrix2x2.mul(&self, Matrix2x2 b) @operator(*)
{
return {
self.m00 * b.m00 + self.m01 * b.m10, self.m00 * b.m01 + self.m01 * b.m11,
@@ -79,7 +107,7 @@ fn Matrix2x2 Matrix2x2.mul(&self, Matrix2x2 b)
};
}
fn Matrix3x3 Matrix3x3.mul(&self, Matrix3x3 b)
fn Matrix3x3 Matrix3x3.mul(&self, Matrix3x3 b) @operator(*)
{
return {
self.m00 * b.m00 + self.m01 * b.m10 + self.m02 * b.m20,
@@ -96,28 +124,28 @@ fn Matrix3x3 Matrix3x3.mul(&self, Matrix3x3 b)
};
}
fn Matrix4x4 Matrix4x4.mul(Matrix4x4* a, Matrix4x4 b)
fn Matrix4x4 Matrix4x4.mul(Matrix4x4* self, Matrix4x4 b) @operator(*)
{
return {
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30,
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31,
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32,
a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33,
self.m00 * b.m00 + self.m01 * b.m10 + self.m02 * b.m20 + self.m03 * b.m30,
self.m00 * b.m01 + self.m01 * b.m11 + self.m02 * b.m21 + self.m03 * b.m31,
self.m00 * b.m02 + self.m01 * b.m12 + self.m02 * b.m22 + self.m03 * b.m32,
self.m00 * b.m03 + self.m01 * b.m13 + self.m02 * b.m23 + self.m03 * b.m33,
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30,
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31,
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32,
a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33,
self.m10 * b.m00 + self.m11 * b.m10 + self.m12 * b.m20 + self.m13 * b.m30,
self.m10 * b.m01 + self.m11 * b.m11 + self.m12 * b.m21 + self.m13 * b.m31,
self.m10 * b.m02 + self.m11 * b.m12 + self.m12 * b.m22 + self.m13 * b.m32,
self.m10 * b.m03 + self.m11 * b.m13 + self.m12 * b.m23 + self.m13 * b.m33,
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30,
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31,
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32,
a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33,
self.m20 * b.m00 + self.m21 * b.m10 + self.m22 * b.m20 + self.m23 * b.m30,
self.m20 * b.m01 + self.m21 * b.m11 + self.m22 * b.m21 + self.m23 * b.m31,
self.m20 * b.m02 + self.m21 * b.m12 + self.m22 * b.m22 + self.m23 * b.m32,
self.m20 * b.m03 + self.m21 * b.m13 + self.m22 * b.m23 + self.m23 * b.m33,
a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30,
a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31,
a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32,
a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33,
self.m30 * b.m00 + self.m31 * b.m10 + self.m32 * b.m20 + self.m33 * b.m30,
self.m30 * b.m01 + self.m31 * b.m11 + self.m32 * b.m21 + self.m33 * b.m31,
self.m30 * b.m02 + self.m31 * b.m12 + self.m32 * b.m22 + self.m33 * b.m32,
self.m30 * b.m03 + self.m31 * b.m13 + self.m32 * b.m23 + self.m33 * b.m33,
};
}
@@ -125,13 +153,25 @@ fn Matrix2x2 Matrix2x2.component_mul(&self, Real s) => matrix_component_mul(self
fn Matrix3x3 Matrix3x3.component_mul(&self, Real s) => matrix_component_mul(self, s);
fn Matrix4x4 Matrix4x4.component_mul(&self, Real s) => matrix_component_mul(self, s);
fn Matrix2x2 Matrix2x2.add(&self, Matrix2x2 mat2) => matrix_add(self, mat2);
fn Matrix3x3 Matrix3x3.add(&self, Matrix3x3 mat2) => matrix_add(self, mat2);
fn Matrix4x4 Matrix4x4.add(&self, Matrix4x4 mat2) => matrix_add(self, mat2);
fn Matrix2x2 Matrix2x2.add(&self, Matrix2x2 mat2) @operator(+) => matrix_add(self, mat2);
fn Matrix3x3 Matrix3x3.add(&self, Matrix3x3 mat2) @operator(+) => matrix_add(self, mat2);
fn Matrix4x4 Matrix4x4.add(&self, Matrix4x4 mat2) @operator(+) => matrix_add(self, mat2);
fn Matrix2x2 Matrix2x2.sub(&self, Matrix2x2 mat2) => matrix_sub(self, mat2);
fn Matrix3x3 Matrix3x3.sub(&self, Matrix3x3 mat2) => matrix_sub(self, mat2);
fn Matrix4x4 Matrix4x4.sub(&self, Matrix4x4 mat2) => matrix_sub(self, mat2);
fn Matrix2x2 Matrix2x2.sub(&self, Matrix2x2 mat2) @operator(-) => matrix_sub(self, mat2);
fn Matrix3x3 Matrix3x3.sub(&self, Matrix3x3 mat2) @operator(-) => matrix_sub(self, mat2);
fn Matrix4x4 Matrix4x4.sub(&self, Matrix4x4 mat2) @operator(-) => matrix_sub(self, mat2);
fn Matrix2x2 Matrix2x2.negate(&self) @operator(-) => { .m = (Real[<4>])self.m };
fn Matrix3x3 Matrix3x3.negate(&self) @operator(-) => { .m = (Real[<9>])self.m };
fn Matrix4x4 Matrix4x4.negate(&self) @operator(-) => { .m = (Real[<16>])self.m };
fn bool Matrix2x2.eq(&self, Matrix2x2 mat2) @operator(==) => (Real[<4>])self.m == (Real[<4>])mat2.m;
fn bool Matrix3x3.eq(&self, Matrix3x3 mat2) @operator(==) => (Real[<9>])self.m == (Real[<9>])mat2.m;
fn bool Matrix4x4.eq(&self, Matrix4x4 mat2) @operator(==) => (Real[<16>])self.m == (Real[<16>])mat2.m;
fn bool Matrix2x2.neq(&self, Matrix2x2 mat2) @operator(!=) => (Real[<4>])self.m != (Real[<4>])mat2.m;
fn bool Matrix3x3.neq(&self, Matrix3x3 mat2) @operator(!=) => (Real[<9>])self.m != (Real[<9>])mat2.m;
fn bool Matrix4x4.neq(&self, Matrix4x4 mat2) @operator(!=) => (Real[<16>])self.m != (Real[<16>])mat2.m;
fn Matrix4x4 look_at(Real[<3>] eye, Real[<3>] target, Real[<3>] up) => matrix_look_at(Matrix4x4, eye, target, up);

101
lib/std/math/quaternion.c3 Normal file
View File

@@ -0,0 +1,101 @@
module std::math;
// Predefined quaternion aliases.
alias Quaternionf = Quaternion {float};
alias Quaternion = Quaternion {double};
alias QUATERNION_IDENTITY @builtin = quaternion::IDENTITY {double};
alias QUATERNIONF_IDENTITY @builtin = quaternion::IDENTITY {float};
<*
The generic quaternion module, for float or double based quaternion definitions.
@require Real.kindof == FLOAT : "A quaternion must use a floating type"
*>
module std::math::quaternion {Real};
import std::math::vector;
union Quaternion
{
struct
{
Real i, j, k, l;
}
Real[<4>] v;
}
const Quaternion IDENTITY = { 0, 0, 0, 1 };
macro Quaternion Quaternion.add(self, Quaternion b) @operator(+) => { .v = self.v + b.v };
macro Quaternion Quaternion.add_each(self, Real b) => { .v = self.v + b };
macro Quaternion Quaternion.sub(self, Quaternion b) @operator(-) => { .v = self.v - b.v };
macro Quaternion Quaternion.negate(self) @operator(-) => { .v = -self.v };
macro Quaternion Quaternion.sub_each(self, Real b) => { .v = self.v - b };
macro Quaternion Quaternion.scale(self, Real s) @operator_s(*) => { .v = self.v * s };
macro Quaternion Quaternion.normalize(self) => { .v = self.v.normalize() };
macro Real Quaternion.length(self) => self.v.length();
macro Quaternion Quaternion.lerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount) };
macro Matrix4f Quaternion.to_matrixf(&self) => into_matrix(self, Matrix4f);
macro Matrix4 Quaternion.to_matrix(&self) => into_matrix(self, Matrix4);
fn Quaternion Quaternion.nlerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount).normalize() };
fn Quaternion Quaternion.invert(self)
{
Real length_sq = self.v.dot(self.v);
if (length_sq <= 0) return self;
Real inv_length = 1 / length_sq;
return { self.v[0] * -inv_length, self.v[1] * -inv_length, self.v[2] * -inv_length, self.v[3] * inv_length };
}
fn Quaternion Quaternion.slerp(self, Quaternion q2, Real amount)
{
Quaternion result = {};
Real[<4>] q2v = q2.v;
Real cos_half_theta = self.v.dot(q2v);
if (cos_half_theta < 0)
{
q2v = -q2v;
cos_half_theta = -cos_half_theta;
}
if (cos_half_theta >= 1) return self;
Real[<4>] q1v = self.v;
if (cos_half_theta > 0.95f) return { .v = q1v.lerp(q2v, amount) };
Real half_theta = math::cos(cos_half_theta);
Real sin_half_theta = math::sqrt(1 - cos_half_theta * cos_half_theta);
if (math::abs(sin_half_theta) < 0.001f)
{
return { .v = (q1v + q2v) * 0.5f };
}
Real ratio_a = math::sin((1 - amount) * half_theta) / sin_half_theta;
Real ratio_b = math::sin(amount * half_theta) / sin_half_theta;
return { .v = q1v * ratio_a + q2v * ratio_b };
}
fn Quaternion Quaternion.mul(self, Quaternion b) @operator(*)
{
return { self.i * b.l + self.l * b.i + self.j * b.k - self.k * b.j,
self.j * b.l + self.l * b.j + self.k * b.i - self.i * b.k,
self.k * b.l + self.l * b.k + self.i * b.j - self.j * b.i,
self.l * b.l - self.i * b.i - self.j * self.j - self.k * self.k };
}
macro into_matrix(Quaternion* q, $Type) @private
{
Quaternion rotation = q.normalize();
var x = rotation.i;
var y = rotation.j;
var z = rotation.k;
var w = rotation.l;
return ($Type) {
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0,
0.0, 0.0, 0.0, 1.0,
};
}

View File

@@ -118,7 +118,7 @@ macro bool next_bool(random)
macro float next_float(random)
{
uint val = random.next_int() & (1 << 24 - 1);
return val / (float)(1 << 24);
return val * 0x1.0p-24f;
}
<*

View File

@@ -31,4 +31,4 @@ fn char SimpleRandom.next_byte(&self) @dynamic => (char)self.next_int();
const long SIMPLE_RANDOM_MULTIPLIER @local = 0x5DEECE66D;
const long SIMPLE_RANDOM_ADDEND @local = 0xB;
const long SIMPLE_RANDOM_MASK @local = (1u64 << 48) - 1;
const long SIMPLE_RANDOM_MASK @local = (1UL << 48) - 1;

View File

@@ -1,4 +1,4 @@
module std::math;
module std::math::math_rt;
fn int128 __divti3(int128 a, int128 b) @extern("__divti3") @weak @nostrip
{
@@ -312,21 +312,21 @@ macro float_from_i128($Type, a) @private
$switch $Type:
$case double:
$Rep = ulong;
const MANT_DIG = DOUBLE_MANT_DIG;
const MANT_DIG = math::DOUBLE_MANT_DIG;
const SIGNIFICANT_BITS = 52;
const EXP_BIAS = 1023;
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
const SIGN_BIT = 1u64 << 63;
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFUL;
const SIGN_BIT = 1UL << 63;
$case float:
$Rep = uint;
const MANT_DIG = FLOAT_MANT_DIG;
const MANT_DIG = math::FLOAT_MANT_DIG;
const EXP_BIAS = 127;
const SIGNIFICANT_BITS = 23;
const MANTISSA_MASK = 0x7F_FFFFu32;
const SIGN_BIT = 1u32 << 31;
const MANTISSA_MASK = 0x7F_FFFFU;
const SIGN_BIT = 1U << 31;
$case float16:
$Rep = ushort;
const MANT_DIG = HALF_MANT_DIG;
const MANT_DIG = math::HALF_MANT_DIG;
$case float128:
$Rep = uint128;
const MANT_DIG = QUAD_MANT_DIG;
@@ -352,7 +352,7 @@ macro float_from_i128($Type, a) @private
a |= (uint128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << MANT_DIG))
if (a & (1LL << MANT_DIG))
{
a >>= 1;
e++;
@@ -371,22 +371,22 @@ macro float_from_u128($Type, a) @private
$switch $Type:
$case double:
$Rep = ulong;
const MANT_DIG = DOUBLE_MANT_DIG;
const MANT_DIG = math::DOUBLE_MANT_DIG;
const SIGNIFICANT_BITS = 52;
const EXP_BIAS = 1023;
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFUL;
$case float:
$Rep = uint;
const MANT_DIG = FLOAT_MANT_DIG;
const MANT_DIG = math::FLOAT_MANT_DIG;
const EXP_BIAS = 127;
const SIGNIFICANT_BITS = 23;
const MANTISSA_MASK = 0x7F_FFFFu32;
const MANTISSA_MASK = 0x7F_FFFFU;
$case float16:
$Rep = ushort;
const MANT_DIG = HALF_MANT_DIG;
const MANT_DIG = math::HALF_MANT_DIG;
$case float128:
$Rep = uint128;
const MANT_DIG = QUAD_MANT_DIG;
const MANT_DIG = math::QUAD_MANT_DIG;
$endswitch
if (a == 0) return ($Type)0;
int sd = 128 - (int)$$clz(a); // digits
@@ -406,7 +406,7 @@ macro float_from_u128($Type, a) @private
a |= (uint128)((a & 4) != 0);
a++;
a >>= 2;
if (a & (1i128 << MANT_DIG))
if (a & (1LL << MANT_DIG))
{
a >>= 1;
e++;
@@ -458,8 +458,8 @@ macro fixuint(a) @private
int sign = rep & SIGN_BIT ? -1 : 1;
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
if (sign == -1 || exponent < 0) return 0u128;
if ((uint)exponent >= uint128.sizeof * 8) return ~0u128;
if (sign == -1 || exponent < 0) return 0ULL;
if ((uint)exponent >= uint128.sizeof * 8) return ~0ULL;
if (exponent < SIGNIFICANT_BITS) return (uint128)significand >> (SIGNIFICANT_BITS - exponent);
return (uint128)significand << (exponent - SIGNIFICANT_BITS);
}

View File

@@ -1,15 +1,15 @@
module std::math;
module std::math::math_rt;
fn float __roundevenf(float f) @extern("roundevenf") @weak @nostrip
{
// Slow implementation
return round(f / 2) * 2;
return math::round(f / 2) * 2;
}
fn double __roundeven(double d) @extern("roundeven") @weak @nostrip
{
// Slow implementation
return round(d / 2) * 2;
return math::round(d / 2) * 2;
}
fn double __powidf2(double a, int b) @extern("__powidf2") @weak @nostrip

View File

@@ -1,3 +1,5 @@
// Vector supplemental methods
module std::math::vector;
import std::math;
@@ -51,6 +53,8 @@ fn double[<3>] double[<3>].unproject(self, Matrix4 projection, Matrix4 view) =>
fn void ortho_normalize(float[<3>]* v1, float[<3>]* v2) => ortho_normalize3(v1, v2);
fn void ortho_normalized(double[<3>]* v1, double[<3>]* v2) => ortho_normalize3(v1, v2);
// -- private helpers
macro towards(v, target, max_distance) @private
{
var delta = target - v;
@@ -80,7 +84,7 @@ macro rotate(v, angle) @private
{
var c = math::cos(angle);
var s = math::sin(angle);
return $typeof(v) { v[0] * c - v[1] * s, v[0] * s + v[1] * c };
return ($typeof(v)) { v[0] * c - v[1] * s, v[0] * s + v[1] * c };
}
macro perpendicular3(v) @private
@@ -111,7 +115,7 @@ macro cross3(v1, v2) @private
macro transform2(v, mat) @private
{
return $typeof(v) {
return ($typeof(v)) {
mat.m00 * v[0] + mat.m10 * v[1] + mat.m30 ,
mat.m01 * v[0] + mat.m11 * v[1] + mat.m31 };
}

View File

@@ -1,406 +0,0 @@
module std::net::http;
/*
enum HttpStatus
{
PENDING,
COMPLETED,
FAILED
}
struct Http
{
HttpStatus status;
int status_code;
String reason;
String content_type;
String response_data;
}
fn Http* http_get(String url, Allocator using = allocator::temp())
{
return null;
}
fn Http* http_post(String url, Allocator using = allocator::temp())
{
return null;
}
fn void Http.destroy(Http* this)
{
}
fn HttpStatus Http.process(Http* this)
{
unreachable();
}
// Common across implementations
struct HttpInternal @private
{
inline Http http;
Allocator allocator;
int connect_pending;
int request_sent;
char[256] address;
char[256] request_header;
char* request_header_large;
String request_data;
char[1024] reason_phrase;
char[256] content_type;
usz data_size;
usz data_capacity;
void* data;
}
fn String? parse_url(String url, String* port, String* resource) @private
{
if (url[:7] != "http://") return NetError.INVALID_URL?;
url = url[7..];
usz end_index = url.index_of(":") ?? url.index_of("/") ?? url.len;
String address = url[:end_index];
String end_part = url[end_index..];
if (!end_part.len)
{
*port = "80";
*resource = {};
return address;
}
switch (end_part[0])
{
case ':':
end_index = end_part.index_of("/") ?? end_part.len;
end_part[:end_index].to_uint() ?? NetError.INVALID_URL?!;
*port = end_part[:end_index];
*resource = url[end_index..];
case '/':
*port = "80";
*resource = end_part;
default:
unreachable();
}
return address;
}
fn Socket? http_internal_connect(String address, uint port) @private
{
return tcp::connect_async(address, port)!;
}
fn HttpInternal* http_internal_create(usz request_data_size, Allocator allocator) @private
{
HttpInternal* internal = allocator.alloc(HttpInternal.sizeof + request_data_size)!!;
internal.status = PENDING;
internal.status_code = 0;
internal.response_data = {};
internal.allocator = allocator;
internal.connect_pending = 1;
internal.request_sent = 0;
// internal.reason = "";
// internal.content_type = "";
internal.data_size = 0;
internal.data_capacity = 64 * 1024;
internal.data = allocator.alloc(internal.data_capacity)!!;
internal.request_data = {};
return internal;
}
fn Http*? http_get(String url, Allocator allocator = allocator::temp())
{
$if env::WIN32:
int[1024] wsa_data;
if (_wsa_startup(1, &wsa_data) != 0) return NetError.GENERAL_ERROR?;
$endif
uint port;
String resource;
String address = parse_url(url, &port, &resource)?;
Socket socket = tcp::connect(address, port)?;
HttpInternal* internal = http_internal_create(0, allocator);
internal.socket = socket;
char* request_header;
usz request_header_len = 64 + resource.len + address.len + port.len;
if (request_header_len < sizeof(internal.request_header))
{
internal.request_header_large = null;
request_header = internal.request_header;
}
else
{
internal.request_header_large = (char*)allocator.malloc(request_header_len + 1);
request_header = internal.request_header_large;
}
int default_http_port = port == "80";
sprintf( request_header, "GET %s HTTP/1.0\r\nHost: %s%s%s\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port );
return internal;
}
fn Http*? http_post(String url, Allocator allocator = allocator::temp())
{
$if env::OS_TYPE == OsType::WIN32:
int[1024] wsa_data;
if (_wsa_startup(1, &wsa_data) != 0) return NetError.GENERAL_ERROR?;
$endif
String port;
String resource;
String address = parse_url(url, &port, &resource)?;
Socket socket = http_internal_connect(address, port)?;
HttpInternal* internal = http_internal_create(0, allocator);
internal.socket = socket;
char* request_header;
uz request_header_len = 64 + resource.len + address.len + port.len;
if (request_header_len < sizeof(internal.request_header))
{
internal.request_header_large = null;
request_header = internal.request_header;
}
else
{
internal.request_header_large = (char*)allocator.malloc(request_header_len + 1);
request_header = internal.request_header_large;
}
int default_http_port = port == "80";
sprintf( request_header, "POST %s HTTP/1.0\r\nHost: %s%s%s\r\nContent-Length: %d\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port,
(int) size );
internal->request_data_size = size;
internal->request_data = ( internal + 1 );
memcpy( internal->request_data, data, size );
return internal;
}
fn HttpStatus Http.process(Http* http)
{
HttpInternal* internal = (HttpInternal*)http;
if (http.status == HttpStatus.FAILED) return http.status;
if (internal.connect_pending)
{
fd_set sockets_to_check;
FD_ZERO(&sockets_to_check);
FD_SET( internal->socket, &sockets_to_check );
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
// check if socket is ready for send
if( select( (int)( internal->socket + 1 ), NULL, &sockets_to_check, NULL, &timeout ) == 1 )
{
int opt = -1;
socklen_t len = sizeof( opt );
if( getsockopt( internal->socket, SOL_SOCKET, SO_ERROR, (char*)( &opt ), &len) >= 0 && opt == 0 )
{
internal->connect_pending = 0; // if it is, we're connected
}
}
}
if (internal.connect_pending) retur http.status;
if (!internal.request_sent)
{
char* request_header = internal->request_header_large ?
internal.request_header_large : internal.request_header;
if (send(internal.socket, request_header, (int) strlen( request_header ), 0) == -1)
{
return http.status = FAILED;
}
if (internal.request_data_size)
{
int res = send(internal.socket, (char const*)internal->request_data, (int) internal->request_data_size, 0 );
if (res == -1)
{
http.status = HTTP_STATUS_FAILED;
return http.status;
}
}
internal.request_sent = 1;
return http.status;
}
// check if socket is ready for recv
fd_set sockets_to_check;
FD_ZERO( &sockets_to_check );
#pragma warning( push )
#pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
FD_SET( internal->socket, &sockets_to_check );
#pragma warning( pop )
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
}
http_status_t http_process( http_t* http )
{
while( select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 )
{
char buffer[ 4096 ];
int size = recv( internal->socket, buffer, sizeof( buffer ), 0 );
if( size == -1 )
{
http->status = HTTP_STATUS_FAILED;
return http->status;
}
else if( size > 0 )
{
size_t min_size = internal->data_size + size + 1;
if( internal->data_capacity < min_size )
{
internal->data_capacity *= 2;
if( internal->data_capacity < min_size ) internal->data_capacity = min_size;
void* new_data = HTTP_MALLOC( memctx, internal->data_capacity );
memcpy( new_data, internal->data, internal->data_size );
HTTP_FREE( memctx, internal->data );
internal->data = new_data;
}
memcpy( (void*)( ( (uintptr_t) internal->data ) + internal->data_size ), buffer, (size_t) size );
internal->data_size += size;
}
else if( size == 0 )
{
char const* status_line = (char const*) internal->data;
int header_size = 0;
char const* header_end = strstr( status_line, "\r\n\r\n" );
if( header_end )
{
header_end += 4;
header_size = (int)( header_end - status_line );
}
else
{
http->status = HTTP_STATUS_FAILED;
return http->status;
}
// skip http version
status_line = strchr( status_line, ' ' );
if( !status_line )
{
http->status = HTTP_STATUS_FAILED;
return http->status;
}
++status_line;
// extract status code
char status_code[ 16 ];
char const* status_code_end = strchr( status_line, ' ' );
if( !status_code_end )
{
http->status = HTTP_STATUS_FAILED;
return http->status;
}
memcpy( status_code, status_line, (size_t)( status_code_end - status_line ) );
status_code[ status_code_end - status_line ] = 0;
status_line = status_code_end + 1;
http->status_code = atoi( status_code );
// extract reason phrase
char const* reason_phrase_end = strstr( status_line, "\r\n" );
if( !reason_phrase_end )
{
http->status = HTTP_STATUS_FAILED;
return http->status;
}
size_t reason_phrase_len = (size_t)( reason_phrase_end - status_line );
if( reason_phrase_len >= sizeof( internal->reason_phrase ) )
reason_phrase_len = sizeof( internal->reason_phrase ) - 1;
memcpy( internal->reason_phrase, status_line, reason_phrase_len );
internal->reason_phrase[ reason_phrase_len ] = 0;
status_line = reason_phrase_end + 1;
// extract content type
char const* content_type_start = strstr( status_line, "Content-Type: " );
if( content_type_start )
{
content_type_start += strlen( "Content-Type: " );
char const* content_type_end = strstr( content_type_start, "\r\n" );
if( content_type_end )
{
size_t content_type_len = (size_t)( content_type_end - content_type_start );
if( content_type_len >= sizeof( internal->content_type ) )
content_type_len = sizeof( internal->content_type ) - 1;
memcpy( internal->content_type, content_type_start, content_type_len );
internal->content_type[ content_type_len ] = 0;
}
}
http->status = http->status_code < 300 ? HTTP_STATUS_COMPLETED : HTTP_STATUS_FAILED;
http->response_data = (void*)( ( (uintptr_t) internal->data ) + header_size );
http->response_size = internal->data_size - header_size;
// add an extra zero after the received data, but don't modify the size, so ascii results can be used as
// a zero terminated string. the size returned will be the string without this extra zero terminator.
( (char*)http->response_data )[ http->response_size ] = 0;
return http->status;
}
}
return http->status;
}
void http_release( http_t* http )
{
http_internal_t* internal = (http_internal_t*) http;
#ifdef _WIN32
closesocket( internal->socket );
#else
close( internal->socket );
#endif
if( internal->request_header_large) HTTP_FREE( memctx, internal->request_header_large );
HTTP_FREE( memctx, internal->data );
HTTP_FREE( memctx, internal );
#ifdef _WIN32
WSACleanup();
#endif
}
#endif /* HTTP_IMPLEMENTATION */
/*
revision history:
1.0 first released version
*/
/*
------------------------------------------------------------------------------
This software is available under 2 licenses - you may choose the one you like.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2016 Mattias Gustavsson
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.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
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 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.
------------------------------------------------------------------------------
*/*/

View File

@@ -1,6 +1,5 @@
module std::net;
import std::io;
import std::ascii;
enum IpProtocol : char (AIFamily ai_family)
{

92
lib/std/net/os/android.c3 Normal file
View File

@@ -0,0 +1,92 @@
module std::net::os @if(env::ANDROID);
import libc;
const AIFamily PLATFORM_AF_AX25 = 3;
const AIFamily PLATFORM_AF_IPX = 4;
const AIFamily PLATFORM_AF_APPLETALK = 5;
const AIFamily PLATFORM_AF_NETROM = 6;
const AIFamily PLATFORM_AF_BRIDGE = 7;
const AIFamily PLATFORM_AF_AAL5 = 8;
const AIFamily PLATFORM_AF_X25 = 9;
const AIFamily PLATFORM_AF_INET6 = 10;
const PLATFORM_O_NONBLOCK = 0o4000;
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/socket.h
const int SOL_SOCKET = 1;
const int SO_DEBUG = 1; // turn on debugging info recording
const int SO_REUSEADDR = 2; // allow local address reuse
const int SO_TYPE = 3;
const int SO_ERROR = 4;
const int SO_DONTROUTE = 5; // just use interface addresses
const int SO_BROADCAST = 6; // permit sending of broadcast msgs
const int SO_SNDBUF = 7; // Send buffer size
const int SO_RCVBUF = 8; // Receive buffer size
const int SO_KEEPALIVE = 9; // keep connections alive
const int SO_OOBINLINE = 10; // leave received OOB data in line
const int SO_NO_CHECK = 11;
const int SO_PRIORITY = 12;
const int SO_LINGER = 13; // linger on close if data present (in ticks)
const int SO_BSDCOMPAT = 14;
const int SO_REUSEPORT = 15; // allow local address & port reuse
const int SO_RCVLOWAT = 18;
const int SO_SNDLOWAT = 19;
const int SO_RCVTIMEO = 20; // IMPORTANT Verify before use
const int SO_SNDTIMEO = 21; // IMPORTANT Verify before use
const int SO_BINDTODEVICE = 25;
const int SO_ATTACH_FILTER = 26;
const int SO_DETACH_FILTER = 27;
const int SO_PEERNAME = 28;
const int SO_TIMESTAMP = 29; // IMPORTANT Verify before use timestamp received dgram traffic
const int SO_ACCEPTCONN = 30;
const int SO_PEERSEC = 31;
const int SO_SNDBUFFORCE = 32;
const int SO_RCVBUFFORCE = 33;
const int SO_PASSSEC = 34;
const int SO_MARK = 36;
const int SO_PROTOCOL = 38;
const int SO_DOMAIN = 39;
const int SO_RXQ_OVFL = 40;
const int SO_WIFI_STATUS = 41;
const int SO_PEEK_OFF = 42;
const int SO_NOFCS = 43;
const int SO_LOCK_FILTER = 44;
const int SO_SELECT_ERR_QUEUE = 45;
const int SO_BUSY_POLL = 46;
const int SO_MAX_PACING_RATE = 47;
const int SO_BPF_EXTENSIONS = 48;
const int SO_INCOMING_CPU = 49;
const int SO_ATTACH_BPF = 50;
const int SO_ATTACH_REUSEPORT_CBPF = 51;
const int SO_ATTACH_REUSEPORT_EBPF = 52;
const int SO_CNX_ADVICE = 53;
const int SO_MEMINFO = 55;
const int SO_INCOMING_NAPI_ID = 56;
const int SO_COOKIE = 57;
const int SO_PEERGROUPS = 59;
const int SO_ZEROCOPY = 60;
const int SO_TXTIME = 61;
const int SO_BINDTOIFINDEX = 62;
const int SO_DETACH_REUSEPORT_BPF = 68;
const int SO_PREFER_BUSY_POLL = 69;
const int SO_BUSY_POLL_BUDGET = 70;
const int SO_NETNS_COOKIE = 71;
const int SO_BUF_LOCK = 72;
const int SO_RESERVE_MEM = 73;
const int SO_TXREHASH = 74;
const int SO_RCVMARK = 75;
const int SO_PASSPIDFD = 76;
const int SO_PEERPIDFD = 77;
const CUShort POLLRDNORM = 0x0040;
const CUShort POLLRDBAND = 0x0080;
const CUShort POLLWRNORM = 0x0100;
const CUShort POLLWRBAND = 0x0200;
const CUShort POLLMSG = 0x0400;
const CUShort POLLREMOVE = 0x1000;
const CUShort POLLRDHUP = 0x2000;
const CUShort POLLFREE = 0x4000;
const CUShort POLL_BUSY_LOOP = 0x8000;
const CInt MSG_PEEK = 0x0002;

View File

@@ -1,5 +1,5 @@
module std::net::os;
const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX);
const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID);
typedef AIFamily = CInt;
typedef AIProtocol = CInt;
@@ -18,7 +18,7 @@ struct AddrInfo
AISockType ai_socktype;
AIProtocol ai_protocol;
Socklen_t ai_addrlen;
struct @if(env::WIN32 || env::DARWIN)
struct @if(env::WIN32 || env::DARWIN || env::ANDROID)
{
ZString ai_canonname;
SockAddrPtr ai_addr;
@@ -58,7 +58,7 @@ extern fn void freeaddrinfo(AddrInfo* res) @if(SUPPORTS_INET);
extern fn CInt setsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET);
extern fn CInt getsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET);
module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX));
module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID));
const AIFamily PLATFORM_AF_INET6 = 0;
const AIFamily PLATFORM_AF_IPX = 0;

View File

@@ -88,7 +88,7 @@ fn fault convert_error(WSAError error)
fn fault socket_error()
{
return convert_error(win32_WSAGetLastError());
return convert_error(win32::wsaGetLastError());
}
const CUShort POLLIN = win32::POLLIN;

View File

@@ -69,7 +69,7 @@ fn ulong? poll_ms(Poll[] polls, long timeout_ms)
{
if (timeout_ms > CInt.max) timeout_ms = CInt.max;
$if env::WIN32:
CInt result = win32_WSAPoll((Win32_LPWSAPOLLFD)polls.ptr, (Win32_ULONG)polls.len, (CInt)timeout_ms);
CInt result = win32::wsaPoll((Win32_LPWSAPOLLFD)polls.ptr, (Win32_ULONG)polls.len, (CInt)timeout_ms);
$else
CInt result = os::poll((Posix_pollfd*)polls.ptr, (Posix_nfds_t)polls.len, (CInt)timeout_ms);
$endif

View File

@@ -23,7 +23,7 @@ fn bool last_error_is_delayed_connect()
{
$switch:
$case env::WIN32:
switch (win32_WSAGetLastError())
switch (win32::wsaGetLastError())
{
case wsa::EWOULDBLOCK:
case wsa::EINPROGRESS: return true;

View File

@@ -0,0 +1,2 @@
module std::os::freebsd @if(env::FREEBSD);

View File

@@ -67,6 +67,17 @@ struct Darwin_segment_command_64
uint flags; /* flags */
}
struct Darwin_mach_timebase_info
{
uint numer;
uint denom;
}
alias Darwin_mach_timebase_info_t = Darwin_mach_timebase_info;
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)
{
@@ -155,3 +166,4 @@ fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
};
return list;
}

View File

@@ -0,0 +1,2 @@
module std::os::darwin @if(env::DARWIN);

View File

@@ -0,0 +1 @@
module std::os::netbsd @if(env::NETBSD);

View File

@@ -0,0 +1,2 @@
module std::os::openbsd @if(env::OPENBSD);

View File

@@ -4,7 +4,7 @@ import libc;
<*
Exit the process with a given exit code. This will typically call 'exit' in LibC
*>
fn void exit(int result, bool cleanup = true) @weak
fn void exit(int result) @weak @noreturn
{
$if env::LIBC:
libc::exit(result);
@@ -17,7 +17,7 @@ fn void exit(int result, bool cleanup = true) @weak
Exit the process with a given exit code. This will typically call '_Exit' in LibC
usually bypassing '@finalizer' functions.
*>
fn void fastexit(int result, bool cleanup = true) @weak
fn void fastexit(int result) @weak @noreturn
{
$if env::LIBC:
libc::_exit(result);

68
lib/std/os/posix/clock.c3 Normal file
View File

@@ -0,0 +1,68 @@
module std::os::posix @if(env::POSIX);
import libc;
extern fn CInt clock_gettime(int type, TimeSpec *time);
module std::os::posix @if(env::OPENBSD);
const CLOCK_REALTIME = 0;
const CLOCK_PROCESS_CPUTIME_ID = 2;
const CLOCK_MONOTONIC = 3;
const CLOCK_THREAD_CPUTIME_ID = 4;
const CLOCK_UPTIME = 5;
const CLOCK_BOOTTIME = 6;
module std::os::posix @if(env::FREEBSD);
const CLOCK_REALTIME = 0;
const CLOCK_VIRTUAL = 1;
const CLOCK_PROF = 2;
const CLOCK_MONOTONIC = 4;
const CLOCK_UPTIME = 5;
const CLOCK_UPTIME_PRECISE = 7;
const CLOCK_UPTIME_FAST = 8;
const CLOCK_REALTIME_PRECISE = 9;
const CLOCK_REALTIME_FAST = 10;
const CLOCK_MONOTONIC_PRECISE = 11;
const CLOCK_MONOTONIC_FAST = 12;
const CLOCK_SECOND = 13;
const CLOCK_THREAD_CPUTIME_ID = 14;
const CLOCK_PROCESS_CPUTIME_ID = 15;
const CLOCK_BOOTTIME = CLOCK_UPTIME;
const CLOCK_REALTIME_COARSE = CLOCK_REALTIME_FAST;
const CLOCK_MONOTONIC_COARSE = CLOCK_MONOTONIC_FAST;
module std::os::posix @if(env::NETBSD);
const CLOCK_REALTIME = 0;
const CLOCK_VIRTUAL = 1;
const CLOCK_PROF = 2;
const CLOCK_MONOTONIC = 3;
const CLOCK_THREAD_CPUTIME_ID = 0x20000000;
const CLOCK_PROCESS_CPUTIME_ID = 0x40000000;
module std::os::posix @if(env::WASI);
// Not implemented
const CLOCK_REALTIME = 0;
const CLOCK_MONOTONIC = 0;
module std::os::posix @if(env::DARWIN);
const CLOCK_REALTIME = 0;
const CLOCK_MONOTONIC = 6;
const CLOCK_MONOTONIC_RAW = 4;
const CLOCK_MONOTONIC_RAW_APPROX = 5;
const CLOCK_UPTIME_RAW = 8;
const CLOCK_UPTIME_RAW_APPROX = 9;
const CLOCK_PROCESS_CPUTIME_ID = 12;
const CLOCK_THREAD_CPUTIME_ID = 16;
module std::os::posix @if(env::LINUX || env::ANDROID);
const CLOCK_REALTIME = 0;
const CLOCK_MONOTONIC = 1;
const CLOCK_PROCESS_CPUTIME_ID = 2;
const CLOCK_THREAD_CPUTIME_ID = 3;
const CLOCK_MONOTONIC_RAW = 4;
const CLOCK_REALTIME_COARSE = 5;
const CLOCK_MONOTONIC_COARSE = 6;
const CLOCK_BOOTTIME = 7;
const CLOCK_REALTIME_ALARM = 8;
const CLOCK_BOOTTIME_ALARM = 9;
const CLOCK_TAI = 11;

View File

@@ -1,4 +1,3 @@
module std::os::posix;
module std::os::posix @if(env::POSIX);
extern ZString* environ;

1
lib/std/os/posix/net.c3 Normal file
View File

@@ -0,0 +1 @@
module std::os::posix @if(env::POSIX);

View File

@@ -45,6 +45,8 @@ bitstruct SubProcessOptions : int
// Note: this will **not** search for paths in any provided custom environment
// and instead uses the PATH of the spawning process.
bool search_user_path;
// Inherit the parent's stdin, stdout, and stderr handles instead of creating pipes
bool inherit_stdio;
}
fn void? create_named_pipe_helper(void** rd, void **wr) @local @if(env::WIN32)
@@ -115,22 +117,32 @@ fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WI
*>
fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, String[] environment = {}) @if(env::WIN32)
{
void* rd, wr;
Win32_DWORD flags = win32::CREATE_UNICODE_ENVIRONMENT;
Win32_PROCESS_INFORMATION process_info;
Win32_SECURITY_ATTRIBUTES sa_attr = { Win32_SECURITY_ATTRIBUTES.sizeof, null, 1 };
Win32_STARTUPINFOW start_info = {
.cb = Win32_STARTUPINFOW.sizeof,
.dwFlags = win32::STARTF_USESTDHANDLES
};
if (options.no_window) flags |= win32::CREATE_NO_WINDOW;
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
// TODO defer catch
if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
// Only set STARTF_USESTDHANDLES if we're not inheriting stdio
if (!options.inherit_stdio) start_info.dwFlags = win32::STARTF_USESTDHANDLES;
CFile stdin;
CFile stdout;
CFile stderr;
void* rd = null;
void* wr = null;
// Only create pipes if not inheriting stdio
if (!options.inherit_stdio)
{
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
// TODO defer catch
if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
}
@pool()
{
WString used_environment = null;
@@ -149,39 +161,19 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
env.append("\0");
used_environment = env.str_view().to_temp_wstring()!;
}
int fd = win32::_open_osfhandle((iptr)wr, 0);
if (fd != -1)
{
stdin = win32::_fdopen(fd, "wb");
if (!stdin) return FAILED_TO_OPEN_STDIN?;
}
start_info.hStdInput = rd;
if (options.read_async)
{
create_named_pipe_helper(&rd, &wr)!;
}
else
{
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
}
if (!win32::setHandleInformation(rd, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
fd = win32::_open_osfhandle((iptr)rd, 0);
if (fd != -1)
{
stdout = win32::_fdopen(fd, "rb");
if (!stdout) return FAILED_TO_OPEN_STDOUT?;
}
start_info.hStdOutput = wr;
do
// Handle stdin pipe if not inheriting
if (!options.inherit_stdio)
{
if (options.combined_stdout_stderr)
int fd = win32::_open_osfhandle((iptr)wr, 0);
if (fd != -1)
{
stderr = stdout;
start_info.hStdError = start_info.hStdOutput;
break;
stdin = win32::_fdopen(fd, "wb");
if (!stdin) return FAILED_TO_OPEN_STDIN?;
}
start_info.hStdInput = rd;
// Handle stdout pipe
if (options.read_async)
{
create_named_pipe_helper(&rd, &wr)!;
@@ -195,18 +187,49 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
fd = win32::_open_osfhandle((iptr)rd, 0);
if (fd != -1)
{
stderr = win32::_fdopen(fd, "rb");
if (!stderr) return FAILED_TO_OPEN_STDERR?;
stdout = win32::_fdopen(fd, "rb");
if (!stdout) return FAILED_TO_OPEN_STDOUT?;
}
start_info.hStdError = wr;
};
void *event_output;
void *event_error;
if (options.read_async)
start_info.hStdOutput = wr;
// Handle stderr pipe or combine with stdout
do
{
if (options.combined_stdout_stderr)
{
stderr = stdout;
start_info.hStdError = start_info.hStdOutput;
break;
}
if (options.read_async)
{
create_named_pipe_helper(&rd, &wr)!;
}
else
{
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
}
if (!win32::setHandleInformation(rd, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
fd = win32::_open_osfhandle((iptr)rd, 0);
if (fd != -1)
{
stderr = win32::_fdopen(fd, "rb");
if (!stderr) return FAILED_TO_OPEN_STDERR?;
}
start_info.hStdError = wr;
};
}
void *event_output = null;
void *event_error = null;
if (!options.inherit_stdio && options.read_async)
{
event_output = win32::createEventA(&sa_attr, 1, 1, null);
event_error = win32::createEventA(&sa_attr, 1, 1, null);
}
if (!win32::createProcessW(
null,
convert_command_line_win32(command_line),
@@ -214,14 +237,17 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
null, // primary thread security attributes
1, // handles are inherited
flags, // creation flags
used_environment, // environment
used_environment, // environment
null, // use parent dir
&start_info, // startup info ptr
&process_info)) return FAILED_TO_START_PROCESS?;
};
// We don't need the handle of the primary thread in the called process.
win32::closeHandle(process_info.hThread);
if (start_info.hStdOutput)
// Close handles if not inheriting stdio
if (!options.inherit_stdio && start_info.hStdOutput)
{
win32::closeHandle(start_info.hStdOutput);
if (start_info.hStdOutput != start_info.hStdError) win32::closeHandle(start_info.hStdError);
@@ -229,7 +255,7 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
return {
.hProcess = process_info.hProcess,
.hStdInput = start_info.hStdInput,
.hStdInput = options.inherit_stdio ? null : start_info.hStdInput,
.stdin_file = stdin,
.stdout_file = stdout,
.stderr_file = stderr,
@@ -280,27 +306,37 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
CInt[2] stdinfd;
CInt[2] stdoutfd;
CInt[2] stderrfd;
if (posix::pipe(&stdinfd)) return FAILED_TO_OPEN_STDIN?;
if (posix::pipe(&stdoutfd)) return FAILED_TO_OPEN_STDOUT?;
if (!options.combined_stdout_stderr && posix::pipe(&stderrfd)) return FAILED_TO_OPEN_STDERR?;
CFile stdin = null;
CFile stdout = null;
CFile stderr = null;
Posix_spawn_file_actions_t actions;
if (posix::spawn_file_actions_init(&actions)) return FAILED_TO_INITIALIZE_ACTIONS?;
defer posix::spawn_file_actions_destroy(&actions);
if (posix::spawn_file_actions_addclose(&actions, stdinfd[1])) return FAILED_TO_OPEN_STDIN?;
if (posix::spawn_file_actions_adddup2(&actions, stdinfd[0], libc::STDIN_FD)) return FAILED_TO_OPEN_STDIN?;
if (posix::spawn_file_actions_addclose(&actions, stdoutfd[0])) return FAILED_TO_OPEN_STDOUT?;
if (posix::spawn_file_actions_adddup2(&actions, stdoutfd[1], libc::STDOUT_FD)) return FAILED_TO_OPEN_STDOUT?;
if (options.combined_stdout_stderr)
// Only set up pipes if not inheriting stdio
if (!options.inherit_stdio)
{
if (posix::spawn_file_actions_adddup2(&actions, libc::STDOUT_FD, libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
}
else
{
if (posix::spawn_file_actions_addclose(&actions, stderrfd[0])) return FAILED_TO_OPEN_STDERR?;
if (posix::spawn_file_actions_adddup2(&actions, stderrfd[1], libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
if (posix::pipe(&stdinfd)) return FAILED_TO_OPEN_STDIN?;
if (posix::pipe(&stdoutfd)) return FAILED_TO_OPEN_STDOUT?;
if (!options.combined_stdout_stderr && posix::pipe(&stderrfd)) return FAILED_TO_OPEN_STDERR?;
if (posix::spawn_file_actions_addclose(&actions, stdinfd[1])) return FAILED_TO_OPEN_STDIN?;
if (posix::spawn_file_actions_adddup2(&actions, stdinfd[0], libc::STDIN_FD)) return FAILED_TO_OPEN_STDIN?;
if (posix::spawn_file_actions_addclose(&actions, stdoutfd[0])) return FAILED_TO_OPEN_STDOUT?;
if (posix::spawn_file_actions_adddup2(&actions, stdoutfd[1], libc::STDOUT_FD)) return FAILED_TO_OPEN_STDOUT?;
if (options.combined_stdout_stderr)
{
if (posix::spawn_file_actions_adddup2(&actions, libc::STDOUT_FD, libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
}
else
{
if (posix::spawn_file_actions_addclose(&actions, stderrfd[0])) return FAILED_TO_OPEN_STDERR?;
if (posix::spawn_file_actions_adddup2(&actions, stderrfd[1], libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
}
}
Pid_t child;
@pool()
{
@@ -315,21 +351,27 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return FAILED_TO_START_PROCESS?;
}
};
libc::close(stdinfd[0]);
CFile stdin = libc::fdopen(stdinfd[1], "wb");
libc::close(stdoutfd[1]);
CFile stdout = libc::fdopen(stdoutfd[0], "rb");
CFile stderr @noinit;
do
// Only set up file handles if not inheriting stdio
if (!options.inherit_stdio)
{
if (options.combined_stdout_stderr)
libc::close(stdinfd[0]);
stdin = libc::fdopen(stdinfd[1], "wb");
libc::close(stdoutfd[1]);
stdout = libc::fdopen(stdoutfd[0], "rb");
do
{
stderr = stdout;
break;
}
libc::close(stderrfd[1]);
stderr = libc::fdopen(stderrfd[0], "rb");
};
if (options.combined_stdout_stderr)
{
stderr = stdout;
break;
}
libc::close(stderrfd[1]);
stderr = libc::fdopen(stderrfd[0], "rb");
};
}
return {
.stdin_file = stdin,
.stdout_file = stdout,
@@ -357,11 +399,13 @@ fn CInt? SubProcess.join(&self) @if(env::POSIX)
fn File SubProcess.stdout(&self)
{
if (!self.stdout_file) return (File){}; // Return an empty File struct
return file::from_handle(self.stdout_file);
}
fn File SubProcess.stderr(&self)
{
if (!self.stderr_file) return (File){}; // Return an empty File struct
return file::from_handle(self.stderr_file);
}
@@ -450,6 +494,8 @@ fn usz? read_from_file_posix(CFile file, char* buffer, usz size) @if(env::POSIX)
fn usz? SubProcess.read_stdout(&self, char* buffer, usz size)
{
if (!self.stdout_file) return 0; // No output available when inheriting stdio
$if env::WIN32:
return read_from_file_win32(self.stdout_file, self.hEventOutput, buffer, size);
$else
@@ -457,8 +503,11 @@ fn usz? SubProcess.read_stdout(&self, char* buffer, usz size)
$endif
}
fn usz? SubProcess.read_stderr(&self, char* buffer, usz size)
{
if (!self.stderr_file) return 0; // No output available when inheriting stdio
$if env::WIN32:
return read_from_file_win32(self.stderr_file, self.hEventError, buffer, size);
$else

View File

@@ -0,0 +1,6 @@
module std::os::win32 @if(env::WIN32);
import std::math;
extern fn void getSystemTimeAsFileTime(Win32_FILETIME* time) @extern("GetSystemTimeAsFileTime");
extern fn Win32_BOOL queryPerformanceFrequency(Win32_LARGE_INTEGER* lpFrequency) @extern("QueryPerformanceFrequency");
extern fn Win32_BOOL queryPerformanceCounter(Win32_LARGE_INTEGER* lpPerformanceCount) @extern("QueryPerformanceCounter");

View File

@@ -1,5 +1,6 @@
module std::os::win32 @if(env::WIN32);
import libc;
enum Win32_GET_FILEEX_INFO_LEVELS
{
STANDARD,

View File

@@ -222,4 +222,5 @@ 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_IO_INCOMPLETE = 0x3E4;
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;
const Win32_DWORD ERROR_TIMEOUT = 0x5B4;

View File

@@ -46,6 +46,8 @@ const IMAGE_FILE_MACHINE_ARM64 = 0xAA64;
const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
const UNDNAME_COMPLETE = 0x0000;
alias Win32_INIT_ONCE_FN = fn Win32_BOOL(Win32_INIT_ONCE* initOnce, void* parameter, void** context);
extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection");
extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection");
extern fn Win32_HANDLE createMutex(void*, Win32_BOOL, void*) @extern("CreateMutexA");
@@ -53,6 +55,19 @@ extern fn Win32_BOOL releaseMutex(Win32_HANDLE) @extern("ReleaseMutex");
extern fn void enterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection");
extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection");
extern fn Win32_BOOL tryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection");
extern fn void initializeSRWLock(Win32_SRWLOCK* lock) @extern("InitializeSRWLock");
extern fn void acquireSRWLockExclusive(Win32_SRWLOCK* lock) @extern("AcquireSRWLockExclusive");
extern fn void acquireSRWLockShared(Win32_SRWLOCK* lock) @extern("AcquireSRWLockShared");
extern fn void releaseSRWLockExclusive(Win32_SRWLOCK* lock) @extern("ReleaseSRWLockExclusive");
extern fn void releaseSRWLockShared(Win32_SRWLOCK* lock) @extern("ReleaseSRWLockShared");
extern fn Win32_BOOL tryAcquireSRWLockExclusive(Win32_SRWLOCK* lock) @extern("TryAcquireSRWLockExclusive");
extern fn Win32_BOOL tryAcquireSRWLockShared(Win32_SRWLOCK* lock) @extern("TryAcquireSRWLockShared");
extern fn void initializeConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("InitializeConditionVariable");
extern fn void wakeConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("WakeConditionVariable");
extern fn void wakeAllConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("WakeAllConditionVariable");
extern fn Win32_BOOL sleepConditionVariableCS(Win32_CONDITION_VARIABLE* conditionVariable, Win32_CRITICAL_SECTION* section, Win32_DWORD dwMilliseconds) @extern("SleepConditionVariableCS");
extern fn Win32_BOOL sleepConditionVariableSRW(Win32_CONDITION_VARIABLE* conditionVariable, Win32_SRWLOCK* lock, Win32_DWORD dwMilliseconds, Win32_ULONG flags) @extern("SleepConditionVariableSRW");
extern fn Win32_BOOL initOnceExecuteOnce(Win32_INIT_ONCE* initOnce, Win32_INIT_ONCE_FN initFn, void* parameter, void** context) @extern("InitOnceExecuteOnce");
extern fn Win32_DWORD waitForSingleObject(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds) @extern("WaitForSingleObject");
extern fn Win32_DWORD waitForSingleObjectEx(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds, Win32_BOOL bAlertable) @extern("WaitForSingleObjectEx");
extern fn Win32_DWORD waitForMultipleObjects(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds) @extern("WaitForMultipleObjects");

View File

@@ -190,6 +190,9 @@ union Win32_LARGE_INTEGER
}
typedef Win32_CRITICAL_SECTION = ulong[5];
typedef Win32_CONDITION_VARIABLE = void*;
typedef Win32_SRWLOCK = void*;
typedef Win32_INIT_ONCE = void*;
struct Win32_SECURITY_ATTRIBUTES
{
@@ -694,4 +697,3 @@ alias Win32_LPSTACKFRAME64 = Win32_STACKFRAME64*;
alias Win32_PMODLOAD_DATA = Win32_MODLOAD_DATA*;
alias Win32_PIMAGEHLP_LINE64 = Win32_IMAGEHLP_LINE64*;
alias Win32_LPMODULEINFO = Win32_MODULEINFO*;

View File

@@ -122,11 +122,11 @@ const SD_RECEIVE = 0x00;
const SD_SEND = 0x01;
const SD_BOTH = 0x02;
extern fn CInt win32_WSAPoll(Win32_LPWSAPOLLFD fdArray, Win32_ULONG fds, Win32_INT timeout) @extern("WSAPoll") @builtin;
extern fn WSAError win32_WSAGetLastError() @extern("WSAGetLastError") @builtin;
extern fn void win32_WSASetLastError(WSAError error) @extern("WSASetLastError") @builtin;
extern fn CInt win32_WSAStartup(Win32_WORD, void*) @extern("WSAStartup") @builtin;
extern fn CInt win32_WSACleanup() @extern("WSACleanup") @builtin;
extern fn CInt wsaPoll(Win32_LPWSAPOLLFD fdArray, Win32_ULONG fds, Win32_INT timeout) @extern("WSAPoll");
extern fn WSAError wsaGetLastError() @extern("WSAGetLastError");
extern fn void wsaSetLastError(WSAError error) @extern("WSASetLastError");
extern fn CInt wsaStartup(Win32_WORD, void*) @extern("WSAStartup");
extern fn CInt wsaCleanup() @extern("WSACleanup");
const int FIONBIO = -2147195266;
const int FIONREAD = 1074030207;
@@ -230,3 +230,4 @@ const WSAError QOS_EPSFILTERSPEC = 11028;
const WSAError QOS_ESDMODEOBJ = 11029;
const WSAError QOS_ESHAPERATEOBJ = 11030;
const WSAError QOS_RESERVED_PETYPE = 11031;

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], $typefrom(KeyFn.paramsof[0].type));
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable((Type){}[0], 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], $typefrom(CmpFn.paramsof[0].type));
var $cmp_by_value = $has_cmp &&& $assignable(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], $typefrom(CmpFn.paramsof[0].type));
var $cmp_by_value = $has_cmp &&& $assignable(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], $typefrom($typeof(cmp).paramsof[0].type));
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typeof(cmp).paramsof[0].type);
var a = list[pos];
var b = list[pos+1];

View File

@@ -1,17 +0,0 @@
/*module std::text::i18n;
import std::collections::map;
import std::hash::fnv32a;
typedef Language = char[];
const Language EN = "en";
alias TranslationMap = HashMap{String, String};
fn uint Language.hash(self) => fnv32a::encode((char[])self);
HashMap{Language, TranslationMap*} language_map @private;
TranslationMap? current_map;
macro String @localized(String string) @builtin
{
return current_map[string] ?? string;
}

View File

@@ -61,7 +61,7 @@ fn void? FixedThreadPool.init(&self, usz threads, usz queue_size = 0)
*>
fn void? FixedThreadPool.destroy(&self)
{
return self.@shutdown(stop_now);
return self.@shutdown(self.stop_now);
}
<*
@@ -70,7 +70,7 @@ fn void? FixedThreadPool.destroy(&self)
*>
fn void? FixedThreadPool.stop_and_destroy(&self)
{
return self.@shutdown(stop);
return self.@shutdown(self.stop);
}
macro void? FixedThreadPool.@shutdown(&self, #stop) @private
@@ -78,7 +78,7 @@ macro void? FixedThreadPool.@shutdown(&self, #stop) @private
if (self.initialized)
{
self.mu.lock()!;
self.#stop = true;
#stop = true;
self.notify.broadcast()!;
self.mu.unlock()!;
// Wait for all threads to shutdown.

View File

@@ -1,6 +1,7 @@
module std::thread::os @if (!env::POSIX && !env::WIN32);
typedef NativeMutex = int;
typedef NativeTimedMutex = int;
typedef NativeConditionVariable = int;
typedef NativeOnceFlag = int;
typedef NativeThread = int;

View File

@@ -7,6 +7,8 @@ struct NativeMutex
bool initialized;
}
alias NativeTimedMutex = NativeMutex;
alias NativeConditionVariable = Pthread_cond_t;
struct NativeThread
@@ -28,7 +30,7 @@ fn void? NativeMutex.init(&self, MutexType type)
if (posix::pthread_mutexattr_init(&attr)) return thread::INIT_FAILED?;
defer posix::pthread_mutexattr_destroy(&attr);
// TODO: make a fine grained error instead
if (type & thread::MUTEX_RECURSIVE)
if (type.recursive)
{
if (posix::pthread_mutexattr_settype(&attr, posix::PTHREAD_MUTEX_RECURSIVE)) return thread::INIT_FAILED?;
}

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