Compare commits

...

243 Commits
v0.3.x ... 0.4

Author SHA1 Message Date
Christoffer Lerno
0778537540 Update mac versions to test 15 and 16. Update release version to 16. 2023-05-21 22:06:24 +02:00
Christoffer Lerno
ddd0497922 Better lowering of distinct types. Noreturn function call expr recognized as a "jump" for escape analysis. Preferring "def" in libs. To upper / to lower for ascii. Initial dynlib support. 2023-05-21 21:41:01 +02:00
Tonis
a877d4458c Improve Matrix identity functions and add Quaternion to matrix function (#765)
* Edit matrix identity fn and add quaternion to matrix fn

* Change matrix identity macros to constants

---------

Co-authored-by: Tonis <tanton@paysure.solutions>
2023-05-16 11:50:01 +02:00
Christoffer Lerno
3a725d1348 Better error on missing ';' in certain cases. 2023-05-15 08:45:10 +02:00
Christoffer Lerno
353a072b75 Fix for getting the correct generic type of consts. Fix of late initialization of structs using compound literals. 2023-05-14 17:23:45 +02:00
Christoffer Lerno
8eddbfb708 Fix to net::os::posix. Remove "\s" 2023-05-12 16:55:15 +02:00
Christoffer Lerno
021bcdcf21 Add "is_initialized" to check if a map has been initialized. 2023-05-10 13:12:31 +02:00
Christoffer Lerno
bff7b492a2 Further bitstruct cast fixes. Updated code. 2023-05-10 13:03:15 +02:00
Christoffer Lerno
4d0f73a8f5 Consistent naming in allocators. Fix where cast from char array -> bitstruct would not work. 2023-05-10 10:30:37 +02:00
Christoffer Lerno
6210522c75 Update error message, disable 17 from CI 2023-05-08 18:48:40 +02:00
Christoffer Lerno
13f808b552 Added acos/asin(h) and atanh 2023-05-08 10:50:05 +02:00
Christoffer Lerno
dc30c8edc2 Fix complaints of broken compilers. 2023-05-08 00:18:39 +02:00
Christoffer Lerno
ee5ad170e0 Simplify detection of initialized variable. 2023-05-08 00:13:12 +02:00
Christoffer Lerno
172d561f07 Change syntax of $if, $assert, $include, $echo. Introduces $error 2023-05-06 12:18:00 +02:00
Christoffer Lerno
3dd6675e1b Fixed const vector codegen. Missing math comparisons. 2023-05-06 02:29:26 +02:00
Christoffer Lerno
db8c46d6c5 Addition of "distinct" and "inline" as keywords. Removal of "alias" keyword. 2023-05-04 08:48:17 +02:00
Christoffer Lerno
6fc38bbcb9 Accidental change. 2023-04-28 23:36:45 +02:00
Christoffer Lerno
184cc19d36 Further grammar fixes. 2023-04-28 19:11:57 +02:00
Christoffer Lerno
e25c06a065 Fixes to the grammar. 2023-04-27 12:26:58 +02:00
Christoffer Lerno
f2f514da74 Fixes to the grammar. 2023-04-26 17:24:35 +02:00
Christoffer Lerno
619fa26c26 Updated LLVM for Windows 2023-04-25 16:09:04 +02:00
Christoffer Lerno
e8642d6797 Fixes to access grammar. "delete" => "remove" 2023-04-24 09:10:35 +02:00
Christoffer Lerno
d1c2fbd79f Fix for MSVC 2023-04-21 17:47:32 +02:00
Christoffer Lerno
2a79e0f1cf Introduce def as a trial. Fixup of timeit. 2023-04-21 17:42:38 +02:00
Christoffer Lerno
c847650579 Introduce def as a trial. Fixup of timeit. 2023-04-21 16:03:28 +02:00
Christoffer Lerno
edd2f1c717 Updated timeit. 2023-04-21 15:57:48 +02:00
Christoffer Lerno
0a12686237 Remove acornvm 2023-04-21 15:51:00 +02:00
Christoffer Lerno
8059dc1539 delete_if, retain_if, rindex_of, compact, compact_count added to List. 2023-04-21 14:45:25 +02:00
Christoffer Lerno
809321e20c Updated grammar. Removal of elif. Removal of ':' ';' in some ct statements. Empty faults is now an error. Remove "define" for types. Remove "private". Better errors on incorrect bitstruct syntax. Introduction of wildcard type rather than optional wildcard. Removal of scaled vector type. mkdir and rmdir. Disallow define @Foo() = { @inline }. Add handling for @optreturn and change it to @return!. Restrict interface style functions. Updated x64 ABI. stdlib updates to string. Removed deprecated functions. Update how variadics are implemented. Extended error messages. x86 ABI fixes. Shift check fixes. '!' and '?' are flipped. No trailing ',' allowed in functions. Fix to string parsing. Allow l suffix. Simplifying flatpath. any replaces variant, anyfault replaces anyerr. Allow getting the underlying type of anyfault. De-duplicate string constants. Fix of readme. Extended list. Fix of "(MyEnum)x + 1". Clock and DateTime types. Fixes to array concat. 2023-04-21 10:56:39 +02:00
Christoffer Lerno
d14e778232 Use different readdir on macOS depending on arch. 2023-04-10 09:54:02 +02:00
WraithGlade
18c1b20ea0 Fixed two typos and merged them properly. (#758)
* Fixed typo: "do" --> "does".

* Fixed typo: missing "is".
2023-04-02 17:25:16 +02:00
Christoffer Lerno
ad7ee06635 Grammar updates. Updated enum member parsing. 2023-03-22 21:46:47 +01:00
Christoffer Lerno
0f80d985fa Fix sema errors on flexible array slices. 2023-03-22 12:56:15 +01:00
Christoffer Lerno
316af36723 Add dstringwriter. 2023-03-22 12:23:47 +01:00
Christoffer Lerno
9850adfa56 Rename OUT_OF_DISK => OUT_OF_SPACE. Allow byte writer to take a buffer. 2023-03-22 11:52:45 +01:00
Christoffer Lerno
172ae8a3a5 Only rudimentary parsing of "private" in prefix location. Make contract handling more correct. 2023-03-21 23:56:49 +01:00
Christoffer Lerno
07e3ced84e Deprecate @extname 2023-03-21 16:41:26 +01:00
Christoffer Lerno
1c4ab48da7 Update for mingw 2023-03-21 14:06:11 +01:00
Christoffer Lerno
0a826d588f Update 2023-03-21 00:59:41 +01:00
Christoffer Lerno
93fa135d28 Deprecation of define Type = ... 2023-03-20 22:56:06 +01:00
Christoffer Lerno
9c145996b0 $elif deprecated. 2023-03-20 16:54:45 +01:00
Christoffer Lerno
02d073675a Fix to deprecated syntax. 2023-03-20 11:21:55 +01:00
Christoffer Lerno
3beed8a0f1 No exceptions compiling C++ 2023-03-20 09:51:57 +01:00
Christoffer Lerno
5851de30ab Try enabling 17 again. 2023-03-20 09:51:57 +01:00
Christoffer Lerno
5ee0d52ff1 Deprecate :; in $if etc. 2023-03-20 01:03:54 +01:00
Christoffer Lerno
cc87c77af3 Add deprecation notice on use of "private" 2023-03-19 23:50:00 +01:00
Christoffer Lerno
24147a85f7 Allow unary plus. 2023-03-19 22:46:28 +01:00
Christoffer Lerno
954521228e Make casts explicit when casting to pointer offset. Better localization of error when narrowing fails. New printf formatting based on musl plus %a output. 2023-03-19 21:15:44 +01:00
Christoffer Lerno
9fa634b78b is_finite / is_nan / is_inf, frexp native. 2023-03-18 21:17:18 +01:00
Christoffer Lerno
48a35b3277 Signbit, tests of frexp. 2023-03-18 19:13:17 +01:00
Christoffer Lerno
a041c53cdd Add frexp. 2023-03-18 16:54:39 +01:00
Christoffer Lerno
9f068ce84d Shorter code. 2023-03-18 14:44:35 +01:00
Christoffer Lerno
8b0df0ee11 try? / catch? 2023-03-17 22:49:48 +01:00
Ox512
1b667cbc93 Update the default project template (#750) 2023-03-15 19:40:49 +01:00
Christoffer Lerno
7075f834dd Remove null-casts for anyerr. 2023-03-15 16:28:48 +01:00
Christoffer Lerno
e2b9a35dfe Add tentative ".ordinal" on faults. Allow anyerr and fault to be initialized with null. 2023-03-15 14:32:00 +01:00
Christoffer Lerno
3cb94a2857 Temporarily disable LLVM 17. Generic module contracts enabled. 2023-03-14 23:25:52 +01:00
Christoffer Lerno
3237f87a09 Added some Win32 declarations. 2023-03-14 11:37:23 +01:00
Christoffer Lerno
1b27264f07 Allow type inference on enum comparisons. Add chdir. Fix bug when command was missing. Allow {} on basic types. 2023-03-13 16:25:03 +01:00
Christoffer Lerno
fb761b0cc5 Added OnStack allocator. Added dirname, basename and extension to path functions. 2023-03-13 11:51:27 +01:00
Christoffer Lerno
4ffeada3c7 Updated stdlib. Prefer file::open. Fix to slice assign with distinct types. 2023-03-12 00:33:16 +01:00
Christoffer Lerno
2607062cb6 Updated Path. Some work towards loading dirs. 2023-03-11 18:32:44 +01:00
Christoffer Lerno
7a2d73c690 mproved Path code. 2023-03-10 18:46:21 +01:00
Christoffer Lerno
d2a16961cf More refactorings in the stdlib. More Path functions. Updated Win32 format for types. Fix bug with codegen of defer if ... More string functions. 2023-03-09 20:44:27 +01:00
Christoffer Lerno
39dd3e40a6 Remove attribute list from test. 2023-03-08 16:23:12 +01:00
Christoffer Lerno
1480b8f872 Update examples and contracts. 2023-03-08 15:57:42 +01:00
Christoffer Lerno
b94c647ead Distinct String 2023-03-08 14:32:07 +01:00
Christoffer Lerno
9b81623680 Make string literals default to char[] 2023-03-08 11:24:11 +01:00
Christoffer Lerno
cfba19ab77 Remove deprecated to avoid warnings for VarString. Fix issue casting subarrays to distinct types. 2023-03-08 00:13:48 +01:00
Christoffer Lerno
89de0a70d2 "@ensure" now correctly only runs on non-optional results. Subtypes now merge to a single type. Beginning deprecation of "std::core::str". Refreshed String functions. Consistent use of ".using" parameter. Functions moved to string methods. Tests on more string methods. Fixes to split, rindex_of. 2023-03-08 00:13:48 +01:00
Christoffer Lerno
33cc2d889b More const modification detection. Grab the version number into CMake. 2023-03-07 14:35:12 +01:00
Christoffer Lerno
ab1c025c05 Fix issue for wasm 2023-03-07 10:12:03 +01:00
Christoffer Lerno
7008dab113 Add primitive and incomplete object printout. First version of json parser. 2023-03-07 09:53:01 +01:00
Christoffer Lerno
300f4d38ab Addition of object type. Fixes to const union initialization. 2023-03-06 23:58:13 +01:00
Christoffer Lerno
9eee250b10 Add DString init. 2023-03-06 09:12:52 +01:00
Christoffer Lerno
a4231823df Add DString init. 2023-03-05 23:28:04 +01:00
Christoffer Lerno
d1626ada6f List no longer uses a temp allocator by default. Incorrect check in the temp allocator removed. Added DString. 2023-03-05 23:14:16 +01:00
Christoffer Lerno
6ac99ed83c Improve debug info. 2023-03-05 20:50:04 +01:00
Tonis
ff4c35fae1 Added Matrix identity macros and few matrix unit tests (#742)
* Add matrix identity macros

* Add matrix some matrix unit tests

---------

Co-authored-by: Tonis <tanton@paysure.solutions>
2023-03-05 20:39:55 +01:00
Dmitry Atamanov
549e1b6029 Added std::collections::enummap. 2023-03-04 22:41:22 +01:00
Christoffer Lerno
1035de3b36 Remove all locations when enums are implicitly lowered to integers. Remove the menagerie of flattening functions. Incidentally this also fixes a bunch of not-yet tested errors with distinct types. 2023-03-04 22:14:29 +01:00
Christoffer Lerno
306677300a Fix of enum alignment / size when based in distinct types. 2023-03-04 01:35:23 +01:00
Christoffer Lerno
f9923de7f9 Remove implicit cast from enum to int. Allow enums to use distinct types as the backing type. enum += 1 returns enum type. 2023-03-04 01:03:57 +01:00
Christoffer Lerno
488472ecbb Improve enum add/sub. 2023-03-03 17:43:13 +01:00
Christoffer Lerno
ea3b32f68b Further casting cleanup. 2023-03-03 14:43:32 +01:00
Christoffer Lerno
a2911292d8 Simplify narrowing semantics. 2023-03-02 22:04:15 +01:00
Christoffer Lerno
3449d2ea88 Remove all array pointer decay. 2023-03-02 19:47:24 +01:00
Christoffer Lerno
3372f36e9d Fix subarray casts to distinct types and constants. 2023-03-02 13:14:11 +01:00
Christoffer Lerno
07e4aab48b Fix of bool -> float vector cast. Correctly widen C style varargs for distinct types and optionals. 2023-03-02 11:35:05 +01:00
Christoffer Lerno
0dcad6f5cf Improve and simplify casts. 2023-03-01 22:43:50 +01:00
Christoffer Lerno
0314f9534f Improve enum checks on enum conversions. 2023-03-01 13:56:36 +01:00
Christoffer Lerno
f8208f946b Remove "generic" code paths. 2023-03-01 12:01:43 +01:00
Christoffer Lerno
b9dbefbe1b Fix bug when initializing nested struct / unions. Fix of mult of 2x2 matrix. Cleanup of cast. 2023-02-28 20:43:43 +01:00
Christoffer Lerno
6188a8b5df Fix bad code in assert 2023-02-28 17:50:01 +01:00
Christoffer Lerno
9db845903e Cleanup and allow complex array length inference, e.g. "int[*][2][*] a = ..." 2023-02-28 17:37:17 +01:00
Christoffer Lerno
cc19168c7b Some general cleanup. 2023-02-28 01:15:30 +01:00
Christoffer Lerno
1c83a484da Print warnings on deprecated. 2023-02-27 18:59:25 +01:00
Christoffer Lerno
bd8bff85d6 Add tracking allocator. Fix substruct issue. 2023-02-27 17:05:27 +01:00
Christoffer Lerno
dd4edfb747 Updated malloc/calloc/realloc/free deprecation of old helper functions. Add checks to prevent incorrect alignment on types when using malloc. Better errors from $assert. Added @deprecated. Fixed issue using named arguments after varargs. 2023-02-27 15:03:27 +01:00
Christoffer Lerno
8ad8af861e Fix to nested $if analysis. 2023-02-27 01:10:46 +01:00
Christoffer Lerno
feaf8b3b2c Update HashMap functionality. Fix check when appending to VarString. Fix issue with decls accidentally invalidated during $checked eval. Fold optional when casting slice to pointer. 2023-02-27 00:10:01 +01:00
Christoffer Lerno
10272dbf38 Delay C abi lowering until requested on-demand to prevent circular dependencies. 2023-02-24 22:01:55 +01:00
Christoffer Lerno
2f255ac5c3 Sha1. 2023-02-24 10:49:01 +01:00
Christoffer Lerno
a7ce0f95e6 Refactor allocator locations. 2023-02-24 00:29:24 +01:00
Christoffer Lerno
fc0cad2894 Remove abs from libc. Create abstract random. 2023-02-24 00:00:36 +01:00
Christoffer Lerno
2a6339a25e Updated native variants of file handling. Fixed $if folding chain. 2023-02-23 18:00:34 +01:00
Christoffer Lerno
f86aa136cb Updated fopen. ZString.len does not output number of Char32. Add example. 2023-02-23 10:42:36 +01:00
Christoffer Lerno
b175b9318a Fix conversion if (int x = foo()). Initial stream api. Extended enumset. 2023-02-22 17:06:06 +01:00
Christoffer Lerno
8f5676b488 Add defer catch/try. Fix missing defer invoked on return a > 0 ? Foo.ABC! : 1 2023-02-21 20:10:03 +01:00
Christoffer Lerno
b5a2b5c68a Added a short project suggestion list. 2023-02-21 15:54:20 +01:00
Christoffer Lerno
afa41f0c10 Updated name mangling. 2023-02-20 17:48:47 +01:00
Christoffer Lerno
b6ff6bae8e Update version. 2023-02-20 16:02:50 +01:00
Christoffer Lerno
e3416a1c40 Allow @test with modules. Change name mangling for non exports. 2023-02-20 16:02:30 +01:00
Christoffer Lerno
d35d50555e ipv4/ipv6 parsing and back to string. 2023-02-20 09:31:11 +01:00
Christoffer Lerno
34eac23e23 Fix when comparing const values > 64 bits. 2023-02-20 08:03:36 +01:00
Christoffer Lerno
19963e4e19 Fix attributes for nested bitstructs. Add some functions to std::net 2023-02-19 23:35:02 +01:00
Christoffer Lerno
62fbf4da47 Add simple bitstruct. 2023-02-18 16:59:52 +01:00
Christoffer Lerno
6e8c69cd52 Add & ^ | to bitstructs. 2023-02-18 16:21:30 +01:00
Christoffer Lerno
ea163636d3 Add defaults to compare_exchange, small fix in printf. Disallow obviously wrong code that returns the pointer to a variable on the stack. 2023-02-18 12:21:15 +01:00
Christoffer Lerno
3da9008fdc $$DATE is fixed. 2023-02-18 00:03:46 +01:00
Christoffer Lerno
9061699adf Fix of $vasplat when used multiple times. 2023-02-17 20:31:17 +01:00
Christoffer Lerno
d6a1cecb9d Fix of LLVM codegen for optionals. 2023-02-17 13:45:16 +01:00
Christoffer Lerno
baa2e474b5 Added $$atomic_store and $$atomic_load. 2023-02-17 13:22:12 +01:00
Christoffer Lerno
a0a5c940f1 Add --strip-unused. 2023-02-16 22:11:42 +01:00
Christoffer Lerno
0aa776d61b Allow @export on modules, to implicitly @export all in the module section. Add expect on error comparisons, expecting them to be false by default. Support @extern on modules. 2023-02-16 13:05:09 +01:00
Christoffer Lerno
c26655a05a Remove usize/isize completely. 2023-02-16 00:48:37 +01:00
Christoffer Lerno
818396b6f3 Updated module visibility import visibility. Fixes to @local 2023-02-15 09:47:51 +01:00
Dmitry Atamanov
4519eebe4d Added @likely, @unlikely and @expect macros. (#727) 2023-02-15 00:02:01 +01:00
Christoffer Lerno
adc424ba1d Fixed missing check on &var.myFunction 2023-02-14 23:44:05 +01:00
Christoffer Lerno
b85d521dc1 Fixed missing check on &var.myFunction 2023-02-14 21:56:56 +01:00
Christoffer Lerno
8b099293a3 Fix no-entry. Make printf more strict and also fix distinct printing. 2023-02-14 20:39:15 +01:00
Christoffer Lerno
8d306ce64d Fix no-entry. 2023-02-14 20:13:22 +01:00
Christoffer Lerno
df77b692d6 Support "typedef" 2023-02-14 16:49:27 +01:00
Christoffer Lerno
b7e19b75d0 Improve error reporting for define and function definition. 2023-02-14 15:49:36 +01:00
Christoffer Lerno
61e26d8188 Fixes to $include 2023-02-14 12:27:17 +01:00
Christoffer Lerno
03cd56e46b Add @local and fix visibility issues for generic methods. 2023-02-14 12:17:56 +01:00
Christoffer Lerno
8184fba34b Delay type evaluation further. Current analysis shows that this code should be safe, but there may be some issues lurking. 2023-02-14 09:27:03 +01:00
Christoffer Lerno
3cfef690d3 Support (void)foo for any type. 2023-02-13 13:52:48 +01:00
Christoffer Lerno
5e457be605 Implement more @export / @private improvements. Make @private default… (#729) 2023-02-13 08:31:40 +01:00
Christoffer Lerno
3b49b87784 Fix for nolibc (eg wasm). Add multi global declarations. Simplicity wasm export / import. Prevent "extern" functions to have a function body. 2023-02-11 11:16:31 +01:00
Christoffer Lerno
2fa3ae7bc9 Restore CMake settings. 2023-02-11 02:05:37 +01:00
Christoffer Lerno
1548cd06ef Enable local multi-declarations. Fix of builtin argument checking. Migrate to @noinit. 2023-02-11 02:03:02 +01:00
Christoffer Lerno
18de9a146b Fix copysign and make floatparse more strict. 2023-02-11 00:41:44 +01:00
Christoffer Lerno
ddd8be0f38 Fix global noinit. Add @noinit. With tests. 2023-02-11 00:10:02 +01:00
Christoffer Lerno
74d868d113 Fix of bug in !floatval codegen. Added float parsing. 2023-02-10 22:17:55 +01:00
Christoffer Lerno
b3f15a867c Added "clear" to VarString 2023-02-10 08:40:08 +01:00
Christoffer Lerno
0a0cc4b5df Header printout complete. "@export" added but might get removed. 2023-02-09 23:16:18 +01:00
Christoffer Lerno
f313e90e28 Some work on headers. Fix unsigned negation on consts. 2023-02-09 09:15:51 +01:00
Christoffer Lerno
a6b9405e21 Fix @expect. 2023-02-07 15:45:46 +01:00
Christoffer Lerno
508cc8f29f Update comment. 2023-02-07 15:40:56 +01:00
Christoffer Lerno
1843870dfd Fix MSVC 2023-02-07 11:16:35 +01:00
Christoffer Lerno
926dbfc535 Fix "int $f = 1;" and int $f; 2023-02-07 00:22:31 +01:00
Christoffer Lerno
cc189a8166 Support user defined attributes in generic modules. Fix bug with user defined attributes with parameters. Fix bug that would overwrite attributes when parsing user defined attributes. 2023-02-06 23:31:03 +01:00
Christoffer Lerno
c89dbba6d1 Conform to the aarch64 ABI when passing invalid vectors. 2023-02-06 22:54:59 +01:00
Christoffer Lerno
9e4203e267 Fix missing "max type" cases. 2023-02-06 21:53:33 +01:00
Christoffer Lerno
6cef75b608 Removes win x86 target. Add win aarch64. Fixes to jump buffer sizes. Fix returning bool[2] in the SysV ABI. Array comparison now works. Prevent flexible array comparisons. Prevent zero size unions. 2023-02-06 18:09:31 +01:00
Christoffer Lerno
3c4796d65a Insert nullcheck for & params on the callee side. 2023-02-05 23:18:35 +01:00
Christoffer Lerno
e0252a6d7a Make it possible to use @deprecated in docs. 2023-02-05 22:13:50 +01:00
Christoffer Lerno
505543e9b4 Remove import of std::array 2023-02-05 20:59:42 +01:00
Christoffer Lerno
86e085e0c0 Move collection types. Improve linked list interface. Update map.destroy => map.free 2023-02-05 20:55:47 +01:00
Christoffer Lerno
4a102698b2 Add tests to math and add info in readme how to contribute. 2023-02-05 14:30:06 +01:00
Christoffer Lerno
5a65a57e42 Catch macro infinite recursion. Fix of tan function. 2023-02-05 00:21:26 +01:00
Christoffer Lerno
38b44a7265 Fix of tan. 2023-02-04 23:32:17 +01:00
Christoffer Lerno
0f7d21330a Optimize optional return. 2023-02-04 19:48:42 +01:00
Christoffer Lerno
dce171670f Use printn rather than println. Add string methods for copying. 2023-02-04 19:48:42 +01:00
Christoffer Lerno
6b928c7a3d Add saturated math and expect macros. 2023-02-03 00:25:29 +01:00
Christoffer Lerno
6407eb47a4 Remove of @extname in stdlib. 2023-02-02 21:53:37 +01:00
Christoffer Lerno
3b3dd334e0 Remove $if in thread 2023-02-02 21:41:57 +01:00
Christoffer Lerno
3b3773663a Adding sincos / libc tan/tanf. 2023-02-02 19:29:29 +01:00
Christoffer Lerno
0f4d20f168 Fix issue with hex floats with f being assumed to be double. Added cos sincos sincosf pow2 pow2f to nolibc. 2023-02-02 13:20:52 +01:00
Christoffer Lerno
1d8e341572 Wasm allocator is now __heap_base aware. Make builds overridable from the project. 2023-02-01 16:42:45 +01:00
Kenta
f9b6f1da0e Update trig.c3
Add cosf
2023-02-01 16:42:16 +01:00
Christoffer Lerno
6a3219ad43 Make thread allocator take the thread allocator by default for allocating initial memory. Add some int128 methods. Fix attribute parsing. 2023-02-01 14:06:14 +01:00
Christoffer Lerno
f916aa9189 More support for working with win32 types. 2023-01-31 20:54:42 +01:00
Christoffer Lerno
c665a431ad Make wasm use .wasm for executables. 2023-01-31 19:46:53 +01:00
Christoffer Lerno
4ea81fe636 More riscv tests. Fix missing zero/signext attributes on calls. Fixes and simplification to riscv ABI. 2023-01-31 14:09:32 +01:00
Christoffer Lerno
0afd55425a More ABI cleanup and fixes to RISC-V ABI + tests for riscv32. 2023-01-30 23:46:06 +01:00
Kenta
dbb759713f Update build-with-docker.sh
Improve the bash script by taking user input straight to a variable with read -p. 
Clean the code up.
2023-01-30 23:44:40 +01:00
Kenta
61cc8163f9 Update docker script and dockerfile
Using Ubuntu 23 throws an error "groupadd: GID '1000' already exists" when trying to build. Ubuntu 22 works fine.

There should be no difference between building C3 on Ubuntu 22 vs 23.

To avoid issues raised it's best to move to single Ubuntu version that builds the compiler.
2023-01-30 21:47:37 +01:00
Christoffer Lerno
adf84e38d0 Update LLVM versions. 2023-01-30 13:54:28 +01:00
Christoffer Lerno
ae4658933a Add missing version bump. 2023-01-30 12:39:46 +01:00
Christoffer Lerno
539d733ceb Remove LLVM 14 support. Simplify ABI lowering. 2023-01-30 12:39:04 +01:00
Christoffer Lerno
124a18a486 Fix bug with enum associated values. 2023-01-29 19:15:13 +01:00
Christoffer Lerno
3046a9f0c2 Fix File.printf 2023-01-29 17:58:17 +01:00
Christoffer Lerno
a77e0bf7b3 Add LLVM 17 to CI. 2023-01-29 14:44:03 +01:00
Christoffer Lerno
84582f86b6 Cleanup. 2023-01-29 14:20:58 +01:00
Christoffer Lerno
3f5fb5da92 Fix of variant type. 2023-01-29 13:59:07 +01:00
Christoffer Lerno
2fb85e5e95 Fix of variant type. 2023-01-29 13:58:57 +01:00
Christoffer Lerno
e604d40fd3 Added $$FILEPATH 2023-01-29 13:26:34 +01:00
Christoffer Lerno
77608e137e Updated winmain handling 2023-01-29 13:18:43 +01:00
Christoffer Lerno
52d7e58c19 Make Windows use wWinMain always. Support /SUBSYSTEM:WINDOWS 2023-01-29 01:32:35 +01:00
Christoffer Lerno
5ea15eb708 Make Windows use wWinMain always. Support /SUBSYSTEM:WINDOWS 2023-01-29 01:32:35 +01:00
Christoffer Lerno
f89bf9ea2f Update clamp. 2023-01-28 03:52:07 +01:00
Christoffer Lerno
4ddb9d9fbc Fix tests. 2023-01-28 03:52:07 +01:00
Christoffer Lerno
1095f098ef Reduce formatter register formatter register type memory usage. 2023-01-28 01:23:15 +01:00
Christoffer Lerno
9f63f77f22 Update precedence clarification rules for ^|& 2023-01-28 00:54:01 +01:00
Christoffer Lerno
9a08c9d821 Fixes to wasm and function attributes. 2023-01-28 00:37:46 +01:00
Christoffer Lerno
445239b418 Add simple heap allocator and update lambda and #lazy checking. 2023-01-27 14:40:54 +01:00
Christoffer Lerno
03b659373a Make the quine use "String" rather than char[] 2023-01-26 21:19:16 +01:00
Christoffer Lerno
4561bf5a85 Add quine example. 2023-01-26 21:17:08 +01:00
Christoffer Lerno
6eb65d5b37 Add memory-env option. 2023-01-26 21:16:47 +01:00
Christoffer Lerno
6a73c8e90e Clean lib before copy. 2023-01-26 16:28:47 +01:00
Christoffer Lerno
5c7d859fdb Some updates to builtin checking. 2023-01-26 16:24:18 +01:00
Christoffer Lerno
a95710c93f Add no-entry to project/command line. Add "link-args" to project. Add @wasm and @extern attributes. Added $$wasm_memory_size and $$wasm_memory_grow builtins. 2023-01-26 12:02:09 +01:00
Christoffer Lerno
39801a304d Improved support for freestanding. 2023-01-25 11:27:57 +01:00
Christoffer Lerno
a22ebbb0ef Improved support for freestanding. 2023-01-25 11:10:37 +01:00
Christoffer Lerno
f37f779e5a Fix in utf8to16 conversion. 2023-01-24 14:20:37 +01:00
Christoffer Lerno
b508a43f8f Add lambdas. 2023-01-24 10:15:23 +01:00
Christoffer Lerno
c9e1e2d763 Replace $$shufflevector with $$swizzle and $$swizzle2. Add builtin swizzle accessors. 2023-01-22 01:12:55 +01:00
Christoffer Lerno
1e18e576c7 Fix int[] -> void* casts. 2023-01-21 20:17:48 +01:00
Christoffer Lerno
5151586450 Updated complex / matrix. Added quaternion math, vectors. Possible to add and mult scalar with vector. Fix where negating a float vector would be lowered incorrectly. Fix where $typeof(x) { ... } would not be valid compound literal. Fix where var would not be recognized as starting a declaration (e.g. in if (var x = ...) 2023-01-21 00:59:33 +01:00
Christoffer Lerno
e09628b664 Added easings. Move of math to own folder. 2023-01-19 22:49:30 +01:00
Christoffer Lerno
92507ee388 More work on TB 2023-01-19 20:32:19 +01:00
Christoffer Lerno
1ab304dc64 Fix of overalignment on deref. 2023-01-19 12:29:17 +01:00
Christoffer Lerno
e284d49dd9 Initial add of tilde backend. 2023-01-19 00:08:31 +01:00
Christoffer Lerno
6da6288ad8 Support roundevenf/roundeven 2023-01-14 15:24:49 +01:00
Dmitry Atamanov
925e4e6e46 Added native option to --x86vec help. 2023-01-14 15:09:22 +01:00
Christoffer Lerno
bd12ef0a53 Add version bump. 2023-01-13 01:10:02 +01:00
Christoffer Lerno
2123e81e8e Make more of the builtins do promotion from int to double for convenience. 2023-01-13 01:09:27 +01:00
Dmitry Atamanov
50b0958fb6 Add Loong arch deps for LLD >= 16 2023-01-12 18:43:05 +01:00
Christoffer Lerno
026861051a Make LLVM 15 compile in CI again. (#699)
* Fixes to LLVM 15 CI
2023-01-12 18:41:51 +01:00
Christoffer Lerno
dc16f65c8e Change printfln to printfn. Make LLVM 15 tests default. 2023-01-11 18:00:08 +01:00
Christoffer Lerno
3298ff2e15 Fix of typo. 2023-01-11 00:53:57 +01:00
Christoffer Lerno
aded1cb736 Use 15.0.6 on windows. Exclude 14 on linux. Update docker build. 2023-01-11 00:52:43 +01:00
Christoffer Lerno
da65de2d01 Add compare_exchange. Rudimentary threads (subject to change) 2023-01-10 20:46:39 +01:00
Christoffer Lerno
c9e40cfa37 Cast cleanup, removing structural casts. 2023-01-09 00:08:29 +01:00
Christoffer Lerno
43dc2d650c Use "String" consistently for "char[]" (#694)
Use "String" consistently for "char[]". Fix win32 return value.
2023-01-07 22:50:33 +01:00
Christoffer Lerno
5b2b4e900f Add download links. 2023-01-07 15:12:05 +01:00
Christoffer Lerno
6c9de52de7 Revert. 2023-01-07 15:02:52 +01:00
Christoffer Lerno
98f3decc1a Set release to real and not prerelease. 2023-01-07 14:59:57 +01:00
Christoffer Lerno
c485a89940 Updated README 2023-01-07 02:39:26 +01:00
Christoffer Lerno
73000680e5 Update readme 2023-01-07 02:18:03 +01:00
Christoffer Lerno
009bbeb48f Using C files now correctly places object files in build folder. 2023-01-07 01:38:44 +01:00
Christoffer Lerno
38be3d57dd Vendor fetch. 2023-01-06 16:49:45 +01:00
Christoffer Lerno
ad48770977 Updated errno. Socket constants and some functions added. Fix error when a macro returns a void! and that macro is in turn set to a return. Removed too permissive casts to subarrays. 2023-01-06 12:46:58 +01:00
Dmitry Atamanov
8390655d79 Fixes $$get_rounding_mode() for LLVM > 15. 2023-01-05 12:56:18 +01:00
Christoffer Lerno
4330740cf8 Support printing of object files. Issue #687 2023-01-04 23:16:47 +01:00
Christoffer Lerno
4a99190f96 C3L zip support. Version bump. 2023-01-03 22:40:35 +01:00
Christoffer Lerno
f8a505754d Added arcfour crypto. 2022-12-30 16:34:31 +01:00
Christoffer Lerno
23a78a9ae5 Updated nbodies. Fixed sum/product on floats. 2022-12-30 02:41:17 +01:00
Christoffer Lerno
bf222557fb Handle missing tests. 2022-12-29 21:13:58 +01:00
Christoffer Lerno
aa33536ab1 Remove test global when not in testing mode. 2022-12-29 20:40:23 +01:00
Christoffer Lerno
7b1fc87566 Bump to 0.4.0 2022-12-29 19:51:00 +01:00
1368 changed files with 62119 additions and 68859 deletions

View File

@@ -7,7 +7,7 @@ on:
branches: [ master ]
env:
LLVM_RELEASE_VERSION: 15
LLVM_RELEASE_VERSION: 16
jobs:
@@ -34,8 +34,11 @@ jobs:
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\hello_world_many.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\time.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\fannkuch-redux.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\load_world.c3
- name: Build testproject
run: |
@@ -47,6 +50,10 @@ jobs:
cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log build hello_world_win32_lib
- name: Vendor-fetch
run: |
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
- name: run compiler tests
run: |
cd test
@@ -95,14 +102,20 @@ jobs:
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
- name: Build testproject
run: |
cd resources/testproject
../../build/c3c run --debug-log
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib
- name: Build testproject lib
run: |
cd resources/testproject
@@ -111,12 +124,12 @@ jobs:
- name: run compiler tests
run: |
cd test
python3 src/tester.py ../build/c3c.exe test_suite2/
python3 src/tester.py ../build/c3c.exe test_suite/
build-msys2-clang:
runs-on: windows-latest
if: ${{ false }}
#if: ${{ false }}
strategy:
# Don't abort runners if a single one fails
fail-fast: false
@@ -144,8 +157,10 @@ jobs:
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
- name: Build testproject
run: |
@@ -169,18 +184,18 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [13, 14, 15, 16]
llvm_version: [16]
steps:
- uses: actions/checkout@v3
- name: Install common deps
run: |
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
- name: Install Clang ${{matrix.llvm_version}}
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
if [[ "${{matrix.llvm_version}}" < 16 ]]; then
if [[ "${{matrix.llvm_version}}" < 17 ]]; then
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
else
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
@@ -188,7 +203,7 @@ jobs:
sudo apt-get update
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
sudo apt-get install -y libpolly-${{matrix.llvm_version}}-dev
- name: CMake
run: |
cmake -B build \
@@ -206,9 +221,31 @@ jobs:
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile examples/base64.c3
../build/c3c compile examples/binarydigits.c3
../build/c3c compile examples/brainfk.c3
../build/c3c compile examples/factorial_macro.c3
../build/c3c compile examples/fasta.c3
../build/c3c compile examples/gameoflife.c3
../build/c3c compile examples/hash.c3
../build/c3c compile examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/nbodies.c3
../build/c3c compile examples/spectralnorm.c3
../build/c3c compile examples/swap.c3
../build/c3c compile examples/contextfree/boolerr.c3
../build/c3c compile examples/contextfree/dynscope.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
- name: Compile run unit tests
run: |
@@ -228,14 +265,10 @@ jobs:
- name: run compiler tests
run: |
cd test
if [[ "${{matrix.llvm_version}}" < 15 ]]; then
python3 src/tester.py ../build/c3c test_suite/
else
python3 src/tester.py ../build/c3c test_suite2/
fi
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
if: matrix.llvm_version == 16
run: |
mkdir linux
cp -r lib linux
@@ -244,7 +277,7 @@ jobs:
tar czf c3-linux-${{matrix.build_type}}.tar.gz linux
- name: upload artifacts
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
if: matrix.llvm_version == 16
uses: actions/upload-artifact@v3
with:
name: c3-linux-${{matrix.build_type}}
@@ -258,13 +291,12 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [13, 14, 15]
llvm_version: [15, 16]
steps:
- uses: actions/checkout@v3
- name: Download LLVM
run: |
brew update && brew install --overwrite python && brew install python-tk
brew install llvm@${{ matrix.llvm_version }} botan ninja
brew install llvm@${{ matrix.llvm_version }} ninja curl
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
@@ -274,12 +306,18 @@ jobs:
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake --build build
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib
- name: Compile and run some examples
run: |
cd resources
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
- name: Compile run unit tests
run: |
@@ -304,11 +342,7 @@ jobs:
- name: run compiler tests
run: |
cd test
if [[ "${{matrix.llvm_version}}" < 15 ]]; then
python3 src/tester.py ../build/c3c test_suite/
else
python3 src/tester.py ../build/c3c test_suite2/
fi
python3 src/tester.py ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "tilde-backend"]
path = tilde-backend
url = https://github.com/c3lang/tilde-backend

View File

@@ -1,22 +1,37 @@
cmake_minimum_required(VERSION 3.15)
project(c3c)
# Grab the version
file(READ "src/version.h" ver)
if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
message(FATAL_ERROR "Compiler version could not be parsed from version.h")
endif()
# Set the project and version
project(c3c VERSION ${CMAKE_MATCH_1})
message("C3C version: ${CMAKE_PROJECT_VERSION}")
# Enable fetching (for Windows)
include(FetchContent)
include(FeatureSummary)
set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
# We use C11 and C++17
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
if(MSVC)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHsc")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHsc")
else()
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -fno-exceptions")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -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")
endif()
#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O1 -fsanitize=undefined")
#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O1 -fsanitize=undefined")
@@ -24,9 +39,9 @@ endif()
#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fsanitize=undefined")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
option(C3_USE_TB "Enable TB" OFF)
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")
set(C3_USE_MIMALLOC OFF)
@@ -42,24 +57,41 @@ if(C3_USE_MIMALLOC)
)
FetchContent_MakeAvailable(mimalloc)
endif()
if (NOT WIN32)
find_package(CURL)
endif()
if (NOT C3_LLVM_VERSION STREQUAL "auto")
if (${C3_LLVM_VERSION} VERSION_LESS 13 OR ${C3_LLVM_VERSION} VERSION_GREATER 16)
if (${C3_LLVM_VERSION} VERSION_LESS 15 OR ${C3_LLVM_VERSION} VERSION_GREATER 17)
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
endif()
endif()
find_package(Git QUIET)
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
# Update submodules as needed
option(GIT_SUBMODULE "Check submodules during build" ON)
if(GIT_SUBMODULE)
message(STATUS "Submodule update")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_SUBMOD_RESULT)
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
endif()
endif()
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION "14")
set(C3_LLVM_VERSION "16")
endif()
FetchContent_Declare(
LLVM_Windows
URL https://github.com/c3lang/win-llvm/releases/download/llvm1406/llvm-14.0.6-windows-amd64-msvc17-libcmt.7z
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt.7z
)
FetchContent_Declare(
LLVM_Windows_debug
URL https://github.com/c3lang/win-llvm/releases/download/llvm1406/llvm-14.0.6-windows-amd64-msvc17-libcmt-dbg.7z
URL https://github.com/c3lang/win-llvm/releases/download/llvm_16_0_2/llvm-16.0.2-windows-amd64-msvc17-libcmt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
@@ -114,52 +146,44 @@ set(LLVM_LINK_COMPONENTS
Target
TransformUtils
WindowsManifest
WindowsDriver
)
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER 14.1)
set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} WindowsDriver)
endif()
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
# These don't seem to be reliable on windows.
message(STATUS "using find_library")
if(C3_USE_TB)
find_library(TB_LIB NAMES tildebackend.a tildebackend.lib PATHS ${CMAKE_SOURCE_DIR}/tb/)
endif()
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
find_library(LLD_MACHO NAMES lldMachO2.lib lldMachO2.a liblldMachO2.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
else ()
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
endif ()
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_MINGW NAMES lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_WASM NAMES lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
find_library(LLD_CORE NAMES lldCore.lib lldCore.a liblldCore.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_DRIVER NAMES lldDriver.lib lldDriver.a liblldDriver.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_READER_WRITER NAMES lldReaderWriter.lib lldReaderWriter.a liblldReaderWriter.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_YAML NAMES lldYAML.lib lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
endif ()
set(lld_libs
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER_EQUAL 16)
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_DRIVER}
${LLD_READER_WRITER}
${LLD_MACHO}
${LLD_YAML}
${LLD_CORE}
)
else()
set(lld_libs
${LLD_COFF}
${LLD_COMMON}
${LLD_WASM}
${LLD_MINGW}
${LLD_ELF}
${LLD_MACHO}
)
endif()
if (APPLE)
set(lld_libs ${lld_libs} xar)
endif ()
@@ -167,7 +191,9 @@ endif ()
message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Found lld libs ${lld_libs}")
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
add_library(miniz STATIC dependencies/miniz/miniz.c)
add_executable(c3c
src/build/builder.c
@@ -190,13 +216,13 @@ add_executable(c3c
src/compiler/libraries.c
src/compiler/linker.c
src/compiler/llvm_codegen.c
src/compiler/llvm_codegen_c_abi_aarch64.c
src/compiler/llvm_codegen_c_abi.c
src/compiler/llvm_codegen_c_abi_riscv.c
src/compiler/llvm_codegen_c_abi_wasm.c
src/compiler/llvm_codegen_c_abi_win64.c
src/compiler/llvm_codegen_c_abi_x64.c
src/compiler/llvm_codegen_c_abi_x86.c
src/compiler/abi/c_abi_aarch64.c
src/compiler/abi/c_abi.c
src/compiler/abi/c_abi_riscv.c
src/compiler/abi/c_abi_wasm.c
src/compiler/abi/c_abi_win64.c
src/compiler/abi/c_abi_x64.c
src/compiler/abi/c_abi_x86.c
src/compiler/llvm_codegen_debug_info.c
src/compiler/llvm_codegen_expr.c
src/compiler/llvm_codegen_function.c
@@ -228,11 +254,6 @@ add_executable(c3c
src/compiler/symtab.c
src/compiler/target.c
src/compiler/sema_asm.c
src/compiler/tb_codegen.c
src/compiler/tilde_codegen.c
src/compiler/tilde_codegen_instr.c
src/compiler/tilde_codegen_value.c
src/compiler/tilde_codegen_storeload.c
src/compiler_tests/benchmark.c
src/compiler_tests/tests.c
src/compiler/tokens.c
@@ -250,44 +271,86 @@ add_executable(c3c
src/utils/vmem.h
src/utils/whereami.c
src/utils/cpus.c
src/utils/unzipper.c
src/compiler/decltable.c
src/compiler/mac_support.c
src/compiler/tilde_codegen_storeload.c
src/compiler/llvm_codegen_storeload.c
src/compiler/tilde_codegen_expr.c
src/compiler/tilde_codegen_stmt.c
src/compiler/tilde_codegen_type.c
src/compiler/windows_support.c
src/compiler/codegen_asm.c
src/compiler/asm_target.c
src/compiler/llvm_codegen_builtins.c
src/compiler/expr.c src/utils/time.c)
src/compiler/expr.c
src/utils/time.c
src/utils/http.c
src/compiler/sema_liveness.c)
if (C3_USE_TB)
file(GLOB tilde-sources
tilde-backend/src/tb/*.c
tilde-backend/src/tb/codegen/*.c
tilde-backend/src/tb/bigint/*.c
tilde-backend/src/tb/objects/*.c
tilde-backend/src/tb/system/*.c
tilde-backend/src/tb/debug/cv/*.c
tilde-backend/src/tb/opt/*.c
tilde-backend/src/tb/x64/*.c
tilde-backend/src/tb/wasm/*.c
tilde-backend/src/tb/aarch64/*.c
)
target_sources(c3c PRIVATE
src/compiler/tilde_codegen.c
src/compiler/tilde_codegen_instr.c
src/compiler/tilde_codegen_value.c
src/compiler/tilde_codegen_storeload.c
src/compiler/tilde_codegen_expr.c
src/compiler/tilde_codegen_stmt.c
src/compiler/tilde_codegen_type.c
src/compiler/tilde_codegen_abi.c
src/compiler/tilde_codegen_storeload.c)
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=1)
target_link_libraries(c3c tilde-backend)
add_library(tilde-backend STATIC ${tilde-sources})
target_include_directories(tilde-backend PRIVATE
"${CMAKE_SOURCE_DIR}/tilde-backend/src/" "${CMAKE_SOURCE_DIR}/tilde-backend/include")
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/tilde-backend/include/")
else()
target_compile_definitions(c3c PUBLIC TB_AVAILABLE=0)
endif()
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/src/")
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/tb/")
target_include_directories(c3c_wrappers PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/src/")
target_include_directories(miniz PUBLIC
"${CMAKE_SOURCE_DIR}/dependencies/miniz/")
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
target_link_libraries(c3c ${llvm_libs} c3c_wrappers ${lld_libs})
if(C3_USE_TB)
target_link_libraries(c3c c3c_wrappers ${TB_LIB})
target_compile_definitions(c3c PUBLIC TB_BACKEND=1)
else()
target_compile_definitions(c3c PUBLIC TB_BACKEND=0)
endif()
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
if(C3_USE_MIMALLOC)
target_link_libraries(c3c mimalloc-static)
endif()
if (WIN32)
target_link_libraries(c3c Winhttp.lib)
endif()
if (CURL_FOUND)
target_link_libraries(c3c ${CURL_LIBRARIES})
target_include_directories(c3c PRIVATE ${CURL_INCLUDES})
target_compile_definitions(c3c PUBLIC CURL_FOUND=1)
else()
target_compile_definitions(c3c PUBLIC CURL_FOUND=0)
endif()
if(MSVC)
message("Adding MSVC options")
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
@@ -296,10 +359,19 @@ if(MSVC)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_options(c3c PUBLIC /MTd)
target_compile_options(c3c_wrappers PUBLIC /MTd)
target_compile_options(miniz PUBLIC /MTd)
if (C3_USE_TB)
target_compile_options(tilde-backend PUBLIC /MTd)
endif()
else()
target_compile_options(c3c PUBLIC /MT)
target_compile_options(c3c_wrappers PUBLIC /MT)
target_compile_options(miniz PUBLIC /MT)
if (C3_USE_TB)
target_compile_options(tilde-backend PUBLIC /MT)
endif()
endif()
else()
message(STATUS "using gcc/clang warning switches")
target_link_options(c3c PRIVATE -pthread)

View File

@@ -116,7 +116,8 @@ more tests as you go along.
### Don't bring in dependencies
External libraries has maintainability issues. Try to depend on as few libraries
as possible. Currently c3c only depends on LLVM and libc.
as possible. Currently, c3c only depends on LLVM and libc with an optional
dependency on libcurl.
Do use rewrites of subsets of other libraries to bring in functionality, but avoid
copying in libraries that needs to be updated separately.

View File

@@ -1,8 +1,16 @@
# C3 Language
C3 is a C-like language striving to be an evolution of C, rather than a
completely new language. As an alternative in the C/C++ niche it
aims to be fast and close to the metal.
C3 is a programming language that builds on the syntax and semantics of the C language,
with the goal of evolving it while still retaining familiarity for C programmers.
It's an evolution, not a revolution: the C-like
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).
- MacOS x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-mac-with-precompiled-binaries).
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
@@ -46,6 +54,7 @@ fn void Stack.push(Stack* this, Type element)
if (this.capacity == this.size)
{
this.capacity *= 2;
if (this.capacity < 16) this.capacity = 16;
this.elems = mem::realloc(this.elems, Type.sizeof * this.capacity);
}
this.elems[this.size++] = element;
@@ -70,9 +79,9 @@ import stack;
// Define our new types, the first will implicitly create
// a complete copy of the entire Stack module with "Type" set to "int"
define IntStack = Stack<int>;
typedef IntStack = Stack<int>;
// The second creates another copy with "Type" set to "double"
define DoubleStack = Stack<double>;
typedef DoubleStack = Stack<double>;
// If we had added "define IntStack2 = Stack<int>"
// no additional copy would have been made (since we already
@@ -108,7 +117,7 @@ fn void test()
}
```
### In what ways do C3 differ from C?
### In what ways does C3 differ from C?
- No mandatory header files
- New semantic macro system
@@ -128,12 +137,9 @@ fn void test()
### Current status
The current version of the compiler is alpha release 0.3.
The current version of the compiler is alpha release 0.4.
It's possible to try out the current C3 compiler in the browser: https://ide.judge0.com/ this is courtesy of the
developer of Judge0.
Design work on C3 complete aside from fleshing out details, such as
Design work on C3 is complete aside from fleshing out details, such as
inline asm. As the standard library work progresses, changes and improvements
to the language will happen continuously.
Follow the issues [here](https://github.com/c3lang/c3c/issues).
@@ -158,10 +164,10 @@ The compiler is currently verified to compile on Linux, Windows and MacOS.
### Installing
#### Installing on Windows with precompiled binaries
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++" (there is also `c3c/resources/install_win_reqs.bat` to automate this)
2. 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)
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))
3. Unzip exe and standard lib.
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
@@ -206,7 +212,7 @@ A `c3c` executable will be found under `bin/`.
#### Installing on OS X using Homebrew
2. Install CMake: `brew install cmake`
3. Install LLVM 13: `brew install llvm`
3. Install LLVM 15: `brew install llvm`
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
5. Enter the C3C directory `cd c3c`.
6. Create a build directory `mkdir build`
@@ -223,7 +229,7 @@ import std::io;
fn void main()
{
io::println("Hello, world!");
io::printn("Hello, world!");
}
```
@@ -261,7 +267,7 @@ You can try it out by running some sample code: `c3c.exe compile ../resources/ex
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
2. Install CMake: `sudo apt install cmake`
3. Install LLVM 13 (or greater: C3C supports LLVM 13-16): `sudo apt-get install clang-13 zlib1g zlib1g-dev libllvm13 llvm-13 llvm-13-dev llvm-13-runtime liblld-13-dev liblld-13`
3. Install LLVM 15 (or greater: C3C supports LLVM 15-17): `sudo apt-get install clang-15 zlib1g zlib1g-dev libllvm15 llvm-15 llvm-15-dev llvm-15-runtime liblld-15-dev liblld-15`
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
5. Enter the C3C directory `cd c3c`.
6. Create a build directory `mkdir build`
@@ -277,7 +283,7 @@ You can try it out by running some sample code: `./c3c compile ../resources/exam
#### Compiling on other Linux / Unix variants
1. Install CMake.
2. Install or compile LLVM and LLD *libraries* (version 13+ or higher)
2. Install or compile LLVM and LLD *libraries* (version 15+ or higher)
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory `cd c3c`.
5. Create a build directory `mkdir build`
@@ -286,6 +292,10 @@ You can try it out by running some sample code: `./c3c compile ../resources/exam
provide the link path to the LLVM CMake directories, e.g. `cmake -DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm/ ..`
8. Build: `cmake --build .`
*A note on compiling for Linux/Unix/MacOS: to be able to fetch vendor libraries
libcurl is needed. The CMake script should detect it if it is available. Note that
this functionality is non-essential and it is perfectly fine to user the compiler without it.*
#### Licensing
The C3 compiler is licensed under LGPL 3.0, the standard library itself is
@@ -294,3 +304,12 @@ MIT licensed.
#### Editor plugins
Editor plugins can be found at https://github.com/c3lang/editor-plugins.
#### Contributing unit tests
1. Write the test, either adding to existing test files in `/test/unit/` or add
a new file. (If testing the standard library, put it in the `/test/unit/stdlib/` subdirectory).
2. Make sure that the test functions have the `@test` attribute.
3. Run tests and see that they pass. (Recommended settings: `c3c compile-test --safe -g1 -O0 test/unit`.
- in this example `test/unit/` is the relative path to the test directory, so adjust as required)
4. Make a pull request for the new tests.

View File

@@ -1,15 +1,13 @@
#!/bin/bash
## build-with-docker.sh
## @author gdm85
## @modified by Kenta
##
## Script to build c3c for either Ubuntu 20, 21 or 22.
## Script to build c3c for Ubuntu 22
##
#
if [ $# -ne 1 -a $# -ne 2 ]; then
echo "Usage: build-with-docker.sh (20|21|22) [Debug|Release]" 1>&2
exit 1
fi
read -p "Select Build Type: Debug/Release: " config
set -e
@@ -17,37 +15,30 @@ DOCKER=docker
DOCKER_RUN=""
IMAGE="c3c-builder"
if type podman 2>/dev/null >/dev/null; then
DOCKER=podman
DOCKER_RUN="--userns=keep-id"
IMAGE="localhost/$IMAGE"
DOCKER=podman
DOCKER_RUN="--userns=keep-id"
IMAGE="localhost/$IMAGE"
fi
if [ -z "$2" ]; then
CMAKE_BUILD_TYPE=Debug
if [ $config == "Debug" ]; then
echo "debug???"
CMAKE_BUILD_TYPE=Debug
else
CMAKE_BUILD_TYPE="$2"
CMAKE_BUILD_TYPE="$config"
fi
TAG="$1"
if [ "$1" = 21 ]; then
UBUNTU_VERSION="21.10"
LLVM_VERSION="13"
elif [ "$1" = 22 ]; then
UBUNTU_VERSION="22.04"
LLVM_VERSION="14"
else
echo "ERROR: expected 21 or 22 as Ubuntu version argument" 1>&2
exit 2
fi
IMAGE="$IMAGE:$TAG"
UBUNTU_VERSION="22.10"
LLVM_VERSION="15"
cd docker && $DOCKER build -t $IMAGE --build-arg UID=$(id -u) --build-arg GID=$(id -g) \
--build-arg DEPS="llvm-$LLVM_VERSION-dev liblld-$LLVM_VERSION-dev clang-$LLVM_VERSION libllvm$LLVM_VERSION llvm-$LLVM_VERSION-runtime" \
--build-arg UBUNTU_VERSION="$UBUNTU_VERSION" .
IMAGE="$IMAGE:22"
cd docker && $DOCKER build -t $IMAGE\
--build-arg DEPS="llvm-$LLVM_VERSION-dev liblld-$LLVM_VERSION-dev clang-$LLVM_VERSION libllvm$LLVM_VERSION llvm-$LLVM_VERSION-runtime" \
--build-arg UBUNTU_VERSION="$UBUNTU_VERSION" .
cd ..
rm -rf build bin
mkdir -p build bin
exec $DOCKER run -ti --rm --tmpfs=/tmp $DOCKER_RUN -v "$PWD":/home/c3c/source -w /home/c3c/source $IMAGE bash -c \
"cd build && cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DC3_LLVM_VERSION=$LLVM_VERSION .. && cmake --build . && mv c3c lib ../bin/"
"cd build && cmake -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DC3_LLVM_VERSION=$LLVM_VERSION .. && cmake --build . && mv c3c lib ../bin/"

7833
dependencies/miniz/miniz.c vendored Normal file

File diff suppressed because it is too large Load Diff

1422
dependencies/miniz/miniz.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && export TERM=xterm && apt-get update
ARG GID=1000
ARG UID=1000
RUN groupadd --gid=$GID c3c && useradd --gid=$GID --uid=$GID --create-home --shell /bin/bash c3c
RUN groupadd -o --gid=$GID c3c && useradd --gid=$GID --uid=$GID --create-home --shell /bin/bash c3c
USER c3c

View File

@@ -4,169 +4,169 @@ module std::bits;
/**
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
**/
macro reverse(i) = $$bitreverse(i);
macro reverse(i) => $$bitreverse(i);
/**
* @require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
**/
macro bswap(i) @builtin = $$bswap(i);
macro bswap(i) @builtin => $$bswap(i);
macro uint[<*>].popcount(uint[<*>] i) = $$popcount(i);
macro uint[<*>].ctz(uint[<*>] i) = $$ctz(i);
macro uint[<*>].clz(uint[<*>] i) = $$clz(i);
macro uint[<*>].popcount(uint[<*>] i) => $$popcount(i);
macro uint[<*>].ctz(uint[<*>] i) => $$ctz(i);
macro uint[<*>].clz(uint[<*>] i) => $$clz(i);
macro uint[<*>] uint[<*>].fshl(uint[<*>] hi, uint[<*>] lo, uint[<*>] shift) => $$fshl(hi, lo, shift);
macro uint[<*>] uint[<*>].fshr(uint[<*>] hi, uint[<*>] lo, uint[<*>] shift) => $$fshr(hi, lo, shift);
macro uint[<*>] uint[<*>].rotl(uint[<*>] i, uint[<*>] shift) => $$fshl(i, i, shift);
macro uint[<*>] uint[<*>].rotr(uint[<*>] i, uint[<*>] shift) => $$fshr(i, i, shift);
macro int[<*>].popcount(int[<*>] i) = $$popcount(i);
macro int[<*>].ctz(int[<*>] i) = $$ctz(i);
macro int[<*>].clz(int[<*>] i) = $$clz(i);
macro int[<*>].popcount(int[<*>] i) => $$popcount(i);
macro int[<*>].ctz(int[<*>] i) => $$ctz(i);
macro int[<*>].clz(int[<*>] i) => $$clz(i);
macro int[<*>] int[<*>].fshl(int[<*>] hi, int[<*>] lo, int[<*>] shift) => $$fshl(hi, lo, shift);
macro int[<*>] int[<*>].fshr(int[<*>] hi, int[<*>] lo, int[<*>] shift) => $$fshr(hi, lo, shift);
macro int[<*>] int[<*>].rotl(int[<*>] i, int[<*>] shift) => $$fshl(i, i, shift);
macro int[<*>] int[<*>].rotr(int[<*>] i, int[<*>] shift) => $$fshr(i, i, shift);
macro ushort[<*>].popcount(ushort[<*>] i) = $$popcount(i);
macro ushort[<*>].ctz(ushort[<*>] i) = $$ctz(i);
macro ushort[<*>].clz(ushort[<*>] i) = $$clz(i);
macro ushort[<*>].popcount(ushort[<*>] i) => $$popcount(i);
macro ushort[<*>].ctz(ushort[<*>] i) => $$ctz(i);
macro ushort[<*>].clz(ushort[<*>] i) => $$clz(i);
macro ushort[<*>] ushort[<*>].fshl(ushort[<*>] hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshl(hi, lo, shift);
macro ushort[<*>] ushort[<*>].fshr(ushort[<*>] hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshr(hi, lo, shift);
macro ushort[<*>] ushort[<*>].rotl(ushort[<*>] i, ushort[<*>] shift) => $$fshl(i, i, shift);
macro ushort[<*>] ushort[<*>].rotr(ushort[<*>] i, ushort[<*>] shift) => $$fshr(i, i, shift);
macro short[<*>].popcount(short[<*>] i) = $$popcount(i);
macro short[<*>].ctz(short[<*>] i) = $$ctz(i);
macro short[<*>].clz(short[<*>] i) = $$clz(i);
macro short[<*>].popcount(short[<*>] i) => $$popcount(i);
macro short[<*>].ctz(short[<*>] i) => $$ctz(i);
macro short[<*>].clz(short[<*>] i) => $$clz(i);
macro short[<*>] short[<*>].fshl(short[<*>] hi, short[<*>] lo, short[<*>] shift) => $$fshl(hi, lo, shift);
macro short[<*>] short[<*>].fshr(short[<*>] hi, short[<*>] lo, short[<*>] shift) => $$fshr(hi, lo, shift);
macro short[<*>] short[<*>].rotl(short[<*>] i, short[<*>] shift) => $$fshl(i, i, shift);
macro short[<*>] short[<*>].rotr(short[<*>] i, short[<*>] shift) => $$fshr(i, i, shift);
macro char[<*>].popcount(char[<*>] i) = $$popcount(i);
macro char[<*>].ctz(char[<*>] i) = $$ctz(i);
macro char[<*>].clz(char[<*>] i) = $$clz(i);
macro char[<*>].popcount(char[<*>] i) => $$popcount(i);
macro char[<*>].ctz(char[<*>] i) => $$ctz(i);
macro char[<*>].clz(char[<*>] i) => $$clz(i);
macro char[<*>] char[<*>].fshl(char[<*>] hi, char[<*>] lo, char[<*>] shift) => $$fshl(hi, lo, shift);
macro char[<*>] char[<*>].fshr(char[<*>] hi, char[<*>] lo, char[<*>] shift) => $$fshr(hi, lo, shift);
macro char[<*>] char[<*>].rotl(char[<*>] i, char[<*>] shift) => $$fshl(i, i, shift);
macro char[<*>] char[<*>].rotr(char[<*>] i, char[<*>] shift) => $$fshr(i, i, shift);
macro ichar[<*>].popcount(ichar[<*>] i) = $$popcount(i);
macro ichar[<*>].ctz(ichar[<*>] i) = $$ctz(i);
macro ichar[<*>].clz(ichar[<*>] i) = $$clz(i);
macro ichar[<*>].popcount(ichar[<*>] i) => $$popcount(i);
macro ichar[<*>].ctz(ichar[<*>] i) => $$ctz(i);
macro ichar[<*>].clz(ichar[<*>] i) => $$clz(i);
macro ichar[<*>] ichar[<*>].fshl(ichar[<*>] hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshl(hi, lo, shift);
macro ichar[<*>] ichar[<*>].fshr(ichar[<*>] hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshr(hi, lo, shift);
macro ichar[<*>] ichar[<*>].rotl(ichar[<*>] i, ichar[<*>] shift) => $$fshl(i, i, shift);
macro ichar[<*>] ichar[<*>].rotr(ichar[<*>] i, ichar[<*>] shift) => $$fshr(i, i, shift);
macro ulong[<*>].popcount(ulong[<*>] i) = $$popcount(i);
macro ulong[<*>].ctz(ulong[<*>] i) = $$ctz(i);
macro ulong[<*>].clz(ulong[<*>] i) = $$clz(i);
macro ulong[<*>].popcount(ulong[<*>] i) => $$popcount(i);
macro ulong[<*>].ctz(ulong[<*>] i) => $$ctz(i);
macro ulong[<*>].clz(ulong[<*>] i) => $$clz(i);
macro ulong[<*>] ulong[<*>].fshl(ulong[<*>] hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshl(hi, lo, shift);
macro ulong[<*>] ulong[<*>].fshr(ulong[<*>] hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshr(hi, lo, shift);
macro ulong[<*>] ulong[<*>].rotl(ulong[<*>] i, ulong[<*>] shift) => $$fshl(i, i, shift);
macro ulong[<*>] ulong[<*>].rotr(ulong[<*>] i, ulong[<*>] shift) => $$fshr(i, i, shift);
macro long[<*>].popcount(long[<*>] i) = $$popcount(i);
macro long[<*>].ctz(long[<*>] i) = $$ctz(i);
macro long[<*>].clz(long[<*>] i) = $$clz(i);
macro long[<*>].popcount(long[<*>] i) => $$popcount(i);
macro long[<*>].ctz(long[<*>] i) => $$ctz(i);
macro long[<*>].clz(long[<*>] i) => $$clz(i);
macro long[<*>] long[<*>].fshl(long[<*>] hi, long[<*>] lo, long[<*>] shift) => $$fshl(hi, lo, shift);
macro long[<*>] long[<*>].fshr(long[<*>] hi, long[<*>] lo, long[<*>] shift) => $$fshr(hi, lo, shift);
macro long[<*>] long[<*>].rotl(long[<*>] i, long[<*>] shift) => $$fshl(i, i, shift);
macro long[<*>] long[<*>].rotr(long[<*>] i, long[<*>] shift) => $$fshr(i, i, shift);
macro uint128[<*>].popcount(uint128[<*>] i) = $$popcount(i);
macro uint128[<*>].ctz(uint128[<*>] i) = $$ctz(i);
macro uint128[<*>].clz(uint128[<*>] i) = $$clz(i);
macro uint128[<*>].popcount(uint128[<*>] i) => $$popcount(i);
macro uint128[<*>].ctz(uint128[<*>] i) => $$ctz(i);
macro uint128[<*>].clz(uint128[<*>] i) => $$clz(i);
macro uint128[<*>] uint128[<*>].fshl(uint128[<*>] hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshl(hi, lo, shift);
macro uint128[<*>] uint128[<*>].fshr(uint128[<*>] hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshr(hi, lo, shift);
macro uint128[<*>] uint128[<*>].rotl(uint128[<*>] i, uint128[<*>] shift) => $$fshl(i, i, shift);
macro uint128[<*>] uint128[<*>].rotr(uint128[<*>] i, uint128[<*>] shift) => $$fshr(i, i, shift);
macro int128[<*>].popcount(int128[<*>] i) = $$popcount(i);
macro int128[<*>].ctz(int128[<*>] i) = $$ctz(i);
macro int128[<*>].clz(int128[<*>] i) = $$clz(i);
macro int128[<*>].popcount(int128[<*>] i) => $$popcount(i);
macro int128[<*>].ctz(int128[<*>] i) => $$ctz(i);
macro int128[<*>].clz(int128[<*>] i) => $$clz(i);
macro int128[<*>] int128[<*>].fshl(int128[<*>] hi, int128[<*>] lo, int128[<*>] shift) => $$fshl(hi, lo, shift);
macro int128[<*>] int128[<*>].fshr(int128[<*>] hi, int128[<*>] lo, int128[<*>] shift) => $$fshr(hi, lo, shift);
macro int128[<*>] int128[<*>].rotl(int128[<*>] i, int128[<*>] shift) => $$fshl(i, i, shift);
macro int128[<*>] int128[<*>].rotr(int128[<*>] i, int128[<*>] shift) => $$fshr(i, i, shift);
macro uint.popcount(uint i) = $$popcount(i);
macro uint.ctz(uint i) = $$ctz(i);
macro uint.clz(uint i) = $$clz(i);
macro uint.popcount(uint i) => $$popcount(i);
macro uint.ctz(uint i) => $$ctz(i);
macro uint.clz(uint i) => $$clz(i);
macro uint uint.fshl(uint hi, uint lo, uint shift) => $$fshl(hi, lo, shift);
macro uint uint.fshr(uint hi, uint lo, uint shift) => $$fshr(hi, lo, shift);
macro uint uint.rotl(uint i, uint shift) => $$fshl(i, i, shift);
macro uint uint.rotr(uint i, uint shift) => $$fshr(i, i, shift);
macro int.popcount(int i) = $$popcount(i);
macro int.ctz(int i) = $$ctz(i);
macro int.clz(int i) = $$clz(i);
macro int.popcount(int i) => $$popcount(i);
macro int.ctz(int i) => $$ctz(i);
macro int.clz(int i) => $$clz(i);
macro int int.fshl(int hi, int lo, int shift) => $$fshl(hi, lo, shift);
macro int int.fshr(int hi, int lo, int shift) => $$fshr(hi, lo, shift);
macro int int.rotl(int i, int shift) => $$fshl(i, i, shift);
macro int int.rotr(int i, int shift) => $$fshr(i, i, shift);
macro ushort.popcount(ushort i) = $$popcount(i);
macro ushort.ctz(ushort i) = $$ctz(i);
macro ushort.clz(ushort i) = $$clz(i);
macro ushort.popcount(ushort i) => $$popcount(i);
macro ushort.ctz(ushort i) => $$ctz(i);
macro ushort.clz(ushort i) => $$clz(i);
macro ushort ushort.fshl(ushort hi, ushort lo, ushort shift) => $$fshl(hi, lo, shift);
macro ushort ushort.fshr(ushort hi, ushort lo, ushort shift) => $$fshr(hi, lo, shift);
macro ushort ushort.rotl(ushort i, ushort shift) => $$fshl(i, i, shift);
macro ushort ushort.rotr(ushort i, ushort shift) => $$fshr(i, i, shift);
macro short.popcount(short i) = $$popcount(i);
macro short.ctz(short i) = $$ctz(i);
macro short.clz(short i) = $$clz(i);
macro short.popcount(short i) => $$popcount(i);
macro short.ctz(short i) => $$ctz(i);
macro short.clz(short i) => $$clz(i);
macro short short.fshl(short hi, short lo, short shift) => $$fshl(hi, lo, shift);
macro short short.fshr(short hi, short lo, short shift) => $$fshr(hi, lo, shift);
macro short short.rotl(short i, short shift) => $$fshl(i, i, shift);
macro short short.rotr(short i, short shift) => $$fshr(i, i, shift);
macro char.popcount(char i) = $$popcount(i);
macro char.ctz(char i) = $$ctz(i);
macro char.clz(char i) = $$clz(i);
macro char.popcount(char i) => $$popcount(i);
macro char.ctz(char i) => $$ctz(i);
macro char.clz(char i) => $$clz(i);
macro char char.fshl(char hi, char lo, char shift) => $$fshl(hi, lo, shift);
macro char char.fshr(char hi, char lo, char shift) => $$fshr(hi, lo, shift);
macro char char.rotl(char i, char shift) => $$fshl(i, i, shift);
macro char char.rotr(char i, char shift) => $$fshr(i, i, shift);
macro ichar.popcount(ichar i) = $$popcount(i);
macro ichar.ctz(ichar i) = $$ctz(i);
macro ichar.clz(ichar i) = $$clz(i);
macro ichar.popcount(ichar i) => $$popcount(i);
macro ichar.ctz(ichar i) => $$ctz(i);
macro ichar.clz(ichar i) => $$clz(i);
macro ichar ichar.fshl(ichar hi, ichar lo, ichar shift) => $$fshl(hi, lo, shift);
macro ichar ichar.fshr(ichar hi, ichar lo, ichar shift) => $$fshr(hi, lo, shift);
macro ichar ichar.rotl(ichar i, ichar shift) => $$fshl(i, i, shift);
macro ichar ichar.rotr(ichar i, ichar shift) => $$fshr(i, i, shift);
macro ulong.popcount(ulong i) = $$popcount(i);
macro ulong.ctz(ulong i) = $$ctz(i);
macro ulong.clz(ulong i) = $$clz(i);
macro ulong.popcount(ulong i) => $$popcount(i);
macro ulong.ctz(ulong i) => $$ctz(i);
macro ulong.clz(ulong i) => $$clz(i);
macro ulong ulong.fshl(ulong hi, ulong lo, ulong shift) => $$fshl(hi, lo, shift);
macro ulong ulong.fshr(ulong hi, ulong lo, ulong shift) => $$fshr(hi, lo, shift);
macro ulong ulong.rotl(ulong i, ulong shift) => $$fshl(i, i, shift);
macro ulong ulong.rotr(ulong i, ulong shift) => $$fshr(i, i, shift);
macro long.popcount(long i) = $$popcount(i);
macro long.ctz(long i) = $$ctz(i);
macro long.clz(long i) = $$clz(i);
macro long.popcount(long i) => $$popcount(i);
macro long.ctz(long i) => $$ctz(i);
macro long.clz(long i) => $$clz(i);
macro long long.fshl(long hi, long lo, long shift) => $$fshl(hi, lo, shift);
macro long long.fshr(long hi, long lo, long shift) => $$fshr(hi, lo, shift);
macro long long.rotl(long i, long shift) => $$fshl(i, i, shift);
macro long long.rotr(long i, long shift) => $$fshr(i, i, shift);
macro uint128.popcount(uint128 i) = $$popcount(i);
macro uint128.ctz(uint128 i) = $$ctz(i);
macro uint128.clz(uint128 i) = $$clz(i);
macro uint128.popcount(uint128 i) => $$popcount(i);
macro uint128.ctz(uint128 i) => $$ctz(i);
macro uint128.clz(uint128 i) => $$clz(i);
macro uint128 uint128.fshl(uint128 hi, uint128 lo, uint128 shift) => $$fshl(hi, lo, shift);
macro uint128 uint128.fshr(uint128 hi, uint128 lo, uint128 shift) => $$fshr(hi, lo, shift);
macro uint128 uint128.rotl(uint128 i, uint128 shift) => $$fshl(i, i, shift);
macro uint128 uint128.rotr(uint128 i, uint128 shift) => $$fshr(i, i, shift);
macro int128.popcount(int128 i) = $$popcount(i);
macro int128.ctz(int128 i) = $$ctz(i);
macro int128.clz(int128 i) = $$clz(i);
macro int128.popcount(int128 i) => $$popcount(i);
macro int128.ctz(int128 i) => $$ctz(i);
macro int128.clz(int128 i) => $$clz(i);
macro int128 int128.fshl(int128 hi, int128 lo, int128 shift) => $$fshl(hi, lo, shift);
macro int128 int128.fshr(int128 hi, int128 lo, int128 shift) => $$fshr(hi, lo, shift);
macro int128 int128.rotl(int128 i, int128 shift) => $$fshl(i, i, shift);

View File

@@ -0,0 +1,18 @@
module std::collections::enummap<Enum, ValueType>;
struct EnumMap
{
ValueType[Enum.len] values;
}
fn void EnumMap.init(EnumMap* this, ValueType init_value)
{
foreach(&a : this.values)
{
*a = init_value;
}
}
fn uint EnumMap.len(EnumMap* this) @operator(len) => this.values.len;
fn ValueType EnumMap.get(EnumMap* this, Enum key) @operator([]) => this.values[key.ordinal];
fn void EnumMap.set(EnumMap* this, Enum key, ValueType value) @operator([]=) => this.values[key.ordinal] = value;

View File

@@ -0,0 +1,148 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
/**
* @require Enum.kindof == TypeKind.ENUM : "Only enums maybe be used with an enumset"
**/
module std::collections::enumset<Enum>;
const IS_CHAR_ARRAY = Enum.elements > 128;
$switch
$case (Enum.elements > 128):
def EnumSetType @private = char[(Enum.elements + 7) / 8];
$case (Enum.elements > 64):
def EnumSetType @private = uint128;
$case (Enum.elements > 32 || $$C_INT_SIZE > 32):
def EnumSetType @private = ulong;
$case (Enum.elements > 16 || $$C_INT_SIZE > 16):
def EnumSetType @private = uint;
$case (Enum.elements > 8 || $$C_INT_SIZE > 8):
def EnumSetType @private = ushort;
$default:
def EnumSetType @private = char;
$endswitch
def EnumSet = distinct EnumSetType;
fn void EnumSet.add(EnumSet* this, Enum v)
{
$if IS_CHAR_ARRAY:
(*this)[v / 8] |= (char)(1u << (v % 8));
$else
*this = (EnumSet)((EnumSetType)*this | 1u << (EnumSetType)v);
$endif
}
fn void EnumSet.clear(EnumSet* this)
{
$if IS_CHAR_ARRAY:
*this = {};
$else
*this = 0;
$endif
}
fn bool EnumSet.remove(EnumSet* this, Enum v)
{
$if IS_CHAR_ARRAY:
if (!this.has(v) @inline) return false;
(*this)[v / 8] &= (char)~(1 << (v % 8));
return true;
$else
EnumSetType old = (EnumSetType)*this;
EnumSetType new = old & ~(1u << (EnumSetType)v);
*this = (EnumSet)new;
return old != new;
$endif
}
fn bool EnumSet.has(EnumSet* this, Enum v)
{
$if IS_CHAR_ARRAY:
return (bool)(((*this)[v / 8] << (v % 8)) & 0x01);
$else
return ((EnumSetType)*this & (1u << (EnumSetType)v)) != 0;
$endif
}
fn void EnumSet.add_all(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*this)[i] |= c;
$else
*this = (EnumSet)((EnumSetType)*this | (EnumSetType)s);
$endif
}
fn void EnumSet.retain_all(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*this)[i] &= c;
$else
*this = (EnumSet)((EnumSetType)*this & (EnumSetType)s);
$endif
}
fn void EnumSet.remove_all(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
foreach (i, c : s) (*this)[i] &= ~c;
$else
*this = (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
$endif
}
fn EnumSet EnumSet.and_of(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
copy.retain_all(s);
return copy;
$else
return (EnumSet)((EnumSetType)*this & (EnumSetType)s);
$endif
}
fn EnumSet EnumSet.or_of(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
copy.add_all(s);
return copy;
$else
return (EnumSet)((EnumSetType)*this | (EnumSetType)s);
$endif
}
fn EnumSet EnumSet.diff_of(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
copy.remove_all(s);
return copy;
$else
return (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
$endif
}
fn EnumSet EnumSet.xor_of(EnumSet* this, EnumSet s)
{
$if IS_CHAR_ARRAY:
EnumSet copy = *this;
foreach (i, c : s) copy[i] ^= c;
return copy;
$else
return (EnumSet)((EnumSetType)*this ^ (EnumSetType)s);
$endif
}

View File

@@ -0,0 +1,312 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::linkedlist<Type>;
struct Node @private
{
Node *next;
Node *prev;
Type value;
}
struct LinkedList
{
Allocator *allocator;
usz size;
Node *_first;
Node *_last;
}
fn void LinkedList.push(LinkedList* list, Type value)
{
list.link_first(value);
}
fn void LinkedList.push_last(LinkedList* list, Type value)
{
list.link_last(value);
}
fn void LinkedList.init(LinkedList* list, Allocator* using = mem::heap())
{
*list = { .allocator = using };
}
fn void LinkedList.tinit(LinkedList* list) => list.init(mem::temp()) @inline;
/**
* @require list.allocator
**/
macro void LinkedList.@free_node(LinkedList &list, Node* node) @private
{
list.allocator.free(node)!!;
}
macro Node* LinkedList.@alloc_node(LinkedList &list) @private
{
if (!list.allocator) list.allocator = mem::heap();
return malloc(Node, .using = list.allocator);
}
fn void LinkedList.link_first(LinkedList* list, Type value) @private
{
Node *first = list._first;
Node *new_node = list.@alloc_node();
*new_node = { .next = first, .value = value };
list._first = new_node;
if (!first)
{
list._last = new_node;
}
else
{
first.prev = new_node;
}
list.size++;
}
fn void LinkedList.link_last(LinkedList* list, Type value) @private
{
Node *last = list._last;
Node *new_node = list.@alloc_node();
*new_node = { .prev = last, .value = value };
list._last = new_node;
if (!last)
{
list._first = new_node;
}
else
{
last.next = new_node;
}
list.size++;
}
fn Type! peek(LinkedList* list) => list.first() @inline;
fn Type! peek_last(LinkedList* list) => list.last() @inline;
fn Type! LinkedList.first(LinkedList *list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
return list._first.value;
}
fn Type! LinkedList.last(LinkedList* list)
{
if (!list._last) return IteratorResult.NO_MORE_ELEMENT?;
return list._last.value;
}
fn void LinkedList.free(LinkedList* list) => list.clear() @inline;
fn void LinkedList.clear(LinkedList* list)
{
for (Node* node = list._first; node != null;)
{
Node* next = node.next;
list.@free_node(node);
node = next;
}
list._first = null;
list._last = null;
list.size = 0;
}
fn usz LinkedList.len(LinkedList* list) @inline => list.size;
/**
* @require index < list.size
**/
macro Node* LinkedList.node_at_index(LinkedList* list, usz index)
{
if (index * 2 >= list.size)
{
Node* node = list._last;
index = list.size - index - 1;
while (index--) node = node.prev;
return node;
}
Node* node = list._first;
while (index--) node = node.next;
return node;
}
/**
* @require index < list.size
**/
fn Type LinkedList.get(LinkedList* list, usz index)
{
return list.node_at_index(index).value;
}
/**
* @require index < list.size
**/
fn void LinkedList.set(LinkedList* list, usz index, Type element)
{
list.node_at_index(index).value = element;
}
/**
* @require index < list.size
**/
fn void LinkedList.remove(LinkedList* list, usz index)
{
list.unlink(list.node_at_index(index));
}
/**
* @require index <= list.size
**/
fn void LinkedList.insert(LinkedList* list, usz index, Type element)
{
switch (index)
{
case 0:
list.push(element);
case list.size:
list.push_last(element);
default:
list.link_before(list.node_at_index(index), element);
}
}
/**
* @require succ != null
**/
fn void LinkedList.link_before(LinkedList *list, Node *succ, Type value) @private
{
Node* pred = succ.prev;
Node* new_node = malloc(Node);
*new_node = { .prev = pred, .next = succ, .value = value };
succ.prev = new_node;
if (!pred)
{
list._first = new_node;
}
else
{
pred.next = new_node;
}
list.size++;
}
/**
* @require list && list._first
**/
fn void LinkedList.unlink_first(LinkedList* list) @private
{
Node* f = list._first;
Node* next = f.next;
list.@free_node(f);
list._first = next;
if (!next)
{
list._last = null;
}
else
{
next.prev = null;
}
list.size--;
}
fn bool LinkedList.remove_value(LinkedList* list, Type t)
{
for (Node* node = list._first; node != null; node = node.next)
{
if (node.value == t)
{
list.unlink(node);
return true;
}
}
return false;
}
fn bool LinkedList.remove_last_value(LinkedList* list, Type t)
{
for (Node* node = list._last; node != null; node = node.prev)
{
if (node.value == t)
{
list.unlink(node);
return true;
}
}
return false;
}
/**
* @param [&inout] list
**/
fn Type! LinkedList.pop(LinkedList* list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
defer list.unlink_first();
return list._first.value;
}
/**
* @param [&inout] list
**/
fn void! LinkedList.remove_last(LinkedList* list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
list.unlink_last();
}
/**
* @param [&inout] list
**/
fn void! LinkedList.remove_first(LinkedList* list)
{
if (!list._first) return IteratorResult.NO_MORE_ELEMENT?;
list.unlink_first();
}
/**
* @param [&inout] list
* @require list._last
**/
fn void LinkedList.unlink_last(LinkedList *list) @inline @private
{
Node* l = list._last;
Node* prev = l.prev;
list._last = prev;
list.@free_node(l);
if (!prev)
{
list._first = null;
}
else
{
prev.next = null;
}
list.size--;
}
/**
* @require list != null, x != null
**/
fn void LinkedList.unlink(LinkedList* list, Node* x) @private
{
Node* next = x.next;
Node* prev = x.prev;
if (!prev)
{
list._first = next;
}
else
{
prev.next = next;
}
if (!next)
{
list._last = prev;
}
else
{
next.prev = prev;
}
list.@free_node(x);
list.size--;
}

388
lib/std/collections/list.c3 Normal file
View File

@@ -0,0 +1,388 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::list<Type>;
import std::math;
def ElementPredicate = fn bool(Type *type);
struct List
{
usz size;
usz capacity;
Allocator *allocator;
Type *entries;
}
/**
* @require using != null "A valid allocator must be provided"
**/
fn void List.init(List* list, usz initial_capacity = 16, Allocator* using = mem::heap())
{
list.allocator = using;
list.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
list.entries = malloc_aligned(Type, initial_capacity, .alignment = Type[1].alignof, .using = using)!!;
}
else
{
list.entries = null;
}
list.capacity = initial_capacity;
}
fn void List.tinit(List* list, usz initial_capacity = 16)
{
list.init(initial_capacity, mem::temp()) @inline;
}
fn void List.push(List* list, Type element) @inline
{
list.append(element);
}
fn void List.append(List* list, Type element)
{
list.ensure_capacity();
list.entries[list.size++] = element;
}
/**
* @require list.size > 0
*/
fn Type List.pop(List* list)
{
return list.entries[--list.size];
}
fn void List.clear(List* list)
{
list.size = 0;
}
/**
* @require list.size > 0
*/
fn Type List.pop_first(List* list)
{
Type value = list.entries[0];
list.remove_at(0);
return value;
}
fn void List.remove_at(List* list, usz index)
{
for (usz i = index + 1; i < list.size; i++)
{
list.entries[i - 1] = list.entries[i];
}
list.size--;
}
fn void List.add_all(List* list, List* other_list)
{
if (!other_list.size) return;
list.reserve(other_list.size);
foreach (&value : other_list)
{
list.entries[list.size++] = *value;
}
}
fn Type[] List.to_array(List* list, Allocator* using = mem::heap())
{
if (!list.size) return Type[] {};
Type[] result = malloc(Type, list.size, .using = using);
result[..] = list.entries[:list.size];
return result;
}
/**
* Reverse the elements in a list.
*
* @param [&inout] list "The list to reverse"
**/
fn void List.reverse(List* list)
{
if (list.size < 2) return;
usz half = list.size / 2U;
usz end = list.size - 1;
for (usz i = 0; i < half; i++)
{
@swap(list.entries[i], list.entries[end - i]);
}
}
fn Type[] List.array_view(List* list)
{
return list.entries[:list.size];
}
fn void List.add_array(List* list, Type[] array)
{
if (!array.len) return;
list.reserve(array.len);
foreach (&value : array)
{
list.entries[list.size++] = *value;
}
}
fn void List.push_front(List* list, Type type) @inline
{
list.insert_at(0, type);
}
fn void List.insert_at(List* list, usz index, Type type)
{
list.ensure_capacity();
for (usz i = list.size; i > index; i--)
{
list.entries[i] = list.entries[i - 1];
}
list.size++;
list.entries[index] = type;
}
/**
* @require index < list.size
**/
fn void List.set_at(List* list, usz index, Type type)
{
list.entries[index] = type;
}
fn void List.remove_last(List* list)
{
list.size--;
}
fn void List.remove_first(List* list)
{
list.remove_at(0);
}
fn Type* List.first(List* list)
{
return list.size ? &list.entries[0] : null;
}
fn Type* List.last(List* list)
{
return list.size ? &list.entries[list.size - 1] : null;
}
fn bool List.is_empty(List* list)
{
return !list.size;
}
fn usz List.len(List* list) @operator(len)
{
return list.size;
}
fn Type List.get(List* list, usz index)
{
return list.entries[index];
}
fn void List.free(List* list)
{
if (!list.allocator) return;
free_aligned(list.entries, .using = list.allocator);
list.capacity = 0;
list.size = 0;
list.entries = null;
}
fn void List.swap(List* list, usz i, usz j)
{
@swap(list.entries[i], list.entries[j]);
}
/**
* @param [&inout] list "The list to remove elements from"
* @param filter "The function to determine if it should be removed or not"
* @return "the number of deleted elements"
**/
fn usz List.remove_if(List* list, ElementPredicate filter)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (filter(&list.entries[i - 1])) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
/**
* @param [&inout] list "The list to remove elements from"
* @param selection "The function to determine if it should be kept or not"
* @return "the number of deleted elements"
**/
fn usz List.retain_if(List* list, ElementPredicate selection)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (!selection(&list.entries[i - 1])) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
/**
* Reserve at least min_capacity
**/
fn void List.reserve(List* list, usz min_capacity)
{
if (!min_capacity) return;
if (list.capacity >= min_capacity) return;
if (!list.allocator) list.allocator = mem::heap();
min_capacity = math::next_power_of_2(min_capacity);
list.entries = realloc_aligned(list.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof, .using = list.allocator) ?? null;
list.capacity = min_capacity;
}
macro Type List.@item_at(List &list, usz index) @operator([])
{
return list.entries[index];
}
fn Type* List.get_ref(List* list, usz index) @operator(&[]) @inline
{
return &list.entries[index];
}
fn void List.ensure_capacity(List* list, usz added = 1) @inline @private
{
usz new_size = list.size + added;
if (list.capacity > new_size) return;
assert(new_size < usz.max / 2U);
usz new_capacity = list.capacity ? 2U * list.capacity : 16U;
while (new_size >= new_capacity) new_capacity *= 2U;
list.reserve(new_capacity);
}
// Functions for equatable types
$if types::is_equatable_type(Type):
fn usz! List.index_of(List* list, Type type)
{
foreach (i, v : list)
{
if (v == type) return i;
}
return SearchResult.MISSING?;
}
fn usz! List.rindex_of(List* list, Type type)
{
foreach_r (i, v : list)
{
if (v == type) return i;
}
return SearchResult.MISSING?;
}
fn bool List.equals(List* list, List other_list)
{
if (list.size != other_list.size) return false;
foreach (i, v : list)
{
if (v != other_list.entries[i]) return false;
}
return true;
}
/**
* Check for presence of a value in a list.
*
* @param [&in] list "the list to find elements in"
* @param value "The value to search for"
* @return "True if the value is found, false otherwise"
**/
fn bool List.contains(List* list, Type value)
{
foreach (i, v : list)
{
if (v == value) return true;
}
return false;
}
/**
* @param [&inout] list "The list to remove elements from"
* @param value "The value to remove"
* @return "the number of deleted elements."
**/
fn usz List.remove(List* list, Type value)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (list.entries[i - 1] != value) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
fn void List.remove_all(List* list, List* other_list)
{
if (!other_list.size) return;
foreach (v : other_list) list.remove(v);
}
$endif
$if Type.kindof == POINTER:
/**
* @param [&in] list
* @return "The number non-null values in the list"
**/
fn usz List.compact_count(List* list)
{
usz vals = 0;
foreach (v : list) if (v) vals++;
return vals;
}
fn usz List.compact(List* list)
{
usz size = list.size;
for (usz i = size; i > 0; i--)
{
if (list.entries[i - 1]) continue;
for (usz j = i; j < size; j++)
{
list.entries[j - 1] = list.entries[j];
}
list.size--;
}
return size - list.size;
}
$endif

View File

@@ -1,17 +1,14 @@
module std::map<Key, Value>;
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::map<Key, Value>;
import std::math;
const uint DEFAULT_INITIAL_CAPACITY = 16;
const uint MAXIMUM_CAPACITY = 1u << 31;
const float DEFAULT_LOAD_FACTOR = 0.75;
private struct Entry
{
uint hash;
Key key;
Value value;
Entry* next;
}
struct HashMap
{
Entry*[] table;
@@ -26,15 +23,15 @@ struct HashMap
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
* @require allocator != null "The allocator must be non-null"
* @require using != null "The allocator must be non-null"
**/
fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = mem::current_allocator())
fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* using = mem::heap())
{
capacity = math::next_power_of_2(capacity);
map.allocator = allocator;
map.allocator = using;
map.load_factor = load_factor;
map.threshold = (uint)(capacity * load_factor);
map.table = array::make(Entry*, capacity, allocator);
map.table = calloc(Entry*, capacity, .using = using);
}
/**
@@ -45,18 +42,29 @@ fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, flo
**/
fn void HashMap.tinit(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
map.init(capacity, load_factor, mem::temp_allocator());
map.init(capacity, load_factor, mem::temp());
}
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* allocator = mem::current_allocator())
/**
* Has this hash map been initialized yet?
*
* @param [&in] map "The hash map we are testing"
* @return "Returns true if it has been initialized, false otherwise"
**/
fn bool HashMap.is_initialized(HashMap* map)
{
map.init(other_map.table.len, other_map.load_factor, allocator);
return map.allocator != null;
}
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* using = mem::heap())
{
map.init(other_map.table.len, other_map.load_factor, using);
map.put_all_for_create(other_map);
}
fn void HashMap.tinit_from_map(HashMap* map, HashMap* other_map)
{
map.init_from_map(other_map, mem::temp_allocator()) @inline;
map.init_from_map(other_map, mem::temp()) @inline;
}
fn bool HashMap.is_empty(HashMap* map) @inline
@@ -66,13 +74,24 @@ fn bool HashMap.is_empty(HashMap* map) @inline
fn Value*! HashMap.get_ref(HashMap* map, Key key)
{
if (!map.count) return SearchResult.MISSING!;
if (!map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return SearchResult.MISSING!;
return SearchResult.MISSING?;
}
fn Entry*! HashMap.get_entry(HashMap* map, Key key)
{
if (!map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return SearchResult.MISSING?;
}
/**
@@ -104,7 +123,7 @@ fn Value! HashMap.get(HashMap* map, Key key) @operator([])
fn bool HashMap.has_key(HashMap* map, Key key)
{
return try(map.get_ref(key));
return @ok(map.get_ref(key));
}
fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
@@ -130,7 +149,7 @@ fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
fn void! HashMap.remove(HashMap* map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING!;
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
}
fn void HashMap.clear(HashMap* map)
@@ -140,30 +159,30 @@ fn void HashMap.clear(HashMap* map)
{
Entry* entry = *entry_ref;
if (!entry) continue;
map.free(entry);
map.free_internal(entry);
*entry_ref = null;
}
map.count = 0;
}
fn void HashMap.destroy(HashMap* map)
fn void HashMap.free(HashMap* map)
{
if (!map.allocator) return;
map.clear();
map.free(map.table.ptr);
map.free_internal(map.table.ptr);
map.table = Entry*[] {};
}
fn Key[] HashMap.key_tlist(HashMap* map)
{
return map.key_list(mem::temp_allocator()) @inline;
return map.key_list(mem::temp()) @inline;
}
fn Key[] HashMap.key_list(HashMap* map, Allocator* allocator = mem::current_allocator())
fn Key[] HashMap.key_list(HashMap* map, Allocator* using = mem::heap())
{
if (!map.count) return Key[] {};
Key[] list = array::make(Key, map.count, allocator);
Key[] list = calloc(Key, map.count, .using = using);
usz index = 0;
foreach (Entry* entry : map.table)
{
@@ -178,13 +197,13 @@ fn Key[] HashMap.key_list(HashMap* map, Allocator* allocator = mem::current_allo
fn Value[] HashMap.value_tlist(HashMap* map)
{
return map.value_list(mem::temp_allocator()) @inline;
return map.value_list(mem::temp()) @inline;
}
fn Value[] HashMap.value_list(HashMap* map, Allocator* allocator = mem::current_allocator())
fn Value[] HashMap.value_list(HashMap* map, Allocator* using = mem::heap())
{
if (!map.count) return Value[] {};
Value[] list = array::make(Value, map.count, allocator);
Value[] list = calloc(Value, map.count, .using = using);
usz index = 0;
foreach (Entry* entry : map.table)
{
@@ -197,7 +216,7 @@ fn Value[] HashMap.value_list(HashMap* map, Allocator* allocator = mem::current_
return list;
}
$if (types::is_equatable(Value)):
$if types::is_equatable(Value):
fn bool HashMap.has_value(HashMap* map, Value v)
{
if (!map.count) return false;
@@ -211,13 +230,13 @@ fn bool HashMap.has_value(HashMap* map, Value v)
}
return false;
}
$endif;
$endif
// --- private methods
private fn void HashMap.add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index)
fn void HashMap.add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
{
Entry* entry = map.allocator.alloc(Entry.sizeof)!!;
Entry* entry = malloc(Entry, .using = map.allocator);
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
@@ -226,7 +245,7 @@ private fn void HashMap.add_entry(HashMap* map, uint hash, Key key, Value value,
}
}
private fn void HashMap.resize(HashMap* map, uint new_capacity)
fn void HashMap.resize(HashMap* map, uint new_capacity) @private
{
Entry*[] old_table = map.table;
uint old_capacity = old_table.len;
@@ -235,25 +254,25 @@ private fn void HashMap.resize(HashMap* map, uint new_capacity)
map.threshold = uint.max;
return;
}
Entry*[] new_table = array::make(Entry*, new_capacity, map.allocator);
Entry*[] new_table = calloc(Entry*, new_capacity, .using = map.allocator);
map.transfer(new_table);
map.table = new_table;
map.free(old_table.ptr);
map.free_internal(old_table.ptr);
map.threshold = (uint)(new_capacity * map.load_factor);
}
private fn uint rehash(uint hash) @inline
fn uint rehash(uint hash) @inline @private
{
hash ^= (hash >> 20) ^ (hash >> 12);
return hash ^ ((hash >> 7) ^ (hash >> 4));
}
private macro uint index_for(uint hash, uint capacity)
macro uint index_for(uint hash, uint capacity) @private
{
return hash & (capacity - 1);
}
private fn void HashMap.transfer(HashMap* map, Entry*[] new_table)
fn void HashMap.transfer(HashMap* map, Entry*[] new_table) @private
{
Entry*[] src = map.table;
uint new_capacity = new_table.len;
@@ -272,7 +291,7 @@ private fn void HashMap.transfer(HashMap* map, Entry*[] new_table)
}
}
private fn void HashMap.put_all_for_create(HashMap* map, HashMap* other_map)
fn void HashMap.put_all_for_create(HashMap* map, HashMap* other_map) @private
{
if (!other_map.count) return;
foreach (Entry *e : other_map.table)
@@ -282,7 +301,7 @@ private fn void HashMap.put_all_for_create(HashMap* map, HashMap* other_map)
}
}
private fn void HashMap.put_for_create(HashMap* map, Key key, Value value)
fn void HashMap.put_for_create(HashMap* map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
@@ -297,12 +316,12 @@ private fn void HashMap.put_for_create(HashMap* map, Key key, Value value)
map.create_entry(hash, key, value, i);
}
private fn void HashMap.free(HashMap* map, void* ptr)
fn void HashMap.free_internal(HashMap* map, void* ptr) @inline @private
{
map.allocator.free(ptr)!!;
}
private fn bool HashMap.remove_entry_for_key(HashMap* map, Key key)
fn bool HashMap.remove_entry_for_key(HashMap* map, Key key) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
@@ -322,7 +341,7 @@ private fn bool HashMap.remove_entry_for_key(HashMap* map, Key key)
{
prev.next = next;
}
map.free(e);
map.free_internal(e);
return true;
}
prev = e;
@@ -331,11 +350,19 @@ private fn bool HashMap.remove_entry_for_key(HashMap* map, Key key)
return false;
}
private fn void HashMap.create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index)
fn void HashMap.create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
Entry* entry = map.allocator.alloc(Entry.sizeof)!!;
Entry* entry = malloc(Entry, .using = map.allocator);
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
map.table[bucket_index] = entry;
map.count++;
}
}
struct Entry
{
uint hash;
Key key;
Value value;
Entry* next;
}

View File

@@ -0,0 +1,478 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::object;
import std::collections::map;
import std::collections::list;
import std::io;
const Object TRUE_OBJECT = { .b = true, .type = bool.typeid };
const Object FALSE_OBJECT = { .b = false, .type = bool.typeid };
const Object NULL_OBJECT = { .type = void*.typeid };
struct Object
{
typeid type;
Allocator* allocator;
union
{
uint128 i;
double f;
bool b;
String s;
void* other;
ObjectInternalList array;
ObjectInternalMap map;
}
}
static initialize
{
io::formatter_register_type(Object);
}
fn void! Object.to_format(Object* o, Formatter* formatter)
{
switch (o.type)
{
case void:
formatter.printf("{}")!;
case void*:
formatter.printf("null")!;
case String:
formatter.printf(`"%s"`, o.s)!;
case bool:
formatter.printf(o.b ? "true" : "false")!;
case ObjectInternalList:
formatter.printf("[")!;
foreach (i, ol : o.array)
{
formatter.printf(i == 0 ? " " : ", ")!;
ol.to_format(formatter)!;
}
formatter.printf(" ]")!;
case ObjectInternalMap:
formatter.printf("{")!;
@pool()
{
foreach (i, key : o.map.key_tlist())
{
formatter.printf(i == 0 ? " " : ", ")!;
formatter.printf(`"%s": `, key)!;
o.map.get(key).to_format(formatter)!;
}
};
formatter.printf(" }")!;
default:
switch (o.type.kindof)
{
case SIGNED_INT:
formatter.printf("%d", o.i)!;
case UNSIGNED_INT:
formatter.printf("%d", (uint128)o.i)!;
case FLOAT:
formatter.printf("%d", o.f)!;
case ENUM:
formatter.printf("%d", o.i)!;
default:
formatter.printf("<>")!;
}
}
}
fn Object* new_obj(Allocator* using = mem::heap())
{
Object* o = malloc(Object, .using = using);
*o = { .allocator = using, .type = void.typeid };
return o;
}
fn Object* new_null()
{
return &NULL_OBJECT;
}
fn Object* new_int(int128 i, Allocator* using = mem::heap())
{
Object* o = malloc(Object, .using = using);
*o = { .i = i, .allocator = using, .type = int128.typeid };
return o;
}
macro Object* new_enum(e, Allocator* using = mem::heap())
{
Object* o = malloc(Object, .using = using);
*o = { .i = (int128)e, .allocator = using, .type = $typeof(e).typeid };
return o;
}
fn Object* new_float(double f, Allocator* using = mem::current_allocator())
{
Object* o = malloc(Object, .using = using);
*o = { .f = f, .allocator = using, .type = double.typeid };
return o;
}
fn Object* new_string(String s, Allocator* using = mem::heap())
{
Object* o = malloc(Object, .using = using);
*o = { .s = s.copy(using), .allocator = using, .type = String.typeid };
return o;
}
fn Object* new_bool(bool b)
{
return b ? &TRUE_OBJECT : &FALSE_OBJECT;
}
/**
* @param [&inout] o
**/
fn void Object.free(Object* o)
{
switch (o.type)
{
case void:
break;
case String:
free(o.s, .using = o.allocator);
case ObjectInternalList:
foreach (ol : o.array)
{
ol.free();
}
o.array.free();
case ObjectInternalMap:
@pool()
{
foreach (key : o.map.key_tlist())
{
o.map.get(key).free();
free(key, .using = o.allocator);
}
o.map.free();
};
default:
break;
}
if (o.allocator) free(o, .using = o.allocator);
}
fn bool Object.is_null(Object* this) @inline => this == &NULL_OBJECT;
fn bool Object.is_empty(Object* this) @inline => this.type == void.typeid;
fn bool Object.is_map(Object* this) @inline => this.type == ObjectInternalMap.typeid;
fn bool Object.is_array(Object* this) @inline => this.type == ObjectInternalList.typeid;
fn bool Object.is_bool(Object* this) @inline => this.type == bool.typeid;
fn bool Object.is_string(Object* this) @inline => this.type == String.typeid;
fn bool Object.is_float(Object* this) @inline => this.type == double.typeid;
fn bool Object.is_int(Object* this) @inline => this.type == int128.typeid;
fn bool Object.is_keyable(Object* this) => this.is_empty() || this.is_map();
fn bool Object.is_indexable(Object* this) => this.is_empty() || this.is_array();
/**
* @require o.is_keyable()
**/
fn void Object.init_map_if_needed(Object* o) @private
{
if (o.is_empty())
{
o.type = ObjectInternalMap.typeid;
o.map.init(.using = o.allocator);
}
}
/**
* @require o.is_indexable()
**/
fn void Object.init_array_if_needed(Object* o) @private
{
if (o.is_empty())
{
o.type = ObjectInternalList.typeid;
o.array.init(.using = o.allocator);
}
}
/**
* @require o.is_keyable()
**/
fn void Object.set_object(Object* o, String key, Object* new_object) @private
{
o.init_map_if_needed();
ObjectInternalMapEntry*! entry = o.map.get_entry(key);
defer
{
(void)free(entry.key, .using = o.allocator);
entry.value.free();
}
o.map.set(key.copy(o.map.allocator), new_object);
}
macro Object* object_from_value(value) @private
{
var $Type = $typeof(value);
$switch
$case types::is_int($Type):
return new_int(value);
$case types::is_float($Type):
return new_float(value);
$case $Type.typeid == String.typeid:
return new_string(value);
$case $Type.typeid == bool.typeid:
return new_bool(value);
$case $Type.typeid == Object*.typeid:
return value;
$case $Type.typeid == void*.typeid:
assert(value == null);
return &NULL_OBJECT;
$case $checks(String s = value):
return new_string(value);
$default:
$error "Unsupported object type.";
$endswitch
}
macro Object* Object.set(Object* o, String key, value)
{
Object* val = object_from_value(value);
o.set_object(key, val);
return val;
}
/**
* @require o.is_indexable()
**/
macro Object* Object.set_at(Object* o, usz index, String key, value)
{
Object* val = object_from_value(value);
o.set_object_at(key, index, val);
return val;
}
/**
* @require o.is_indexable()
* @ensure return != null
**/
macro Object* Object.append(Object* o, value)
{
Object* val = object_from_value(value);
o.append_object(val);
return val;
}
/**
* @require o.is_keyable()
**/
fn Object*! Object.get(Object* o, String key) => o.is_empty() ? SearchResult.MISSING? : o.map.get(key);
fn bool Object.has_key(Object* o, String key) => o.is_map() && o.map.has_key(key);
/**
* @require o.is_indexable()
**/
fn Object* Object.get_at(Object* o, usz index)
{
return o.array.get(index);
}
/**
* @require o.is_indexable()
**/
fn void Object.append_object(Object* o, Object* to_append)
{
o.init_array_if_needed();
o.array.append(to_append);
}
/**
* @require o.is_indexable()
**/
fn void Object.set_object_at(Object* o, usz index, Object* to_set)
{
o.init_array_if_needed();
while (o.array.len() < index)
{
o.array.append(&NULL_OBJECT);
}
if (o.array.len() == index)
{
o.array.append(to_set);
return;
}
o.array.get(index).free();
o.array.set_at(index, to_set);
}
macro get_integer_value(Object* value, $Type)
{
if (value.is_float())
{
return ($Type)value.f;
}
if (value.is_string())
{
$if $Type.kindof == TypeKind.SIGNED_INT:
return ($Type)value.s.to_int128();
$else
return ($Type)value.s.to_uint128();
$endif
}
if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER?;
return ($Type)value.i;
}
/**
* @require o.is_indexable()
**/
macro Object.get_integer_at(Object* o, $Type, usz index) @private
{
return get_integer_value(o.get_at(index), $Type);
}
/**
* @require o.is_keyable()
**/
macro Object.get_integer(Object* o, $Type, String key) @private
{
return get_integer_value(o.get(key), $Type);
}
fn ichar! Object.get_ichar(Object* o, String key) => o.get_integer(ichar, key);
fn short! Object.get_short(Object* o, String key) => o.get_integer(short, key);
fn int! Object.get_int(Object* o, String key) => o.get_integer(int, key);
fn long! Object.get_long(Object* o, String key) => o.get_integer(long, key);
fn int128! Object.get_int128(Object* o, String key) => o.get_integer(int128, key);
fn ichar! Object.get_ichar_at(Object* o, usz index) => o.get_integer_at(ichar, index);
fn short! Object.get_short_at(Object* o, usz index) => o.get_integer_at(short, index);
fn int! Object.get_int_at(Object* o, usz index) => o.get_integer_at(int, index);
fn long! Object.get_long_at(Object* o, usz index) => o.get_integer_at(long, index);
fn int128! Object.get_int128_at(Object* o, usz index) => o.get_integer_at(int128, index);
fn char! Object.get_char(Object* o, String key) => o.get_integer(ichar, key);
fn short! Object.get_ushort(Object* o, String key) => o.get_integer(ushort, key);
fn uint! Object.get_uint(Object* o, String key) => o.get_integer(uint, key);
fn ulong! Object.get_ulong(Object* o, String key) => o.get_integer(ulong, key);
fn uint128! Object.get_uint128(Object* o, String key) => o.get_integer(uint128, key);
fn char! Object.get_char_at(Object* o, usz index) => o.get_integer_at(char, index);
fn ushort! Object.get_ushort_at(Object* o, usz index) => o.get_integer_at(ushort, index);
fn uint! Object.get_uint_at(Object* o, usz index) => o.get_integer_at(uint, index);
fn ulong! Object.get_ulong_at(Object* o, usz index) => o.get_integer_at(ulong, index);
fn uint128! Object.get_uint128_at(Object* o, usz index) => o.get_integer_at(uint128, index);
/**
* @require o.is_keyable()
**/
fn String! Object.get_string(Object* o, String key)
{
Object* value = o.get(key)!;
assert(value.is_string());
return value.s;
}
/**
* @require o.is_indexable()
**/
fn String Object.get_string_at(Object* o, usz index)
{
Object* value = o.get_at(index);
assert(value.is_string());
return value.s;
}
/**
* @require o.is_keyable()
**/
macro String! Object.get_enum(Object* o, $EnumType, String key)
{
Object value = o.get(key)!;
assert($EnumType.typeid == value.type);
return ($EnumType)value.i;
}
/**
* @require o.is_indexable()
**/
macro String Object.get_enum_at(Object* o, $EnumType, usz index)
{
Object value = o.get_at(index);
assert($EnumType.typeid == value.type);
return ($EnumType)value.i;
}
/**
* @require o.is_keyable()
**/
fn bool! Object.get_bool(Object* o, String key)
{
Object* value = o.get(key)!;
assert(value.is_bool());
return value.b;
}
/**
* @require o.is_indexable()
**/
fn bool Object.get_bool_at(Object* o, usz index)
{
Object* value = o.get_at(index);
assert(value.is_bool());
return value.b;
}
/**
* @require o.is_keyable()
**/
fn double! Object.get_float(Object* o, String key)
{
Object* value = o.get(key)!;
switch (value.type.kindof)
{
case SIGNED_INT:
return (double)value.i;
case UNSIGNED_INT:
return (double)(uint128)value.i;
case FLOAT:
return value.f;
default:
unreachable();
}
}
/**
* @require o.is_indexable()
**/
fn double Object.get_float_at(Object* o, usz index)
{
Object* value = o.get_at(index);
switch (value.type.kindof)
{
case SIGNED_INT:
return (double)value.i;
case UNSIGNED_INT:
return (double)(uint128)value.i;
case FLOAT:
return value.f;
default:
unreachable();
}
}
fn Object* Object.get_or_create_obj(Object* o, String key)
{
if (try obj = o.get(key) && !obj.is_null()) return obj;
Object* container = new_obj();
o.set(key, container);
return container;
}
def ObjectInternalMap @private = HashMap<String, Object*>;
def ObjectInternalList @private = List<Object*>;
def ObjectInternalMapEntry @private = Entry<String, Object*>;

View File

@@ -20,10 +20,10 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
module std::priorityqueue<Type>;
import std::array::list;
module std::collections::priorityqueue<Type>;
import std::collections::list;
define Heap = List<Type>;
def Heap = List<Type>;
struct PriorityQueue
{
@@ -55,7 +55,7 @@ fn Type! PriorityQueue.pop(PriorityQueue* pq)
{
usz i = 0;
usz len = pq.heap.len() @inline;
if (!len) return IteratorResult.NO_MORE_ELEMENT!;
if (!len) return IteratorResult.NO_MORE_ELEMENT?;
usz newCount = len - 1;
pq.heap.swap(0, newCount);
while ((2 * i + 1) < newCount)
@@ -84,7 +84,7 @@ fn Type! PriorityQueue.pop(PriorityQueue* pq)
*/
fn Type! PriorityQueue.peek(PriorityQueue* pq)
{
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT!;
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT?;
return pq.heap.get(0);
}

View File

@@ -1,5 +1,35 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
struct ArenaAllocator
{
inline Allocator allocator;
char[] data;
usz used;
}
/**
* Initialize a memory arena for use using the provided bytes.
*
* @require this != null
**/
fn void ArenaAllocator.init(ArenaAllocator* this, char[] data)
{
this.function = &arena_allocator_function;
this.data = data;
this.used = 0;
}
/**
* @require this != null
**/
fn void ArenaAllocator.reset(ArenaAllocator* this)
{
this.used = 0;
}
struct ArenaAllocatorHeader
{
usz size;
@@ -9,7 +39,7 @@ struct ArenaAllocatorHeader
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
private fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
ArenaAllocator* arena = (ArenaAllocator*)data;
bool clear = false;
@@ -24,15 +54,15 @@ private fn void*! arena_allocator_function(Allocator* data, usz size, usz alignm
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
alignment = alignment_for_allocation(alignment);
void* mem = arena._alloc(size, alignment, offset)?;
if (clear) mem::clear(mem, size, DEFAULT_MEM_ALIGNMENT);
void* mem = arena._alloc(size, alignment, offset)!;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
case ALIGNED_REALLOC:
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase ALLOC;
alignment = alignment_for_allocation(alignment);
return arena._realloc(old_pointer, size, alignment, offset)?;
return arena._realloc(old_pointer, size, alignment, offset)!;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
@@ -57,23 +87,23 @@ private fn void*! arena_allocator_function(Allocator* data, usz size, usz alignm
* @require alignment > 0 `alignment must be non zero`
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= MAX_MEMORY_ALIGNMENT `offset too big`
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require this != null
**/
private fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usz size, usz alignment, usz offset)
fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usz size, usz alignment, usz offset) @private
{
usz total_len = this.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE!;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
void* start_mem = this.data.ptr;
void* unaligned_pointer_to_offset = start_mem + this.used + ArenaAllocatorHeader.sizeof + offset;
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(aligned_pointer_to_offset - this.data.ptr) + size - offset;
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY!;
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
this.used = end;
void *mem = aligned_pointer_to_offset - offset;
void* mem = aligned_pointer_to_offset - offset;
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
header.size = size;
return mem;
@@ -83,17 +113,17 @@ private fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usz size, usz alig
* @require alignment > 0 `alignment must be non zero`
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= MAX_MEMORY_ALIGNMENT `offset too big`
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require this != null
**/
private fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usz size, usz alignment, usz offset)
fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usz size, usz alignment, usz offset) @private
{
assert(old_pointer >= this.data.ptr, "Pointer originates from a different allocator.");
usz total_len = this.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE!;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
usz old_size = header.size;
// Do last allocation and alignment match?
@@ -106,14 +136,14 @@ private fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointe
else
{
usz new_used = this.used + size - old_size;
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY!;
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY?;
this.used = new_used;
}
header.size = size;
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = this._alloc(size, alignment, offset)?;
mem::copy(mem, old_pointer, old_size, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
void* mem = this._alloc(size, alignment, offset)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -1,6 +1,55 @@
// Copyright (c) 2021 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;
private struct DynamicArenaPage
struct DynamicArenaAllocator
{
inline Allocator allocator;
Allocator* backing_allocator;
DynamicArenaPage* page;
DynamicArenaPage* unused_page;
usz page_size;
}
/**
* @require page_size >= 128
* @require this != null
**/
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usz page_size, Allocator* using = mem::heap())
{
this.function = &dynamic_arena_allocator_function;
this.page = null;
this.unused_page = null;
this.page_size = page_size;
this.backing_allocator = using;
}
/**
* @require this != null
**/
fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this)
{
DynamicArenaPage* page = this.page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
free(page, .using = this.backing_allocator);
page = next_page;
}
page = this.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
free(page, .using = this.backing_allocator);
page = next_page;
}
this.page = null;
this.unused_page = null;
}
struct DynamicArenaPage
{
void* memory;
void* prev_arena;
@@ -9,7 +58,7 @@ private struct DynamicArenaPage
void* last_ptr;
}
private struct DynamicArenaChunk
struct DynamicArenaChunk @local
{
usz size;
}
@@ -18,7 +67,7 @@ private struct DynamicArenaChunk
* @require ptr && this
* @require this.page `tried to free pointer on invalid allocator`
*/
private fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* ptr)
fn void DynamicArenaAllocator.free_ptr(DynamicArenaAllocator* this, void* ptr) @private
{
DynamicArenaPage* current_page = this.page;
if (ptr == current_page.last_ptr)
@@ -32,7 +81,7 @@ private fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* pt
* @require old_pointer && size > 0
* @require this.page `tried to realloc pointer on invalid allocator`
*/
private fn void*! DynamicArenaAllocator._realloc(DynamicArenaAllocator* this, void* old_pointer, usz size, usz alignment, usz offset)
fn void*! DynamicArenaAllocator._realloc(DynamicArenaAllocator* this, void* old_pointer, usz size, usz alignment, usz offset) @local
{
DynamicArenaPage* current_page = this.page;
alignment = alignment_for_allocation(alignment);
@@ -57,12 +106,12 @@ private fn void*! DynamicArenaAllocator._realloc(DynamicArenaAllocator* this, vo
current_page.used += add_size;
return old_pointer;
}
void* new_mem = this._alloc(size, alignment, offset)?;
mem::copy(new_mem, old_pointer, old_size, DEFAULT_MEM_ALIGNMENT);
void* new_mem = this._alloc(size, alignment, offset)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}
private fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this)
fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this) @private
{
DynamicArenaPage* page = this.page;
DynamicArenaPage** unused_page_ptr = &this.unused_page;
@@ -82,18 +131,18 @@ private fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this)
* @require math::is_power_of_2(alignment)
* @require size > 0
*/
private fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this, usz size, usz alignment, usz offset)
fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this, usz size, usz alignment, usz offset) @local
{
// First, make sure that we can align it, extending the page size if needed.
usz page_size = max(this.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
// Grab the page without alignment (we do it ourselves)
void* mem = this.backing_allocator.alloc(page_size)?;
DynamicArenaPage*! page = this.backing_allocator.alloc(DynamicArenaPage.sizeof);
void* mem = this.backing_allocator.alloc(page_size)!;
DynamicArenaPage*! page = malloc(DynamicArenaPage, .using = this.backing_allocator);
if (catch err = page)
{
this.backing_allocator.free(mem)?;
return err!;
free(mem, .using = this.backing_allocator);
return err?;
}
page.memory = mem;
void* mem_start = mem::aligned_pointer(mem + offset + DynamicArenaChunk.sizeof, alignment) - offset;
@@ -113,7 +162,7 @@ private fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this,
* @require size > 0
* @require this
*/
private fn void*! DynamicArenaAllocator._alloc(DynamicArenaAllocator* this, usz size, usz alignment, usz offset)
fn void*! DynamicArenaAllocator._alloc(DynamicArenaAllocator* this, usz size, usz alignment, usz offset) @local
{
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = this.page;
@@ -155,7 +204,7 @@ private fn void*! DynamicArenaAllocator._alloc(DynamicArenaAllocator* this, usz
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
private fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
DynamicArenaAllocator* allocator = (DynamicArenaAllocator*)data;
switch (kind)
@@ -164,8 +213,8 @@ private fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, us
case ALIGNED_CALLOC:
assert(!old_pointer, "Unexpected no old pointer for calloc.");
if (!size) return null;
void* mem = allocator._alloc(size, alignment, offset)?;
mem::clear(mem, size, DEFAULT_MEM_ALIGNMENT);
void* mem = allocator._alloc(size, alignment, offset)!;
mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
case ALLOC:
case ALIGNED_ALLOC:
@@ -177,16 +226,16 @@ private fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, us
if (!size)
{
if (!old_pointer) return null;
allocator.free(old_pointer);
allocator.free_ptr(old_pointer);
return null;
}
if (!old_pointer) return allocator._alloc(size, alignment, offset);
void* mem = allocator._realloc(old_pointer, size, alignment, offset)?;
void* mem = allocator._realloc(old_pointer, size, alignment, offset)!;
return mem;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
allocator.free(old_pointer);
allocator.free_ptr(old_pointer);
return null;
case MARK:
unreachable("Tried to mark a dynamic arena");

View File

@@ -0,0 +1,223 @@
// Copyright (c) 2021 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;
def MemoryAllocFn = fn char[]!(usz);
struct SimpleHeapAllocator
{
inline Allocator allocator;
MemoryAllocFn alloc_fn;
Header* free_list;
}
/**
* @require this "Unexpectedly missing the allocator"
* @require allocator "An underlying memory provider must be given"
* @require !this.free_list "The allocator may not be already initialized"
**/
fn void SimpleHeapAllocator.init(SimpleHeapAllocator* this, MemoryAllocFn allocator)
{
this.alloc_fn = allocator;
this.allocator = { &simple_heap_allocator_function };
this.free_list = null;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require this `unexpectedly missing the allocator`
*/
fn void*! simple_heap_allocator_function(Allocator* this, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
SimpleHeapAllocator* heap = (SimpleHeapAllocator*)this;
switch (kind)
{
case ALIGNED_ALLOC:
return @aligned_alloc(heap._alloc, size, alignment, offset);
case ALLOC:
return heap._alloc(size);
case ALIGNED_CALLOC:
return @aligned_calloc(heap._calloc, size, alignment, offset);
case CALLOC:
return heap._calloc(size);
case ALIGNED_REALLOC:
if (!size) nextcase ALIGNED_FREE;
if (!old_pointer) nextcase ALIGNED_CALLOC;
return @aligned_realloc(heap._calloc, heap._free, old_pointer, size, alignment, offset);
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase CALLOC;
return heap._realloc(old_pointer, size);
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION?;
case ALIGNED_FREE:
@aligned_free(heap._free, old_pointer)!;
return null;
case FREE:
heap._free(old_pointer);
return null;
default:
unreachable();
}
}
/**
* @require this && old_pointer && bytes > 0
**/
fn void*! SimpleHeapAllocator._realloc(SimpleHeapAllocator* this, void* old_pointer, usz bytes)
{
// Find the block header.
Header* block = (Header*)old_pointer - 1;
if (block.size >= bytes) return old_pointer;
void* new = this._alloc(bytes)!;
usz max_to_copy = math::min(block.size, bytes);
mem::copy(new, old_pointer, max_to_copy);
this._free(old_pointer);
return new;
}
fn void*! SimpleHeapAllocator._calloc(SimpleHeapAllocator* this, usz bytes) @local
{
void* data = this._alloc(bytes)!;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
fn void*! SimpleHeapAllocator._alloc(SimpleHeapAllocator* this, usz bytes) @local
{
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
if (!this.free_list)
{
this.add_block(aligned_bytes)!;
}
Header* current = this.free_list;
Header* previous = current;
while (current)
{
switch
{
case current.size >= aligned_bytes && current.size <= aligned_bytes + Header.sizeof + 64:
if (current == previous)
{
this.free_list = current.next;
}
else
{
previous.next = current.next;
}
current.next = null;
return current + 1;
case current.size > aligned_bytes:
Header* unallocated = (Header*)((char*)current + aligned_bytes + Header.sizeof);
unallocated.size = current.size - aligned_bytes;
unallocated.next = current.next;
if (current == this.free_list)
{
this.free_list = unallocated;
}
else
{
previous.next = unallocated;
}
current.size = aligned_bytes;
current.next = null;
return current + 1;
default:
previous = current;
current = current.next;
}
}
this.add_block(aligned_bytes)!;
return this.alloc(aligned_bytes);
}
fn void! SimpleHeapAllocator.add_block(SimpleHeapAllocator* this, usz aligned_bytes) @local
{
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
char[] result = this.alloc_fn(aligned_bytes + Header.sizeof)!;
Header* new_block = (Header*)result.ptr;
new_block.size = result.len - Header.sizeof;
new_block.next = null;
this._free(new_block + 1);
}
fn void SimpleHeapAllocator._free(SimpleHeapAllocator* this, void* ptr) @local
{
// Empty ptr -> do nothing.
if (!ptr) return;
// Find the block header.
Header* block = (Header*)ptr - 1;
// No free list? Then just return this.
if (!this.free_list)
{
this.free_list = block;
return;
}
// Find where in the list it should be inserted.
Header* current = this.free_list;
Header* prev = current;
while (current)
{
if (block < current)
{
// Between prev and current
if (block > prev) break;
// Before current
if (current == prev) break;
}
prev = current;
current = prev.next;
}
if (current)
{
// Insert after the current block.
// Are the blocks adjacent?
if (current == (Header*)((char*)(block + 1) + block.size))
{
// Merge
block.size += current.size + Header.sizeof;
block.next = current.next;
}
else
{
// Chain to current
block.next = current;
}
}
if (prev == current)
{
// Swap new start of free list
this.free_list = block;
}
else
{
// Prev adjacent?
if (block == (Header*)((char*)(prev + 1) + prev.size))
{
prev.size += block.size + Header.sizeof;
prev.next = block.next;
}
else
{
// Link prev to block
prev.next = block;
}
}
}
union Header @private
{
struct
{
Header* next;
usz size;
}
usz align;
}

View File

@@ -1,10 +1,14 @@
// Copyright (c) 2021 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 libc;
private const Allocator _NULL_ALLOCATOR = { &null_allocator_fn };
private const Allocator _SYSTEM_ALLOCATOR = { &libc_allocator_fn };
const Allocator _NULL_ALLOCATOR @private = { &null_allocator_fn };
const Allocator _SYSTEM_ALLOCATOR @private = { &libc_allocator_fn };
private fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
switch (kind)
{
@@ -14,27 +18,30 @@ private fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, u
case ALIGNED_ALLOC:
case ALIGNED_REALLOC:
case ALIGNED_CALLOC:
return AllocationFailure.OUT_OF_MEMORY!;
return AllocationFailure.OUT_OF_MEMORY?;
default:
return null;
}
}
private struct AlignedBlock
struct AlignedBlock
{
usz len;
void* start;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
private fn void* _libc_aligned_alloc(usz bytes, usz alignment, usz offset) @inline
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset)
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
void* data = libc::malloc(header + bytes);
$if $checks(#alloc_fn(bytes)!):
void* data = #alloc_fn(header + bytes)!;
$else
void* data = #alloc_fn(header + bytes);
$endif
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
assert(mem > data);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
@@ -46,10 +53,14 @@ private fn void* _libc_aligned_alloc(usz bytes, usz alignment, usz offset) @inli
* @require bytes > 0
* @require alignment > 0
**/
private fn void* _libc_aligned_calloc(usz bytes, usz alignment, usz offset) @inline
macro void*! @aligned_calloc(#calloc_fn, usz bytes, usz alignment, usz offset)
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
void* data = libc::calloc(header + bytes, 1);
$if $checks(#calloc_fn(bytes)!):
void* data = #calloc_fn(header + bytes)!;
$else
void* data = #calloc_fn(header + bytes);
$endif
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
AlignedBlock* desc = (AlignedBlock*)mem - 1;
assert(mem > data);
@@ -61,50 +72,58 @@ private fn void* _libc_aligned_calloc(usz bytes, usz alignment, usz offset) @inl
* @require bytes > 0
* @require alignment > 0
**/
private fn void* _libc_aligned_realloc(void* old_pointer, usz bytes, usz alignment, usz offset) @inline
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment, usz offset)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
void* new_data = _libc_aligned_calloc(bytes, alignment, offset);
mem::copy(new_data, old_pointer, desc.len > bytes ? desc.len : bytes, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
libc::free(data_start);
void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)!;
mem::copy(new_data, old_pointer, desc.len > bytes ? desc.len : bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
$if $checks(#free_fn(data_start)!):
#free_fn(data_start)!;
$else
#free_fn(data_start);
$endif
return new_data;
}
private fn void _libc_aligned_free(void* old_pointer) @inline
macro void! @aligned_free(#free_fn, void* old_pointer)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
libc::free(desc.start);
$if $checks(#free_fn(desc.start)!):
#free_fn(desc.start)!;
$else
#free_fn(desc.start);
$endif
}
fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @inline
{
if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT;
if (!alignment) alignment = mem::DEFAULT_MEM_ALIGNMENT;
assert(math::is_power_of_2(alignment), "Alignment was not a power of 2");
void* data;
switch (kind)
{
case ALIGNED_ALLOC:
data = _libc_aligned_alloc(bytes, alignment, offset);
data = @aligned_alloc(libc::malloc, bytes, alignment, offset)!!;
case ALLOC:
data = libc::malloc(bytes);
case ALIGNED_CALLOC:
data = _libc_aligned_calloc(bytes, alignment, offset);
data = @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!!;
case CALLOC:
data = libc::calloc(bytes, 1);
case ALIGNED_REALLOC:
if (!bytes) nextcase ALIGNED_FREE;
if (!old_pointer) nextcase ALIGNED_CALLOC;
data = _libc_aligned_realloc(old_pointer, bytes, alignment, offset);
data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_pointer, bytes, alignment, offset)!!;
case REALLOC:
if (!bytes) nextcase FREE;
if (!old_pointer) nextcase CALLOC;
data = libc::realloc(old_pointer, bytes);
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION!;
return AllocationFailure.UNSUPPORTED_OPERATION?;
case ALIGNED_FREE:
_libc_aligned_free(old_pointer);
@aligned_free(libc::free, old_pointer)!!;
return null;
case FREE:
libc::free(old_pointer);
@@ -112,6 +131,6 @@ fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz off
default:
unreachable();
}
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
return data;
}

View File

@@ -0,0 +1,235 @@
module std::core::mem::allocator;
struct OnStackAllocator
{
inline Allocator allocator;
Allocator* backing_allocator;
char[] data;
usz used;
OnStackAllocatorExtraChunk* chunk;
}
macro void @stack_mem(usz $size; @body(Allocator* mem)) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, mem::heap());
defer allocator.free();
@body(&allocator);
}
macro void @stack_pool(usz $size; @body) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, mem::heap());
defer allocator.free();
mem::@scoped(&allocator)
{
@body();
};
}
struct OnStackAllocatorExtraChunk @local
{
bool is_aligned;
OnStackAllocatorExtraChunk* prev;
void* data;
}
/**
* Initialize a memory arena for use using the provided bytes.
*
* @require this != null
**/
fn void OnStackAllocator.init(OnStackAllocator* this, char[] data, Allocator* using = mem::heap())
{
this.function = &on_stack_allocator_function;
this.data = data;
this.backing_allocator = using;
this.used = 0;
}
/**
* @require this != null
**/
fn void OnStackAllocator.free(OnStackAllocator* this)
{
OnStackAllocatorExtraChunk* chunk = this.chunk;
while (chunk)
{
if (chunk.is_aligned)
{
this.backing_allocator.free_aligned(chunk.data)!!;
}
else
{
this.backing_allocator.free(chunk.data)!!;
}
void* old = chunk;
chunk = chunk.prev;
this.backing_allocator.free(old)!!;
}
this.chunk = null;
this.used = 0;
}
struct OnStackAllocatorHeader
{
usz size;
char[*] data;
}
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
fn void*! on_stack_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
OnStackAllocator* allocator = (OnStackAllocator*)data;
bool clear = false;
switch (kind)
{
case CALLOC:
case ALIGNED_CALLOC:
clear = true;
nextcase;
case ALLOC:
case ALIGNED_ALLOC:
assert(!old_pointer, "Unexpected old pointer for alloc.");
if (!size) return null;
return on_stack_allocator_alloc(allocator, size, alignment, offset, clear, kind == AllocationKind.ALIGNED_ALLOC || kind == AllocationKind.ALIGNED_CALLOC);
case ALIGNED_REALLOC:
case REALLOC:
if (!size) nextcase FREE;
if (!old_pointer) nextcase ALLOC;
return on_stack_allocator_realloc(allocator, old_pointer, size, alignment, offset, kind == AllocationKind.ALIGNED_REALLOC);
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
if (allocation_in_stack_mem(allocator, old_pointer)) return null;
on_stack_allocator_remove_chunk(allocator, old_pointer);
if (kind == AllocationKind.ALIGNED_FREE)
{
allocator.backing_allocator.free_aligned(old_pointer)!;
}
else
{
allocator.backing_allocator.free(old_pointer)!;
}
return null;
case MARK:
case RESET:
return AllocationFailure.UNSUPPORTED_OPERATION?;
}
unreachable();
}
fn bool allocation_in_stack_mem(OnStackAllocator* a, void* ptr) @local
{
return ptr >= a.data.ptr && ptr <= &a.data[^1];
}
fn void on_stack_allocator_remove_chunk(OnStackAllocator* a, void* ptr) @local
{
OnStackAllocatorExtraChunk* chunk = a.chunk;
OnStackAllocatorExtraChunk** addr = &a.chunk;
while (chunk)
{
if (chunk.data == ptr)
{
*addr = chunk.prev;
a.backing_allocator.free(chunk)!!;
return;
}
addr = &chunk.prev;
chunk = *addr;
}
unreachable("Missing chunk");
}
fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a, void* ptr) @local
{
OnStackAllocatorExtraChunk* chunk = a.chunk;
while (chunk)
{
if (chunk.data == ptr) return chunk;
chunk = chunk.prev;
}
return null;
}
/**
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require a != null
**/
fn void*! on_stack_allocator_realloc(OnStackAllocator* a, void* old_pointer, usz size, usz alignment, usz offset, bool aligned) @local @inline
{
if (!allocation_in_stack_mem(a, old_pointer))
{
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(a, old_pointer);
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
if (aligned)
{
return chunk.data = a.backing_allocator.realloc_aligned(old_pointer, size, alignment, offset)!;
}
return chunk.data = a.backing_allocator.realloc(old_pointer, size)!;
}
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = on_stack_allocator_alloc(a, size, alignment, offset, true, aligned)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
import std::io;
/**
* @require size > 0
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require a != null
**/
fn void*! on_stack_allocator_alloc(OnStackAllocator* a, usz size, usz alignment, usz offset, bool clear, bool aligned) @local @inline
{
alignment = alignment_for_allocation(alignment);
usz total_len = a.data.len;
void* start_mem = a.data.ptr;
void* unaligned_pointer_to_offset = start_mem + a.used + OnStackAllocatorHeader.sizeof + offset;
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(aligned_pointer_to_offset - a.data.ptr) + size - offset;
Allocator* backing_allocator = a.backing_allocator;
if (end > total_len)
{
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc(OnStackAllocatorExtraChunk.sizeof)!;
defer catch backing_allocator.free(chunk)!!;
defer try a.chunk = chunk;
*chunk = { .prev = a.chunk, .is_aligned = aligned };
void* data @noinit;
switch
{
case !aligned && !clear:
data = backing_allocator.alloc(size)!;
case aligned && !clear:
data = backing_allocator.alloc_aligned(size, alignment, offset)!;
case !aligned && clear:
data = backing_allocator.calloc(size)!;
case aligned && clear:
data = backing_allocator.calloc_aligned(size, alignment, offset)!;
}
return chunk.data = data;
}
a.used = end;
void *mem = aligned_pointer_to_offset - offset;
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
header.size = size;
return mem;
}

View File

@@ -1,7 +1,7 @@
module std::core::mem::allocator;
import std::io;
private struct TempAllocatorChunk
struct TempAllocatorChunk @local
{
usz size;
char[*] data;
@@ -18,7 +18,7 @@ struct TempAllocator
}
private const usz PAGE_IS_ALIGNED = (usz)isz.max + 1u;
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
struct TempAllocatorPage
@@ -31,18 +31,18 @@ struct TempAllocatorPage
char[*] data;
}
macro usz TempAllocatorPage.pagesize(TempAllocatorPage* page) { return page.size & ~PAGE_IS_ALIGNED; }
macro bool TempAllocatorPage.is_aligned(TempAllocatorPage* page) { return page.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED; }
macro usz TempAllocatorPage.pagesize(TempAllocatorPage* page) => page.size & ~PAGE_IS_ALIGNED;
macro bool TempAllocatorPage.is_aligned(TempAllocatorPage* page) => page.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED;
/**
* @require size >= 16
**/
fn TempAllocator*! new_temp(usz size, Allocator* backing_allocator)
fn TempAllocator*! new_temp(usz size, Allocator* using)
{
TempAllocator* allocator = backing_allocator.alloc(size + TempAllocator.sizeof)?;
TempAllocator* allocator = malloc_checked(TempAllocator, .using = using, .end_padding = size)!;
allocator.last_page = null;
allocator.function = &temp_allocator_function;
allocator.backing_allocator = backing_allocator;
allocator.backing_allocator = using;
allocator.used = 0;
allocator.capacity = size;
return allocator;
@@ -52,7 +52,7 @@ fn TempAllocator*! new_temp(usz size, Allocator* backing_allocator)
* @require !alignment || math::is_power_of_2(alignment)
* @require data `unexpectedly missing the allocator`
*/
private fn void*! temp_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
fn void*! temp_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
TempAllocator* arena = (TempAllocator*)data;
switch (kind)
@@ -75,18 +75,18 @@ private fn void*! temp_allocator_function(Allocator* data, usz size, usz alignme
case FREE:
case ALIGNED_FREE:
if (!old_pointer) return null;
arena._free(old_pointer)?;
arena._free(old_pointer)!;
return null;
case MARK:
return (void*)(uptr)arena.used;
case RESET:
arena._reset(size)?;
arena._reset(size)!;
return null;
}
unreachable();
}
private fn void! TempAllocator._free(TempAllocator* this, void* old_pointer)
fn void! TempAllocator._free(TempAllocator* this, void* old_pointer) @local
{
// TODO fix free
assert((uptr)old_pointer >= (uptr)&this.data, "Pointer originates from a different allocator.");
@@ -96,27 +96,27 @@ private fn void! TempAllocator._free(TempAllocator* this, void* old_pointer)
this.used -= old_size;
}
}
private fn void! TempAllocator._reset(TempAllocator* this, usz mark)
fn void! TempAllocator._reset(TempAllocator* this, usz mark) @local
{
TempAllocatorPage *last_page = this.last_page;
while (last_page && last_page.mark > mark)
{
TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page;
this._free_page(to_free)?;
this._free_page(to_free)!;
}
this.last_page = last_page;
this.used = mark;
}
private fn void! TempAllocator._free_page(TempAllocator* this, TempAllocatorPage* page) @inline
fn void! TempAllocator._free_page(TempAllocator* this, TempAllocatorPage* page) @inline @local
{
void* mem = page.start;
if (page.is_aligned()) return this.backing_allocator.free_aligned(mem);
return this.backing_allocator.free(mem);
}
private fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline
fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
@@ -131,20 +131,20 @@ private fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocator
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = this._alloc(size, alignment, offset, false)?;
mem::copy(data, &page.data[0], page_size, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
void* data = this._alloc(size, alignment, offset, false)!;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
if (page.is_aligned())
{
this.backing_allocator.free_aligned(real_pointer)?;
this.backing_allocator.free_aligned(real_pointer)!;
}
else
{
this.backing_allocator.free(real_pointer)?;
this.backing_allocator.free(real_pointer)!;
}
return data;
}
private fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz size, usz alignment, usz offset) @inline
fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz size, usz alignment, usz offset) @inline @local
{
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
if (chunk.size == (usz)-1)
@@ -154,12 +154,10 @@ private fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
return this._realloc_page(page, size, alignment, offset);
}
assert(pointer < &this.data + this.capacity && pointer >= &this.data, "This is not a temp allocated pointer.");
assert(pointer < &this.data + this.used, "This is a stale temp pointer.");
// TODO optimize last allocation
TempAllocatorChunk* data = this._alloc(size, alignment, offset, size > chunk.size)?;
mem::copy(data, pointer, chunk.size, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
TempAllocatorChunk* data = this._alloc(size, alignment, offset, size > chunk.size)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
@@ -167,10 +165,10 @@ private fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz
/**
* @require math::is_power_of_2(alignment)
* @require size > 0
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require this != null
**/
private fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignment, usz offset, bool clear)
fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignment, usz offset, bool clear) @local
{
void* start_mem = &this.data;
void* starting_ptr = start_mem + this.used;
@@ -188,7 +186,7 @@ private fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignm
TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof;
chunk_start.size = size;
this.used = new_usage;
if (clear) mem::clear(mem, size, DEFAULT_MEM_ALIGNMENT);
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
@@ -196,17 +194,17 @@ private fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignm
TempAllocatorPage* page;
// We have something we need to align.
if (alignment > DEFAULT_MEM_ALIGNMENT || offset)
if (alignment > mem::DEFAULT_MEM_ALIGNMENT || offset)
{
// This is actually simpler, since it will create the offset for us.
usz total_alloc_size = TempAllocatorPage.sizeof + size;
if (clear)
{
page = this.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)?;
page = this.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
}
else
{
page = this.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)?;
page = this.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
}
page.start = page;
page.size = size | PAGE_IS_ALIGNED;
@@ -214,14 +212,14 @@ private fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignm
else
{
// Here we might need to pad
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, DEFAULT_MEM_ALIGNMENT);
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
usz total_alloc_size = padded_header_size + size;
void* alloc = (clear ? this.backing_allocator.calloc(total_alloc_size) : this.backing_allocator.alloc(total_alloc_size))?;
void* alloc = (clear ? this.backing_allocator.calloc(total_alloc_size) : this.backing_allocator.alloc(total_alloc_size))!;
// Find the page.
page = alloc + padded_header_size - TempAllocatorPage.sizeof;
assert(mem::ptr_is_aligned(page, TempAllocator.alignof));
assert(mem::ptr_is_aligned(&page.data[0], DEFAULT_MEM_ALIGNMENT));
assert(mem::ptr_is_aligned(&page.data[0], mem::DEFAULT_MEM_ALIGNMENT));
page.start = alloc;
page.size = size;
}

View File

@@ -0,0 +1,105 @@
// Copyright (c) 2021 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::collections::map;
def PtrMap = HashMap<uptr, usz>;
// A simple tracking allocator.
// It tracks allocations using a hash map but
// is not compatible with allocators that uses mark()
struct TrackingAllocator
{
inline Allocator allocator;
Allocator* inner_allocator;
PtrMap map;
usz mem_total;
usz allocs_total;
}
/**
* Initialize a memory arena for use using the provided bytes.
*
* @require this != null
**/
fn void TrackingAllocator.init(TrackingAllocator* this, Allocator* using)
{
*this = { .inner_allocator = using, .allocator.function = &tracking_allocator_fn };
this.map.init(.using = using);
}
fn void TrackingAllocator.free(TrackingAllocator* this)
{
this.map.free();
*this = {};
}
/**
* @param [inout] data
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void*! tracking_allocator_fn(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private
{
TrackingAllocator* this = (TrackingAllocator*)data;
void* result = this.inner_allocator.function(this.inner_allocator, size, alignment, offset, old_pointer, kind)!;
switch (kind)
{
case CALLOC:
case ALIGNED_CALLOC:
case ALLOC:
case ALIGNED_ALLOC:
this.map.set((uptr)result, size);
this.mem_total += size;
this.allocs_total++;
return result;
case REALLOC:
case ALIGNED_REALLOC:
this.map.remove((uptr)old_pointer);
this.map.set((uptr)result, size);
this.mem_total += size;
if (size > 0) this.allocs_total++;
return result;
case ALIGNED_FREE:
case FREE:
if (!old_pointer) return null;
this.map.remove((uptr)old_pointer);
return null;
case MARK:
// Unsupported
return null;
case RESET:
this.map.clear();
return null;
}
unreachable();
}
fn usz TrackingAllocator.allocated(TrackingAllocator* this)
{
usz allocated = 0;
@pool()
{
foreach (usz allocation : this.map.value_tlist())
{
allocated += allocation;
}
};
return allocated;
}
fn usz TrackingAllocator.total_allocated(TrackingAllocator* this)
{
return this.mem_total;
}
fn usz TrackingAllocator.total_allocation_count(TrackingAllocator* this)
{
return this.allocs_total;
}
fn usz TrackingAllocator.allocation_count(TrackingAllocator* this)
{
return this.map.count;
}

View File

@@ -1,33 +1,50 @@
module std::core::array;
macro tconcat(arr1, arr2)
{
var $Type = $typeof(arr1[0]);
$Type[] result = array::talloc($Type, arr1.len + arr2.len);
if (arr1.len > 0)
{
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
}
if (arr2.len > 0)
{
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $Type.alignof, $Type.alignof);
}
return result;
}
/**
* @param [in] array
* @param [in] element
* @return "the first index of the element"
* @return! SearchResult.MISSING
**/
macro index_of(array, element)
{
foreach (i, &e : array)
{
if (*e == element) return i;
}
return SearchResult.MISSING!;
return SearchResult.MISSING?;
}
macro concat(arr1, arr2)
/**
* @param [in] array
* @param [in] element
* @return "the last index of the element"
* @return! SearchResult.MISSING
**/
macro rindex_of(array, element)
{
foreach_r (i, &e : array)
{
if (*e == element) return i;
}
return SearchResult.MISSING?;
}
/**
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them.
*
* @param [in] arr1
* @param [in] arr2
* @param [&inout] using "The allocator to use, default is the heap allocator"
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
* @require @typekind(arr2) == SUBARRAY || @typekind(arr2) == ARRAY
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
* @ensure result.len == arr1.len + arr2.len
**/
macro concat(arr1, arr2, Allocator* using = mem::heap())
{
var $Type = $typeof(arr1[0]);
$Type[] result = array::alloc($Type, arr1.len + arr2.len);
$Type[] result = malloc($Type, arr1.len + arr2.len, .using = using);
if (arr1.len > 0)
{
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
@@ -37,4 +54,17 @@ macro concat(arr1, arr2)
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $Type.alignof, $Type.alignof);
}
return result;
}
}
/**
* Concatenate two arrays or subarrays, returning a subarray containing the concatenation of them,
* allocated using the temp allocator.
*
* @param [in] arr1
* @param [in] arr2
* @require @typekind(arr1) == SUBARRAY || @typekind(arr1) == ARRAY
* @require @typekind(arr2) == SUBARRAY || @typekind(arr2) == ARRAY
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
* @ensure result.len == arr1.len + arr2.len
**/
macro tconcat(arr1, arr2) => concat(arr1, arr2, mem::temp());

View File

@@ -1,5 +1,12 @@
// Copyright (c) 2023 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;
// This module contains types of different endianness.
// *BE types represent big-endian types
// *LE types represent little-endian types.
bitstruct ShortBE : short @bigendian
{
short val : 0..15;

View File

@@ -5,17 +5,26 @@ module std::core::builtin;
import libc;
import std::hash;
/**
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
**/
fault IteratorResult
{
NO_MORE_ELEMENT
}
/**
* Use `SearchResult` when trying to return a value from some collection but the element is missing.
**/
fault SearchResult
{
MISSING
}
fault VarCastResult
/**
* Use `CastResult` when an attempt at conversion fails.
**/
fault CastResult
{
TYPE_MISMATCH
}
@@ -33,6 +42,9 @@ macro void @scope(&variable; @body) @builtin
@body();
}
/**
* Swap two variables
**/
macro void @swap(&a, &b) @builtin
{
var temp = a;
@@ -41,83 +53,169 @@ macro void @swap(&a, &b) @builtin
}
/**
* Convert a variant type to a type, returning an failure if there is a type mismatch.
* Convert an `any` type to a type, returning an failure if there is a type mismatch.
*
* @param v `the variant to convert to the given type.`
* @param v `the any to convert to the given type.`
* @param $Type `the type to convert to`
* @return `The variant.ptr converted to its type.`
* @return `The any.ptr converted to its type.`
* @ensure @typeis(return, $Type*)
* @return! CastResult.TYPE_MISMATCH
**/
macro varcast(variant v, $Type) @builtin
macro anycast(any v, $Type) @builtin
{
if (v.type != $Type.typeid) return VarCastResult.TYPE_MISMATCH!;
if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?;
return ($Type*)v.ptr;
}
struct CallstackElement
{
CallstackElement* prev;
char[] function;
char[] file;
String function;
String file;
uint line;
}
fn void default_panic(char[] message, char[] file, char[] function, uint line)
fn void default_panic(String message, String file, String function, uint line)
{
CallstackElement* stack = $$stacktrace();
$if ($defined(libc::stderr) && $defined(libc::fprintf)):
$if $defined(io::stderr) && $defined(File.printf):
if (stack) stack = stack.prev;
if (stack)
{
libc::fprintf(libc::stderr(), "\nERROR: '%.*s'\n", (int)message.len, message.ptr);
(void)io::stderr().print("\nERROR: '");
(void)io::stderr().print(message);
(void)io::stderr().printn("'");
}
else
{
libc::fprintf(libc::stderr(), "\nERROR: '%.*s', function %.*s (%.*s:%d)\n",
(int)message.len, message.ptr, (int)function.len, function.ptr, (int)file.len, file.ptr, line);
(void)io::stderr().print("\nERROR: '");
(void)io::stderr().print(message);
(void)io::stderr().printfn("', in function %s (%s:%d)", function, file, line);
}
while (stack)
{
libc::fprintf(libc::stderr(), " at function %.*s (%.*s:%u)\n", (int)stack.function.len, stack.function.ptr,
(int)stack.file.len, stack.file.ptr, stack.line);
(void)io::stderr().printfn(" in function %s (%s:%d)", stack.function, stack.file, stack.line);
if (stack == stack.prev) break;
stack = stack.prev;
}
$endif;
$endif
$$trap();
}
define PanicFn = fn void(char[] message, char[] file, char[] function, uint line);
def PanicFn = fn void(String message, String file, String function, uint line);
PanicFn panic = &default_panic;
macro void unreachable($string = "Unreachable statement reached.") @builtin @noreturn
fn void panicf(String fmt, String file, String function, uint line, args...)
{
panic($string, $$FILE, $$FUNC, $$LINE);
@stack_mem(512; Allocator* mem)
{
DString s;
s.init(.using = mem);
s.printf(fmt, ...args);
panic(s.str(), file, function, line);
};
}
/**
* Marks the path as unreachable. This will panic in safe mode, and in fast will simply be assumed
* never happens.
* @param [in] string "The panic message or format string"
**/
macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
$$unreachable();
}
/**
* Marks the path as unsupported, this is similar to unreachable.
* @param [in] string "The error message"
**/
macro void unsupported(String string = "Unsupported function invoked") @builtin @noreturn
{
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat());
$$unreachable();
}
/**
* @param expr "the expression to cast"
* @param $Type "the type to cast to"
*
* @require $sizeof(expr) == $Type.sizeof "Cannot bitcast between types of different size."
* @ensure @typeis(result, $Type)
**/
macro bitcast(expr, $Type) @builtin
{
var $size = (usz)($sizeof(expr));
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
$Type x = void;
usz $size = $sizeof(expr);
$Type x @noinit;
mem::copy(&x, &expr, $size, $Type.alignof, $alignof(expr));
return x;
}
/**
* @require $Type.kindof == TypeKind.ENUM `Only enums may be used`
* @param $Type `The type of the enum`
* @param [in] enum_name `The name of the enum to search for`
* @require $Type.kindof == ENUM `Only enums may be used`
* @ensure @typeis(return, $Type)
* @return! SearchResult.MISSING
**/
macro enum_by_name($Type, char[] enum_name) @builtin
macro enum_by_name($Type, String enum_name) @builtin
{
typeid x = $Type.typeid;
foreach (i, name : x.names)
{
if (str::compare(name, enum_name)) return ($Type)i;
if (name == enum_name) return ($Type)i;
}
return SearchResult.MISSING!;
return SearchResult.MISSING?;
}
/**
* Mark an expression as likely to be true
*
* @param #value "expression to be marked likely"
* @param $probability "in the range 0 - 1"
* @require $probability >= 0 && $probability <= 1.0
**/
macro bool @likely(bool #value, $probability = 1.0) @builtin
{
$if $probability == 1.0:
return $$expect(#value, true);
$else
return $$expect_with_probability(#value, true, $probability);
$endif
}
/**
* Mark an expression as unlikely to be true
*
* @param #value "expression to be marked unlikely"
* @param $probability "in the range 0 - 1"
* @require $probability >= 0 && $probability <= 1.0
**/
macro bool @unlikely(bool #value, $probability = 1.0) @builtin
{
$if $probability == 1.0:
return $$expect(#value, false);
$else
return $$expect_with_probability(#value, false, $probability);
$endif
}
/**
* @require values::@is_int(#value) || values::@is_bool(#value)
* @checked $typeof(#value) a = expected
* @require $probability >= 0 && $probability <= 1.0
**/
macro @expect(#value, expected, $probability = 1.0) @builtin
{
$if $probability == 1.0:
return $$expect(#value, ($typeof(#value))expected);
$else
return $$expect_with_probability(#value, expected, $probability);
$endif
}
/**
@@ -144,6 +242,16 @@ macro prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write =
$$prefetch(ptr, $write ? 1 : 0, $locality.ordinal);
}
macro swizzle(v, ...) @builtin
{
return $$swizzle(v, $vasplat());
}
macro swizzle2(v, v2, ...) @builtin
{
return $$swizzle2(v, v2, $vasplat());
}
macro bool @castable(#expr, $To) @builtin
{
return $checks(($To)#expr);
@@ -154,14 +262,29 @@ macro bool @convertible(#expr, $To) @builtin
return $checks($To x = #expr);
}
macro uint int.hash(int i) = i;
macro uint uint.hash(uint i) = i;
macro uint short.hash(short s) = s;
macro uint ushort.hash(ushort s) = s;
macro uint char.hash(char c) = c;
macro uint ichar.hash(ichar c) = c;
macro uint long.hash(long i) = (uint)((i >> 32) ^ i);
macro uint ulong.hash(ulong i) = (uint)((i >> 32) ^ i);
macro uint bool.hash(bool b) = (uint)b;
macro uint typeid.hash(typeid t) = (uint)(((uptr)t >> 32) ^ (uptr)t);
macro uint char[].hash(char[] c) = (uint)fnv32a::encode(c);
macro anyfault @catchof(#expr) @builtin
{
if (catch f = #expr) return f;
return anyfault {};
}
macro bool @ok(#expr) @builtin
{
if (catch #expr) return false;
return true;
}
macro uint int.hash(int i) => i;
macro uint uint.hash(uint i) => i;
macro uint short.hash(short s) => s;
macro uint ushort.hash(ushort s) => s;
macro uint char.hash(char c) => c;
macro uint ichar.hash(ichar c) => c;
macro uint long.hash(long i) => (uint)((i >> 32) ^ i);
macro uint ulong.hash(ulong i) => (uint)((i >> 32) ^ i);
macro uint int128.hash(int128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
macro uint uint128.hash(uint128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i);
macro uint bool.hash(bool b) => (uint)b;
macro uint typeid.hash(typeid t) => ((ulong)(uptr)t).hash();
macro uint String.hash(String c) => (uint)fnv32a::encode(c);
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);

View File

@@ -8,13 +8,14 @@ module std::core::builtin;
**/
macro less(a, b) @builtin
{
$if ($defined(a.less)):
$switch
$case $defined(a.less):
return a.less(b);
$elif ($defined(a.compare_to)):
$case $defined(a.compare_to):
return a.compare_to(b) < 0;
$else:
$default:
return a < b;
$endif;
$endswitch
}
/**
@@ -22,13 +23,14 @@ macro less(a, b) @builtin
**/
macro less_eq(a, b) @builtin
{
$if ($defined(a.less)):
$switch
$case $defined(a.less):
return !b.less(a);
$elif ($defined(a.compare_to)):
$case $defined(a.compare_to):
return a.compare_to(b) <= 0;
$else:
$default:
return a <= b;
$endif;
$endswitch
}
/**
@@ -36,13 +38,14 @@ macro less_eq(a, b) @builtin
**/
macro greater(a, b) @builtin
{
$if ($defined(a.less)):
$switch
$case $defined(a.less):
return b.less(a);
$elif ($defined(a.compare_to)):
$case $defined(a.compare_to):
return a.compare_to(b) > 0;
$else:
$default:
return a > b;
$endif;
$endswitch
}
/**
@@ -50,13 +53,14 @@ macro greater(a, b) @builtin
**/
macro greater_eq(a, b) @builtin
{
$if ($defined(a.less)):
$switch
$case $defined(a.less):
return !a.less(b);
$elif ($defined(a.compare_to)):
$case $defined(a.compare_to):
return a.compare_to(b) >= 0;
$else:
$default:
return a >= b;
$endif;
$endswitch
}
/**
@@ -64,46 +68,47 @@ macro greater_eq(a, b) @builtin
**/
macro bool equals(a, b) @builtin
{
$if ($defined(a.equals)):
$switch
$case $defined(a.equals):
return a.equals(b);
$elif ($defined(a.compare_to)):
$case $defined(a.compare_to):
return a.compare_to(b) == 0;
$elif ($defined(a.less)):
$case $defined(a.less):
return !a.less(b) && !b.less(a);
$else:
$default:
return a == b;
$endif;
$endswitch
}
macro min(x, ...) @builtin
{
$if ($vacount == 1):
$if $vacount == 1:
return less(x, $vaarg(0)) ? x : $vaarg(0);
$else:
$else
var result = x;
$for (var $i = 0; $i < $vacount; $i++):
$for (var $i = 0; $i < $vacount; $i++)
if (less($vaarg($i), result))
{
result = $vaarg($i);
}
$endfor;
$endfor
return result;
$endif;
$endif
}
macro max(x, ...) @builtin
{
$if ($vacount == 1):
$if $vacount == 1:
return greater(x, $vaarg(0)) ? x : $vaarg(0);
$else:
$else
var result = x;
$for (var $i = 0; $i < $vacount; $i++):
$for (var $i = 0; $i < $vacount; $i++)
if (greater($vaarg($i), result))
{
result = $vaarg($i);
}
$endfor;
$endfor
return result;
$endif;
$endif
}

View File

@@ -8,80 +8,80 @@ const C_LONG_SIZE = $$C_LONG_SIZE;
const C_SHORT_SIZE = $$C_SHORT_SIZE;
const C_LONG_LONG_SIZE = $$C_LONG_LONG_SIZE;
$assert (C_SHORT_SIZE < 32);
$assert (C_INT_SIZE < 128);
$assert (C_LONG_SIZE < 128);
$assert (C_LONG_LONG_SIZE <= 128);
$assert (C_SHORT_SIZE <= C_INT_SIZE);
$assert (C_INT_SIZE <= C_LONG_SIZE);
$assert (C_LONG_SIZE <= C_LONG_LONG_SIZE);
$assert C_SHORT_SIZE < 32;
$assert C_INT_SIZE < 128;
$assert C_LONG_SIZE < 128;
$assert C_LONG_LONG_SIZE <= 128;
$assert C_SHORT_SIZE <= C_INT_SIZE;
$assert C_INT_SIZE <= C_LONG_SIZE;
$assert C_LONG_SIZE <= C_LONG_LONG_SIZE;
$switch ($$C_INT_SIZE):
$switch ($$C_INT_SIZE)
$case 64:
define CInt = long;
define CUInt = ulong;
def CInt = long;
def CUInt = ulong;
$case 32:
define CInt = int;
define CUInt = uint;
def CInt = int;
def CUInt = uint;
$case 16:
define CInt = short;
define CUInt = ushort;
def CInt = short;
def CUInt = ushort;
$default:
$assert(false, "Invalid C int size");
$endswitch;
$error "Invalid C int size";
$endswitch
$switch ($$C_LONG_SIZE):
$switch ($$C_LONG_SIZE)
$case 64:
define CLong = long;
define CULong = ulong;
def CLong = long;
def CULong = ulong;
$case 32:
define CLong = int;
define CULong = uint;
def CLong = int;
def CULong = uint;
$case 16:
define CLong = short;
define CULong = ushort;
def CLong = short;
def CULong = ushort;
$default:
$assert(false, "Invalid C long size");
$endswitch;
$error "Invalid C long size";
$endswitch
$switch ($$C_SHORT_SIZE):
$switch ($$C_SHORT_SIZE)
$case 32:
define CShort = int;
define CUShort = uint;
def CShort = int;
def CUShort = uint;
$case 16:
define CShort = short;
define CUShort = ushort;
def CShort = short;
def CUShort = ushort;
$case 8:
define CShort = ichar;
define CUShort = char;
def CShort = ichar;
def CUShort = char;
$default:
$assert(false, "Invalid C short size");
$endswitch;
$error "Invalid C short size";
$endswitch
$switch ($$C_LONG_LONG_SIZE):
$switch ($$C_LONG_LONG_SIZE)
$case 128:
define CLongLong = int128;
define CULongLong = uint128;
def CLongLong = int128;
def CULongLong = uint128;
$case 64:
define CLongLong = long;
define CULongLong = ulong;
def CLongLong = long;
def CULongLong = ulong;
$case 32:
define CLongLong = int;
define CULongLong = uint;
def CLongLong = int;
def CULongLong = uint;
$case 16:
define CLongLong = short;
define CULongLong = ushort;
def CLongLong = short;
def CULongLong = ushort;
$default:
$assert(false, "Invalid C long long size");
$endswitch;
$error "Invalid C long long size";
$endswitch
define CSChar = ichar;
define CUChar = char;
def CSChar = ichar;
def CUChar = char;
$if ($$C_CHAR_IS_SIGNED):
define CChar = ichar;
$else:
define CChar = char;
$endif;
$if $$C_CHAR_IS_SIGNED:
def CChar = ichar;
$else
def CChar = char;
$endif

View File

@@ -1,13 +1,13 @@
module std::core::string::conv;
private const uint UTF16_SURROGATE_OFFSET = 0x10000;
private const uint UTF16_SURROGATE_GENERIC_MASK = 0xF800;
private const uint UTF16_SURROGATE_GENERIC_VALUE = 0xD800;
private const uint UTF16_SURROGATE_MASK = 0xFC00;
private const uint UTF16_SURROGATE_CODEPOINT_MASK = 0x03FF;
private const uint UTF16_SURROGATE_BITS = 10;
private const uint UTF16_SURROGATE_LOW_VALUE = 0xDC00;
private const uint UTF16_SURROGATE_HIGH_VALUE = 0xD800;
const uint UTF16_SURROGATE_OFFSET @private = 0x10000;
const uint UTF16_SURROGATE_GENERIC_MASK @private = 0xF800;
const uint UTF16_SURROGATE_GENERIC_VALUE @private = 0xD800;
const uint UTF16_SURROGATE_MASK @private = 0xFC00;
const uint UTF16_SURROGATE_CODEPOINT_MASK @private = 0x03FF;
const uint UTF16_SURROGATE_BITS @private = 10;
const uint UTF16_SURROGATE_LOW_VALUE @private = 0xDC00;
const uint UTF16_SURROGATE_HIGH_VALUE @private = 0xD800;
/**
* @param c `The utf32 codepoint to convert`
@@ -16,25 +16,25 @@ private const uint UTF16_SURROGATE_HIGH_VALUE = 0xD800;
**/
fn usz! char32_to_utf8(Char32 c, char* output, usz available)
{
if (!available) return UnicodeResult.CONVERSION_FAILED!;
if (!available) return UnicodeResult.CONVERSION_FAILED?;
switch (true)
{
case c <= 0x7f:
output[0] = (char)c;
return 1;
case c <= 0x7ff:
if (available < 2) return UnicodeResult.CONVERSION_FAILED!;
if (available < 2) return UnicodeResult.CONVERSION_FAILED?;
output[0] = (char)(0xC0 | c >> 6);
output[1] = (char)(0x80 | (c & 0x3F));
return 2;
case c <= 0xffff:
if (available < 3) return UnicodeResult.CONVERSION_FAILED!;
if (available < 3) return UnicodeResult.CONVERSION_FAILED?;
output[0] = (char)(0xE0 | c >> 12);
output[1] = (char)(0x80 | (c >> 6 & 0x3F));
output[2] = (char)(0x80 | (c & 0x3F));
return 3;
case c <= 0x10ffff:
if (available < 4) return UnicodeResult.CONVERSION_FAILED!;
if (available < 4) return UnicodeResult.CONVERSION_FAILED?;
output[0] = (char)(0xF0 | c >> 18);
output[1] = (char)(0x80 | (c >> 12 & 0x3F));
output[2] = (char)(0x80 | (c >> 6 & 0x3F));
@@ -42,7 +42,7 @@ fn usz! char32_to_utf8(Char32 c, char* output, usz available)
return 4;
default:
// 0x10FFFF and above is not defined.
return UnicodeResult.CONVERSION_FAILED!;
return UnicodeResult.CONVERSION_FAILED?;
}
}
@@ -84,15 +84,15 @@ fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
return;
}
// Low surrogate first is an error
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16!;
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16?;
// Unmatched high surrogate is an error
if (*available == 1) return UnicodeResult.INVALID_UTF16!;
if (*available == 1) return UnicodeResult.INVALID_UTF16?;
Char16 low = ptr[1];
// Unmatched high surrogate, invalid
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.INVALID_UTF16!;
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.INVALID_UTF16?;
// The high bits of the codepoint are the value bits of the high surrogate
// The low bits of the codepoint are the value bits of the low surrogate
@@ -134,7 +134,7 @@ fn void char32_to_utf8_unsafe(Char32 c, char** output)
fn Char32! utf8_to_char32(char* ptr, usz* size)
{
usz max_size = *size;
if (max_size < 1) return UnicodeResult.INVALID_UTF8!;
if (max_size < 1) return UnicodeResult.INVALID_UTF8?;
char c = (ptr++)[0];
if ((c & 0x80) == 0)
@@ -144,40 +144,40 @@ fn Char32! utf8_to_char32(char* ptr, usz* size)
}
if ((c & 0xE0) == 0xC0)
{
if (max_size < 2) return UnicodeResult.INVALID_UTF8!;
if (max_size < 2) return UnicodeResult.INVALID_UTF8?;
*size = 2;
Char32 uc = (c & 0x1F) << 6;
c = *ptr;
// Overlong sequence or invalid second.
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
return uc + c & 0x3F;
}
if ((c & 0xF0) == 0xE0)
{
if (max_size < 3) return UnicodeResult.INVALID_UTF8!;
if (max_size < 3) return UnicodeResult.INVALID_UTF8?;
*size = 3;
Char32 uc = (c & 0x0F) << 12;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
uc += (c & 0x3F) << 6;
c = ptr++[0];
// Overlong sequence or invalid last
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
return uc + c & 0x3F;
}
if (max_size < 4) return UnicodeResult.INVALID_UTF8!;
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8!;
if (max_size < 4) return UnicodeResult.INVALID_UTF8?;
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8?;
*size = 4;
Char32 uc = (c & 0x07) << 18;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
uc += (c & 0x3F) << 12;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
uc += (c & 0x3F) << 6;
c = ptr++[0];
// Overlong sequence or invalid last
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
return uc + c & 0x3F;
}
@@ -185,7 +185,7 @@ fn Char32! utf8_to_char32(char* ptr, usz* size)
* @param utf8 `An UTF-8 encoded slice of bytes`
* @return `the number of encoded code points`
**/
fn usz utf8_codepoints(char[] utf8)
fn usz utf8_codepoints(String utf8)
{
usz len = 0;
foreach (char c : utf8)
@@ -257,7 +257,7 @@ fn usz utf8len_for_utf16(Char16[] utf16)
* @param utf8 `the utf8 data to calculate from`
* @return `the length of the resulting UTF16 array`
**/
fn usz utf16len_for_utf8(char[] utf8)
fn usz utf16len_for_utf8(String utf8)
{
usz len = utf8.len;
usz len16 = 0;
@@ -297,16 +297,18 @@ fn usz utf16len_for_utf32(Char32[] utf32)
* @param [out] utf8_buffer
* @return `the number of bytes written.`
**/
fn usz! utf32to8(Char32[] utf32, char[] utf8_buffer)
fn usz! utf32to8(Char32[] utf32, String utf8_buffer)
{
usz len = utf8_buffer.len;
char* ptr = utf8_buffer.ptr;
foreach (Char32 uc : utf32)
{
usz used = char32_to_utf8(uc, ptr, len) @inline?;
usz used = char32_to_utf8(uc, ptr, len) @inline!;
len -= used;
ptr += used;
}
// Zero terminate if there is space.
if (len > 0) ptr[0] = 0;
return utf8_buffer.len - len;
}
@@ -317,7 +319,7 @@ fn usz! utf32to8(Char32[] utf32, char[] utf8_buffer)
* @param [out] utf32_buffer
* @return `the number of Char32s written.`
**/
fn usz! utf8to32(char[] utf8, Char32[] utf32_buffer)
fn usz! utf8to32(String utf8, Char32[] utf32_buffer)
{
usz len = utf8.len;
Char32* ptr = utf32_buffer.ptr;
@@ -325,12 +327,14 @@ fn usz! utf8to32(char[] utf8, Char32[] utf32_buffer)
usz buf_len = utf32_buffer.len;
for (usz i = 0; i < len;)
{
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED!;
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED?;
usz width = len - i;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
i += width;
ptr[len32++] = uc;
}
// Zero terminate if possible
if (len32 + 1 < buf_len) ptr[len32] = 0;
return len32;
}
@@ -348,7 +352,7 @@ fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
for (usz i = 0; i < len16;)
{
usz available = len16 - i;
char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline?;
char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline!;
i += available;
}
}
@@ -361,13 +365,13 @@ fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
* @param [in] utf8 `The UTF8 buffer containing the data to convert.`
* @param [out] utf32_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
**/
fn void! utf8to32_unsafe(char[] utf8, Char32* utf32_buffer)
fn void! utf8to32_unsafe(String utf8, Char32* utf32_buffer)
{
usz len = utf8.len;
for (usz i = 0; i < len;)
{
usz width = len - i;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
i += width;
(utf32_buffer++)[0] = uc;
}
@@ -381,13 +385,13 @@ fn void! utf8to32_unsafe(char[] utf8, Char32* utf32_buffer)
* @param [in] utf8 `The UTF8 buffer containing the data to convert.`
* @param [out] utf16_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
**/
fn void! utf8to16_unsafe(char[] utf8, Char16* utf16_buffer)
fn void! utf8to16_unsafe(String utf8, Char16* utf16_buffer)
{
usz len = utf8.len;
for (usz i = 0; i < len;)
{
usz width = len - i;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
char32_to_utf16_unsafe(uc, &utf16_buffer) @inline;
i += width;
}

414
lib/std/core/dstring.c3 Normal file
View File

@@ -0,0 +1,414 @@
module std::core::dstring;
def DString = distinct void*;
const usz MIN_CAPACITY @private = 16;
/**
* @require !str.data() "String already initialized"
**/
fn void DString.init(DString *str, usz capacity = MIN_CAPACITY, Allocator* using = mem::heap())
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = malloc(StringData, 1, .using = using, .end_padding = capacity);
data.allocator = using;
data.len = 0;
data.capacity = capacity;
*str = (DString)data;
}
/**
* @require !str.data() "String already initialized"
**/
fn void DString.tinit(DString *str, usz capacity = MIN_CAPACITY) => str.init(capacity, mem::temp()) @inline;
fn DString new_with_capacity(usz capacity, Allocator* using = mem::heap())
{
DString dstr;
dstr.init(capacity, using);
return dstr;
}
fn DString tnew_with_capacity(usz capacity) => new_with_capacity(capacity, mem::temp()) @inline;
fn DString new(String c = "", Allocator* using = mem::heap())
{
usz len = c.len;
StringData* data = (StringData*)new_with_capacity(len, using);
if (len)
{
data.len = len;
mem::copy(&data.chars, c.ptr, len);
}
return (DString)data;
}
fn DString tnew(String s = "") => new(s, mem::temp()) @inline;
fn DString DString.new_concat(DString a, DString b, Allocator* using = mem::heap())
{
DString string;
string.init(a.len() + b.len(), using);
string.append(a);
string.append(b);
return string;
}
fn DString DString.new_tconcat(DString a, DString b) => a.new_concat(b, mem::temp());
fn ZString DString.zstr(DString str)
{
StringData* data = str.data();
if (!data) return "";
if (data.capacity == data.len)
{
str.reserve(1);
data.chars[data.len] = 0;
}
else if (data.chars[data.len] != 0)
{
data.chars[data.len] = 0;
}
return (ZString)&data.chars[0];
}
fn usz DString.capacity(DString this)
{
if (!this) return 0;
return this.data().capacity;
}
fn usz DString.len(DString this)
{
if (!this) return 0;
return this.data().len;
}
/**
* @require new_size <= this.len()
*/
fn void DString.chop(DString this, usz new_size)
{
if (!this) return;
this.data().len = new_size;
}
fn String DString.str(DString str)
{
StringData* data = (StringData*)str;
if (!data) return "";
return (String)data.chars[:data.len];
}
fn void DString.append_utf32(DString* str, Char32[] chars)
{
str.reserve(chars.len);
foreach (Char32 c : chars)
{
str.append_char32(c);
}
}
/**
* @require index < str.len()
**/
fn void DString.set(DString str, usz index, char c)
{
str.data().chars[index] = c;
}
fn void DString.append_repeat(DString* str, char c, usz times)
{
if (times == 0) return;
str.reserve(times);
StringData* data = str.data();
for (usz i = 0; i < times; i++)
{
data.chars[data.len++] = c;
}
}
/**
* @require c <= 0x10ffff
*/
fn void DString.append_char32(DString* str, Char32 c)
{
if (c < 0x7f)
{
str.reserve(1);
StringData* data = str.data();
data.chars[data.len++] = (char)c;
return;
}
if (c < 0x7ff)
{
str.reserve(2);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xC0 | c >> 6);
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
return;
}
if (c < 0xffff)
{
str.reserve(3);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xE0 | c >> 12);
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
return;
}
str.reserve(4);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xF0 | c >> 18);
data.chars[data.len++] = (char)(0x80 | (c >> 12 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
}
fn DString DString.tcopy(DString* str) => str.copy(mem::temp());
fn DString DString.copy(DString* str, Allocator* using = null)
{
if (!str)
{
if (using) return new_with_capacity(0, using);
return (DString)null;
}
if (!using) using = mem::heap();
StringData* data = str.data();
DString new_string = new_with_capacity(data.capacity, using);
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
return new_string;
}
fn ZString DString.copy_zstr(DString* str, Allocator* using = mem::heap())
{
usz str_len = str.len();
if (!str_len)
{
return (ZString)calloc(1, .using = using);
}
char* zstr = malloc(str_len + 1, .using = using);
StringData* data = str.data();
mem::copy(zstr, &data.chars, str_len);
zstr[str_len] = 0;
return (ZString)zstr;
}
fn String DString.copy_str(DString* str, Allocator* using = mem::heap())
{
return (String)str.copy_zstr(using)[:str.len()];
}
fn String DString.tcopy_str(DString* str) => str.copy_str(mem::temp()) @inline;
fn bool DString.equals(DString str, DString other_string)
{
StringData *str1 = str.data();
StringData *str2 = other_string.data();
if (str1 == str2) return true;
if (!str1) return str2.len == 0;
if (!str2) return str1.len == 0;
usz str1_len = str1.len;
if (str1_len != str2.len) return false;
for (int i = 0; i < str1_len; i++)
{
if (str1.chars[i] != str2.chars[i]) return false;
}
return true;
}
fn void DString.free(DString* str)
{
if (!*str) return;
StringData* data = str.data();
if (!data) return;
free(data, .using = data.allocator);
*str = (DString)null;
}
fn bool DString.less(DString str, DString other_string)
{
StringData* str1 = str.data();
StringData* str2 = other_string.data();
if (str1 == str2) return false;
if (!str1) return str2.len != 0;
if (!str2) return str1.len == 0;
usz str1_len = str1.len;
usz str2_len = str2.len;
if (str1_len != str2_len) return str1_len < str2_len;
for (int i = 0; i < str1_len; i++)
{
if (str1.chars[i] >= str2.chars[i]) return false;
}
return true;
}
fn void DString.append_chars(DString* this, String str)
{
usz other_len = str.len;
if (!other_len) return;
if (!*this)
{
*this = new(str);
return;
}
this.reserve(other_len);
StringData* data = (StringData*)*this;
mem::copy(&data.chars[data.len], str.ptr, other_len);
data.len += other_len;
}
fn Char32[] DString.copy_utf32(DString* this, Allocator* using = mem::heap())
{
return this.str().to_utf32(using) @inline!!;
}
fn void DString.append_string(DString* this, DString str)
{
StringData* other = (StringData*)str;
if (!other) return;
this.append(str.str());
}
fn void DString.clear(DString* str)
{
str.data().len = 0;
}
fn void DString.append_char(DString* str, char c)
{
if (!*str)
{
*str = new_with_capacity(MIN_CAPACITY);
}
str.reserve(1);
StringData* data = (StringData*)*str;
data.chars[data.len++] = c;
}
macro void DString.append(DString* str, value)
{
var $Type = $typeof(value);
$switch ($Type)
$case char:
$case ichar:
str.append_char(value);
$case DString:
str.append_string(value);
$case String:
str.append_chars(value);
$case Char32:
str.append_char32(value);
$default:
$switch
$case @convertible(value, Char32):
str.append_char32(value);
$case @convertible(value, String):
str.append_chars(value);
$default:
$error "Unsupported type for append use printf instead.";
$endswitch
$endswitch
}
fn usz! DString.printf(DString* str, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
return formatter.vprintf(format, args);
}
fn usz! DString.printfn(DString* str, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
usz len = formatter.vprintf(format, args)!;
str.append('\n');
return len + 1;
}
fn DString new_join(String[] s, String joiner, Allocator* using = mem::heap())
{
if (!s.len) return (DString)null;
usz total_size = joiner.len * s.len;
foreach (String* &str : s)
{
total_size += str.len;
}
DString res = new_with_capacity(total_size, using);
res.append(s[0]);
foreach (String* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res;
}
fn void! out_string_append_fn(char c, void* data) @private
{
DString* s = data;
s.append_char(c);
}
fn StringData* DString.data(DString str) @inline @private
{
return (StringData*)str;
}
fn void DString.reserve(DString* str, usz addition)
{
StringData* data = str.data();
if (!data)
{
*str = dstring::new_with_capacity(addition);
return;
}
usz len = data.len + addition;
if (data.capacity >= len) return;
usz new_capacity = data.capacity *= 2;
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
*str = (DString)realloc(data, StringData.sizeof + new_capacity, .using = data.allocator);
}
fn usz! DString.read_from_stream(DString* string, Stream* reader)
{
if (reader.supports_available())
{
usz total_read = 0;
while (usz available = reader.available()!)
{
string.reserve(available);
StringData* data = string.data();
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])!;
total_read += len;
data.len += len;
}
return total_read;
}
usz total_read = 0;
while (true)
{
// Reserve at least 16 bytes
string.reserve(16);
StringData* data = string.data();
// Read into the rest of the buffer
usz read = reader.read(data.chars[data.len..(data.capacity - 1)])!;
data.len += read;
// Ok, we reached the end.
if (read < 16) return total_read;
// Otherwise go another round
}
}
struct StringData @private
{
Allocator* allocator;
usz len;
usz capacity;
char[*] chars;
}

View File

@@ -2,7 +2,7 @@
// 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::env;
import libc;
enum CompilerOptLevel
{
O0,
@@ -11,6 +11,14 @@ enum CompilerOptLevel
O3
}
enum MemoryEnvironment
{
NORMAL,
SMALL,
TINY,
NONE
}
enum OsType
{
UNKNOWN,
@@ -24,7 +32,7 @@ enum OsType
KFREEBSD,
LINUX,
PS3,
MACOSX,
MACOS,
NETBSD,
OPENBSD,
SOLARIS,
@@ -51,9 +59,65 @@ enum OsType
EMSCRIPTEN,
}
const OsType OS_TYPE = (OsType)($$OS_TYPE);
enum ArchType
{
UNKNOWN,
ARM, // ARM (little endian): arm, armv.*, xscale
ARMB, // ARM (big endian): armeb
AARCH64, // AArch64 (little endian): aarch64
AARCH64_BE, // AArch64 (big endian): aarch64_be
AARCH64_32, // AArch64 (little endian) ILP32: aarch64_32
ARC, // ARC: Synopsys ARC
AVR, // AVR: Atmel AVR microcontroller
BPFEL, // eBPF or extended BPF or 64-bit BPF (little endian)
BPFEB, // eBPF or extended BPF or 64-bit BPF (big endian)
HEXAGON, // Hexagon: hexagon
MIPS, // MIPS: mips, mipsallegrex, mipsr6
MIPSEL, // MIPSEL: mipsel, mipsallegrexe, mipsr6el
MIPS64, // MIPS64: mips64, mips64r6, mipsn32, mipsn32r6
MIPS64EL, // MIPS64EL: mips64el, mips64r6el, mipsn32el, mipsn32r6el
MSP430, // MSP430: msp430
PPC, // PPC: powerpc
PPC64, // PPC64: powerpc64, ppu
PPC64LE, // PPC64LE: powerpc64le
R600, // R600: AMD GPUs HD2XXX - HD6XXX
AMDGCN, // AMDGCN: AMD GCN GPUs
RISCV32, // RISC-V (32-bit): riscv32
RISCV64, // RISC-V (64-bit): riscv64
SPARC, // Sparc: sparc
SPARCV9, // Sparcv9: Sparcv9
SPARCEL, // Sparc: (endianness = little). NB: 'Sparcle' is a CPU variant
SYSTEMZ, // SystemZ: s390x
TCE, // TCE (http://tce.cs.tut.fi/): tce
TCELE, // TCE little endian (http://tce.cs.tut.fi/): tcele
THUMB, // Thumb (little endian): thumb, thumbv.*
THUMBEB, // Thumb (big endian): thumbeb
X86, // X86: i[3-9]86
X86_64, // X86-64: amd64, x86_64
XCORE, // XCore: xcore
NVPTX, // NVPTX: 32-bit
NVPTX64, // NVPTX: 64-bit
LE32, // le32: generic little-endian 32-bit CPU (PNaCl)
LE64, // le64: generic little-endian 64-bit CPU (PNaCl)
AMDIL, // AMDIL
AMDIL64, // AMDIL with 64-bit pointers
HSAIL, // AMD HSAIL
HSAIL64, // AMD HSAIL with 64-bit pointers
SPIR, // SPIR: standard portable IR for OpenCL 32-bit version
SPIR64, // SPIR: standard portable IR for OpenCL 64-bit version
KALIMBA, // Kalimba: generic kalimba
SHAVE, // SHAVE: Movidius vector VLIW processors
LANAI, // Lanai: Lanai 32-bit
WASM32, // WebAssembly with 32-bit pointers
WASM64, // WebAssembly with 64-bit pointers
RSCRIPT32, // 32-bit RenderScript
RSCRIPT64, // 64-bit RenderScript
}
const OsType OS_TYPE = (OsType)$$OS_TYPE;
const ArchType ARCH_TYPE = (ArchType)$$ARCH_TYPE;
const bool COMPILER_LIBC_AVAILABLE = $$COMPILER_LIBC_AVAILABLE;
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)($$COMPILER_OPT_LEVEL);
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)$$COMPILER_OPT_LEVEL;
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
@@ -62,13 +126,31 @@ const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;
const usz LLVM_VERSION = $$LLVM_VERSION;
const bool BENCHMARKING = $$BENCHMARKING;
const bool TESTING = $$TESTING;
const usz TEMP_ALLOCATOR_SIZE = 128 * 1024;
const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT;
macro bool os_is_win32()
{
return OS_TYPE == WIN32;
}
macro bool os_is_darwin()
{
$switch (OS_TYPE)
$case IOS:
$case MACOS:
$case TVOS:
$case WATCHOS:
return true;
$default:
return false;
$endswitch
}
macro bool os_is_posix()
{
$switch (OS_TYPE):
$switch (OS_TYPE)
$case IOS:
$case MACOSX:
$case MACOS:
$case NETBSD:
$case LINUX:
$case KFREEBSD:
@@ -85,5 +167,60 @@ macro bool os_is_posix()
$default:
$echo("Assuming non-Posix environment");
return false;
$endswitch;
}
$endswitch
}
/**
* @param [&in] name
* @require name.len > 0
**/
fn String! get_var(String name)
{
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
@pool()
{
ZString val = libc::getenv(name.zstr_tcopy());
return val ? val.as_str() : SearchResult.MISSING?;
};
$else
return "";
$endif
}
/**
* @param [&in] name
* @param [&in] value
* @require name.len > 0
**/
fn void set_var(String name, String value, bool overwrite = true)
{
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
@pool()
{
if (libc::setenv(name.zstr_tcopy(), value.zstr_copy(), (int)overwrite))
{
unreachable();
}
};
$endif
}
/**
* @param [&in] name
* @require name.len > 0
**/
fn void clear_var(String name)
{
$if COMPILER_LIBC_AVAILABLE && !os_is_win32():
@pool()
{
if (libc::unsetenv(name.zstr_tcopy()))
{
unreachable();
}
};
$endif
}

View File

@@ -1,16 +1,71 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem;
macro @volatile_load(&x)
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
macro @volatile_load(&x) @builtin
{
return $$volatile_load(&x);
}
macro @volatile_store(&x, y)
macro @volatile_store(&x, y) @builtin
{
return $$volatile_store(&x, y);
return $$volatile_store(&x, ($typeof(x))y);
}
enum AtomicOrdering : int
{
NOT_ATOMIC, // Not atomic
UNORDERED, // No lock
MONOTONIC, // Consistent ordering
ACQUIRE, // Barrier locking load/store
RELEASE, // Barrier releasing load/store
ACQUIRE_RELEASE, // Barrier fence to load/store
SEQ_CONSISTENT, // Acquire semantics, ordered with other seq_consistent
}
/**
* @param [in] x "the variable or dereferenced pointer to load."
* @param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
* @param $volatile "whether the load should be volatile, defaults to 'false'"
* @return "returns the value of x"
*
* @require $ordering != AtomicOrdering.RELEASE "Release ordering is not valid for load."
* @require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for load."
* @require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
**/
macro @atomic_load(&x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
{
return $$atomic_load(&x, $volatile, (int)$ordering);
}
/**
* @param [out] x "the variable or dereferenced pointer to store to."
* @param value "the value to store."
* @param $ordering "the atomic ordering of the store, defaults to SEQ_CONSISTENT"
* @param $volatile "whether the store should be volatile, defaults to 'false'"
*
* @require $ordering != AtomicOrdering.ACQUIRE "Acquire ordering is not valid for store."
* @require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for store."
* @require types::may_load_atomic($typeof(x)) "Only integer, float and pointers may be used."
**/
macro void @atomic_store(&x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
{
$$atomic_store(&x, value, $volatile, (int)$ordering);
}
macro compare_exchange(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT, bool $volatile = true, bool $weak = false, usz $alignment = 0)
{
return $$compare_exchange(ptr, compare, value, $volatile, $weak, $success.ordinal, $failure.ordinal, $alignment);
}
macro compare_exchange_volatile(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT)
{
return compare_exchange(ptr, compare, value, $success, $failure, true);
}
/**
@@ -36,20 +91,20 @@ fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if ($inlined):
$if $inlined:
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
$else:
$else
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
$endif;
$endif
}
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if ($inlined):
$if $inlined:
$$memcpy_inline(dst, src, len, $is_volatile, $dst_align, $src_align);
$else:
$else
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
$endif;
$endif
}
macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
@@ -59,41 +114,41 @@ macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_alig
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$if ($inlined):
$if $inlined:
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
$else:
$else
$$memset(dst, val, len, $is_volatile, $dst_align);
$endif;
$endif
}
/**
* @require $typeof(a).kindof == TypeKind.SUBARRAY || $typeof(a).kindof == TypeKind.POINTER
* @require $typeof(b).kindof == TypeKind.SUBARRAY || $typeof(b).kindof == TypeKind.POINTER
* @require $typeof(a).kindof != TypeKind.SUBARRAY || len == -1
* @require $typeof(a).kindof != TypeKind.POINTER || len > -1
* @require values::@inner_kind(a) == TypeKind.SUBARRAY || values::@inner_kind(a) == TypeKind.POINTER
* @require values::@inner_kind(b) == TypeKind.SUBARRAY || values::@inner_kind(b) == TypeKind.POINTER
* @require values::@inner_kind(a) != TypeKind.SUBARRAY || len == -1
* @require values::@inner_kind(a) != TypeKind.POINTER || len > -1
* @checked (a = b), (b = a)
**/
macro bool equals(a, b, isz len = -1, usz $align = 0)
{
$if (!$align):
$if !$align:
$align = $typeof(a[0]).alignof;
$endif;
void* x = void;
void* y = void;
$if ($typeof(a).kindof == TypeKind.SUBARRAY):
$endif
void* x @noinit;
void* y @noinit;
$if values::@inner_kind(a) == TypeKind.SUBARRAY:
len = a.len;
if (len != b.len) return false;
x = a.ptr;
y = b.ptr;
$else:
$else
x = a;
y = b;
assert(len >= 0, "A zero or positive length must be given when comparing pointers.");
$endif;
$endif
if (!len) return true;
var $Type;
$switch ($align):
$switch ($align)
$case 1:
$Type = char;
$case 2:
@@ -103,7 +158,7 @@ macro bool equals(a, b, isz len = -1, usz $align = 0)
$case 8:
$default:
$Type = ulong;
$endswitch;
$endswitch
var $step = $Type.sizeof;
usz end = len / $step;
for (usz i = 0; i < end; i++)
@@ -132,138 +187,245 @@ macro @tclone(&value) @builtin
return x;
}
fn void* malloc(usz size) @builtin @inline
macro type_alloc_must_be_aligned($Type)
{
return thread_allocator.alloc(size)!!;
return $Type.alignof > DEFAULT_MEM_ALIGNMENT;
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro malloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
return malloc_checked($vasplat(), .using = using, .end_padding = end_padding)!!;
}
fn void*! malloc_checked(usz size) @builtin @inline
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro malloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
return thread_allocator.alloc(size);
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with malloc_aligned";
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.alloc($Type.sizeof * size + end_padding))[:size];
$else
return ($Type*)using.alloc($Type.sizeof + end_padding);
$endif
$else
return using.alloc($vaarg(0) + end_padding);
$endif
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
* @require alignment && math::is_power_of_2(alignment)
**/
macro malloc_aligned(..., usz alignment = 0, usz end_padding = 0, Allocator* using = mem::heap()) @builtin
{
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size];
$else
return ($Type*)using.alloc_aligned($Type.sizeof + end_padding, alignment);
$endif
$else
return using.alloc_aligned($vaarg(0) + end_padding, alignment);
$endif
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro calloc(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
return calloc_checked($vasplat(), .using = using, .end_padding = end_padding)!!;
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro calloc_checked(..., Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$assert !type_alloc_must_be_aligned($vatype(0)) : "Type must be allocated with calloc_aligned";
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.calloc($Type.sizeof * size + end_padding))[:size];
$else
return ($Type*)using.calloc($Type.sizeof + end_padding);
$endif
$else
return using.calloc($vaarg(0) + end_padding);
$endif
}
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
* @require alignment && math::is_power_of_2(alignment)
**/
macro calloc_aligned(..., usz alignment = 0, Allocator* using = mem::heap(), usz end_padding = 0) @builtin
{
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)using.calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size];
$else
return ($Type*)using.calloc_aligned($Type.sizeof + end_padding, alignment);
$endif
$else
return using.calloc_aligned($vaarg(0) + end_padding, alignment);
$endif
}
fn void* realloc(void *ptr, usz new_size, Allocator* using = mem::heap()) @builtin @inline
{
return using.realloc(ptr, new_size)!!;
}
fn void*! realloc_checked(void *ptr, usz new_size, Allocator* using = mem::heap()) @builtin @inline
{
return using.realloc(ptr, new_size);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! malloc_aligned(usz size, usz alignment) @builtin @inline
fn void*! realloc_aligned(void *ptr, usz new_size, usz alignment, Allocator* using = mem::heap()) @builtin @inline
{
return thread_allocator.alloc_aligned(size, alignment);
return using.realloc_aligned(ptr, new_size, alignment);
}
fn char[] alloc_bytes(usz bytes) @inline
{
return ((char*)thread_allocator.alloc(bytes))[:bytes]!!;
}
macro alloc($Type)
{
return ($Type*)thread_allocator.alloc($Type.sizeof)!!;
}
fn void* calloc(usz size) @builtin @inline
{
return thread_allocator.calloc(size)!!;
}
fn void*! calloc_checked(usz size) @builtin @inline
{
return thread_allocator.calloc(size);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! calloc_aligned(usz size, usz alignment) @builtin @inline
{
return thread_allocator.calloc_aligned(size, alignment);
}
fn void* realloc(void *ptr, usz new_size) @builtin @inline
{
return thread_allocator.realloc(ptr, new_size)!!;
}
fn void*! realloc_checked(void *ptr, usz new_size) @builtin @inline
{
return thread_allocator.realloc(ptr, new_size);
}
/**
* @require alignment && math::is_power_of_2(alignment)
*/
fn void*! realloc_aligned(void *ptr, usz new_size, usz alignment) @builtin @inline
{
return thread_allocator.realloc_aligned(ptr, new_size, alignment);
}
fn void free(void* ptr) @builtin @inline
{
return thread_allocator.free(ptr)!!;
}
fn void free_aligned(void* ptr) @builtin @inline
{
return thread_allocator.free_aligned(ptr)!!;
}
macro void free(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(ptr)!!;
macro void! free_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free(ptr);
macro void free_aligned(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr)!!;
macro void! free_aligned_checked(void* ptr, Allocator* using = mem::heap()) @builtin => using.free_aligned(ptr);
/**
* Run with a specific allocator inside of the macro body.
**/
macro void @scoped(Allocator* allocator; @body())
macro void @scoped(Allocator* using; @body())
{
Allocator* old_allocator = thread_allocator;
thread_allocator = allocator;
thread_allocator = using;
defer thread_allocator = old_allocator;
@body();
}
macro talloc($Type) @builtin
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro tmalloc(..., usz end_padding = 0, usz alignment = DEFAULT_MEM_ALIGNMENT) @builtin
{
return temp_allocator().alloc_aligned($Type.sizeof, $Type.alignof)!!;
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)temp().alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!;
$else
return ($Type*)temp().alloc_aligned($Type.sizeof + end_padding, alignment)!!;
$endif
$else
return temp().alloc_aligned($vaarg(0) + end_padding, alignment)!!;
$endif
}
fn void* tmalloc(usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
/**
* @require $vacount > 0 && $vacount < 3 "Expected size, type, or type + len"
* @require $vacount != 2 || $checks($vatype(0).sizeof) "Expected 'malloc(Foo, 12)'"
**/
macro tcalloc(..., usz end_padding = 0, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin
{
return temp_allocator().alloc_aligned(size, alignment)!!;
$if $checks($vatype(0).sizeof):
var $Type = $vatype(0);
$if $vacount == 2:
usz size = $vaarg(1);
return (($Type*)temp().calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!;
$else
return ($Type*)temp().calloc_aligned($Type.sizeof + end_padding, alignment)!!;
$endif
$else
return temp().calloc_aligned($vaarg(0) + end_padding, alignment)!!;
$endif
}
fn void* tcalloc(usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline
{
return temp_allocator().calloc_aligned(size, alignment)!!;
}
fn void* trealloc(void* ptr, usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
{
return temp_allocator().realloc_aligned(ptr, size, alignment)!!;
return temp().realloc_aligned(ptr, size, alignment)!!;
}
macro void @pool(;@body) @builtin
{
TempAllocator* temp = temp_allocator();
usz mark = temp.used;
defer temp.reset(mark);
TempAllocator* allocator = temp();
usz mark = allocator.used;
defer allocator.reset(mark);
@body();
}
private tlocal Allocator* thread_allocator = allocator::LIBC_ALLOCATOR;
private tlocal TempAllocator* thread_temp_allocator = null;
macro void @allocating_pool(Allocator* using; @body(bool is_temp)) @builtin
{
TempAllocator* allocator = temp();
usz mark = allocator.used;
bool is_temp = allocator == using;
defer if (!is_temp) allocator.reset(mark);
@body(is_temp);
}
macro TempAllocator* temp_allocator()
tlocal Allocator* thread_allocator @private = allocator::LIBC_ALLOCATOR;
tlocal TempAllocator* thread_temp_allocator @private = null;
macro TempAllocator* temp_allocator() => temp();
macro TempAllocator* temp()
{
if (!thread_temp_allocator)
{
thread_temp_allocator = allocator::new_temp(env::TEMP_ALLOCATOR_SIZE, allocator::LIBC_ALLOCATOR)!!;
$switch (env::MEMORY_ENV)
$case NORMAL:
thread_temp_allocator = allocator::new_temp(1024 * 256, thread_allocator)!!;
$case SMALL:
thread_temp_allocator = allocator::new_temp(1024 * 16, thread_allocator)!!;
$case TINY:
thread_temp_allocator = allocator::new_temp(1024 * 2, thread_allocator)!!;
$case NONE:
unreachable("Temp allocator must explicitly created when memory-env is set to 'none'.");
$endswitch
}
return thread_temp_allocator;
}
macro Allocator* current_allocator()
macro Allocator* current_allocator() => thread_allocator;
macro Allocator* heap() => thread_allocator;
$if !env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64:
SimpleHeapAllocator wasm_allocator @private;
extern int __heap_base;
static initialize @priority(1)
{
return thread_allocator;
allocator::wasm_memory.allocate_block(mem::DEFAULT_MEM_ALIGNMENT)!!; // Give us a valid null.
// Check if we need to move the heap.
uptr start = (uptr)&__heap_base;
if (start > mem::DEFAULT_MEM_ALIGNMENT) allocator::wasm_memory.use = start;
wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x));
thread_allocator = &wasm_allocator;
}
$endif

View File

@@ -1,14 +1,12 @@
module std::core::mem::allocator;
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
const DEFAULT_SIZE_PREFIX = usz.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR;
const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR;
define AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind);
def AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind);
struct Allocator
{
@@ -85,12 +83,12 @@ fn void*! Allocator.calloc_aligned(Allocator* allocator, usz size, usz alignment
fn void! Allocator.free(Allocator* allocator, void* old_pointer) @inline
{
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)?;
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)!;
}
fn void! Allocator.free_aligned(Allocator* allocator, void* old_pointer) @inline
{
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)?;
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)!;
}
fn void Allocator.reset(Allocator* allocator, usz mark = 0)
@@ -98,84 +96,14 @@ fn void Allocator.reset(Allocator* allocator, usz mark = 0)
allocator.function(allocator, mark, 0, 0, null, RESET)!!;
}
private fn usz alignment_for_allocation(usz alignment) @inline
fn usz alignment_for_allocation(usz alignment) @inline @private
{
if (alignment < DEFAULT_MEM_ALIGNMENT)
if (alignment < mem::DEFAULT_MEM_ALIGNMENT)
{
alignment = DEFAULT_MEM_ALIGNMENT;
alignment = mem::DEFAULT_MEM_ALIGNMENT;
}
return alignment;
}
struct DynamicArenaAllocator
{
inline Allocator allocator;
Allocator* backing_allocator;
DynamicArenaPage* page;
DynamicArenaPage* unused_page;
usz page_size;
}
/**
* @require page_size >= 128
* @require this != null
**/
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usz page_size, Allocator* backing_allocator = mem::current_allocator())
{
this.function = &dynamic_arena_allocator_function;
this.page = null;
this.unused_page = null;
this.page_size = page_size;
this.backing_allocator = backing_allocator;
}
/**
* @require this != null
**/
fn void DynamicArenaAllocator.destroy(DynamicArenaAllocator* this)
{
DynamicArenaPage* page = this.page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
this.backing_allocator.free(page)!!;
page = next_page;
}
page = this.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
this.backing_allocator.free(page)!!;
page = next_page;
}
this.page = null;
this.unused_page = null;
}
struct ArenaAllocator
{
inline Allocator allocator;
char[] data;
usz used;
}
/**
* Initialize a memory arena for use using the provided bytes.
*
* @require this != null
**/
fn void ArenaAllocator.init(ArenaAllocator* this, char[] data)
{
this.function = &arena_allocator_function;
this.data = data;
this.used = 0;
}
/**
* @require this != null
**/
fn void ArenaAllocator.reset(ArenaAllocator* this)
{
this.used = 0;
}

View File

@@ -1,40 +0,0 @@
// Copyright (c) 2021 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::array;
/**
* @require usz.max / elements > $Type.sizeof
**/
macro alloc($Type, usz elements)
{
$Type* ptr = malloc($Type.sizeof * elements);
return ptr[:elements];
}
/**
* @require usz.max / elements > $Type.sizeof
**/
macro talloc($Type, usz elements)
{
$Type* ptr = tmalloc($Type.sizeof * elements, $Type[1].alignof);
return ptr[:elements];
}
/**
* @require (usz.max / elements > $Type.sizeof)
**/
macro make($Type, usz elements, Allocator* allocator = mem::current_allocator())
{
$Type* ptr = allocator.calloc($Type.sizeof * elements)!!;
return ptr[:elements];
}
/**
* @require (usz.max / elements > $Type.sizeof)
**/
macro tmake($Type, usz elements)
{
$Type* ptr = tcalloc($Type.sizeof * elements, $Type[1].alignof);
return ptr[:elements];
}

View File

@@ -0,0 +1,32 @@
module std::core::mem::allocator;
const usz WASM_BLOCK_SIZE = 65536;
WasmMemory wasm_memory;
struct WasmMemory
{
usz allocation;
uptr use;
}
fn char[]! WasmMemory.allocate_block(WasmMemory* this, usz bytes)
{
if (!this.allocation)
{
this.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
}
isz bytes_required = bytes + this.use - this.allocation;
if (bytes_required <= 0)
{
defer this.use += bytes;
return ((char*)this.use)[:bytes];
}
usz blocks_required = (bytes_required + WASM_BLOCK_SIZE + 1) / WASM_BLOCK_SIZE;
if ($$wasm_memory_grow(0, blocks_required) == -1) return AllocationFailure.OUT_OF_MEMORY?;
this.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
defer this.use += bytes;
return ((char*)this.use)[:bytes];
}

View File

@@ -0,0 +1,168 @@
module std::core::main_stub;
macro usz _strlen(ptr) @private
{
usz len = 0;
while (ptr[len]) len++;
return len;
}
macro int @main_to_err_main(#m, int, char**)
{
if (catch #m()) return 1;
return 0;
}
macro int @main_to_int_main(#m, int, char**) => #m();
macro int @main_to_void_main(#m, int, char**)
{
#m();
return 0;
}
macro String[] args_to_strings(int argc, char** argv) @private
{
String[] list = malloc(String, argc);
for (int i = 0; i < argc; i++)
{
char* arg = argv[i];
usz len = 0;
list[i] = (String)arg[:_strlen(arg)];
}
return list;
}
macro int @main_to_err_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
if (catch #m(list)) return 1;
return 0;
}
macro int @main_to_int_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
return #m(list);
}
macro int @main_to_void_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
#m(list);
return 0;
}
$if env::os_is_win32():
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @extern("CommandLineToArgvW");
macro String[] win_command_line_to_strings(ushort* cmd_line) @private
{
int argc;
Char16** argv = _win_command_line_to_argv_w(cmd_line, &argc);
return wargs_strings(argc, argv);
}
macro String[] wargs_strings(int argc, Char16** argv) @private
{
String[] list = malloc(String, argc);
for (int i = 0; i < argc; i++)
{
Char16* arg = argv[i];
Char16[] argstring = arg[:_strlen(arg)];
list[i] = string::from_utf16(argstring) ?? "?".copy();
}
return list[:argc];
}
macro void release_wargs(String[] list) @private
{
foreach (s : list) free(s.ptr);
free(list.ptr);
}
macro int @win_to_err_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
{
if (catch #m()) return 1;
return 0;
}
macro int @win_to_int_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd) => #m();
macro int @win_to_void_main_noargs(#m, void* handle, Char16* cmd_line, int show_cmd)
{
#m();
return 0;
}
macro int @win_to_err_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
if (catch #m(args)) return 1;
return 0;
}
macro int @win_to_int_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(args);
}
macro int @win_to_void_main_args(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
#m(args);
return 0;
}
macro int @win_to_err_main(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
if (catch #m(handle, args, show_cmd)) return 1;
return 0;
}
macro int @win_to_int_main(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(handle, args, show_cmd);
}
macro int @win_to_void_main(#m, void* handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
#m(handle, args, show_cmd);
return 0;
}
macro int @wmain_to_err_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
if (catch #m(args)) return 1;
return 1;
}
macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
return #m(args);
}
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
#m(args);
return 0;
}
$endif

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2021 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::runtime;
module std::core::runtime;
struct VirtualAny
{
@@ -9,30 +9,17 @@ struct VirtualAny
typeid type_id;
}
struct VirtualContainer
{
void* ptr;
void* impl_ptr;
}
struct SubArrayContainer
{
void* ptr;
usz len;
}
struct VarArrayHeader
{
usz size;
usz capacity;
void *allocator;
}
define TestFn = fn void!();
def TestFn = fn void!();
struct TestRunner
{
char[][] test_names;
String[] test_names;
TestFn[] test_fns;
JmpBuf buf;
}
@@ -47,12 +34,15 @@ fn TestRunner test_runner_create()
import libc;
private TestRunner* current_runner;
fn void test_panic(char[] message, char[] file, char[] function, uint line)
TestRunner* current_runner @private;
fn void test_panic(String message, String file, String function, uint line)
{
io::println("[error]");
io::printfln("\n Error: %s", message);
io::printfln(" - in %s %s:%s.\n", function, file, line);
io::printn("[error]");
io::print("\n Error: ");
io::print(message);
io::printn();
io::printfn(" - in %s %s:%s.\n", function, file, line);
libc::longjmp(&current_runner.buf, 1);
}
@@ -64,22 +54,22 @@ fn bool TestRunner.run(TestRunner* runner)
builtin::panic = &test_panic;
int tests_passed = 0;
int tests = runner.test_names.len;
io::println("----- TESTS -----");
foreach(i, char[] name : runner.test_names)
io::printn("----- TESTS -----");
foreach(i, String name : runner.test_names)
{
io::printf("Testing %s ... ", name);
if (libc::setjmp(&runner.buf) == 0)
{
if (catch err = runner.test_fns[i]())
{
io::println("[failed]");
io::printn("[failed]");
continue;
}
io::println("[ok]");
io::printn("[ok]");
tests_passed++;
}
}
io::printfln("\n%d test(s) run.\n", tests);
io::printfn("\n%d test(s) run.\n", tests);
io::print("Test Result: ");
if (tests_passed < tests)
{
@@ -89,11 +79,21 @@ fn bool TestRunner.run(TestRunner* runner)
{
io::print("ok");
}
io::printfln(". %d passed, %d failed.", tests_passed, tests - tests_passed);
io::printfn(". %d passed, %d failed.", tests_passed, tests - tests_passed);
return tests == tests_passed;
}
fn bool __run_default_test_runner()
{
return test_runner_create().run();
}
}
$if !env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64:
extern fn void __wasm_call_ctors();
fn void wasm_initialize() @extern("_initialize") @wasm
{
// The linker synthesizes this to call constructors.
__wasm_call_ctors();
}
$endif

View File

@@ -1,344 +0,0 @@
module std::core::str;
define ZString = distinct char*;
define Char32 = uint;
define Char16 = ushort;
private const uint SURROGATE_OFFSET = 0x10000;
private const uint SURROGATE_GENERIC_MASK = 0xF800;
private const uint SURROGATE_MASK = 0xFC00;
private const uint SURROGATE_CODEPOINT_MASK = 0x03FF;
private const uint SURROGATE_BITS = 10;
private const uint SURROGATE_LOW_VALUE = 0xDC00;
private const uint SURROGATE_HIGH_VALUE = 0xD800;
fault NumberConversion
{
EMPTY_STRING,
NEGATIVE_VALUE,
MALFORMED_INTEGER,
INTEGER_OVERFLOW,
}
fn String join(char[][] s, char[] joiner)
{
if (!s.len) return (String)null;
usz total_size = joiner.len * s.len;
foreach (char[]* &str : s)
{
total_size += str.len;
}
String res = string::new_with_capacity(total_size);
res.append(s[0]);
foreach (char[]* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res;
}
macro bool char_in_set(char c, char[] set)
{
foreach (ch : set)
{
if (ch == c) return true;
}
return false;
}
private macro char_is_space_tab(char c)
{
return c == ' ' || c == '\t';
}
private macro to_signed_integer($Type, char[] string)
{
usz len = string.len;
usz index = 0;
char* ptr = string.ptr;
while (index < len && char_is_space_tab(ptr[index])) index++;
if (len == index) return NumberConversion.EMPTY_STRING!;
bool is_negative;
switch (string[index])
{
case '-':
if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE!;
is_negative = true;
index++;
case '+':
index++;
default:
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER!;
$Type base = 10;
if (string[index] == '0')
{
index++;
if (index == len) return ($Type)0;
switch (string[index])
{
case 'x':
case 'X':
base = 16;
index++;
case 'b':
case 'B':
base = 2;
index++;
case 'o':
case 'O':
base = 8;
index++;
default:
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER!;
}
$Type value = 0;
while (index != len)
{
char c = {|
char ch = string[index++];
if (base != 16 || ch < 'A') return (char)(ch - '0');
if (ch <= 'F') return (char)(ch - 'A');
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER!;
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER!;
return (char)(ch - 'a');
|}?;
if (c >= base) return NumberConversion.MALFORMED_INTEGER!;
value = {|
if (is_negative)
{
$Type new_value = value * base - c;
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW!;
return new_value;
}
$Type new_value = value * base + c;
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW!;
return new_value;
|}?;
}
return value;
}
fn int128! to_int128(char[] string) = to_signed_integer(int128, string);
fn long! to_long(char[] string) = to_signed_integer(long, string);
fn int! to_int(char[] string) = to_signed_integer(int, string);
fn short! to_short(char[] string) = to_signed_integer(short, string);
fn ichar! to_ichar(char[] string) = to_signed_integer(ichar, string);
fn char[] trim(char[] string, char[] to_trim = "\t\n\r ")
{
usz start = 0;
usz len = string.len;
while (start < len && char_in_set(string[start], to_trim)) start++;
if (start == len) return string[:0];
usz end = len - 1;
while (end > start && char_in_set(string[end], to_trim)) end--;
return string[start..end];
}
fn bool starts_with(char[] s, char[] needle)
{
if (needle.len > s.len) return false;
foreach (i, c : needle)
{
if (c != s[i]) return false;
}
return true;
}
fn char[][] tsplit(char[] s, char[] needle) = split(s, needle, mem::temp_allocator()) @inline;
fn char[][] split(char[] s, char[] needle, Allocator* allocator = mem::current_allocator())
{
usz capacity = 16;
usz i = 0;
char[]* holder = allocator.alloc(char[].sizeof * capacity)!!;
while (s.len)
{
usz! index = index_of(s, needle);
char[] res = void;
if (try index)
{
res = s[:index];
s = s[index + needle.len..];
}
else
{
res = s;
s = s[:0];
}
if (i == capacity)
{
capacity *= 2;
holder = allocator.realloc(holder, char[].sizeof * capacity)!!;
}
holder[i++] = res;
}
return holder[:i];
}
fn usz! index_of(char[] s, char[] needle)
{
usz match = 0;
usz needed = needle.len;
if (!needed) return SearchResult.MISSING!;
usz index_start = 0;
char search = needle[0];
foreach (usz i, char c : s)
{
if (c == search)
{
if (!match) index_start = i;
match++;
if (match == needed) return index_start;
search = needle[match];
continue;
}
if (match)
{
match = 0;
search = needle[0];
}
}
return SearchResult.MISSING!;
}
fn ZString copy_zstring(char[] s, Allocator* allocator = mem::current_allocator())
{
usz len = s.len;
char* str = allocator.alloc(len + 1)!!;
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
fn char[] copyz(char[] s, Allocator* allocator = mem::current_allocator())
{
usz len = s.len;
char* str = allocator.alloc(len + 1)!!;
mem::copy(str, s.ptr, len);
str[len] = 0;
return str[:len];
}
fn ZString tcopy_zstring(char[] s)
{
return copy_zstring(s, mem::temp_allocator());
}
fn bool compare(char[] a, char[] b)
{
if (a.len != b.len) return false;
foreach (i, c : a)
{
if (c != b[i]) return false;
}
return true;
}
fault UnicodeResult
{
INVALID_UTF8,
INVALID_UTF16,
CONVERSION_FAILED,
}
fn usz utf8_codepoints(char[] utf8)
{
usz len = 0;
foreach (char c : utf8)
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}
fn Char32[]! utf8to32(char[] utf8, Allocator* allocator = mem::current_allocator)
{
usz codepoints = conv::utf8_codepoints(utf8);
Char32* data = allocator.alloc(Char32.sizeof * (codepoints + 1))?;
conv::utf8to32_unsafe(utf8, data)?;
data[codepoints] = 0;
return data[:codepoints];
}
fn char[] utf32to8(Char32[] utf32, Allocator* allocator = mem::current_allocator)
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = allocator.alloc(len + 1)!!;
conv::utf32to8_unsafe(utf32, data);
data[len] = 0;
return data[:len];
}
fn Char16[]! utf8to16(char[] utf8, Allocator* allocator = mem::current_allocator)
{
usz len16 = conv::utf16len_for_utf8(utf8);
Char16* data = allocator.alloc((len16 + 1) * Char16.sizeof)?;
conv::utf8to16_unsafe(utf8, data)?;
data[len16] = 0;
return data[:len16];
}
fn char[]! utf16to8(Char16[] utf16, Allocator* allocator = mem::current_allocator())
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = allocator.alloc(len + 1)?;
conv::utf16to8_unsafe(utf16, data)?;
return data[:len];
}
fn char[] copy(char[] s, Allocator* allocator = mem::current_allocator())
{
usz len = s.len;
ZString str_copy = copy_zstring(s, allocator) @inline;
return str_copy[:len];
}
fn char[] tcopy(char[] s)
{
usz len = s.len;
ZString str_copy = tcopy_zstring(s) @inline;
return str_copy[:len];
}
fn char[] tconcat(char[] s1, char[] s2)
{
usz full_len = s1.len + s2.len;
char* str = tmalloc(full_len + 1);
usz s1_len = s1.len;
mem::copy(str, s1.ptr, s1_len);
mem::copy(str + s1_len, s2.ptr, s2.len);
str[full_len] = 0;
return str[:full_len];
}
fn char[] concat(char[] s1, char[] s2)
{
usz full_len = s1.len + s2.len;
char* str = malloc(full_len + 1);
usz s1_len = s1.len;
mem::copy(str, s1.ptr, s1_len);
mem::copy(str + s1_len, s2.ptr, s2.len);
str[full_len] = 0;
return str[:full_len];
}
fn char[] ZString.as_str(ZString str)
{
return ((char*)str)[:str.len()];
}
fn usz ZString.len(ZString str)
{
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0])
{
if (c & 0xC0 != 0x80) len++;
}
return len;
}

View File

@@ -1,311 +1,514 @@
module std::core::string;
import libc;
import std::ascii;
define String = distinct void*;
def ZString = distinct inline char*;
def Char32 = uint;
def Char16 = ushort;
private struct StringData
fault UnicodeResult
{
Allocator* allocator;
usz len;
usz capacity;
char[*] chars;
INVALID_UTF8,
INVALID_UTF16,
CONVERSION_FAILED,
}
const usz MIN_CAPACITY = 16;
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;
fn String new_with_capacity(usz capacity, Allocator* allocator = mem::current_allocator())
fault NumberConversion
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = allocator.alloc(StringData.sizeof + capacity)!!;
data.allocator = allocator;
data.len = 0;
data.capacity = capacity;
return (String)data;
EMPTY_STRING,
NEGATIVE_VALUE,
MALFORMED_INTEGER,
INTEGER_OVERFLOW,
MALFORMED_FLOAT,
FLOAT_OUT_OF_RANGE,
}
fn String new(char[] c)
macro String printf(String fmt, ..., Allocator* using = mem::heap())
{
usz len = c.len;
String str = new_with_capacity(len);
StringData* data = str.data();
if (len)
@stack_mem(256; Allocator* mem)
{
data.len = len;
mem::copy(&data.chars, c.ptr, len);
}
return (String)data;
DString str;
str.init(.using = mem);
str.printf(fmt, $vasplat());
return str.copy_str(using);
};
}
fn ZString String.zstr(String str)
macro String tprintf(String fmt, ...)
{
StringData* data = str.data();
if (!data) return (ZString)"";
if (data.capacity == data.len)
{
str.reserve(1);
data.chars[data.len] = 0;
}
else if (data.chars[data.len] != 0)
{
data.chars[data.len] = 0;
}
return (ZString)&data.chars[0];
DString str;
str.tinit();
str.printf(fmt, $vasplat());
return str.str();
}
fn usz String.len(String this)
macro bool char_in_set(char c, String set)
{
if (!this) return 0;
return this.data().len;
foreach (ch : set) if (ch == c) return true;
return false;
}
fn String join(String[] s, String joiner, Allocator* using = mem::heap())
{
if (!s)
{
return (String)(calloc(char, 2, .using = using)[:0]);
}
usz total_size = joiner.len * s.len;
foreach (String* &str : s)
{
total_size += str.len;
}
@stack_mem(256; Allocator* mem)
{
DString res = dstring::new_with_capacity(total_size, .using = mem);
res.append(s[0]);
foreach (String* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res.copy_str(using);
};
}
/**
* @require new_size <= this.len()
*/
fn void String.chop(String this, usz new_size)
{
if (!this) return;
this.data().len = new_size;
}
fn char[] String.str(String str)
{
StringData* data = (StringData*)str;
if (!data) return char[] {};
return data.chars[:data.len];
}
fn void String.append_utf32(String* str, Char32[] chars)
{
str.reserve(chars.len);
foreach (Char32 c : chars)
{
str.append_char32(c);
}
}
/**
* @require index < str.len()
* @param [in] string
* @param [in] to_trim
**/
fn void String.set(String str, usz index, char c)
fn String String.trim(String string, String to_trim = "\t\n\r ")
{
str.data().chars[index] = c;
}
fn void String.append_repeat(String* str, char c, usz times)
{
if (times == 0) return;
str.reserve(times);
StringData* data = str.data();
for (usz i = 0; i < times; i++)
{
data.chars[data.len++] = c;
}
usz start = 0;
usz len = string.len;
while (start < len && char_in_set(string[start], to_trim)) start++;
if (start == len) return string[:0];
usz end = len - 1;
while (end > start && char_in_set(string[end], to_trim)) end--;
return string[start..end];
}
/**
* @require c < 0x10ffff
*/
fn void String.append_char32(String* str, Char32 c)
* @param [in] string
* @param [in] needle
**/
fn bool String.starts_with(String string, String needle)
{
if (c < 0x7f)
{
str.reserve(1);
StringData* data = str.data();
data.chars[data.len++] = (char)c;
return;
}
if (c < 0x7ff)
{
str.reserve(2);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xC0 | c >> 6);
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
return;
}
if (c < 0xffff)
{
str.reserve(3);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xE0 | c >> 12);
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
return;
}
str.reserve(4);
StringData* data = str.data();
data.chars[data.len++] = (char)(0xF0 | c >> 18);
data.chars[data.len++] = (char)(0x80 | (c >> 12 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
if (needle.len > string.len) return false;
if (!needle.len) return true;
return string[:needle.len] == needle;
}
fn String String.tcopy(String* str) = str.copy(mem::temp_allocator());
fn String String.copy(String* str, Allocator* allocator = null)
/**
* @param [in] string
* @param [in] needle
**/
fn bool String.ends_with(String string, String needle)
{
if (!str)
if (needle.len > string.len) return false;
if (!needle.len) return true;
return string[^needle.len..] == needle;
}
/**
* Strip the front of the string if the prefix exists.
*
* @param [in] string
* @param [in] needle
**/
fn String String.strip(String string, String needle)
{
if (!needle.len || !string.starts_with(needle)) return string;
return string[needle.len..];
}
/**
* Strip the end of the string if the suffix exists.
*
* @param [in] string
* @param [in] needle
**/
fn String String.strip_end(String string, String needle)
{
if (!needle.len || !string.ends_with(needle)) return string;
// Note that this is the safe way if we want to support zero length.
return string[:(string.len - needle.len)];
}
/**
* Split a string into parts, e.g "a|b|c" split with "|" yields { "a", "b", "c" }
*
* @param [in] s
* @param [in] needle
* @param [&inout] using "The allocator, defaults to the heap allocator"
* @param max "Max number of elements, 0 means no limit, defaults to 0"
* @require needle.len > 0 "The needle must be at least 1 character long"
* @ensure return.len > 0
**/
fn String[] String.split(String s, String needle, usz max = 0, Allocator* using = mem::heap())
{
usz capacity = 16;
usz i = 0;
String* holder = malloc(String, capacity, .using = using);
bool no_more = false;
while (!no_more)
{
if (allocator) return new_with_capacity(0, allocator);
return (String)null;
usz! index = i == max - 1 ? SearchResult.MISSING? : s.index_of(needle);
String res @noinit;
if (try index)
{
res = s[:index];
s = s[index + needle.len..];
}
else
{
res = s;
no_more = true;
}
if (i == capacity)
{
capacity *= 2;
holder = realloc(holder, String.sizeof * capacity, .using = using);
}
holder[i++] = res;
}
if (!allocator) allocator = mem::current_allocator();
StringData* data = str.data();
String new_string = new_with_capacity(data.capacity, allocator);
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
return new_string;
return holder[:i];
}
fn ZString String.copy_zstr(String* str, Allocator* allocator = mem::current_allocator())
/**
* This function is identical to String.split, but implicitly uses the
* temporary allocator.
*
* @param [in] s
* @param [in] needle
* @param max "Max number of elements, 0 means no limit, defaults to 0"
**/
fn String[] String.tsplit(String s, String needle, usz max = 0)
{
usz str_len = str.len();
if (!str_len)
return s.split(needle, max, mem::temp()) @inline;
}
fn bool String.contains(String s, String needle)
{
return @ok(s.index_of(needle));
}
/**
* Find the index of the first incidence of a string.
*
* @param [in] s
* @param [in] needle
* @pure
* @ensure return < s.len
* @require needle.len > 0 : "The needle must be len 1 or more"
* @return "the index of the needle"
* @return! SearchResult.MISSING "if the needle cannot be found"
**/
fn usz! String.index_of(String s, String needle)
{
usz match = 0;
usz needed = needle.len;
usz index_start = 0;
char search = needle[0];
foreach (usz i, char c : s)
{
return (ZString)allocator.calloc(1)!!;
if (c == search)
{
if (!match) index_start = i;
match++;
if (match == needed) return index_start;
search = needle[match];
continue;
}
if (match)
{
match = 0;
search = needle[0];
}
}
char* zstr = allocator.alloc(str_len + 1)!!;
StringData* data = str.data();
mem::copy(zstr, &data.chars, str_len);
zstr[str_len] = 0;
return (ZString)zstr;
return SearchResult.MISSING?;
}
fn char[] String.copy_str(String* str, Allocator* allocator = mem::current_allocator())
/**
* Find the index of the last incidence of a string.
*
* @param [in] s
* @param [in] needle
* @pure
* @ensure return < s.len
* @require needle.len > 0 "The needle must be len 1 or more"
* @return "the index of the needle"
* @return! SearchResult.MISSING "if the needle cannot be found"
**/
fn usz! String.rindex_of(String s, String needle)
{
return str.copy_zstr(allocator)[:str.len()];
}
fn char[] String.tcopy_str(String* str) = str.copy_str(mem::temp_allocator()) @inline;
fn bool String.equals(String str, String other_string)
{
StringData *str1 = str.data();
StringData *str2 = other_string.data();
if (str1 == str2) return true;
if (!str1) return str2.len == 0;
if (!str2) return str1.len == 0;
usz str1_len = str1.len;
if (str1_len != str2.len) return false;
for (int i = 0; i < str1_len; i++)
usz match = 0;
usz needed = needle.len;
usz index_start = 0;
char search = needle[^1];
foreach_r (usz i, char c : s)
{
if (str1.chars[i] != str2.chars[i]) return false;
if (c == search)
{
if (!match) index_start = i;
match++;
if (match == needed) return index_start - needle.len + 1;
search = needle[^(match + 1)];
continue;
}
if (match)
{
match = 0;
search = needle[^1];
}
}
return true;
return SearchResult.MISSING?;
}
fn void String.destroy(String* str)
fn String ZString.as_str(ZString str)
{
if (!*str) return;
StringData* data = str.data();
if (!data) return;
data.allocator.free(data)!!;
*str = (String)null;
return (String)((char*)str)[:str.len()];
}
fn bool String.less(String str, String other_string)
fn usz ZString.char_len(ZString str)
{
StringData* str1 = str.data();
StringData* str2 = other_string.data();
if (str1 == str2) return false;
if (!str1) return str2.len != 0;
if (!str2) return str1.len == 0;
usz str1_len = str1.len;
usz str2_len = str2.len;
if (str1_len != str2_len) return str1_len < str2_len;
for (int i = 0; i < str1_len; i++)
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0])
{
if (str1.chars[i] >= str2.chars[i]) return false;
if (c & 0xC0 != 0x80) len++;
}
return true;
return len;
}
fn void String.append_chars(String* this, char[] str)
fn usz ZString.len(ZString str)
{
usz other_len = str.len;
if (!other_len) return;
if (!*this)
usz len = 0;
char* ptr = (char*)str;
while (char c = ptr++[0]) len++;
return len;
}
fn ZString String.zstr_copy(String s, Allocator* using = mem::heap())
{
usz len = s.len;
char* str = malloc(len + 1, .using = using);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
fn String String.concat(String s1, String s2, Allocator* using = mem::heap())
{
usz full_len = s1.len + s2.len;
char* str = malloc(full_len + 1, .using = using);
usz s1_len = s1.len;
mem::copy(str, s1.ptr, s1_len);
mem::copy(str + s1_len, s2.ptr, s2.len);
str[full_len] = 0;
return (String)str[:full_len];
}
fn String String.tconcat(String s1, String s2) => s1.concat(s2, mem::temp());
fn ZString String.zstr_tcopy(String s) => s.zstr_copy(mem::temp()) @inline;
fn String String.copy(String s, Allocator* using = mem::heap())
{
usz len = s.len;
char* str = malloc(len + 1, .using = using);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (String)str[:len];
}
fn String String.tcopy(String s) => s.copy(mem::temp()) @inline;
fn String ZString.copy(ZString z, Allocator* using = mem::heap()) => z.as_str().copy(using) @inline;
fn String ZString.tcopy(ZString z) => z.as_str().copy(mem::temp()) @inline;
/**
* Convert an UTF-8 string to UTF-16
* @return "The UTF-16 string as a slice, allocated using the given allocator"
* @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
* @return! AllocationFailure "If allocation of the string fails"
**/
fn Char16[]! String.to_utf16(String s, Allocator* using = mem::heap())
{
usz len16 = conv::utf16len_for_utf8(s);
Char16* data = malloc_checked(Char16, len16 + 1, .using = using)!;
conv::utf8to16_unsafe(s, data)!;
data[len16] = 0;
return data[:len16];
}
fn Char32[]! String.to_utf32(String s, Allocator* using = mem::heap())
{
usz codepoints = conv::utf8_codepoints(s);
Char32* data = malloc_checked(Char32, codepoints + 1, .using = using)!;
conv::utf8to32_unsafe(s, data)!;
data[codepoints] = 0;
return data[:codepoints];
}
fn void String.convert_ascii_to_lower(String s)
{
foreach (&c : s) if (*c >= 'A' && *c <= 'Z') *c += 'a' - 'A';
}
fn String String.ascii_to_lower(String s, Allocator* using = mem::heap())
{
String copy = s.copy(using);
copy.convert_ascii_to_lower();
return copy;
}
fn void String.convert_ascii_to_upper(String s)
{
foreach (&c : s) if (*c >= 'a' && *c <= 'z') *c -= 'a' - 'A';
}
fn String String.ascii_to_upper(String s, Allocator* using = mem::heap())
{
String copy = s.copy(using);
copy.convert_ascii_to_upper();
return copy;
}
fn String! from_utf32(Char32[] utf32, Allocator* using = mem::heap())
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = malloc_checked(len + 1, .using = using)!;
defer catch free(data, .using = using);
conv::utf32to8_unsafe(utf32, data);
data[len] = 0;
return (String)data[:len];
}
fn String! from_utf16(Char16[] utf16, Allocator* using = mem::heap())
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = malloc_checked(len + 1, .using = using)!;
defer catch free(data, .using = using);
conv::utf16to8_unsafe(utf16, data)!;
data[len] = 0;
return (String)data[:len];
}
fn String! from_zutf16(Char16* utf16_pointer, Allocator* using = mem::heap())
{
usz utf16_len;
while (utf16_pointer[utf16_len] != 0) utf16_len++;
Char16[] utf16 = utf16_pointer[:utf16_len];
return from_utf16(utf16, using);
}
fn usz String.utf8_codepoints(String s)
{
usz len = 0;
foreach (char c : s)
{
*this = new(str);
return;
if (c & 0xC0 != 0x80) len++;
}
this.reserve(other_len);
StringData* data = (StringData*)*this;
mem::copy(&data.chars[data.len], str.ptr, other_len);
data.len += other_len;
return len;
}
fn Char32[] String.copy_utf32(String* this, Allocator* allocator = mem::current_allocator())
{
return str::utf8to32(this.str(), allocator) @inline!!;
}
fn void String.append_string(String* this, String str)
macro String.to_integer(String string, $Type)
{
StringData* other = (StringData*)str;
if (!other) return;
this.append(str.str());
}
fn void String.append_char(String* str, char c)
{
if (!*str)
usz len = string.len;
usz index = 0;
char* ptr = string.ptr;
while (index < len && ascii::is_blank_m(ptr[index])) index++;
if (len == index) return NumberConversion.EMPTY_STRING?;
bool is_negative;
switch (string[index])
{
*str = new_with_capacity(MIN_CAPACITY);
case '-':
if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE?;
is_negative = true;
index++;
case '+':
index++;
default:
break;
}
str.reserve(1);
StringData* data = (StringData*)*str;
data.chars[data.len++] = c;
}
macro void String.append(String* str, value)
{
var $Type = $typeof(value);
$switch ($Type):
$case char:
$case ichar:
str.append_char(value);
$case String:
str.append_string(value);
$case char[]:
str.append_chars(value);
$case Char32:
str.append_char32(value);
$default:
$if (@convertible($Type, Char32)):
str.append_char32(value);
$elif (@convertible($Type, char[])):
str.append_chars(value);
$else:
$assert("Unsupported type for appending");
$endif;
$endswitch;
}
private fn StringData* String.data(String str) @inline
{
return (StringData*)str;
}
private fn void String.reserve(String* str, usz addition)
{
StringData* data = str.data();
if (!data)
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
$Type base = 10;
if (string[index] == '0')
{
*str = string::new_with_capacity(addition);
return;
index++;
if (index == len) return ($Type)0;
switch (string[index])
{
case 'x':
case 'X':
base = 16;
index++;
case 'b':
case 'B':
base = 2;
index++;
case 'o':
case 'O':
base = 8;
index++;
default:
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
}
usz len = data.len + addition;
if (data.capacity >= len) return;
usz new_capacity = data.capacity *= 2;
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
*str = (String)data.allocator.realloc(data, StringData.sizeof + new_capacity)!!;
$Type value = 0;
while (index != len)
{
char c = {|
char ch = string[index++];
if (base != 16 || ch < 'A') return (char)(ch - '0');
if (ch <= 'F') return (char)(ch - 'A');
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER?;
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER?;
return (char)(ch - 'a');
|}!;
if (c >= base) return NumberConversion.MALFORMED_INTEGER?;
value = {|
if (is_negative)
{
$Type new_value = value * base - c;
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
}
$Type new_value = value * base + c;
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
|}!;
}
return value;
}
fn String String.new_concat(String a, String b, Allocator* allocator = mem::current_allocator())
{
String string = new_with_capacity(a.len() + b.len(), allocator);
string.append(a);
string.append(b);
return string;
}
fn Char16[]! String.to_temp_utf16(String s) => s.to_utf16(mem::temp());
fn int128! String.to_int128(String s) => s.to_integer(int128);
fn long! String.to_long(String s) => s.to_integer(long);
fn int! String.to_int(String s) => s.to_integer(int);
fn short! String.to_short(String s) => s.to_integer(short);
fn ichar! String.to_ichar(String s) => s.to_integer(ichar);
fn uint128! String.to_uint128(String s) => s.to_integer(uint128);
fn ulong! String.to_ulong(String s) => s.to_integer(ulong);
fn uint! String.to_uint(String s) => s.to_integer(uint);
fn ushort! String.to_ushort(String s) => s.to_integer(ushort);
fn char! String.to_uchar(String s) => s.to_integer(char);
fn double! String.to_double(String s) => s.to_real(double);
fn float! String.to_float(String s) => s.to_real(float);

View File

@@ -2,7 +2,7 @@ module std::core::string::iterator;
struct StringIterator
{
char[] utf8;
String utf8;
usz current;
}
@@ -15,9 +15,9 @@ fn Char32! StringIterator.next(StringIterator* this)
{
usz len = this.utf8.len;
usz current = this.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT!;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
usz read = (len - current < 4 ? len - current : 4);
Char32 res = conv::utf8_to_char32(&this.utf8[current], &read)?;
Char32 res = conv::utf8_to_char32(&this.utf8[current], &read)!;
this.current += read;
return res;
}

View File

@@ -0,0 +1,487 @@
module std::core::string;
import std::math;
// Float parsing based on code in Musl floatscan.c by Rich Felker.
// Musl uses the MIT license, copied below:
// ----------------------------------------------------------------------
// Copyright © 2005-2014 Rich Felker, et al.
//
// 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.
// ----------------------------------------------------------------------
const KMAX = 128;
const MASK = KMAX - 1;
const B1B_DIG = 2;
const uint[2] B1B_MAX = { 9007199, 254740991 };
/**
* @require chars.len > 0
**/
macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
{
uint[KMAX] x;
const uint[2] TH = B1B_MAX;
int emax = - $emin - $bits + 3;
const int[*] P10S = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
usz index;
bool got_digit = chars[0] == '0';
bool got_rad;
long lrp, dc;
int k, j, lnz;
usz len = chars.len;
usz last_char = len - 1;
assert(len);
char c @noinit;
// Skip past first characters
while ((c = chars[index]) == '0')
{
if (index == last_char) return sign * 0.0;
index++;
}
if (c == '.')
{
got_rad = true;
if (index == last_char)
{
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
{
lrp--;
got_digit = true;
while (last_char != index && (c = chars[++index]) == '0')
{
lrp--;
}
}
}
while (c - '0' < 10u || c == '.')
{
switch
{
case c == '.':
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
got_rad = true;
lrp = dc;
case k < KMAX - 3:
dc++;
if (c != '0') lnz = (int)dc;
if (j)
{
x[k] = x[k] * 10 + c - '0';
}
else
{
x[k] = c - '0';
}
if (++j == 9)
{
k++;
j = 0;
}
got_digit = true;
default:
dc++;
if (c != '0') x[KMAX - 4] |= 1;
}
if (index == last_char) break;
assert(index < last_char);
c = chars[++index];
}
if (!got_rad) lrp = dc;
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if ((c | 32) == 'e')
{
if (last_char == index) return NumberConversion.MALFORMED_FLOAT?;
long e10 = String.to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT?!;
lrp += e10;
}
else if (index != last_char)
{
return NumberConversion.MALFORMED_FLOAT?;
}
// Handle zero specially to avoid nasty special cases later
if (!x[0]) return sign * 0.0;
// Optimize small integers (w/no exponent) and over/under-flow
if (lrp == dc && dc < 10 && ($bits > 30 || (ulong)x[0] >> $bits == 0)) return sign * (double)x[0];
if (lrp > - $emin / 2) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
// Align incomplete final B1B digit
if (j)
{
for (; j < 9; j++) x[k] *= 10;
k++;
j = 0;
}
int a;
int z = k;
int e2;
long rp = lrp;
// Optimize small to mid-size integers (even in exp. notation)
if (lnz < 9 && lnz <= rp && rp < 18)
{
if (rp == 9) return sign * (double)x[0];
if (rp < 9) return sign * (double)x[0] / P10S[8 - rp];
int bitlim = $bits - 3 * (int)(rp - 9);
if (bitlim > 30 || x[0] >> bitlim == 0) return sign * (double)x[0] * P10S[rp - 10];
}
// Align radix point to B1B digit boundary
if (rp % 9)
{
long rpm9 = rp >= 0 ? rp % 9 : rp % 9 + 9;
int p10 = P10S[8 - rpm9];
uint carry = 0;
for (k = a; k != z; k++)
{
uint tmp = x[k] % p10;
x[k] = x[k] / p10 + carry;
carry = 1000000000 / p10 * tmp;
if (k == a && !x[k])
{
a = (a + 1) & MASK;
rp -= 9;
}
}
if (carry) x[z++] = carry;
rp += 9 - rpm9;
}
// Upscale until desired number of bits are left of radix point
while (rp < 9 * B1B_DIG || (rp == 9 * B1B_DIG && x[a] < TH[0]))
{
uint carry = 0;
e2 -= 29;
for (k = (z - 1) & MASK; ; k = (k - 1) & MASK)
{
ulong tmp = (ulong)x[k] << 29 + carry;
if (tmp > 1000000000)
{
carry = (uint)(tmp / 1000000000);
x[k] = (uint)(tmp % 1000000000);
}
else
{
carry = 0;
x[k] = (uint)tmp;
}
if (k == (z - 1) & MASK && k != a && !x[k]) z = k;
if (k == a) break;
}
if (carry)
{
rp += 9;
a = (a - 1) & MASK;
if (a == z)
{
z = (z - 1) & MASK;
x[(z - 1) & MASK] |= x[z];
}
x[a] = carry;
}
}
// Downscale until exactly number of bits are left of radix point
while (true)
{
uint carry = 0;
int sh = 1;
int i;
for (i = 0; i < B1B_DIG; i++)
{
k = (a + i) & MASK;
if (k == z || x[k] < TH[i])
{
i = B1B_DIG;
break;
}
if (x[(a + i) & MASK] > TH[i]) break;
}
if (i == B1B_DIG && rp == 9 * B1B_DIG) break;
if (rp > 9 + 9 * B1B_DIG) sh = 9;
e2 += sh;
for (k = a; k != z; k = (k+1) & MASK)
{
uint tmp = x[k] & (1 << sh - 1);
x[k] = x[k] >> sh + carry;
carry = (1000000000 >> sh) * tmp;
if (k == a && !x[k])
{
a = (a + 1) & MASK;
i--;
rp -= 9;
}
}
if (carry)
{
if ((z + 1) & MASK != a)
{
x[z] = carry;
z = (z + 1) & MASK;
}
else
{
x[(z - 1) & MASK] |= 1;
}
}
}
// Assemble desired bits into floating point variable
double y;
int i;
for (i = 0; i < B1B_DIG; i++)
{
if ((a + i) & MASK == z) x[(z = (z + 1) & MASK) - 1] = 0;
y = 1000000000.0 * y + x[(a + i) & MASK];
}
y *= sign;
bool denormal;
/* Limit precision for denormal results */
uint bits = $bits;
if (bits > math::DOUBLE_MANT_DIG + e2 - $emin)
{
bits = math::DOUBLE_MANT_DIG + e2 - $emin;
if (bits < 0) bits = 0;
denormal = true;
}
// Calculate bias term to force rounding, move out lower bits
double bias;
double frac;
if (bits < math::DOUBLE_MANT_DIG)
{
bias = math::copysign(math::scalbn(1, 2 * math::DOUBLE_MANT_DIG - bits - 1), y);
frac = y % math::scalbn(1, math::DOUBLE_MANT_DIG - bits);
y -= frac;
y += bias;
}
// Process tail of decimal input so it can affect rounding
if ((a + i) & MASK != z)
{
uint t = x[(a + i) & MASK];
switch
{
case t < 500000000 && (t || (a + i + 1) & MASK != z):
frac += 0.25 * sign;
case t > 500000000:
frac += 0.75 * sign;
case t == 500000000:
if ((a + i + 1) & MASK == z)
{
frac += 0.5 * sign;
}
else
{
frac += 0.75 * sign;
}
}
if (math::DOUBLE_MANT_DIG - bits >= 2 && !(frac % 1)) frac++;
}
y += frac;
y -= bias;
if (((e2 + math::DOUBLE_MANT_DIG) & int.max) > emax - 5)
{
if (math::abs(y) >= 0x1p53)
{
if (denormal && bits == math::DOUBLE_MANT_DIG + e2 - $emin) denormal = false;
y *= 0.5;
e2++;
}
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return NumberConversion.MALFORMED_FLOAT?;
}
return math::scalbn(y, e2);
}
macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
{
double scale = 1;
uint x;
long rp;
long dc;
char c @noinit;
bool got_rad;
bool got_digit;
bool got_tail;
usz len = chars.len;
usz last_char = len - 1;
usz index;
double y;
// Skip past first characters
while ((c = chars[index]) == '0')
{
if (index == last_char) return 0.0;
index++;
}
if (c == '.')
{
got_rad = true;
if (index == last_char)
{
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
{
rp--;
got_digit = true;
while (last_char != index && (c = chars[++index]) == '0')
{
rp--;
}
}
}
while ((c - '0') < 10u || ((c | 32) - 'a') < 6u || c == '.')
{
if (c == '.')
{
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
got_rad = true;
rp = dc;
}
else
{
got_digit = true;
int d = {|
if (c > '9') return (c | 32) + 10 - 'a';
return c - '0';
|};
switch
{
case dc < 8:
x = x * 16 + d;
case dc < math::DOUBLE_MANT_DIG / 4 + 1:
y += d * (scale /= 16);
got_tail = true;
case d && !got_tail:
y += 0.5 * scale;
got_tail = true;
}
dc++;
}
if (index == last_char) break;
c = chars[++index];
}
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if (!got_rad) rp = dc;
for (; dc < 8; dc++) x *= 16;
long e2;
if ((c | 32) == 'p')
{
long e2val = String.to_long((String)chars[index + 1..]) ?? (NumberConversion.MALFORMED_FLOAT?)!;
e2 = e2val;
}
e2 += 4 * rp - 32;
if (!x) return sign * 0.0;
if (e2 > -$emin) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
while (x < 0x80000000)
{
if (y >= 0.5)
{
x += x + 1;
y += y - 1;
}
else
{
x += x;
y += y;
}
e2--;
}
int bits = $bits;
if ($bits > 32 + e2 - $emin)
{
bits = (int)(32 + e2 - $emin);
if (bits < 0) bits = 0;
}
double bias;
if (bits < math::DOUBLE_MANT_DIG)
{
bias = math::copysign(math::scalbn(1, 32 + math::DOUBLE_MANT_DIG - bits - 1), (double)sign);
}
if (bits < 32 && y && !(x & 1))
{
x++;
y = 0;
}
y = bias + sign * (double)x + sign * y;
y -= bias;
if (!y) return NumberConversion.FLOAT_OUT_OF_RANGE?;
return math::scalbn(y, (int)e2);
}
macro String.to_real(String chars, $Type) @private
{
int sign = 1;
$switch ($Type)
$case float:
const int BITS = math::FLOAT_MANT_DIG;
const int EMIN = math::FLOAT_MIN_EXP - BITS;
$case double:
const int BITS = math::DOUBLE_MANT_DIG;
const int EMIN = math::DOUBLE_MIN_EXP - BITS;
$case float128:
$error "Not yet supported";
$default:
$error "Unexpected type";
$endswitch
while (chars.len && chars[0] == ' ') chars = chars[1..];
if (!chars.len) return NumberConversion.MALFORMED_FLOAT?;
switch (chars[0])
{
case '-':
sign = -1;
nextcase;
case '+':
chars = chars[1..];
}
if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf;
if (chars == "NAN" || chars == "nan") return $Type.nan;
if (chars.len > 2 && chars[0] == '0' && (chars[1] | 32) == 'x')
{
return ($Type)hexfloat((char[])chars[2..], BITS, EMIN, sign);
}
return ($Type)decfloat((char[])chars, BITS, EMIN, sign);
}

View File

@@ -1,3 +1,4 @@
module std::core::types;
import libc;
@@ -10,63 +11,63 @@ fault ConversionResult
/**
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
**/
macro variant_to_int(variant v, $Type)
macro any_to_int(any v, $Type)
{
typeid variant_type = v.type;
TypeKind kind = variant_type.kindof;
typeid any_type = v.type;
TypeKind kind = any_type.kindof;
if (kind == TypeKind.ENUM)
{
variant_type = variant_type.inner;
kind = variant_type.kindof;
any_type = any_type.inner;
kind = any_type.kindof;
}
bool is_mixed_signed = $Type.kindof != variant_type.kindof;
bool is_mixed_signed = $Type.kindof != any_type.kindof;
$Type max = $Type.max;
$Type min = $Type.min;
switch (variant_type)
switch (any_type)
{
case ichar:
ichar c = *(char*)v.ptr;
if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
return ($Type)c;
case short:
short s = *(short*)v.ptr;
if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)s;
case int:
int i = *(int*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case long:
long l = *(long*)v.ptr;;
if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
long l = *(long*)v.ptr;
if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)l;
case int128:
int128 i = *(int128*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE!;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case char:
char c = *(char*)v.ptr;
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)c;
case ushort:
ushort s = *(ushort*)v.ptr;;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
ushort s = *(ushort*)v.ptr;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)s;
case uint:
uint i = *(uint*)v.ptr;;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
uint i = *(uint*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
case ulong:
ulong l = *(ulong*)v.ptr;;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
ulong l = *(ulong*)v.ptr;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)l;
case uint128:
uint128 i = *(uint128*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
return ($Type)i;
default:
unreachable();
@@ -76,12 +77,12 @@ macro variant_to_int(variant v, $Type)
macro bool is_numerical($Type)
{
var $kind = $Type.kindof;
$if ($kind == TypeKind.DISTINCT):
$if $kind == TypeKind.DISTINCT:
return is_numerical($Type.inner);
$else:
$else
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|| $kind == TypeKind.VECTOR;
$endif;
$endif
}
fn bool TypeKind.is_int(TypeKind kind) @inline
@@ -97,13 +98,13 @@ macro bool is_indexable($Type)
macro bool is_comparable($Type)
{
var $kind = $Type.kindof;
$if ($kind == TypeKind.DISTINCT):
$if $kind == TypeKind.DISTINCT:
return is_comparable($Type.inner);
$else:
$else
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|| $kind == TypeKind.VECTOR || $kind == TypeKind.BOOL || $kind == TypeKind.POINTER
|| $kind == TypeKind.ENUM;
$endif;
$endif
}
macro bool is_equatable($Type)
@@ -113,21 +114,22 @@ macro bool is_equatable($Type)
macro bool is_subarray_convertable($Type)
{
$switch ($Type.kindof):
$switch ($Type.kindof)
$case SUBARRAY:
return true;
$case POINTER:
return $Type.inner.kindof == TypeKind.ARRAY;
$default:
return false;
$endswitch;
$endswitch
}
macro bool is_int($Type) = $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
macro bool is_bool($Type) => $Type.kindof == TypeKind.BOOL;
macro bool is_int($Type) => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
macro bool is_intlike($Type)
{
$switch ($Type.kindof):
$switch ($Type.kindof)
$case SIGNED_INT:
$case UNSIGNED_INT:
return true;
@@ -135,22 +137,22 @@ macro bool is_intlike($Type)
return $Type.inner.kindof == TypeKind.SIGNED_INT || $Type.inner.kindof == TypeKind.UNSIGNED_INT;
$default:
return false;
$endswitch;
$endswitch
}
macro bool is_float($Type) = $Type.kindof == TypeKind.FLOAT;
macro bool is_float($Type) => $Type.kindof == TypeKind.FLOAT;
macro bool is_floatlike($Type)
{
$switch ($Type.kindof):
$switch ($Type.kindof)
$case FLOAT:
return true;
$case VECTOR:
return $Type.inner.kindof == TypeKind.FLOAT;
$default:
return false;
$endswitch;
$endswitch
}
macro bool is_vector($Type)
@@ -158,7 +160,16 @@ macro bool is_vector($Type)
return $Type.kindof == TypeKind.VECTOR;
}
macro bool @convertable(#a, $TypeB)
macro TypeKind inner_kind($Type)
{
$if $Type.kindof == TypeKind.DISTINCT:
return inner_kind($typefrom($Type.inner));
$else
return $Type.kindof;
$endif
}
macro bool @convertable(#a, $TypeB) @builtin
{
return $checks($TypeB x = #a);
}
@@ -171,33 +182,65 @@ macro bool is_same($TypeA, $TypeB)
macro bool @has_same(#a, #b, ...)
{
var $type_a = $typeof(#a).typeid;
$if ($type_a != $typeof(#b).typeid):
$if $type_a != $typeof(#b).typeid:
return false;
$endif;
$for (var $i = 0; $i < $vacount; $i++):
$if ($typeof($vaexpr($i)).typeid != $type_a):
$endif
$for (var $i = 0; $i < $vacount; $i++)
$if $typeof($vaexpr($i)).typeid != $type_a:
return false;
$endif;
$endfor;
$endif
$endfor
return true;
}
macro bool may_load_atomic($Type)
{
$switch ($Type.kindof)
$case SIGNED_INT:
$case UNSIGNED_INT:
$case POINTER:
$case FLOAT:
return true;
$case DISTINCT:
return may_load_atomic($Type.inner);
$default:
return false;
$endswitch
}
macro bool is_promotable_to_floatlike($Type) => types::is_floatlike($Type) || types::is_int($Type);
macro bool is_promotable_to_float($Type) => types::is_float($Type) || types::is_int($Type);
macro bool is_same_vector_type($Type1, $Type2)
{
$if $Type1.kindof != TypeKind.VECTOR:
return $Type2.kindof != TypeKind.VECTOR;
$else
return $Type1.inner == $Type2.inner && $Type1.len == $Type2.len;
$endif
}
macro bool is_equatable_type($Type)
{
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
return true;
$else
return is_equatable($Type);
$endif
}
macro bool is_equatable_value(value)
{
$if ($defined(value.less) || $defined(value.compare_to) || $defined(value.equals)):
return true;
$else:
return is_equatable($typeof(value));
$endif;
return is_equatable_type($typeof(value));
}
macro bool is_comparable_value(value)
{
$if ($defined(value.less) || $defined(value.compare_to)):
$if $defined(value.less) || $defined(value.compare_to):
return true;
$else:
$else
return is_comparable($typeof(value));
$endif;
$endif
}
enum TypeKind : char
@@ -208,7 +251,7 @@ enum TypeKind : char
UNSIGNED_INT,
FLOAT,
TYPEID,
ANYERR,
ANYFAULT,
ANY,
ENUM,
FAULT,
@@ -222,7 +265,6 @@ enum TypeKind : char
VECTOR,
DISTINCT,
POINTER,
VARIANT
}
struct TypeEnum

View File

@@ -1,6 +1,23 @@
module std::core::values;
macro bool @is_int(#value) = types::is_int($typeof(#value));
macro bool @convertable_to(#a, #b) = $checks($typeof(#b) x = #a);
macro bool @is_floatlike(#value) = types::is_floatlike($typeof(#value));
macro bool @is_float(#value) = types::is_float($typeof(#value));
macro TypeKind @typekind(#value) @builtin => $typeof(#value).kindof;
macro bool @typeis(#value, $Type) @builtin => $typeof(#value).typeid == $Type.typeid;
macro bool @is_bool(#value) => types::is_bool($typeof(#value));
macro bool @is_int(#value) => types::is_int($typeof(#value));
macro bool @convertable_to(#a, #b) => $checks($typeof(#b) x = #a);
macro bool @is_floatlike(#value) => types::is_floatlike($typeof(#value));
macro bool @is_float(#value) => types::is_float($typeof(#value));
macro bool @is_promotable_to_floatlike(#value) => types::is_promotable_to_floatlike($typeof(#value));
macro bool @is_promotable_to_float(#value) => types::is_promotable_to_float($typeof(#value));
macro bool @is_same_vector_type(#value1, #value2) => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
macro promote_int(x)
{
$if @is_int(x):
return (double)x;
$else
return x;
$endif
}
macro TypeKind @inner_kind(#value) => types::inner_kind($typeof(#value));

2
lib/std/crypto/crypto.c3 Normal file
View File

@@ -0,0 +1,2 @@
module std::crypto;

65
lib/std/crypto/rc4.c3 Normal file
View File

@@ -0,0 +1,65 @@
module std::crypto::rc4;
// Copyright (c) 2021 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.
struct Rc4
{
uint i, j;
char[256] state;
}
/**
* Initialize the RC4 state.
*
* @param [inout] this "The RC4 state"
* @param [in] key "The key to use"
* @require key.len > 0 "The key must be at least 1 byte long"
**/
fn void Rc4.init(Rc4* this, char[] key)
{
// Init the state matrix
foreach (char i, &c : this.state) *c = i;
for (int i = 0, int j = 0; i < 256; i++)
{
j = (j + this.state[i] + key[i % key.len]) & 0xFF;
@swap(this.state[i], this.state[j]);
}
this.i = 0;
this.j = 0;
}
/**
* Encrypt or decrypt a sequence of bytes.
*
* @param [inout] this "The RC4 State"
* @param [in] in "The input"
* @param [out] out "The output"
* @require in.len <= out.len "Output would overflow"
**/
fn void Rc4.crypt(Rc4* this, char[] in, char[] out)
{
uint i = this.i;
uint j = this.j;
char* state = &this.state;
isz len = in.len;
foreach (idx, c : in)
{
i = (i + 1) & 0xFF;
j = (j + state[i]) & 0xFF;
@swap(state[i], state[j]);
out[idx] = in[idx] ^ state[(state[i] + state[j]) & 0xFF];
}
this.i = i;
this.j = j;
}
/**
* Clear the rc4 state.
*
* @param [out] this "The RC4 State"
**/
fn void Rc4.destroy(Rc4* this)
{
*this = {};
}

353
lib/std/encoding/json.c3 Normal file
View File

@@ -0,0 +1,353 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::encoding::json;
import std::io;
import std::ascii;
import std::collections::object;
enum JsonTokenType
{
NO_TOKEN,
LBRACE,
LBRACKET,
COMMA,
COLON,
RBRACE,
RBRACKET,
STRING,
NUMBER,
TRUE,
FALSE,
NULL,
EOF,
}
struct JsonParser
{
uint line;
Stream stream;
Allocator* allocator;
JsonTokenType token;
DString last_string;
double last_number;
char current;
anyfault current_err;
bool skip_comments;
bool reached_end;
}
fault JsonParsingError
{
EOF,
UNEXPECTED_CHARACTER,
INVALID_ESCAPE_SEQUENCE,
DUPLICATE_MEMBERS,
INVALID_NUMBER,
}
fn void JsonParser.init(JsonParser* parser, Stream s, Allocator* using = mem::heap())
{
*parser = { .last_string = dstring::new_with_capacity(64, using), .stream = s, .allocator = using };
}
fn Object*! JsonParser.parse_from_token(JsonParser* this, JsonTokenType token)
{
switch (token)
{
case NO_TOKEN: unreachable();
case LBRACE: return this.parse_map();
case LBRACKET: return this.parse_array();
case COMMA:
case RBRACE:
case RBRACKET:
case COLON: return JsonParsingError.UNEXPECTED_CHARACTER?;
case STRING: return object::new_string(this.last_string.str(), this.allocator);
case NUMBER: return object::new_float(this.last_number, this.allocator);
case TRUE: return object::new_bool(true);
case FALSE: return object::new_bool(false);
case NULL: return object::new_null();
case EOF: return JsonParsingError.EOF?;
}
unreachable();
}
fn Object*! JsonParser.parse_any(JsonParser* this)
{
return this.parse_from_token(this.advance());
}
fn JsonTokenType! JsonParser.lex_number(JsonParser* this, char c)
{
@pool()
{
DString t = dstring::tnew_with_capacity(32);
bool negate = c == '-';
if (negate)
{
t.append(c);
c = this.read_next()!;
}
while (c >= '0' && c <= '9')
{
t.append(c);
c = this.read_next()!;
}
if (c == '.')
{
t.append(c);
while (c = this.read_next()!, c >= '0' && c <= '9')
{
t.append(c);
}
}
if ((c | 32) == 'e')
{
t.append(c);
c = this.read_next()!;
switch (c)
{
case '-':
case '+':
t.append(c);
c = this.read_next()!;
}
if (c < '0' || c > '9') return JsonParsingError.INVALID_NUMBER?;
while (c >= '0' && c <= '9')
{
t.append(c);
c = this.read_next()!;
}
}
this.pushback();
double! d = t.str().to_double() ?? JsonParsingError.INVALID_NUMBER?;
this.last_number = d!;
return NUMBER;
};
}
fn Object*! JsonParser.parse_map(JsonParser* this)
{
Object* map = object::new_obj(this.allocator);
JsonTokenType token = this.advance()!;
defer catch map.free();
DString temp_key = dstring::new_with_capacity(32, this.allocator);
defer temp_key.free();
while (token != JsonTokenType.RBRACE)
{
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
DString string = this.last_string;
if (map.has_key(string.str())) return JsonParsingError.DUPLICATE_MEMBERS?;
// Copy the key to our temp holder. We do this to work around the issue
// if the temp allocator should be used as the default allocator.
temp_key.clear();
temp_key.append(string);
this.parse_expected(COLON)!;
Object* element = this.parse_any()!;
map.set(temp_key.str(), element);
token = this.advance()!;
if (token == JsonTokenType.COMMA)
{
token = this.advance()!;
continue;
}
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
return map;
}
fn Object*! JsonParser.parse_array(JsonParser* this)
{
Object* list = object::new_obj(this.allocator);
defer catch list.free();
JsonTokenType token = this.advance()!;
while (token != JsonTokenType.RBRACKET)
{
Object* element = this.parse_from_token(token)!;
list.append(element);
token = this.advance()!;
if (token == JsonTokenType.COMMA)
{
token = this.advance()!;
continue;
}
if (token != JsonTokenType.RBRACKET) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
return list;
}
fn void JsonParser.pushback(JsonParser* this)
{
if (!this.reached_end) this.stream.pushback_byte()!!;
}
fn char! JsonParser.read_next(JsonParser* this)
{
if (this.reached_end) return '\0';
char! c = this.stream.read_byte();
if (catch err = c)
{
case IoError.EOF:
this.reached_end = true;
return '\0';
default:
return err?;
}
if (c == 0)
{
this.reached_end = true;
}
return c;
}
fn JsonTokenType! JsonParser.advance(JsonParser* this)
{
char c;
// Skip whitespace
while WS: (c = this.read_next()!)
{
switch (c)
{
case '\n':
this.line++;
nextcase;
case ' ':
case '\t':
case '\r':
case '\v':
continue;
case '/':
if (!this.skip_comments) break;
c = this.read_next()!;
if (c != '*')
{
this.pushback();
break WS;
}
while COMMENT: (1)
{
// Skip to */
while (c = this.read_next()!)
{
if (c == '\n') this.line++;
if (c != '*') continue;
// Skip through all the '*'
while (c = this.read_next()!)
{
if (c == '\n') this.line++;
if (c != '*') break;
}
if (c == '/') break COMMENT;
}
}
continue;
default:
break WS;
}
}
switch (c)
{
case '\0':
return IoError.EOF?;
case '{':
return LBRACE;
case '}':
return RBRACE;
case '[':
return LBRACKET;
case ']':
return RBRACKET;
case ':':
return COLON;
case ',':
return COMMA;
case '"':
return this.lex_string();
case '-':
case '0'..'9':
return this.lex_number(c);
case 't':
this.match("rue")!;
return TRUE;
case 'f':
this.match("alse")!;
return FALSE;
case 'n':
this.match("ull")!;
return NULL;
default:
return JsonParsingError.UNEXPECTED_CHARACTER?;
}
}
fn void! JsonParser.match(JsonParser* this, String str)
{
foreach (c : str)
{
char l = this.read_next()!;
if (l != c) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
}
fn void! JsonParser.parse_expected(JsonParser* this, JsonTokenType token) @local
{
if (this.advance()! != token) return JsonParsingError.UNEXPECTED_CHARACTER?;
}
fn JsonTokenType! JsonParser.lex_string(JsonParser *this)
{
this.last_string.clear();
while LOOP: (1)
{
char c = this.read_next()!;
switch (c)
{
case '\0':
return JsonParsingError.EOF?;
case 1..31:
return JsonParsingError.UNEXPECTED_CHARACTER?;
case '"':
break LOOP;
case '\\':
break;
default:
this.last_string.append(c);
continue;
}
c = this.read_next()!;
switch (c)
{
case '\0':
return JsonParsingError.EOF?;
case 1..31:
return JsonParsingError.UNEXPECTED_CHARACTER?;
case '"':
case '\\':
case '/':
break;
case 'b':
c = '\b';
case 'f':
c = '\f';
case 'n':
c = '\n';
case 'r':
c = '\r';
case 't':
c = '\t';
case 'u':
uint val;
for (int i = 0; i < 4; i++)
{
c = this.read_next()!;
if (!c.is_xdigit()) return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
val = val << 4 + (c > '9' ? (c | 32) - 'a' + 10 : c - '0');
}
this.last_string.append_char32(val);
continue;
default:
return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
}
}
return STRING;
}

View File

@@ -1,83 +0,0 @@
// TODO: ensure the type is an enum first.
module std::enumset<Enum>;
$assert(Enum.elements < 64, "Maximum number of elements for an enum used as enum set is 63");
$switch ($$C_INT_SIZE):
$case 64:
private define EnumSetType = ulong;
$case 32:
$if (Enum.elements < 32):
private define EnumSetType = uint;
$else:
private define EnumSetType = ulong;
$endif;
$default:
$if (Enum.elements < 16):
private define EnumSetType = ushort;
$elif (Enum.elements < 31):
private define EnumSetType = uint;
$else:
private define EnumSetType = ulong;
$endif;
$endswitch;
define EnumSet = distinct EnumSetType;
fn void EnumSet.add(EnumSet* this, Enum v)
{
*this = (EnumSet)((EnumSetType)*this | 1u << (EnumSetType)v);
}
fn void EnumSet.clear(EnumSet* this)
{
*this = 0;
}
fn bool EnumSet.remove(EnumSet* this, Enum v)
{
EnumSetType old = (EnumSetType)*this;
EnumSetType new = old & ~(1u << (EnumSetType)v);
*this = (EnumSet)new;
return old != new;
}
fn bool EnumSet.has(EnumSet* this, Enum v)
{
return ((EnumSetType)*this & (1u << (EnumSetType)v)) != 0;
}
fn void EnumSet.add_all(EnumSet* this, EnumSet s)
{
*this = (EnumSet)((EnumSetType)*this | (EnumSetType)s);
}
fn void EnumSet.retain_all(EnumSet* this, EnumSet s)
{
*this = (EnumSet)((EnumSetType)*this & (EnumSetType)s);
}
fn void EnumSet.remove_all(EnumSet* this, EnumSet s)
{
*this = (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
}
fn EnumSet EnumSet.and_of(EnumSet* this, EnumSet s)
{
return (EnumSet)((EnumSetType)*this & (EnumSetType)s);
}
fn EnumSet EnumSet.or_of(EnumSet* this, EnumSet s)
{
return (EnumSet)((EnumSetType)*this | (EnumSetType)s);
}
fn EnumSet EnumSet.diff_of(EnumSet* this, EnumSet s)
{
return (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
}
fn EnumSet EnumSet.xor_of(EnumSet* this, EnumSet s)
{
return (EnumSet)((EnumSetType)*this ^ (EnumSetType)s);
}

View File

@@ -4,7 +4,7 @@
module std::hash::adler32;
private const uint ADLER_CONST = 65521;
const uint ADLER_CONST @private = 65521;
struct Adler32
{

View File

@@ -43,7 +43,7 @@ fn uint encode(char[] data)
return ~result;
}
private const uint[256] CRC32_TABLE = {
const uint[256] CRC32_TABLE @private = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,

View File

@@ -43,7 +43,7 @@ fn ulong encode(char[] data)
return result;
}
private const ulong[256] CRC64_TABLE = {
const ulong[256] CRC64_TABLE @private = {
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,

View File

@@ -3,12 +3,12 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::hash::fnv32a;
define Fnv32a = distinct uint;
def Fnv32a = distinct uint;
private const FNV32A_START = 0x811c9dc5;
private const FNV32A_MUL = 0x01000193;
const FNV32A_START @private = 0x811c9dc5;
const FNV32A_MUL @private = 0x01000193;
private macro void @update(uint &h, char x) => h = (h * FNV32A_MUL) ^ x;
macro void @update(uint &h, char x) @private => h = (h * FNV32A_MUL) ^ x;
fn void Fnv32a.init(Fnv32a* this)
{

237
lib/std/hash/sha1.c3 Normal file
View File

@@ -0,0 +1,237 @@
// Copyright (c) 2021 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.
//
// Implementation was off Steve Reid's SHA-1 C implementation
module std::hash::sha1;
import std::bits;
struct Sha1
{
uint[5] state;
uint[2] count;
char[64] buffer;
}
fn void Sha1.init(Sha1* this)
{
// SHA1 initialization constants
*this = {
.state = {
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0
}
};
}
/**
* @param [&inout] this
* @param [in] data
* @require data.len <= uint.max
**/
fn void Sha1.update(Sha1* this, char[] data)
{
uint j = this.count[0];
uint len = data.len;
if ((this.count[0] += len << 3) < j) this.count[1]++;
this.count[1] += len >> 29;
j = (j >> 3) & 63;
uint i;
if (j + len > 63)
{
i = 64 - j;
this.buffer[j..] = data[:i];
sha1_transform(&this.state, &this.buffer);
for (; i + 63 < len; i += 64)
{
sha1_transform(&this.state, &data[i]);
}
j = 0;
}
this.buffer[j:len - i] = data[i..];
}
fn char[20] Sha1.final(Sha1* this)
{
char[8] finalcount;
for (uint i = 0; i < 8; i++)
{
finalcount[i] = (char)((this.count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 0xFF);
}
this.update(char[] { 0o200 });
while ((this.count[0] & 504) != 448)
{
this.update(char[] { 0 });
}
this.update(&finalcount);
char[20] digest;
for (uint i = 0; i < 20; i++)
{
digest[i] = (char)((this.state[i >> 2] >> ((3 - (i & 3)) * 8)) & 0xFF);
}
// Clear mem
mem::clear(this, Sha1.sizeof);
finalcount = {};
return digest;
}
union Long16 @local
{
char[64] c;
uint[16] l;
}
macro @blk(&block, i) @local
{
return (block.l[i & 15] = (block.l[(i + 13) & 15] ^ block.l[(i + 8) & 15]
^ block.l[(i + 2) & 15] ^ block.l[i & 15]).rotl(1));
}
macro @blk0(&block, i) @local
{
$if env::BIG_ENDIAN:
return block.l[i];
$else
return block.l[i] = (block.l[i].rotl(24) & 0xFF00FF00)
| (block.l[i].rotl(8) & 0x00FF00FF);
$endif
}
macro @r0(&block, v, &w, x, y, &z, i) @local
{
z += ((w & (x ^ y)) ^ y) + @blk0(block, i) + 0x5A827999 + v.rotl(5);
w = w.rotl(30);
}
macro @r1(&block, v, &w, x, y, &z, i) @local
{
z += ((w & (x ^ y)) ^ y) + @blk(block, i) + 0x5A827999 + v.rotl(5);
w = w.rotl(30);
}
macro @r2(&block, v, &w, x, y, &z, i) @local
{
z += (w ^ x ^ y) + @blk(block, i) + 0x6ED9EBA1 + v.rotl(5);
w = w.rotl(30);
}
macro @r3(&block, v, &w, x, y, &z, i) @local
{
z += (((w | x) &y) | (w & x)) + @blk(block, i) + 0x8F1BBCDC + v.rotl(5);
w = w.rotl(30);
}
macro @r4(&block, v, &w, x, y, &z, i) @local
{
z += (w ^ x ^ y) + @blk(block, i) + 0xCA62C1D6 + v.rotl(5);
w = w.rotl(30);
}
/**
* @param [&inout] state
* @param [&in] buffer
**/
fn void sha1_transform(uint* state, char* buffer) @local
{
Long16 block;
block.c[..] = buffer[:64];
uint a = state[0];
uint b = state[1];
uint c = state[2];
uint d = state[3];
uint e = state[4];
@r0(block, a, b, c, d, e, 0);
@r0(block, e, a, b, c, d, 1);
@r0(block, d, e, a, b, c, 2);
@r0(block, c, d, e, a, b, 3);
@r0(block, b, c, d, e, a, 4);
@r0(block, a, b, c, d, e, 5);
@r0(block, e, a, b, c, d, 6);
@r0(block, d, e, a, b, c, 7);
@r0(block, c, d, e, a, b, 8);
@r0(block, b, c, d, e, a, 9);
@r0(block, a, b, c, d, e, 10);
@r0(block, e, a, b, c, d, 11);
@r0(block, d, e, a, b, c, 12);
@r0(block, c, d, e, a, b, 13);
@r0(block, b, c, d, e, a, 14);
@r0(block, a, b, c, d, e, 15);
@r1(block, e, a, b, c, d, 16);
@r1(block, d, e, a, b, c, 17);
@r1(block, c, d, e, a, b, 18);
@r1(block, b, c, d, e, a, 19);
@r2(block, a, b, c, d, e, 20);
@r2(block, e, a, b, c, d, 21);
@r2(block, d, e, a, b, c, 22);
@r2(block, c, d, e, a, b, 23);
@r2(block, b, c, d, e, a, 24);
@r2(block, a, b, c, d, e, 25);
@r2(block, e, a, b, c, d, 26);
@r2(block, d, e, a, b, c, 27);
@r2(block, c, d, e, a, b, 28);
@r2(block, b, c, d, e, a, 29);
@r2(block, a, b, c, d, e, 30);
@r2(block, e, a, b, c, d, 31);
@r2(block, d, e, a, b, c, 32);
@r2(block, c, d, e, a, b, 33);
@r2(block, b, c, d, e, a, 34);
@r2(block, a, b, c, d, e, 35);
@r2(block, e, a, b, c, d, 36);
@r2(block, d, e, a, b, c, 37);
@r2(block, c, d, e, a, b, 38);
@r2(block, b, c, d, e, a, 39);
@r3(block, a, b, c, d, e, 40);
@r3(block, e, a, b, c, d, 41);
@r3(block, d, e, a, b, c, 42);
@r3(block, c, d, e, a, b, 43);
@r3(block, b, c, d, e, a, 44);
@r3(block, a, b, c, d, e, 45);
@r3(block, e, a, b, c, d, 46);
@r3(block, d, e, a, b, c, 47);
@r3(block, c, d, e, a, b, 48);
@r3(block, b, c, d, e, a, 49);
@r3(block, a, b, c, d, e, 50);
@r3(block, e, a, b, c, d, 51);
@r3(block, d, e, a, b, c, 52);
@r3(block, c, d, e, a, b, 53);
@r3(block, b, c, d, e, a, 54);
@r3(block, a, b, c, d, e, 55);
@r3(block, e, a, b, c, d, 56);
@r3(block, d, e, a, b, c, 57);
@r3(block, c, d, e, a, b, 58);
@r3(block, b, c, d, e, a, 59);
@r4(block, a, b, c, d, e, 60);
@r4(block, e, a, b, c, d, 61);
@r4(block, d, e, a, b, c, 62);
@r4(block, c, d, e, a, b, 63);
@r4(block, b, c, d, e, a, 64);
@r4(block, a, b, c, d, e, 65);
@r4(block, e, a, b, c, d, 66);
@r4(block, d, e, a, b, c, 67);
@r4(block, c, d, e, a, b, 68);
@r4(block, b, c, d, e, a, 69);
@r4(block, a, b, c, d, e, 70);
@r4(block, e, a, b, c, d, 71);
@r4(block, d, e, a, b, c, 72);
@r4(block, c, d, e, a, b, 73);
@r4(block, b, c, d, e, a, 74);
@r4(block, a, b, c, d, e, 75);
@r4(block, e, a, b, c, d, 76);
@r4(block, d, e, a, b, c, 77);
@r4(block, c, d, e, a, b, 78);
@r4(block, b, c, d, e, a, 79);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
a = b = c = d = e = 0;
buffer[:64] = 0;
}

View File

@@ -1,183 +1,3 @@
module std::io::dir;
import std::io::os;
// In progress.
define Path = distinct char[];
const PREFERRED_SEPARATOR = USE_WIN32_FILESYSTEM ? '\\' : '/';
private const USE_WIN32_FILESYSTEM = env::OS_TYPE != OsType.WIN32;
fault PathResult
{
INVALID_PATH
}
fn char[]! getcwd(Allocator* allocator = mem::default_allocator())
{
return os::getcwd(allocator);
}
fn char[]! tgetcwd()
{
return getcwd(mem::temp_allocator()) @inline;
}
macro bool is_separator(char c)
{
$if (USE_WIN32_FILESYSTEM):
return c == '/' || c == '\\';
$else:
return c == '/';
$endif;
}
const bool[256] RESERVED_PATH_CHAR_POSIX = {
[0] = true,
['/'] = true,
};
const bool[256] RESERVED_PATH_CHAR_WIN32 = {
[0..31] = true,
['>'] = true,
['<'] = true,
[':'] = true,
['\"'] = true,
['/'] = true,
['\\'] = true,
['|'] = true,
['?'] = true,
['*'] = true,
};
macro bool is_reserved_path_char(char c)
{
$if (USE_WIN32_FILESYSTEM):
return RESERVED_PATH_CHAR_WIN32[c];
$else:
return RESERVED_PATH_CHAR_POSIX[c];
$endif;
}
private fn usz! root_name_len(char[] path)
{
usz len = path.len;
if (!len) return 0;
$if (USE_WIN32_FILESYSTEM):
switch (path[0])
{
case '\\':
if (len < 2 || path[1] != '\\') break;
if (len == 2 || is_separator(path[2])) return PathResult.INVALID_PATH!;
for (usz i = 2; i < len; i++)
{
char c = path[i];
if (is_separator(c)) return i;
if (is_reserved_path_char(c)) return PathResult.INVALID_PATH!;
}
return len;
case 'A'..'Z':
case 'a'..'z':
if (len < 2 || path[1] != ':') break;
if (len < 3 || !is_separator(path[2])) return PathResult.INVALID_PATH!;
return 2;
}
$endif;
return 0;
}
private fn void! normalize_path(char[]* path_ref)
{
char[] path = *path_ref;
if (!path.len) return;
usz path_start = root_name_len(path)?;
usz len = path_start;
bool previous_was_separator = false;
usz path_len = path.len;
for (usz i = path_start; i < path_len; i++)
{
char c = path[i];
// Fold foo///bar into foo/bar
if (is_separator(c))
{
if (previous_was_separator)
{
continue;
}
path.ptr[len++] = PREFERRED_SEPARATOR;
previous_was_separator = true;
continue;
}
// If we get . we have different things that might happen:
if (c == '.' && i < path_len - 1)
{
// Is this ./ or /./ ?
if ((previous_was_separator || i == path_start) && is_separator(path[i + 1]))
{
// Then we skip this
i += 2;
continue;
}
// Is this /../ in that case we must walk back and erase(!)
if (i < path_len - 2 && previous_was_separator && path[i + 1] == '.' && is_separator(path[i + 2]))
{
assert(len > path_start);
len--;
while (len > path_start && !is_separator(path[len - 1]))
{
len--;
}
i += 2;
continue;
}
}
if (i != len) path[len] = c;
previous_was_separator = false;
len++;
}
path.ptr[len] = 0;
*path_ref = path[:len];
}
fn Path new_path(char[] path)
{
char[] copy = str::copy(path);
normalize_path(&copy)!!;
return (Path)copy;
}
fn Path Path.root_name(Path path)
{
char[] path_str = (char[])path;
usz len = root_name_len(path_str)!!;
if (!len) return (Path)"";
return (Path)path_str[0:len];
}
fn Path Path.root_directory(Path path)
{
char[] path_str = (char[])path;
usz len = path_str.len;
if (!len) return (Path)"";
$if (USE_WIN32_FILESYSTEM):
usz root_len = root_name_len(path_str)!!;
if (root_len == len || !is_separator(path_str[root_len])) return (Path)"";
return (Path)path_str[root_len..root_len];
$else:
if (!is_separator(path_str[0])) return (Path)"";
for (usz i = 1; i < len; i++)
{
if (is_separator(path_str[i]))
{
return (Path)path_str[:i];
}
}
return path;
$endif;
}
fn void Path.destroy(Path path)
{
free(path.ptr);
}

View File

@@ -19,53 +19,77 @@ enum Seek
fault IoError
{
FILE_NOT_FOUND,
FILE_NOT_SEEKABLE,
FILE_NOT_VALID,
FILE_INVALID_POSITION,
FILE_OVERFLOW,
INVALID_POSITION,
OVERFLOW,
FILE_IS_PIPE,
FILE_EOF,
FILE_INCOMPLETE_WRITE,
FILE_NOT_DIR,
INCOMPLETE_WRITE,
BUSY,
NO_PERMISSION,
OUT_OF_SPACE,
INVALID_PUSHBACK,
EOF,
CANNOT_READ_DIR,
TOO_MANY_DESCRIPTORS,
FILE_IS_DIR,
READ_ONLY,
FILE_NOT_DIR,
SYMLINK_FAILED,
ALREADY_EXISTS,
NOT_SEEKABLE,
NAME_TOO_LONG,
WOULD_BLOCK,
DIR_NOT_EMPTY,
INTERRUPTED,
GENERAL_ERROR,
UNKNOWN_ERROR,
UNSUPPORTED_OPERATION,
}
fn int putchar(char c) @inline
fn void putchar(char c) @inline
{
return libc::putchar(c);
libc::putchar(c);
}
/**
* @param [&in] message
* @return `number of bytes printed.`
*/
fn int print(char* message)
macro void print(x)
{
char* pointer = message;
while (*pointer != '\0')
{
if (!putchar(*pointer)) return 0;
pointer++;
}
return 1;
var $Type = $typeof(x);
$switch ($Type)
$case String:
(void)stdout().print(x);
$case ZString:
(void)stdout().print(x.as_str());
$case DString:
(void)stdout().print(x.str());
$default:
$if @convertible(x, String):
(void)stdout().print((String)x);
$else
(void)stdout().printf("%s", x);
$endif
$endswitch
}
/**
* @param [&in] message
* @return `number of bytes printed.`
*/
fn int println(char *message = "") @inline
macro void printn(x = "")
{
return libc::puts(message);
var $Type = $typeof(x);
$switch ($Type)
$case String:
(void)stdout().printn(x);
$case ZString:
(void)stdout().printn(x.as_str());
$case DString:
(void)stdout().printn(x.str());
$default:
$if @convertible(x, String):
(void)stdout().printn((String)x);
$else
(void)stdout().printfn("%s", x);
$endif
$endswitch
}
fn File stdout()
{
return { libc::stdout() };

View File

@@ -1,46 +1,56 @@
module std::io;
module std::io::file;
import libc;
fn void! File.open(File* file, char[] filename, char[] mode)
fn File! open(String filename, String mode)
{
@pool()
{
char* filename_copy = tmalloc(filename.len + 1);
char* mode_copy = tmalloc(mode.len + 1);
return { .file = os::native_fopen(filename, mode) };
}
mem::copy(filename_copy, (char*)(filename), filename.len);
mem::copy(mode_copy, (char*)(mode), mode.len);
filename_copy[filename.len] = 0;
mode_copy[filename.len] = 0;
file.file = libc::fopen(filename_copy, mode_copy);
if (!file.file) return IoError.FILE_NOT_FOUND!;
};
fn File! open_path(Path path, String mode)
{
return { .file = os::native_fopen(path.as_str(), mode) };
}
/**
* @require file.file != null
**/
fn void! File.seek(File *file, long offset, Seek seekMode = Seek.SET)
fn void! File.reopen(File* file, String filename, String mode)
{
if (libc::fseek(file.file, (SeekIndex)(offset), (int)(seekMode)))
{
switch (libc::errno())
{
case errno::EBADF: return IoError.FILE_NOT_SEEKABLE!;
case errno::EINVAL: return IoError.FILE_INVALID_POSITION!;
case errno::EOVERFLOW: return IoError.FILE_OVERFLOW!;
case errno::ESPIPE: return IoError.FILE_IS_PIPE!;
default: return IoError.UNKNOWN_ERROR!;
}
}
file.file = os::native_freopen(file.file, filename, mode)!;
}
/**
* @require file.file != null
**/
fn usz! File.seek(File file, isz offset, Seek seek_mode = Seek.SET)
{
os::native_fseek(file.file, offset, seek_mode)!;
return os::native_ftell(file.file);
}
/*
Implement later
/**
* @require file.file == null
**/
fn void! File.memopen(File* file, char[] data, String mode)
{
@pool()
{
file.file = libc::memopen(data.ptr, data.len, mode.zstr_tcopy(), file.file);
// TODO errors
};
}
*/
/**
* @require file && file.file != null
*/
fn void! File.putc(File *file, char c)
{
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF!;
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF?;
}
/**
@@ -53,8 +63,8 @@ fn void! File.close(File *file) @inline
switch (libc::errno())
{
case errno::ECONNRESET:
case errno::EBADF: return IoError.FILE_NOT_VALID!;
case errno::EINTR: return IoError.INTERRUPTED!;
case errno::EBADF: return IoError.FILE_NOT_VALID?;
case errno::EINTR: return IoError.INTERRUPTED?;
case errno::EDQUOT:
case errno::EFAULT:
case errno::EAGAIN:
@@ -62,8 +72,8 @@ fn void! File.close(File *file) @inline
case errno::ENETDOWN:
case errno::ENETUNREACH:
case errno::ENOSPC:
case errno::EIO: return IoError.FILE_INCOMPLETE_WRITE!;
default: return IoError.UNKNOWN_ERROR!;
case errno::EIO: return IoError.INCOMPLETE_WRITE?;
default: return IoError.UNKNOWN_ERROR?;
}
}
file.file = null;
@@ -78,35 +88,31 @@ fn bool File.eof(File* file) @inline
}
/**
* @require file && file.file
* @param [in] buffer
*/
fn usz File.read(File* file, void* buffer, usz items, usz element_size = 1)
fn usz! File.read(File* file, char[] buffer)
{
return libc::fread(buffer, element_size, items, file.file);
return os::native_fread(file.file, buffer);
}
/**
* @param [&in] file
* @param [&out] buffer
* @param items
* @param element_size
* @require file.file `File must be initialized`
* @require element_size > 1
*/
fn usz File.write(File* file, void* buffer, usz items, usz element_size = 1)
fn usz! File.write(File file, char[] buffer)
{
return libc::fwrite(buffer, element_size, items, file.file);
return os::native_fwrite(file.file, buffer);
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn usz! File.println(File* file, char[] string)
fn usz! File.printn(File file, String string = "")
{
usz len = string.len;
if (len != libc::fwrite(string.ptr, 1, len, file.file)) return IoError.UNKNOWN_ERROR!;
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR!;
usz len = file.print(string)!;
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR?;
return len + 1;
}
@@ -114,9 +120,20 @@ fn usz! File.println(File* file, char[] string)
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn String File.getline(File* file, Allocator* allocator = mem::current_allocator())
fn usz! File.print(File file, String string)
{
String s = string::new_with_capacity(120, allocator);
usz len = string.len;
if (len != file.write((char[])string)!) return IoError.UNKNOWN_ERROR?;
return len;
}
/**
* @param [&in] file
* @require file.file `File must be initialized`
*/
fn DString File.getline(File* file, Allocator* using = mem::heap())
{
DString s = dstring::new_with_capacity(120, using);
while (!file.eof())
{
int c = libc::fgetc(file.file);
@@ -130,20 +147,17 @@ fn String File.getline(File* file, Allocator* allocator = mem::current_allocator
/**
* @param [&in] file
* @require file.file `File must be initialized`
* @return "a zero terminated char[] (the pointer may be safely cast into a ZString)"
* @return "a zero terminated String (the pointer may be safely cast into a ZString)"
*/
fn char[] File.tgetline(File* file)
fn String File.tgetline(File* file)
{
String s = file.getline(mem::temp_allocator());
ZString z = s.zstr();
return z[:s.len()];
return file.getline(mem::temp()).zstr().as_str();
}
fn char! File.getc(File* file)
{
int c = libc::fgetc(file.file);
if (c == -1) return IoError.FILE_EOF!;
if (c == -1) return IoError.FILE_EOF?;
return (char)c;
}

View File

@@ -1,81 +1,14 @@
module std::io::file;
import libc;
struct FileInfo
fn bool is_file(String path)
{
ulong size;
return os::native_is_file(path);
}
$switch (env::OS_TYPE):
$case MACOSX:
$case IOS:
$case WATCHOS:
$case TVOS:
private struct DarwinTimespec
fn usz! get_size(String path)
{
long tv_sec;
long tv_nsec;
}
private struct Darwin64Stat
{
int st_dev;
ushort st_mode;
ushort st_nlink;
ulong st_ino;
uint st_uid;
uint st_gid;
int st_rdev;
DarwinTimespec st_atimespec; // time of last access
DarwinTimespec st_mtimespec; // time of last data modification
DarwinTimespec st_ctimespec; // time of last status change
DarwinTimespec st_birthtimespec; // time of file creation(birth)
long st_size;
long st_blocks;
int st_blocksize;
uint st_flags;
uint st_gen;
int st_lspare;
long[2] st_qspare;
}
extern fn int _stat(ZString str, Darwin64Stat* stat) @extname("stat64");
fn void! FileInfo.read(FileInfo* info, Path path)
{
@pool()
{
Darwin64Stat stat;
int res = _stat(str::tcopy_zstring((char[])path), &stat);
if (res != 0)
{
switch (libc::errno())
{
case errno::EBADF:
return IoError.FILE_NOT_VALID!;
case errno::EFAULT:
unreachable("Invalid stat");
case errno::EIO:
return IoError.GENERAL_ERROR!;
case errno::EACCES:
return IoError.NO_PERMISSION!;
case errno::ELOOP:
return IoError.NO_PERMISSION!;
case errno::ENAMETOOLONG:
return IoError.NAME_TOO_LONG!;
case errno::ENOENT:
return IoError.FILE_NOT_FOUND!;
case errno::ENOTDIR:
return IoError.FILE_NOT_DIR!;
case errno::EOVERFLOW:
return IoError.GENERAL_ERROR!;
default:
return IoError.UNKNOWN_ERROR!;
}
}
info.size = stat.st_size;
};
}
$default:
macro void! FileInfo.read(FileInfo* info, Path path)
{
$assert("Unsupported function");
}
$endswitch;
return os::native_file_size(path);
}

View File

@@ -1,25 +1,32 @@
module std::io;
private fn void! Formatter.left_adjust(Formatter* this, usz len)
const char[16] XDIGITS_H = "0123456789ABCDEF";
const char[16] XDIGITS_L = "0123456789abcdef";
fn void! Formatter.left_adjust(Formatter* this, usz len) @local
{
if (!this.flags.left) return;
for (usz l = len; l < this.width; l++) this.out(' ')?;
for (usz l = len; l < this.width; l++) this.out(' ')!;
}
private fn void! Formatter.right_adjust(Formatter* this, usz len)
fn void! Formatter.right_adjust(Formatter* this, usz len) @local
{
if (this.flags.left) return;
for (usz l = len; l < this.width; l++) this.out(' ')?;
for (usz l = len; l < this.width; l++) this.out(' ')!;
}
private fn uint128 int_from_variant(variant arg, bool *is_neg)
fn uint128! int_from_any(any arg, bool *is_neg) @private
{
*is_neg = false;
if (arg.type.kindof == TypeKind.POINTER)
{
return (uint128)(uptr)*(void**)arg.ptr;
}
if (arg.type.kindof == TypeKind.DISTINCT)
{
return int_from_any(any { arg.ptr, arg.type.inner }, is_neg);
}
switch (arg)
{
case bool:
@@ -56,22 +63,21 @@ private fn uint128 int_from_variant(variant arg, bool *is_neg)
double d = *arg;
return (uint128)((*is_neg = d < 0) ? -d : d);
default:
return 0;
return PrintFault.INVALID_ARGUMENT_TYPE?;
}
}
private fn FloatType float_from_variant(variant arg)
fn FloatType! float_from_any(any arg) @private
{
$if (env::F128_SUPPORT):
if (arg.type == float128.typeid) return *((float128*)arg.ptr);
$endif;
$if (env::F16_SUPPORT):
$if env::F128_SUPPORT:
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
$endif
$if env::F16_SUPPORT:
if (arg.type == float16.typeid) return *((float16*)arg.ptr);
$endif;
if (arg.type.kindof == TypeKind.POINTER)
$endif
if (arg.type.kindof == TypeKind.DISTINCT)
{
return (FloatType)(uptr)(void*)arg.ptr;
return float_from_any(any { arg.ptr, arg.type.inner });
}
switch (arg)
{
@@ -102,7 +108,7 @@ private fn FloatType float_from_variant(variant arg)
case double:
return (FloatType)*arg;
default:
return 0;
return PrintFault.INVALID_ARGUMENT_TYPE?;
}
}
@@ -115,7 +121,7 @@ private fn FloatType float_from_variant(variant arg)
* @param maxlen "the maximum len that can be read."
* @return "The result of the atoi."
**/
private fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline
fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline @private
{
uint i = 0;
usz len = *len_ptr;
@@ -130,13 +136,12 @@ private fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline
return i;
}
private fn void! Formatter.out_substr(Formatter *this, char[] str)
fn void! Formatter.out_substr(Formatter *this, String str) @private
{
usz l = conv::utf8_codepoints(str);
uint prec = this.prec;
if (this.flags.precision && l < prec) l = prec;
this.right_adjust(' ')?;
this.right_adjust(' ')!;
usz index = 0;
usz chars = str.len;
char* ptr = str.ptr;
@@ -145,255 +150,360 @@ private fn void! Formatter.out_substr(Formatter *this, char[] str)
char c = ptr[index];
// Break if we have precision set and we ran out...
if (c & 0xC0 != 0x80 && this.flags.precision && !prec--) break;
this.out(c)?;
this.out(c)!;
index++;
}
return this.left_adjust(l);
}
union ConvUnion
fn void! Formatter.pad(Formatter* this, char c, isz width, isz len) @inline
{
ulong u;
double f;
for (isz i = len; i < width; i++) this.out(c)!;
}
private fn void! Formatter.etoa(Formatter* this, FloatType value)
fn char* fmt_u(uint128 x, char* s)
{
// check for NaN and special values
if (value != value || value < -FloatType.max || value > FloatType.max)
{
return this.ftoa(value);
}
// determine the sign
bool negative = value < 0;
if (negative) value = -value;
// default precision
if (!this.flags.precision)
{
this.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
}
// determine the decimal exponent
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
ConvUnion conv;
conv.f = (double)value;
int exp2 = (int)(conv.u >> 52 & 0x7FF) - 1023; // effectively log2
conv.u = (conv.u & (1u64 << 52 - 1)) | (1023u64 << 52); // drop the exponent so conv.F is now in [1,2)
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.f - 1.5) * 0.289529654602168);
// now we want to compute 10^expval but we want to be sure it won't overflow
exp2 = (int)(expval * 3.321928094887362 + 0.5);
double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
double z2 = z * z;
conv.u = (ulong)(exp2 + 1023) << 52;
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
conv.f *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
// correct for rounding errors
if (value < conv.f)
{
expval--;
conv.f /= 10;
}
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
uint minwidth = ((expval < 100) && (expval > -100)) ? 4 : 5;
// in "%g" mode, "prec" is the number of *significant figures* not decimals
if (this.flags.adapt_exp)
{
// do we want to fall-back to "%f" mode?
if (value >= 1e-4 && value < 1e6)
{
this.prec = this.prec > expval ? this.prec - expval - 1 : 0;
this.flags.precision = true; // make sure ftoa respects precision
// no characters in exponent
minwidth = 0;
expval = 0;
}
else
{
// we use one sigfig for the whole part
if (this.prec > 0 && this.flags.precision) this.prec--;
}
}
// Adjust width
uint fwidth = this.width > minwidth ? this.width - minwidth : 0;
// if we're padding on the right, DON'T pad the floating part
if (this.flags.left && minwidth) fwidth = 0;
// rescale the float value
if (expval) value /= conv.f;
// output the floating part
usz start_idx = this.idx;
PrintFlags old = this.flags;
this.flags.adapt_exp = false;
this.width = fwidth;
this.ftoa(negative ? -value : value)?;
this.flags = old;
// output the exponent part
if (minwidth)
{
// output the exponential symbol
this.out(this.flags.uppercase ? 'E' : 'e')?;
// output the exponent value
this.flags = { .zeropad = true, .plus = true };
this.width = minwidth - 1;
this.prec = 0;
this.ntoa((uint128)(expval < 0 ? -expval : expval), expval < 0, 10)?;
this.flags = old;
// might need to right-pad spaces
this.left_adjust(this.idx - start_idx)?;
}
for (; x > ulong.max; x /= 10) *--s = '0' + (char)(x % 10);
for (ulong y = (ulong)x; y; y /= 10) *--s = '0' + (char)(y % 10);
return s;
}
// internal ftoa for fixed decimal floating point
private fn void! Formatter.ftoa(Formatter* this, FloatType value)
fn void! Formatter.out_chars(Formatter* this, char[] s)
{
char[PRINTF_FTOA_BUFFER_SIZE] buf = void;
usz len = 0;
const FloatType[] POW10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
FloatType diff = 0.0;
foreach (c : s) this.out(c)!;
}
// powers of 10
enum FloatFormatting
{
FLOAT,
EXPONENTIAL,
ADAPTIVE,
HEX
}
// test for special values
if (value != value)
fn void! Formatter.etoa(Formatter* this, double y) => this.floatformat(EXPONENTIAL, y);
fn void! Formatter.ftoa(Formatter* this, double y) => this.floatformat(FLOAT, y);
fn void! Formatter.gtoa(Formatter* this, double y) => this.floatformat(ADAPTIVE, y);
fn void! Formatter.atoa(Formatter* this, double y) => this.floatformat(HEX, y);
fn void! Formatter.floatformat(Formatter* this, FloatFormatting formatting, double y) @private
{
// This code is heavily based on musl's printf code
const BUF_SIZE = (math::DOUBLE_MANT_DIG + 28) / 29 + 1
+ (math::DOUBLE_MAX_EXP + math::DOUBLE_MANT_DIG + 28 + 8) / 9;
uint[BUF_SIZE] big;
bool is_neg = false;
if (math::signbit(y))
{
is_neg = true;
y = -y;
}
int pl = is_neg || this.flags.plus ? 1 : 0;
// Print inf/nan
if (!math::is_finite(y))
{
return this.out_reverse("nan");
// Add padding
if (!this.flags.left) this.pad(' ', this.width, 3 + pl)!;
String s = this.flags.uppercase ? "INF" : "inf";
if (y != y) this.flags.uppercase ? "NAN" : "nan";
if (pl) this.out(is_neg ? '-' : '+')!;
this.out_chars(s)!;
if (this.flags.left) this.pad(' ', this.width, 3 + pl)!;
return;
}
if (value < -FloatType.max)
// Rescale
int e2;
y = math::frexp(y, &e2) * 2;
if (y) e2--;
char[12] ebuf0;
char* ebuf = 12 + (char*)&ebuf0;
char[9 + math::DOUBLE_MANT_DIG / 4] buf_array;
char* buf = &buf_array;
isz p = this.flags.precision ? this.prec : -1;
if (formatting == HEX)
{
return this.out_reverse("fni-");
}
if (value > FloatType.max)
{
if (this.flags.plus)
double round = 8.0;
// 0x / 0X
pl += 2;
if (p > 0 && p < math::DOUBLE_MANT_DIG / 4 - 1)
{
return this.out_reverse("fni+");
int re = math::DOUBLE_MANT_DIG / 4 - 1 - (int)p;
round *= 1 << (math::DOUBLE_MANT_DIG % 4);
while (re--) round *= 16;
if (is_neg)
{
y = -y;
y -= round;
y += round;
y = -y;
}
else
{
y += round;
y -= round;
}
}
return this.out_reverse("fni");
}
// test for very large values
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
if (value > PRINTF_MAX_FLOAT || value < -PRINTF_MAX_FLOAT)
{
return this.etoa(value);
}
// test for negative
bool negative = value < 0;
if (negative) value = 0 - value;
// set default precision, if not set explicitly
if (!this.flags.precision) this.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
while (this.prec > 9)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
this.prec--;
}
// Safe due to 1e9 limit.
int whole = (int)value;
FloatType tmp = (value - whole) * POW10[this.prec];
ulong frac = (ulong)tmp;
diff = tmp - frac;
switch (true)
{
case diff > 0.5:
++frac;
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
if (frac >= POW10[this.prec])
{
frac = 0;
++whole;
}
case diff < 0.5:
break;
case !frac && (frac & 1):
// if halfway, round up if odd OR if last digit is 0
++frac;
}
if (!this.prec)
{
diff = value - (FloatType)whole;
if ((!(diff < 0.5) || diff > 0.5) && (whole & 1))
// Reverse print
char* estr = fmt_u(e2 < 0 ? (int128)-e2 : (int128)e2, ebuf);
if (estr == ebuf) *--estr = '0';
*--estr = (e2 < 0 ? '-' : '+');
*--estr = this.flags.uppercase ? 'P' : 'p';
char* s = buf;
char* xdigits = this.flags.uppercase ? &XDIGITS_H : &XDIGITS_L;
do
{
// exactly 0.5 and ODD, then round up
// 1.5 -> 2, but 2.5 -> 2
++whole;
}
int x = (int)y;
*s++ = xdigits[x];
y = 16 * (y - x);
if (s - buf == 1 && (y || p > 0 || this.flags.hash)) *s++ = '.';
} while (y);
isz outlen = s - buf;
isz explen = ebuf - estr;
if (p > int.max - 2 - explen - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
usz l = p && outlen - 2 < p
? p + 2 + explen
: outlen + explen;
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)!;
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')!;
this.out_chars(this.flags.uppercase ? "0X" : "0x")!;
if (this.flags.zeropad) this.pad('0', this.width, pl + l)!;
this.out_chars(buf[:outlen])!;
this.pad('0', l - outlen - explen, 0)!;
this.out_chars(estr[:explen])!;
if (this.flags.left) this.pad(' ', this.width, pl + l)!;
return;
}
if (p < 0) p = 6;
if (y)
{
y *= 0x1p28;
e2 -= 28;
}
uint* a, z, r;
if (e2 < 0)
{
a = r = z = &big;
}
else
{
uint count = this.prec;
// now do fractional part, as an unsigned number
do
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
--count;
buf[len++] = (char)(48 + (frac % 10));
}
while (frac /= 10);
// add extra 0s
while (count-- > 0)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
}
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
// add decimal
buf[len++] = '.';
a = r = z = (uint*)&big + big.len - math::DOUBLE_MANT_DIG - 1;
}
// do whole part, number is reversed
do
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = (char)(48 + (whole % 10));
}
while (whole /= 10);
uint v = z++[0] = (uint)y;
y = 1000000000 * (y - v);
} while (y);
// pad leading zeros
if (!this.flags.left && this.flags.zeropad)
while (e2 > 0)
{
if (this.width && (negative || this.flags.plus || this.flags.space)) this.width--;
while (len < this.width)
uint carry = 0;
int sh = math::min(29, e2);
for (uint* d = z - 1; d >= a; d--)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = '0';
ulong x = (ulong)*d << sh + carry;
*d = (uint)(x % 1000000000);
carry = (uint)(x / 1000000000);
}
if (carry) *--a = carry;
while (z > a && !z[-1]) z--;
e2 -= sh;
}
while (e2 < 0)
{
uint carry = 0;
uint* b;
int sh = math::min(9, -e2);
int need = (int)(1 + (p + math::DOUBLE_MANT_DIG / 3u + 8) / 9);
for (uint* d = a; d < z; d++)
{
// CHECK THIS
uint rm = *d & ((1 << sh) - 1);
*d = (*d >> sh) + carry;
carry = (1000000000 >> sh) * rm;
}
if (!a[0]) a++;
if (carry) z++[0] = carry;
// Avoid (slow!) computation past requested precision
b = formatting == FLOAT ? r : a;
if (z - b > need) z = b + need;
e2 += sh;
}
int e;
if (a < z)
{
for (int i = 10, e = (int)(9 * (r - a)); *a >= i; i *= 10, e++);
}
// Perform rounding: j is precision after the radix (possibly neg)
int j = (int)(p - (isz)(formatting == FLOAT ? 0 : e - (int)(formatting == ADAPTIVE && p)));
if (j < 9 * (z - r - 1))
{
uint x;
// We avoid C's broken division of negative numbers
uint* d = r + 1 + ((j + 9 * math::DOUBLE_MAX_EXP) / 9 - math::DOUBLE_MAX_EXP);
j += 9 * math::DOUBLE_MAX_EXP;
j %= 9;
int i;
for (i = 10, j++; j < 9; i *= 10, j++);
x = *d % i;
// Are there any significant digits past j?
if (x || (d + 1) != z)
{
double round = 2 / math::DOUBLE_EPSILON;
double small;
if (((*d / i) & 1) || (i == 1000000000 && d > a && (d[-1] & 1)))
{
round += 2;
}
switch
{
case x < i / 2:
small = 0x0.8p0;
case x == i / 2 && d + 1 == z:
small = 0x1.0p0;
default:
small = 0x1.8p0;
}
if (pl && is_neg)
{
round *= -1;
small *= -1;
}
*d -= x;
// Decide whether to round by probing round+small
if (round + small != round)
{
*d = *d + i;
while (*d > 999999999)
{
*d-- = 0;
if (d < a) *--a = 0;
(*d)++;
}
for (i = 10, e = (int)(9 * (r - a)); *a >= i; i *= 10, e++);
}
}
if (z > d + 1) z = d + 1;
}
for (; z>a && !z[-1]; z--);
if (formatting == ADAPTIVE)
{
if (!p) p++;
if (p > e && e >= -4)
{
formatting = FLOAT;
p -= (isz)e + 1;
}
else
{
formatting = EXPONENTIAL;
p--;
}
if (!this.flags.hash)
{
// Count trailing zeros in last place
if (z > a && z[-1])
{
for (int i = 10, j = 0; z[-1] % i == 0; i *= 10, j++);
}
else
{
j = 9;
}
if (formatting == FLOAT)
{
p = math::min(p, math::max((isz)0, 9 * (z - r - 1) - j));
}
else
{
p = math::min(p, math::max((isz)0, 9 * (z - r - 1) + e - j));
}
}
}
char next = {|
if (negative) return '-';
if (this.flags.plus) return '+';
if (this.flags.space) return ' ';
return 0;
|};
if (next)
if (p > int.max - 1 - (isz)(p || this.flags.hash)) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
int l = (int)(1 + p + (isz)(p || this.flags.hash));
char* estr @noinit;
if (formatting == FLOAT)
{
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
buf[len++] = next;
if (e > int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (e > 0) l += e;
}
return this.out_reverse(buf[:len]);
else
{
estr = fmt_u((uint128)(e < 0 ? -e : e), ebuf);
while (ebuf - estr < 2) (--estr)[0] = '0';
*--estr = (e < 0 ? '-' : '+');
*--estr = this.flags.uppercase ? 'E' : 'e';
if (ebuf - estr > (isz)int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
l += (int)(ebuf - estr);
}
if (l > int.max - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (!this.flags.left && !this.flags.zeropad) this.pad(' ', this.width, pl + l)!;
if (is_neg || this.flags.plus) this.out(is_neg ? '-' : '+')!;
if (this.flags.zeropad) this.pad('0', this.width, pl + l)!;
if (formatting == FLOAT)
{
if (a > r) a = r;
uint* d = a;
for (; d <= r; d++)
{
char* s = fmt_u(*d, buf + 9);
switch
{
case d != a:
while (s > buf) (--s)[0] = '0';
case s == buf + 9:
*--s = '0';
}
this.out_chars(s[:buf + 9 - s])!;
}
if (p || this.flags.hash) this.out('.')!;
for (; d < z && p > 0; d++, p -= 9)
{
char* s = fmt_u(*d, buf + 9);
while (s > buf) *--s = '0';
this.out_chars(s[:math::min((isz)9, p)])!;
}
this.pad('0', p + 9, 9)!;
}
else
{
if (z <= a) z = a + 1;
for (uint* d = a; d < z && p >= 0; d++)
{
char* s = fmt_u(*d, buf + 9);
if (s == buf + 9) (--s)[0] = '0';
if (d != a)
{
while (s > buf) (--s)[0] = '0';
}
else
{
this.out(s++[0])!;
if (p > 0 || this.flags.hash) this.out('.')!;
}
this.out_chars(s[:math::min(buf + 9 - s, p)])!;
p -= buf + 9 - s;
}
this.pad('0', p + 18, 18)!;
this.out_chars(estr[:ebuf - estr])!;
}
if (this.flags.left) this.pad(' ', this.width, pl + l)!;
return;
}
private fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, uint base)
fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, uint base) @private
{
char[PRINTF_NTOA_BUFFER_SIZE] buf = void;
char[PRINTF_NTOA_BUFFER_SIZE] buf @noinit;
usz len = 0;
// no hash for 0 values
@@ -405,17 +515,17 @@ private fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, u
char past_10 = (this.flags.uppercase ? 'A' : 'a') - 10;
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
char digit = (char)(value % base);
buf[len++] = digit + (digit < 10 ? '0' : past_10);
value /= base;
}
while (value);
}
return this.ntoa_format(buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
return this.ntoa_format((String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
}
private fn void! Formatter.ntoa_format(Formatter* this, char[] buf, usz len, bool negative, uint base)
fn void! Formatter.ntoa_format(Formatter* this, String buf, usz len, bool negative, uint base) @private
{
// pad leading zeros
if (!this.flags.left)
@@ -423,12 +533,12 @@ private fn void! Formatter.ntoa_format(Formatter* this, char[] buf, usz len, boo
if (this.width && this.flags.zeropad && (negative || this.flags.plus || this.flags.space)) this.width--;
while (len < this.prec)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '0';
}
while (this.flags.zeropad && len < this.width)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '0';
}
}
@@ -443,7 +553,7 @@ private fn void! Formatter.ntoa_format(Formatter* this, char[] buf, usz len, boo
}
if (base != 10)
{
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
switch (base)
{
case 16:
@@ -462,13 +572,13 @@ private fn void! Formatter.ntoa_format(Formatter* this, char[] buf, usz len, boo
switch (true)
{
case negative:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '-';
case this.flags.plus:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '+';
case this.flags.space:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = ' ';
}
if (!len) return;
@@ -476,42 +586,42 @@ private fn void! Formatter.ntoa_format(Formatter* this, char[] buf, usz len, boo
}
private fn void! Formatter.ntoa_variant(Formatter* this, variant arg, uint base)
fn void! Formatter.ntoa_any(Formatter* this, any arg, uint base) @private
{
bool is_neg;
uint128 val = int_from_variant(arg, &is_neg);
uint128 val = int_from_any(arg, &is_neg)!!;
return this.ntoa(val, is_neg, base) @inline;
}
private fn void! Formatter.out_char(Formatter* this, variant arg)
fn void! Formatter.out_char(Formatter* this, any arg) @private
{
uint l = 1;
// pre padding
this.right_adjust(l)?;
this.right_adjust(l)!;
// char output
Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD;
Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD;
switch (true)
{
case c < 0x7f:
this.out((char)c)?;
this.out((char)c)!;
case c < 0x7ff:
this.out((char)(0xC0 | c >> 6))?;
this.out((char)(0x80 | (c & 0x3F)))?;
this.out((char)(0xC0 | c >> 6))!;
this.out((char)(0x80 | (c & 0x3F)))!;
case c < 0xffff:
this.out((char)(0xE0 | c >> 12))?;
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
this.out((char)(0x80 | (c & 0x3F)))?;
this.out((char)(0xE0 | c >> 12))!;
this.out((char)(0x80 | (c >> 6 & 0x3F)))!;
this.out((char)(0x80 | (c & 0x3F)))!;
default:
this.out((char)(0xF0 | c >> 18))?;
this.out((char)(0x80 | (c >> 12 & 0x3F)))?;
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
this.out((char)(0x80 | (c & 0x3F)))?;
this.out((char)(0xF0 | c >> 18))!;
this.out((char)(0x80 | (c >> 12 & 0x3F)))!;
this.out((char)(0x80 | (c >> 6 & 0x3F)))!;
this.out((char)(0x80 | (c & 0x3F)))!;
}
return this.left_adjust(l);
}
private fn void! Formatter.out_reverse(Formatter* this, char[] buf)
fn void! Formatter.out_reverse(Formatter* this, char[] buf) @private
{
usz buffer_start_idx = this.idx;
usz len = buf.len;
@@ -520,37 +630,38 @@ private fn void! Formatter.out_reverse(Formatter* this, char[] buf)
{
for (usz i = len; i < this.width; i++)
{
this.out(' ')?;
this.out(' ')!;
}
}
// reverse string
while (len) this.out(buf[--len])?;
while (len) this.out(buf[--len])!;
// append pad spaces up to given width
return this.left_adjust(this.idx - buffer_start_idx);
}
private fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline
fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline @private
{
usz val = ++(*index_ptr);
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!;
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT?;
}
private fn variant! next_variant(variant* args_ptr, usz args_len, usz* arg_index_ptr) @inline
fn any! next_any(any* args_ptr, usz args_len, usz* arg_index_ptr) @inline @private
{
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG!;
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG?;
return args_ptr[(*arg_index_ptr)++];
}
private fn int! printf_parse_format_field(variant* args_ptr, usz args_len, usz* args_index_ptr, char* format_ptr, usz format_len, usz* index_ptr) @inline
fn int! printf_parse_format_field(
any* args_ptr, usz args_len, usz* args_index_ptr,
char* format_ptr, usz format_len, usz* index_ptr) @inline @private
{
char c = format_ptr[*index_ptr];
if (c >= '0' && c <= '9') return simple_atoi(format_ptr, format_len, index_ptr);
if (c != '*') return 0;
printf_advance_format(format_len, index_ptr)?;
variant val = next_variant(args_ptr, args_len, args_index_ptr)?;
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG!;
uint! intval = types::variant_to_int(val, int);
if (catch intval) return FormattingFault.INVALID_WIDTH_ARG!;
return intval;
printf_advance_format(format_len, index_ptr)!;
any val = next_any(args_ptr, args_len, args_index_ptr)!;
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG?;
uint! intval = types::any_to_int(val, int);
return intval ?? FormattingFault.INVALID_WIDTH_ARG?;
}

View File

@@ -1,11 +1,8 @@
module std::io;
import std::map;
import std::collections::map;
import libc;
const int PRINTF_NTOA_BUFFER_SIZE = 256;
const int PRINTF_FTOA_BUFFER_SIZE = 256;
const float PRINTF_MAX_FLOAT = 1e9;
const uint PRINTF_DEFAULT_FLOAT_PRECISION = 6;
fault PrintFault
{
@@ -13,18 +10,68 @@ fault PrintFault
INTERNAL_BUFFER_EXCEEDED,
INVALID_FORMAT_STRING,
MISSING_ARG,
INVALID_ARGUMENT_TYPE,
}
fault FormattingFault
{
UNTERMINATED_FORMAT,
MISSING_ARG,
INVALID_WIDTH_ARG,
INVALID_FORMAT_TYPE,
}
define OutputFn = fn void!(char c, void* buffer);
define ToStringFunction = fn char[](void* value, Allocator *allocator);
define ToFormatFunction = fn void!(void* value, Formatter* formatter);
define FloatType = double;
def OutputFn = fn void!(char c, void* buffer);
def ToStringFunction = fn String(void* value, Allocator *using);
def ToFormatFunction = fn void!(void* value, Formatter* formatter);
def FloatType = double;
private define StringFunctionMap = std::map::HashMap<typeid, ToStringFunction>;
private define FormatterFunctionMap = std::map::HashMap<typeid, ToFormatFunction>;
private FormatterFunctionMap toformat_functions;
private StringFunctionMap tostring_functions;
fn usz! printf(String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
return formatter.vprintf(format, args);
}
fn usz! printfn(String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
usz len = formatter.vprintf(format, args)!;
putchar('\n');
return len + 1;
}
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
{
Formatter formatter;
BufferData data = { .buffer = buffer };
formatter.init(&out_buffer_fn, &data);
usz size = formatter.vprintf(format, args)!;
return buffer[:data.written];
}
fn usz! File.printf(File file, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_fputchar_fn, &file);
return formatter.vprintf(format, args)!;
}
fn usz! File.printfn(File file, String format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_fputchar_fn, &file);
usz len = formatter.vprintf(format, args)!;
file.putc('\n')!;
file.flush();
return len + 1;
}
fn usz! Formatter.printf(Formatter* this, String format, args...)
{
return this.vprintf(format, args) @inline;
}
struct Formatter
{
@@ -48,7 +95,6 @@ bitstruct PrintFlags : uint
bool hash : 4;
bool uppercase : 5;
bool precision : 6;
bool adapt_exp : 7;
}
fn void Formatter.init(Formatter* this, OutputFn out_fn, void* data = null)
@@ -63,19 +109,19 @@ fn void Formatter.init(Formatter* this, OutputFn out_fn, void* data = null)
*/
macro void formatter_register_type($Type)
{
$if ($checks($Type a, a.to_format(&&Formatter {}))):
$if $checks($Type a, a.to_format(&&Formatter {})):
if (!toformat_functions.table.len)
{
toformat_functions.init(512);
toformat_functions.init(64);
}
toformat_functions.set($Type.typeid, (ToFormatFunction)&$Type.to_format);
$else:
$else
if (!tostring_functions.table.len)
{
tostring_functions.init(512);
tostring_functions.init(64);
}
tostring_functions.set($Type.typeid, (ToStringFunction)&$Type.to_string);
$endif;
$endif
}
@@ -83,20 +129,20 @@ static initialize @priority(101)
{
if (!toformat_functions.table.len)
{
toformat_functions.init(512);
toformat_functions.init(64);
}
if (!tostring_functions.table.len)
{
tostring_functions.init(512);
tostring_functions.init(64);
}
}
private fn void! Formatter.out(Formatter* this, char c)
fn void! Formatter.out(Formatter* this, char c) @private
{
this.out_fn(c, this.data)?;
this.out_fn(c, this.data)!;
}
macro bool! Formatter.print_with_function(Formatter* this, variant arg)
macro bool! Formatter.print_with_function(Formatter* this, any arg)
{
if (try to_format = toformat_functions.get(arg.type))
{
@@ -109,7 +155,7 @@ macro bool! Formatter.print_with_function(Formatter* this, variant arg)
this.width = old_width;
this.prec = old_prec;
}
to_format(arg.ptr, this)?;
to_format(arg.ptr, this)!;
return true;
}
if (try to_string = tostring_functions.get(arg.type))
@@ -125,116 +171,111 @@ macro bool! Formatter.print_with_function(Formatter* this, variant arg)
}
@pool()
{
this.out_substr(to_string(arg.ptr, mem::temp_allocator()))?;
this.out_substr(to_string(arg.ptr, mem::temp()))!;
return true;
};
}
return false;
}
private fn void! Formatter.out_str(Formatter* this, variant arg)
fn void! Formatter.out_str(Formatter* this, any arg) @private
{
switch (arg.type.kindof)
{
case TYPEID:
return this.out_substr("<typeid>");
return this.out_substr("typeid");
case VOID:
return this.out_substr("void");
case ANYERR:
case ANYFAULT:
case FAULT:
return this.out_substr((*(anyerr*)arg.ptr).nameof);
case VARIANT:
return this.out_substr("<variant>");
return this.out_substr((*(anyfault*)arg.ptr).nameof);
case ANY:
return this.out_str(*(any*)arg);
case ENUM:
if (this.print_with_function(arg)?) return;
return this.out_substr(arg.type.names[types::variant_to_int(arg, usz)!!]);
if (this.print_with_function(arg)!) return;
return this.out_substr(arg.type.names[types::any_to_int(arg, usz)!!]);
case STRUCT:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
return this.out_substr("<struct>");
case UNION:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
return this.out_substr("<union>");
case BITSTRUCT:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
return this.out_substr("<bitstruct>");
case FUNC:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
return this.out_substr("<function>");
case OPTIONAL:
unreachable();
case DISTINCT:
if (this.print_with_function(arg)?) return;
if (arg.type == String.typeid)
if (this.print_with_function(arg)!) return;
if (arg.type == DString.typeid)
{
return this.out_substr(((String*)arg).str());
return this.out_substr(((DString*)arg).str());
}
return this.out_str(variant { arg.ptr, arg.type.inner });
return this.out_str(any { arg.ptr, arg.type.inner });
case POINTER:
if (this.print_with_function(arg)?) return;
typeid inner = arg.type.inner;
if (inner.kindof == TypeKind.ARRAY && inner.inner == char.typeid)
{
char *ptr = *(char**)arg.ptr;
return this.out_substr(ptr[:inner.len]);
}
return this.ntoa_variant(arg, 16);
if (this.print_with_function(arg)!) return;
return this.ntoa_any(arg, 16);
case SIGNED_INT:
case UNSIGNED_INT:
return this.ntoa_variant(arg, 10);
return this.ntoa_any(arg, 10);
case FLOAT:
return this.ftoa(float_from_variant(arg));
return this.ftoa(float_from_any(arg)!!);
case ARRAY:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz len = arg.type.len;
// Pretend this is a char[]
// Pretend this is a String
void* ptr = (void*)arg.ptr;
this.out('[')?;
this.out('[')!;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")?;
this.out_str(variant { ptr, inner })?;
if (i != 0) this.out_substr(", ")!;
this.out_str(any { ptr, inner })!;
ptr += size;
}
return this.out(']');
case VECTOR:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz len = arg.type.len;
// Pretend this is a char[]
// Pretend this is a String
void* ptr = (void*)arg.ptr;
this.out_substr("[<")?;
this.out_substr("[<")!;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")?;
this.out_str(variant { ptr, inner })?;
if (i != 0) this.out_substr(", ")!;
this.out_str(any { ptr, inner })!;
ptr += size;
}
return this.out_substr(">]");
case SUBARRAY:
if (this.print_with_function(arg)?) return;
if (this.print_with_function(arg)!) return;
// this is SomeType[] so grab the "SomeType"
typeid inner = arg.type.inner;
if (inner == char.typeid)
{
return this.out_substr(*(char[]*)arg);
return this.out_substr(*(String*)arg);
}
usz size = inner.sizeof;
// Pretend this is a char[]
char[]* temp = (void*)arg.ptr;
// Pretend this is a String
String* temp = (void*)arg.ptr;
void* ptr = (void*)temp.ptr;
usz len = temp.len;
this.out('[')?;
this.out('[')!;
for (usz i = 0; i < len; i++)
{
if (i != 0) this.out_substr(", ")?;
this.out_str(variant { ptr, inner })?;
if (i != 0) this.out_substr(", ")!;
this.out_str(any { ptr, inner })!;
ptr += size;
}
this.out(']')?;
this.out(']')!;
case BOOL:
if (*(bool*)arg.ptr)
{
@@ -245,118 +286,49 @@ private fn void! Formatter.out_str(Formatter* this, variant arg)
return this.out_substr("false");
}
default:
if (this.print_with_function(arg)?) return;
switch (arg)
{
case Object:
arg.to_format(this)!;
return;
}
if (this.print_with_function(arg)!) return;
return this.out_substr("Invalid type");
}
}
fault FormattingFault
{
UNTERMINATED_FORMAT,
MISSING_ARG,
INVALID_WIDTH_ARG,
INVALID_FORMAT_TYPE,
}
private fn void! out_buffer_fn(char c, void *data)
fn void! out_buffer_fn(char c, void *data) @private
{
BufferData *buffer_data = data;
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED!;
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED?;
buffer_data.buffer[buffer_data.written++] = c;
}
private fn void! out_null_fn(char c @unused, void* data @unused)
fn void! out_null_fn(char c @unused, void* data @unused) @private
{
}
private fn void! out_putchar_fn(char c, void* data @unused)
fn void! out_putchar_fn(char c, void* data @unused) @private
{
libc::putchar(c);
}
private fn void! out_fputchar_fn(char c, void* data)
fn void! out_fputchar_fn(char c, void* data) @private
{
File* f = data;
f.putc(c)?;
f.putc(c)!;
}
private fn void! out_string_append_fn(char c, void* data)
{
String* s = data;
s.append_char(c);
}
fn usz! printf(char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
return formatter.vprintf(format, args);
}
fn usz! printfln(char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
usz len = formatter.vprintf(format, args)?;
putchar('\n');
return len + 1;
}
fn usz! String.printf(String* str, char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
return formatter.vprintf(format, args);
}
fn usz! String.printfln(String* str, char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_string_append_fn, str);
usz len = formatter.vprintf(format, args)?;
str.append('\n');
return len + 1;
}
private struct BufferData
struct BufferData @private
{
char[] buffer;
usz written;
}
fn char[]! bprintf(char[] buffer, char[] format, args...) @maydiscard
{
Formatter formatter;
BufferData data = { .buffer = buffer };
formatter.init(&out_buffer_fn, &data);
usz size = formatter.vprintf(format, args)?;
return buffer[:size];
}
fn usz! File.printf(File file, char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn, &file);
return formatter.vprintf(format, args)?;
}
fn usz! File.printfln(File file, char[] format, args...) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn, &file);
usz len = formatter.vprintf(format, args)?;
file.putc('\n')?;
file.flush();
return len + 1;
}
fn usz! Formatter.printf(Formatter* this, char[] format, args...)
{
return this.vprintf(format, args) @inline;
}
fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
fn usz! Formatter.vprintf(Formatter* this, String format, any[] anys)
{
if (!this.out_fn)
{
@@ -372,15 +344,15 @@ fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
if (c != '%')
{
// no
this.out(c)?;
this.out(c)!;
continue;
}
i++;
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
c = format[i];
if (c == '%')
{
this.out(c)?;
this.out(c)!;
continue;
}
// evaluate flags
@@ -396,11 +368,11 @@ fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
case '#': this.flags.hash = true;
default: break FLAG_EVAL;
}
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
c = format[i];
}
// evaluate width field
int w = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
int w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
c = format[i];
if (w < 0)
{
@@ -413,16 +385,16 @@ fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
if (c == '.')
{
this.flags.precision = true;
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
int prec = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
int prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
this.prec = prec < 0 ? 0 : prec;
c = format[i];
}
// evaluate specifier
uint base = 0;
if (variant_index >= variants.len) return PrintFault.MISSING_ARG!;
variant current = variants[variant_index++];
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
any current = anys[variant_index++];
switch (c)
{
case 'd':
@@ -443,37 +415,42 @@ fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
nextcase;
case 'b' :
base = 2;
case 'A':
this.flags.uppercase = true;
nextcase;
case 'a':
this.atoa(float_from_any(current)!!)!;
continue;
case 'F' :
this.flags.uppercase = true;
nextcase;
case 'f':
this.ftoa(float_from_variant(current))?;
this.ftoa(float_from_any(current)!!)!;
continue;
case 'E':
this.flags.uppercase = true;
nextcase;
case 'e':
this.etoa(float_from_variant(current))?;
this.etoa(float_from_any(current)!!)!;
continue;
case 'G':
this.flags.uppercase = true;
nextcase;
case 'g':
this.flags.adapt_exp = true;
this.etoa(float_from_variant(current))?;
this.gtoa(float_from_any(current)!!)!;
continue;
case 'c':
this.out_char(current)?;
this.out_char(current)!;
continue;
case 's':
this.out_str(current)?;
this.out_str(current)!;
continue;
case 'p':
this.flags.zeropad = true;
this.flags.hash = true;
base = 16;
default:
return PrintFault.INVALID_FORMAT_STRING!;
return PrintFault.INVALID_FORMAT_STRING?;
}
if (base != 10)
{
@@ -484,9 +461,9 @@ fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
if (this.flags.precision) this.flags.zeropad = false;
bool is_neg;
uint128 v = int_from_variant(current, &is_neg);
uint128 v = int_from_any(current, &is_neg)!!;
this.ntoa(v, is_neg, base)?;
this.ntoa(v, is_neg, base)!;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
@@ -495,3 +472,8 @@ fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
return this.idx;
}
def StringFunctionMap @private = HashMap<typeid, ToStringFunction>;
def FormatterFunctionMap @private = HashMap<typeid, ToFormatFunction>;
FormatterFunctionMap toformat_functions @private;
StringFunctionMap tostring_functions @private;

205
lib/std/io/io_stream.c3 Normal file
View File

@@ -0,0 +1,205 @@
module std::io;
def CloseStreamFn = fn void!(Stream*);
def FlushStreamFn = fn void!(Stream*);
def SeekStreamFn = fn usz!(Stream*, isz offset, Seek seek);
def LenStreamFn = fn usz(Stream*);
def AvailableStreamFn = fn usz(Stream*);
def ReadStreamFn = fn usz!(Stream*, char[] bytes);
def ReadFromStreamFn = fn usz!(Stream*, Stream*);
def ReadByteStreamFn = fn char!(Stream*);
def PushbackByteStreamFn = fn void!(Stream*);
def WriteStreamFn = fn usz!(Stream*, char[] bytes);
def WriteToStreamFn = fn usz!(Stream*, Stream* out);
def WriteByteStreamFn = fn void!(Stream*, char c);
def DestroyStreamFn = fn void!(Stream*);
struct StreamInterface
{
CloseStreamFn close_fn;
FlushStreamFn flush_fn;
SeekStreamFn seek_fn;
LenStreamFn len_fn;
AvailableStreamFn available_fn;
ReadStreamFn read_fn;
ReadFromStreamFn read_stream_fn;
ReadByteStreamFn read_byte_fn;
PushbackByteStreamFn pushback_byte_fn;
WriteStreamFn write_fn;
WriteToStreamFn write_stream_fn;
WriteByteStreamFn write_byte_fn;
DestroyStreamFn destroy_fn;
}
struct Stream
{
StreamInterface *fns;
void* data;
}
fn bool Stream.supports_seek(Stream* s) @inline => (bool)s.fns.seek_fn;
fn bool Stream.supports_available(Stream* s) @inline => s.fns.available_fn || s.fns.seek_fn;
fn bool Stream.supports_len(Stream* s) @inline => s.fns.len_fn || s.fns.seek_fn;
fn bool Stream.supports_read(Stream* s) @inline => s.fns.read_fn || s.fns.read_byte_fn;
fn bool Stream.supports_read_from(Stream* s) @inline => (bool)s.fns.read_stream_fn;
fn bool Stream.supports_write_to(Stream* s) @inline => (bool)s.fns.write_stream_fn;
fn bool Stream.supports_pushback_byte(Stream* s) @inline => s.fns.pushback_byte_fn || s.fns.seek_fn;
fn bool Stream.supports_write(Stream* s) @inline => s.fns.write_fn || s.fns.write_byte_fn;
fn void! Stream.destroy(Stream* s) @inline @maydiscard
{
if (s.fns.destroy_fn) return s.fns.destroy_fn(s);
return s.close();
}
fn void! Stream.close(Stream* s) @inline @maydiscard
{
if (CloseStreamFn func = s.fns.close_fn) return func(s);
}
fn usz! Stream.seek(Stream* s, isz offset, Seek seek) @inline
{
if (SeekStreamFn func = s.fns.seek_fn) return func(s, offset, seek);
return IoError.NOT_SEEKABLE?;
}
fn usz! Stream.available(Stream* s) @inline
{
if (AvailableStreamFn func = s.fns.available_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
usz curr = func(s, 0, Seek.CURSOR)!;
usz len = func(s, 0, Seek.END)!;
func(s, curr, Seek.SET)!;
return len - curr;
}
return IoError.NOT_SEEKABLE?;
}
fn usz! Stream.read(Stream* s, char[] buffer)
{
if (ReadStreamFn func = s.fns.read_fn) return func(s, buffer);
if (ReadByteStreamFn func = s.fns.read_byte_fn)
{
usz len = 0;
foreach (&cptr : buffer)
{
char! c = func(s);
if (catch err = c)
{
case IoError.EOF: return len;
default: return err?;
}
*cptr = c;
len++;
}
}
return IoError.UNSUPPORTED_OPERATION?;
}
fn char! Stream.read_byte(Stream* s) @inline
{
if (ReadByteStreamFn func = s.fns.read_byte_fn) return func(s);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.write(Stream* s, char[] bytes) @inline
{
if (WriteStreamFn func = s.fns.write_fn) return func(s, bytes);
if (WriteByteStreamFn func = s.fns.write_byte_fn)
{
foreach (c : bytes) func(s, c)!;
return bytes.len;
}
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! Stream.write_byte(Stream* s, char b) @inline
{
if (WriteByteStreamFn func = s.fns.write_byte_fn) return func(s, b);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.write_to(Stream* s, Stream* to) @inline
{
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, to);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.read_from(Stream* s, Stream* from) @inline
{
if (ReadFromStreamFn func = s.fns.read_stream_fn) return func(s, from);
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! Stream.flush(Stream* s) @inline @maydiscard
{
if (FlushStreamFn func = s.fns.flush_fn) return func(s);
return IoError.UNSUPPORTED_OPERATION?;
}
fn usz! Stream.len(Stream* s) @inline
{
if (LenStreamFn func = s.fns.len_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
usz curr = func(s, 0, Seek.CURSOR)!;
usz len = func(s, 0, Seek.END)!;
func(s, curr, Seek.SET)!;
return len;
}
return IoError.NOT_SEEKABLE?;
}
fn void! Stream.pushback_byte(Stream* s) @inline
{
if (PushbackByteStreamFn func = s.fns.pushback_byte_fn) return func(s);
if (SeekStreamFn func = s.fns.seek_fn)
{
func(s, -1, CURSOR)!;
return;
}
return IoError.UNSUPPORTED_OPERATION?;
}
fn void! Stream.write_string(Stream* s, String str) @inline => (void)(s.write((char[])str)!);
fn usz! Stream.copy_to(Stream* s, Stream* dst, char[] buffer = {})
{
if (buffer.len) return copy_through_buffer(s, dst, buffer);
if (WriteToStreamFn func = s.fns.write_stream_fn) return func(s, dst);
if (ReadFromStreamFn func = dst.fns.read_stream_fn) return func(dst, s);
$switch (env::MEMORY_ENV)
$case NORMAL:
@pool()
{
return copy_through_buffer(s, dst, tmalloc(char, 4096));
};
$case SMALL:
@pool()
{
return copy_through_buffer(s, dst, tmalloc(char, 1024));
};
$case TINY:
$case NONE:
return copy_through_buffer(s, dst, &&(char[256]{}));
$endswitch
}
macro usz! copy_through_buffer(Stream* s, Stream* dst, char[] buffer) @local
{
usz total_copied;
while (true)
{
usz! len = s.read(buffer);
if (catch err = len)
{
case IoError.EOF: return total_copied;
default: return err?;
}
if (!len) return total_copied;
usz written = dst.write(buffer[:len])!;
total_copied += len;
if (written != len) return IoError.INCOMPLETE_WRITE?;
}
}

44
lib/std/io/os/chdir.c3 Normal file
View File

@@ -0,0 +1,44 @@
module std::io::os;
import libc;
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
macro void! native_chdir(Path p)
{
if (posix::chdir(p.as_zstr()))
{
switch (libc::errno())
{
case errno::EACCES: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::ENOTDIR: return IoError.FILE_NOT_DIR?;
case errno::ENOENT: return IoError.FILE_NOT_FOUND?;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
default: return IoError.GENERAL_ERROR?;
}
}
}
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
macro void! native_chdir(Path path)
{
@pool()
{
// TODO improve with better error handling.
if (win32::win32_SetCurrentDirectoryW(path.as_str().to_temp_utf16()!!)) return;
};
return IoError.GENERAL_ERROR?;
}
$default:
fn void! native_chdir(Path path)
{
unreachable("'getcwd' not available");
}
$endswitch

187
lib/std/io/os/file.c3 Normal file
View File

@@ -0,0 +1,187 @@
module std::io::os;
import libc;
def FopenFn = fn void*!(String, String);
def FreopenFn = fn void*!(void*, String, String);
def FcloseFn = fn void!(void*);
def FseekFn = fn void!(void*, isz, Seek);
def FtellFn = fn usz!(void*);
def FwriteFn = fn usz!(void*, char[] buffer);
def FreadFn = fn usz!(void*, char[] buffer);
$if !$defined(native_fopen_fn):
FopenFn native_fopen_fn @weak;
$endif
$if !$defined(native_fclose_fn):
FcloseFn native_fclose_fn @weak;
$endif
$if !$defined(native_freopen_fn):
FreopenFn native_freopen_fn @weak;
$endif
$if !$defined(native_fseek_fn):
FseekFn native_fseek_fn @weak;
$endif
$if !$defined(native_ftell_fn):
FtellFn native_ftell_fn @weak;
$endif
$if !$defined(native_fwrite_fn):
FwriteFn native_fwrite_fn @weak;
$endif
$if !$defined(native_fread_fn):
FreadFn native_fread_fn @weak;
$endif
/**
* @require mode.len > 0
* @require filename.len > 0
**/
fn void*! native_fopen(String filename, String mode) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fopen_fn) return native_fopen_fn(filename, mode);
unreachable("Tried to call fopen without support.");
$else
@pool()
{
$if env::os_is_win32():
void* file = (CFile)_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())!;
$else
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
$endif
return file ?: file_open_errno()?;
};
$endif
}
/**
* @require mode.len > 0
* @require filename.len > 0
**/
fn void*! native_freopen(void* file, String filename, String mode) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_freopen_fn) return native_freopen_fn(file, filename, mode);
unreachable("Tried to call freopen without support.");
$else
@pool()
{
$if env::os_is_win32():
file = _wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)!;
$else
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
$endif
return file ?: file_open_errno()?;
};
$endif
}
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode);
unreachable("Tried to call fseek without support.");
$else
$if env::os_is_win32():
bool success = _fseeki64(file, (long)offset, (int)seek_mode) == 0;
$else
bool success = libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode) == 0;
$endif
if (!success) return file_seek_errno()?;
$endif
}
fn usz! native_ftell(CFile file) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_ftell_fn) return native_ftell_fn(file);
unreachable("Tried to call ftell without support.");
$else
$if env::os_is_win32():
long index = _ftelli64(file);
return index >= 0 ? index : file_seek_errno()?;
$else
SeekIndex index = libc::ftell(file);
return index >= 0 ? index : file_seek_errno()?;
$endif
$endif
}
fn usz! native_fwrite(CFile file, char[] buffer) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fwrite_fn) return native_fwrite_fn(file, buffer);
unreachable("Tried to call fwrite without support.");
$else
return libc::fwrite(buffer.ptr, 1, buffer.len, file);
$endif
}
fn usz! native_fread(CFile file, char[] buffer) @inline
{
$if !env::COMPILER_LIBC_AVAILABLE:
if (native_fread_fn) return native_fread_fn(file, buffer);
unreachable("Tried to call fread without support.");
$else
return libc::fread(buffer.ptr, 1, buffer.len, file);
$endif
}
macro anyfault file_open_errno() @local
{
switch (libc::errno())
{
case errno::EACCES: return IoError.NO_PERMISSION;
case errno::EDQUOT: return IoError.OUT_OF_SPACE;
case errno::EBADF: return IoError.FILE_NOT_VALID;
case errno::EEXIST: return IoError.ALREADY_EXISTS;
case errno::EINTR: return IoError.INTERRUPTED;
case errno::EFAULT: return IoError.GENERAL_ERROR;
case errno::EISDIR: return IoError.FILE_IS_DIR;
case errno::ELOOP: return IoError.SYMLINK_FAILED;
case errno::EMFILE: return IoError.TOO_MANY_DESCRIPTORS;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG;
case errno::ENFILE: return IoError.OUT_OF_SPACE;
case errno::ENOTDIR: return IoError.FILE_NOT_DIR;
case errno::ENOENT: return IoError.FILE_NOT_FOUND;
case errno::ENOSPC: return IoError.OUT_OF_SPACE;
case errno::ENXIO: return IoError.FILE_NOT_FOUND;
case errno::EOVERFLOW: return IoError.OVERFLOW;
case errno::EROFS: return IoError.READ_ONLY;
case errno::EOPNOTSUPP: return IoError.UNSUPPORTED_OPERATION;
case errno::EIO: return IoError.INCOMPLETE_WRITE;
case errno::EWOULDBLOCK: return IoError.WOULD_BLOCK;
default: return IoError.UNKNOWN_ERROR;
}
}
macro anyfault file_seek_errno() @local
{
switch (libc::errno())
{
case errno::ESPIPE: return IoError.FILE_IS_PIPE;
case errno::EPIPE: return IoError.FILE_IS_PIPE;
case errno::EOVERFLOW: return IoError.OVERFLOW;
case errno::ENXIO: return IoError.FILE_NOT_FOUND;
case errno::ENOSPC: return IoError.OUT_OF_SPACE;
case errno::EIO: return IoError.INCOMPLETE_WRITE;
case errno::EINVAL: return IoError.INVALID_POSITION;
case errno::EINTR: return IoError.INTERRUPTED;
case errno::EFBIG: return IoError.OUT_OF_SPACE;
case errno::EBADF: return IoError.FILE_NOT_VALID;
case errno::EAGAIN: return IoError.WOULD_BLOCK;
default: return IoError.UNKNOWN_ERROR;
}
}
// Win functions
$if env::os_is_win32():
extern fn void* _wfopen(Char16*, Char16*) @local;
extern fn void* _wfreopen(Char16*, Char16*, CFile) @local;
extern fn int _fseeki64(CFile, long, int) @local;
extern fn long _ftelli64(CFile) @local;
$endif
$if env::os_is_posix():
extern fn CInt access(ZString path, CInt mode);
$endif

View File

@@ -0,0 +1,101 @@
module std::io::file::os;
import libc;
$if env::os_is_darwin():
struct DarwinTimespec @private
{
long tv_sec;
long tv_nsec;
}
struct Darwin64Stat @private
{
int st_dev;
ushort st_mode;
ushort st_nlink;
ulong st_ino;
uint st_uid;
uint st_gid;
int st_rdev;
DarwinTimespec st_atimespec; // time of last access
DarwinTimespec st_mtimespec; // time of last data modification
DarwinTimespec st_ctimespec; // time of last status change
DarwinTimespec st_birthtimespec; // time of file creation(birth)
long st_size;
long st_blocks;
int st_blocksize;
uint st_flags;
uint st_gen;
int st_lspare;
long[2] st_qspare;
}
extern fn int _stat(ZString str, Darwin64Stat* stat) @extern("stat64");
const S_IFMT = 0o170000; // type of file mask
const S_IFIFO = 0o010000; // named pipe (fifo)
const S_IFCHR = 0o020000; // character special
const S_IFDIR = 0o040000; // directory
const S_IFBLK = 0o060000; // block special
const S_IFREG = 0o100000; // regular
const S_IFLNK = 0o120000; // symbolic link
const S_IFSOCK = 0o140000; // socket
fn usz! native_file_size(String path)
{
Darwin64Stat stat;
read_stat(&stat, path)!;
return stat.st_size;
}
fn bool native_file_or_dir_exists(String path)
{
Darwin64Stat stat;
return @ok(read_stat(&stat, path));
}
fn bool native_is_file(String path)
{
Darwin64Stat stat;
return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFREG;
}
fn bool native_is_dir(String path)
{
Darwin64Stat stat;
return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFDIR;
}
fn void! read_stat(Darwin64Stat* stat, String path) @local
{
@pool()
{
int res = _stat(path.zstr_tcopy(), stat);
if (res != 0)
{
switch (libc::errno())
{
case errno::EBADF:
return IoError.FILE_NOT_VALID?;
case errno::EFAULT:
unreachable("Invalid stat");
case errno::EIO:
return IoError.GENERAL_ERROR?;
case errno::EACCES:
return IoError.NO_PERMISSION?;
case errno::ELOOP:
return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG:
return IoError.NAME_TOO_LONG?;
case errno::ENOENT:
return IoError.FILE_NOT_FOUND?;
case errno::ENOTDIR:
return IoError.FILE_NOT_DIR?;
case errno::EOVERFLOW:
return IoError.GENERAL_ERROR?;
default:
return IoError.UNKNOWN_ERROR?;
}
}
};
}
$endif

View File

@@ -0,0 +1,181 @@
module std::io::file::os;
// native_temp_directory, for non Win32
$if !env::os_is_win32():
fn Path! native_temp_directory(Allocator* using = mem::heap())
{
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
{
String tmpdir = env::get_var(env) ?? "";
if (tmpdir) return path::new(tmpdir, using);
}
return path::new("/tmp", using);
}
$if env::COMPILER_LIBC_AVAILABLE:
extern fn void* opendir(ZString);
extern fn void closedir(void*);
extern fn int remove(ZString);
const DT_UNKNOWN = 0;
const DT_FIFO = 1;
const DT_CHR = 2;
const DT_DIR = 4;
const DT_BLK = 6;
const DT_REG = 8;
const DT_LNK = 10;
const DT_SOCK = 12;
const DT_WHT = 14;
fn PathList! native_readdir(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* using)
{
PathList list;
list.init(.using = using);
void* directory = opendir(dir.as_str() ? dir.as_zstr() : (ZString)".");
defer if (directory) closedir(directory);
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
NativeDirentry* entry;
while ((entry = readdir(directory)))
{
String name = ((ZString)&entry.name).as_str();
if (!name || name == "." || name == "..") continue;
if (entry.type == DT_LNK && no_symlinks) continue;
if (entry.type == DT_DIR && no_dirs) continue;
Path path = path::new(name.copy(using), using)!!;
list.append(path);
}
return list;
}
/**
* @require dir.as_str()
**/
fn void! native_rmtree(Path dir)
{
void* directory = opendir(dir.as_zstr());
defer if (directory) closedir(directory);
if (!directory) return path::is_dir(dir) ? IoError.CANNOT_READ_DIR? : IoError.FILE_NOT_DIR?;
NativeDirentry* entry;
while ((entry = readdir(directory)))
{
@pool()
{
String name = ((ZString)&entry.name).as_str();
if (!name || name == "." || name == "..") continue;
Path new_path = dir.tappend(name)!;
if (entry.type == DT_DIR)
{
native_rmtree(new_path)!;
continue;
}
if (remove(new_path.as_zstr()))
{
// TODO improve
return IoError.GENERAL_ERROR?;
}
};
}
os::native_rmdir(dir)!;
}
$endif
$endif
$if !env::os_is_darwin() && !env::os_is_win32():
fn usz! native_file_size(String path)
{
File f = file::open(path, "r")!;
defer (void)f.close();
return f.seek(0, Seek.END)!;
}
$if env::os_is_posix() && env::COMPILER_LIBC_AVAILABLE:
fn bool native_file_or_dir_exists(String path)
{
@pool()
{
return os::access(path.zstr_tcopy(), 0 /* F_OK */) != -1;
};
}
fn bool native_is_file(String path)
{
File! f = file::open(path, "r");
defer (void)f.close();
return @ok(f);
}
fn bool native_is_dir(String path)
{
return native_file_or_dir_exists(path) && !native_is_file(path);
}
$else
fn bool native_file_or_dir_exists(String path)
{
unreachable("Tried to call file_or_dir_exists without support.");
}
fn bool native_is_dir(String path)
{
unreachable("Tried to call is_dir without support.");
}
fn bool native_is_file(String path)
{
unreachable("Tried to call is_file without support.");
}
$endif
$endif
$switch (env::OS_TYPE)
$case IOS:
$case MACOS:
$case TVOS:
$case WATCHOS:
$if env::ARCH_TYPE == X86_64:
extern fn NativeDirentry* readdir(void*) @extern("readdir$INODE64");
$else
extern fn NativeDirentry* readdir(void*) @extern("readdir");
$endif
struct NativeDirentry
{
usz ino;
usz seekoff;
ushort reclen;
ushort namelen;
char type;
char[1024] name;
}
$case LINUX:
extern fn NativeDirentry* readdir(void*);
struct NativeDirentry
{
usz ino;
isz seekoff;
ushort reclen;
char type;
char[*] name;
}
$default:
// Fix this as we go along.
extern fn NativeDirentry* readdir(void*);
struct NativeDirentry
{
usz ino;
isz seekoff;
ushort reclen;
char type;
char[*] name;
}
$endswitch

View File

@@ -0,0 +1,113 @@
module std::io::file::os;
import std::os::win32;
$if env::os_is_win32():
const Win32_DWORD FILE_ATTRIBUTE_READONLY = 0x01;
const Win32_DWORD FILE_ATTRIBUTE_HIDDEN = 0x02;
const Win32_DWORD FILE_ATTRIBUTE_SYSTEM = 0x04;
const Win32_DWORD FILE_ATTRIBUTE_DIRECTORY = 0x10;
const Win32_DWORD FILE_ATTRIBUTE_ARCHIVE = 0x20;
const Win32_DWORD FILE_ATTRIBUTE_DEVICE = 0x40;
const Win32_DWORD FILE_ATTRIBUTE_NORMAL = 0x80;
const Win32_DWORD FILE_ATTRIBUTE_TEMPORARY = 0x100;
const Win32_DWORD FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
const Win32_DWORD FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
const Win32_DWORD FILE_ATTRIBUTE_COMPRESSED = 0x800;
const Win32_DWORD FILE_ATTRIBUTE_OFFLINE = 0x1000;
const Win32_DWORD FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
const Win32_DWORD FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
const Win32_DWORD FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
const Win32_DWORD FILE_ATTRIBUTE_VIRTUAL = 0x10000;
const Win32_DWORD FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
const Win32_DWORD FILE_ATTRIBUTE_EA = 0x40000;
const Win32_DWORD FILE_ATTRIBUTE_PINNED = 0x80000;
const Win32_DWORD FILE_ATTRIBUTE_UNPINNED = 0x100000;
const Win32_DWORD FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
const Win32_DWORD FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
fn usz! native_file_size(String path)
{
@pool()
{
Char16[] path16 = path.to_temp_utf16()!;
Win32_FILE_ATTRIBUTE_DATA data;
win32::win32_GetFileAttributesExW(path16, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
Win32_LARGE_INTEGER size;
size.lowPart = data.nFileSizeLow;
size.highPart = data.nFileSizeHigh;
return (usz)size.quadPart;
};
}
fn bool native_file_or_dir_exists(String path)
{
@pool()
{
return (bool)win32::win32_PathFileExistsW(path.to_temp_utf16()) ?? false;
};
}
fn bool native_is_file(String path)
{
File! f = file::open(path, "r");
defer (void)f.close();
return @ok(f);
}
fn bool native_is_dir(String path)
{
return native_file_or_dir_exists(path) && !native_is_file(path);
}
fn void! native_rmtree(Path path)
{
Win32_WIN32_FIND_DATAW find_data;
String s = path.as_str().tconcat("\\*");
Win32_HANDLE find = win32::win32_FindFirstFileW(s.to_utf16(mem::temp()), &find_data)!;
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
defer win32::win32_FindClose(find);
do
{
String filename = string::from_zutf16(&find_data.cFileName, mem::temp())!;
if (filename == "." || filename == "..") continue;
Path file_path = path.tappend(filename)!;
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
native_rmtree(file_path)!;
}
else
{
win32::win32_DeleteFileW(file_path.as_str().to_utf16(mem::temp()));
}
} while (win32::win32_FindNextFileW(find, &find_data) != 0);
os::native_rmdir(path)!;
}
fn Path! native_temp_directory(Allocator* using = mem::heap())
{
@stack_mem(256; Allocator* mem)
{
Win32_DWORD len = win32::win32_GetTempPathW(0, null);
if (!len) return IoError.GENERAL_ERROR?;
Char16[] buff = malloc(Char16, len + 1, .using = mem);
if (!win32::win32_GetTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
return path::new(string::from_utf16(buff[:len], .using = mem), using);
};
}
/*
}else if(method == file_size_methods::get_attributes){
WIN32_FILE_ATTRIBUTE_DATA file_attr_data;
if(GetFileAttributesEx(path, GetFileExInfoStandard, &file_attr_data)){
file_size.LowPart = file_attr_data.nFileSizeLow;
file_size.HighPart = file_attr_data.nFileSizeHigh;
}
}
*/
$endif

View File

@@ -1,47 +1,51 @@
module std::io::os;
import libc;
$if (env::OS_TYPE == OsType.WIN32):
$switch
extern fn Char16* _wgetcwd(Char16* buffer, int maxlen);
extern fn usz wcslen(Char16* str);
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
macro char[]! getcwd(Allocator* allocator = mem::default_allocator())
macro String! getcwd(Allocator* using = mem::heap())
{
const DEFAULT_BUFFER = 256;
Char16[DEFAULT_BUFFER] buffer;
Char16 *res = _wgetcwd(&buffer, DEFAULT_BUFFER);
Char16 *res = win32::_wgetcwd(&buffer, DEFAULT_BUFFER);
bool free = false;
defer if (free) libc::free(res);
if (!res)
{
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR!;
res = _wgetcwd(null, 0);
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
res = win32::_wgetcwd(null, 0);
free = true;
}
Char16[] str16 = res[:wcslen(res)];
return str::utf16to8(str16, allocator);
Char16[] str16 = res[:win32::wcslen(res)];
return string::from_utf16(str16, using);
}
$else:
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
extern fn ZString _getcwd(char* pwd, usz len) @extname("getcwd");
macro char[]! getcwd(Allocator* allocator = mem::default_allocator())
macro String! getcwd(Allocator* using = mem::heap())
{
const usz DEFAULT_BUFFER = 256;
char[DEFAULT_BUFFER] buffer;
ZString res = _getcwd(&buffer, DEFAULT_BUFFER);
ZString res = posix::getcwd(&buffer, DEFAULT_BUFFER);
bool free = false;
if (!res)
{
// Improve error
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR!;
res = _getcwd(null, 0);
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
res = posix::getcwd(null, 0);
free = true;
}
defer if (free) libc::free((void*)res);
char[] copy = str::copyz(res.as_str(), allocator);
return copy;
return res.copy(using);
}
$endif;
$default:
fn String! getcwd(Allocator* using = mem::heap())
{
unreachable("'getcwd' not available");
}
$endswitch

63
lib/std/io/os/mkdir.c3 Normal file
View File

@@ -0,0 +1,63 @@
module std::io::os;
import libc;
import std::io::path;
import std::os::win32;
import std::os::posix;
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
macro bool! native_mkdir(Path path, MkdirPermissions permissions)
{
if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true;
switch (libc::errno())
{
case errno::EACCES:
case errno::EPERM:
case errno::EROFS:
case errno::EFAULT: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::EDQUOT:
case errno::ENOSPC: return IoError.OUT_OF_SPACE?;
case errno::EISDIR:
case errno::EEXIST: return false;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
case errno::ENOTDIR: return IoError.FILE_NOT_FOUND?;
default: return IoError.GENERAL_ERROR?;
}
}
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
macro bool! native_mkdir(Path path, MkdirPermissions permissions)
{
@pool()
{
// TODO security attributes
if (win32::win32_CreateDirectoryW(path.as_str().to_temp_utf16()!!, null)) return true;
switch (win32::win32_GetLastError())
{
case win32::ERROR_ACCESS_DENIED:
return IoError.NO_PERMISSION?;
case win32::ERROR_DISK_FULL:
return IoError.OUT_OF_SPACE?;
case win32::ERROR_ALREADY_EXISTS:
return false;
case win32::ERROR_PATH_NOT_FOUND:
return IoError.FILE_NOT_FOUND?;
default:
return IoError.GENERAL_ERROR?;
}
};
}
$default:
fn bool! native_mkdir(Path path, MkdirPermissions permissions)
{
unreachable("'mkdir' not available");
return false;
}
$endswitch

61
lib/std/io/os/rmdir.c3 Normal file
View File

@@ -0,0 +1,61 @@
module std::io::os;
import libc;
import std::io::path;
import std::os::win32;
import std::os::posix;
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_posix():
macro bool! native_rmdir(Path path)
{
if (!posix::rmdir(path.as_zstr())) return true;
switch (libc::errno())
{
case errno::EBUSY: return IoError.BUSY?;
case errno::EACCES:
case errno::EPERM:
case errno::EROFS:
case errno::EFAULT: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::ENOTDIR:
case errno::ENOENT: return false;
case errno::ENOTEMPTY: return IoError.DIR_NOT_EMPTY?;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
default: return IoError.GENERAL_ERROR?;
}
}
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
macro bool! native_rmdir(Path path)
{
@pool()
{
if (win32::win32_RemoveDirectoryW(path.as_str().to_temp_utf16()!!)) return true;
switch (win32::win32_GetLastError())
{
case win32::ERROR_ACCESS_DENIED:
return IoError.NO_PERMISSION?;
case win32::ERROR_CURRENT_DIRECTORY:
return IoError.BUSY?;
case win32::ERROR_DIR_NOT_EMPTY:
return IoError.DIR_NOT_EMPTY?;
case win32::ERROR_DIRECTORY:
case win32::ERROR_PATH_NOT_FOUND:
return false;
default:
return IoError.GENERAL_ERROR?;
}
};
}
$default:
fn bool! native_rmdir(Path path)
{
unreachable("'rmdir' not available");
}
$endswitch

413
lib/std/io/path.c3 Normal file
View File

@@ -0,0 +1,413 @@
module std::io::path;
import std::collections::list;
const PathEnv DEFAULT_PATH_ENV = env::os_is_win32() ? PathEnv.WIN32 : PathEnv.POSIX;
const char PREFERRED_SEPARATOR_WIN32 = '\\';
const char PREFERRED_SEPARATOR_POSIX = '/';
const char PREFERRED_SEPARATOR = env::os_is_win32() ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
def PathList = List<Path>;
fault PathResult
{
INVALID_PATH,
NO_PARENT,
}
struct Path
{
String path_string;
PathEnv env;
}
enum PathEnv
{
WIN32,
POSIX
}
fn Path! getcwd(Allocator* using = mem::heap())
{
@stack_mem(256; Allocator* mem)
{
return new(os::getcwd(mem), using);
};
}
fn bool is_dir(Path path) => os::native_is_dir(path.as_str());
fn bool is_file(Path path) => os::native_is_file(path.as_str());
fn usz! file_size(Path path) => os::native_file_size(path.as_str());
fn bool exists(Path path) => os::native_file_or_dir_exists(path.as_str());
fn Path! tgetcwd() => getcwd(mem::temp()) @inline;
fn void! chdir(Path path) => os::native_chdir(path) @inline;
fn Path! temp_directory(Allocator* using = mem::heap()) => os::native_temp_directory(using);
macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV)
{
return c == '/' || (c == '\\' && path_env == PathEnv.WIN32);
}
macro bool is_posix_separator(char c)
{
return c == '/' || c == '\\';
}
macro bool is_win32_separator(char c)
{
return c == '/' || c == '\\';
}
fn Path[]! ls(Path path)
{
unreachable();
}
enum MkdirPermissions
{
NORMAL,
USER_ONLY,
USER_AND_ADMIN
}
fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
if (is_dir(path)) return false;
if (exists(path)) return IoError.FILE_NOT_DIR?;
if (recursive)
{
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
}
if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?;
return os::native_mkdir(path, permissions);
}
fn bool! rmdir(Path path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
return os::native_rmdir(path);
}
fn void! rmtree(Path path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
$if $defined(os::native_rmtree):
return os::native_rmtree(path);
$else
assert(false, "rmtree is not available");
$endif
}
fn Path! new(String path, Allocator* using = mem::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
{
return { normalize(path.copy(using), path_env), path_env };
}
fn Path! new_windows(String path, Allocator* using = mem::heap())
{
return new(path, using, WIN32);
}
fn Path! new_posix(String path, Allocator* using = mem::heap())
{
return new(path, using, POSIX);
}
fn bool Path.equals(Path p1, Path p2)
{
return p1.env == p2.env && p1.path_string == p2.path_string;
}
/**
* Append the string to the current path.
*
* @param [in] path
* @param [in] filename
* @ensure return.path_string.len >= path.path_string.len
**/
fn Path! Path.append(Path path, String filename, Allocator* using = mem::heap())
{
if (!path.path_string.len) return new(filename, using, path.env)!;
assert(!is_separator(path.path_string[^1], path.env));
@stack_mem(256; Allocator* mem)
{
DString dstr = dstring::new_with_capacity(path.path_string.len + 1 + filename.len, .using = mem);
dstr.append(path.path_string);
dstr.append(PREFERRED_SEPARATOR);
dstr.append(filename);
return { normalize(dstr.copy_str(using), path.env), path.env };
};
}
fn Path! Path.tappend(Path path, String filename) => path.append(filename, mem::temp());
fn usz Path.start_of_base_name(Path path) @local
{
String path_str = path.path_string;
if (!path_str.len) return 0;
if (path.env == PathEnv.WIN32)
{
return path_str.rindex_of(`\`) + 1 ?? volume_name_len(path_str, path.env)!!;
}
return path_str.rindex_of("/") + 1 ?? 0;
}
fn String Path.basename(Path path)
{
usz basename_start = path.start_of_base_name();
String path_str = path.path_string;
if (basename_start == path_str.len) return "";
return path_str[basename_start..];
}
fn String Path.dirname(Path path)
{
usz basename_start = path.start_of_base_name();
String path_str = path.path_string;
if (basename_start == 0) return "";
usz start = volume_name_len(path_str, path.env)!!;
if (basename_start <= start + 1) return path_str[:basename_start];
return path_str[:basename_start - 1];
}
fn String! Path.extension(Path path)
{
String basename = path.basename();
usz index = basename.rindex_of(".")!;
// Plain ".foo" does not have an
if (index == 0) return SearchResult.MISSING?;
if (index == basename.len) return "";
return basename[index + 1..];
}
fn String Path.volume_name(Path path)
{
usz len = volume_name_len(path.as_str(), path.env)!!;
if (!len) return "";
return path.path_string[:len];
}
fn usz! volume_name_len(String path, PathEnv path_env) @local
{
usz len = path.len;
if (len < 2 || path_env != PathEnv.WIN32) return 0;
switch (path[0])
{
case '\\':
// "\\" paths.. must be longer than 2
if (len == 2) return 0;
int count = 1;
while (count < len && path[count] == '\\') count++;
// Not 2 => folded paths
if (count != 2) return 0;
// Check that we have a name followed by '/'
for (usz i = 2; i < len; i++)
{
char c = path[i];
if (is_win32_separator(c)) return i;
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?;
}
return PathResult.INVALID_PATH?;
case 'A'..'Z':
case 'a'..'z':
return path[1] == ':' ? 2 : 0;
default:
return 0;
}
}
fn Path! Path.parent(Path path)
{
if (path.path_string.len == 1 && is_separator(path.path_string[0], path.env)) return PathResult.NO_PARENT?;
foreach_r(i, c : path.path_string)
{
if (is_separator(c, path.env))
{
return { path.path_string[:i], path.env };
}
}
return PathResult.NO_PARENT?;
}
fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
{
if (!path_str.len) return path_str;
usz path_start = volume_name_len(path_str, path_env)!;
usz path_len = path_str.len;
if (path_start == path_len) return path_str;
char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
usz len = path_start;
bool has_root = is_separator(path_str[path_start], path_env);
if (has_root)
{
path_str[len++] = path_separator;
path_start++;
}
// It is safe to write it as true, since we already dealt with /foo.
// This allows us to avoid checking whether it is the start of the path.
bool previous_was_separator = true;
for (usz i = path_start; i < path_len; i++)
{
char c = path_str[i];
// Fold foo///bar into foo/bar
if (is_separator(c, path_env))
{
// Fold //
if (previous_was_separator) continue;
// New /, so mark and rewrite
path_str.ptr[len++] = path_separator;
previous_was_separator = true;
continue;
}
// The rest are names of the path elements, so check that the
// characters are valid.
if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH?;
// If we have '.' after a separator
if (c == '.' && previous_was_separator)
{
// Get the number of dots until next separator, expecting 1 or 2
bool is_last = i == path_len - 1;
int dots = 1;
if (!is_last && path_str[i + 1] == '.')
{
dots = 2;
is_last = i == path_len - 2;
if (!is_last && !is_separator(path_str[i + 2], path_env))
{
dots = 0;
}
}
switch (dots)
{
case 1:
// /./abc -> skip to /./abc
// ^ ^
i++;
continue;
case 2:
// This is an error: /a/../..
if (len == path_start && has_root) return PathResult.INVALID_PATH?;
// If this .. at the start, or after ../? If so, we just copy ..
if (len == path_start ||
(len - path_start >= 3 && path_str[len - 1] == path_separator
&& path_str[len - 3] == '.' && path_str[len - 3] == '.' &&
(len - 3 == 0 || path_str[len - 4] == path_separator)))
{
if (i != len)
{
path_str[len] = '.';
path_str[len + 1] = '.';
}
len += 2;
if (len < path_len) path_str[len++] = path_separator;
i += 2;
continue;
}
// Step back, now looking at '/' abc/def/. -> abc/def/
len--;
// Step back until finding a separator or the start.
while (len > path_start && !is_separator(path_str[len - 1], path_env))
{
len--;
}
// Reading, we go from /../abc to /../abc
// ^ ^
i += 2;
continue;
default:
break;
}
}
if (i != len) path_str[len] = c;
previous_was_separator = false;
len++;
}
if (len > path_start + 1 && is_separator(path_str[len - 1], path_env)) len--;
path_str.ptr[len] = 0;
return path_str[:len];
}
fn ZString Path.as_zstr(Path path) => (ZString)path.path_string.ptr;
fn String Path.root_directory(Path path)
{
String path_str = path.as_str();
usz len = path_str.len;
if (!len) return "";
if (path.env == PathEnv.WIN32)
{
usz root_len = volume_name_len(path_str, path.env)!!;
if (root_len == len || !is_win32_separator(path_str[root_len])) return "";
return path_str[root_len..root_len];
}
if (!is_posix_separator(path_str[0])) return "";
for (usz i = 1; i < len; i++)
{
if (is_posix_separator(path_str[i]))
{
return path_str[:i];
}
}
return path_str;
}
fn String Path.as_str(Path path)
{
return path.path_string;
}
fn bool Path.has_suffix(Path path, String str)
{
return path.as_str().ends_with(str);
}
fn void Path.free(Path path)
{
free(path.path_string.ptr);
}
const bool[256] RESERVED_PATH_CHAR_POSIX = {
[0] = true,
['/'] = true,
};
const bool[256] RESERVED_PATH_CHAR_WIN32 = {
[0..31] = true,
['>'] = true,
['<'] = true,
[':'] = true,
['\"'] = true,
['/'] = true,
['\\'] = true,
['|'] = true,
['?'] = true,
['*'] = true,
};
macro bool is_reserved_win32_path_char(char c)
{
return RESERVED_PATH_CHAR_WIN32[c];
}
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_PATH_ENV)
{
return path_env == PathEnv.WIN32
? RESERVED_PATH_CHAR_WIN32[c]
: RESERVED_PATH_CHAR_POSIX[c];
}

View File

@@ -0,0 +1,80 @@
module std::io;
import std::math;
struct ByteReader
{
char[] bytes;
usz index;
}
fn void ByteReader.init(ByteReader* reader, char[] bytes)
{
*reader = { .bytes = bytes };
}
fn Stream ByteReader.as_stream(ByteReader* reader)
{
return { .fns = &bytereader_interface, .data = reader };
}
fn usz! ByteReader.read(ByteReader* reader, char[] bytes)
{
if (reader.index >= reader.bytes.len) return IoError.EOF?;
usz len = math::min(reader.bytes.len - reader.index, bytes.len);
if (len == 0) return 0;
mem::copy(bytes.ptr, &reader.bytes[reader.index], len);
reader.index += len;
return len;
}
fn char! ByteReader.read_byte(ByteReader* reader)
{
if (reader.index >= reader.bytes.len) return IoError.EOF?;
return reader.bytes[reader.index++];
}
fn void! ByteReader.pushback_byte(ByteReader* reader)
{
if (!reader.index) return IoError.INVALID_PUSHBACK?;
reader.index--;
}
fn usz! ByteReader.seek(ByteReader* reader, isz offset, Seek seek)
{
isz new_index;
switch (seek)
{
case SET: new_index = offset;
case CURSOR: new_index = reader.index + offset;
case END: new_index = reader.bytes.len + offset;
}
if (new_index < 0) return IoError.INVALID_POSITION?;
reader.index = new_index;
return new_index;
}
fn usz! ByteReader.write_stream(ByteReader* reader, Stream* writer)
{
if (reader.index >= reader.bytes.len) return 0;
usz written = writer.write(reader.bytes[reader.index..])!;
reader.index += written;
assert(reader.index <= reader.bytes.len);
return written;
}
fn usz ByteReader.available(ByteReader* reader)
{
return math::max((isz)0, (isz)reader.bytes.len - reader.index);
}
StreamInterface bytereader_interface = {
.len_fn = fn (s) => ((ByteReader*)s.data).bytes.len,
.read_fn = fn (s, char[] bytes) => ((ByteReader*)s.data).read(bytes) @inline,
.read_byte_fn = fn (s) => ((ByteReader*)s.data).read_byte() @inline,
.pushback_byte_fn = fn (s) => ((ByteReader*)s.data).pushback_byte() @inline,
.seek_fn = fn (s, offset, seek) => ((ByteReader*)s.data).seek(offset, seek) @inline,
.write_stream_fn = fn (s, writer) => ((ByteReader*)s.data).write_stream(writer) @inline,
.available_fn = fn (s) => ((ByteReader*)s.data).available() @inline,
};

View File

@@ -0,0 +1,119 @@
module std::io;
struct ByteWriter
{
char[] bytes;
usz index;
Allocator* allocator;
}
/**
* @param [&inout] writer
* @param [&in] using
* @require writer.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure using != null, index == 0
**/
fn void ByteWriter.init(ByteWriter* writer, Allocator* using = mem::heap())
{
*writer = { .bytes = {}, .allocator = using };
}
fn void ByteWriter.init_buffer(ByteWriter* writer, char[] data)
{
*writer = { .bytes = data, .allocator = null };
}
/**
* @param [&inout] writer
* @require writer.bytes.len == 0 "Init may not run on on already initialized data"
**/
fn void ByteWriter.tinit(ByteWriter* writer)
{
*writer = { .bytes = {}, .allocator = mem::temp() };
}
fn Stream ByteWriter.as_stream(ByteWriter* writer)
{
return { .fns = &bytewriter_interface, .data = writer };
}
fn void ByteWriter.destroy(ByteWriter* writer)
{
if (!writer.allocator) return;
if (void* ptr = writer.bytes.ptr) free(ptr, .using = writer.allocator);
*writer = { };
}
fn String ByteWriter.as_str(ByteWriter* writer)
{
return (String)writer.bytes[:writer.index];
}
fn void! ByteWriter.ensure_capacity(ByteWriter* writer, usz len) @inline
{
if (writer.bytes.len > len) return;
if (!writer.allocator) return IoError.OUT_OF_SPACE?;
if (len < 16) len = 16;
usz new_capacity = math::next_power_of_2(len);
char* new_ptr = realloc_checked(writer.bytes.ptr, new_capacity, .using = writer.allocator)!;
writer.bytes = new_ptr[:new_capacity];
}
fn usz! ByteWriter.write(ByteWriter* writer, char[] bytes)
{
writer.ensure_capacity(writer.index + bytes.len)!;
mem::copy(&writer.bytes[writer.index], bytes.ptr, bytes.len);
writer.index += bytes.len;
return bytes.len;
}
fn void! ByteWriter.write_byte(ByteWriter* writer, char c)
{
writer.ensure_capacity(writer.index + 1)!;
writer.bytes[writer.index++] = c;
}
/**
* @param [&inout] writer
* @param [&inout] reader
**/
fn usz! ByteWriter.read_from(ByteWriter* writer, Stream* reader)
{
if (reader.supports_available())
{
usz total_read = 0;
while (usz available = reader.available()!)
{
writer.ensure_capacity(writer.index + available)!;
usz len = reader.read(writer.bytes[writer.index..])!;
total_read += len;
writer.index += len;
}
return total_read;
}
usz total_read = 0;
while (true)
{
// See how much we can read.
usz len_to_read = writer.bytes.len - writer.index;
// Less than 16 bytes? Double the capacity
if (len_to_read < 16)
{
writer.ensure_capacity(writer.bytes.len * 2)!;
}
// Read into the rest of the buffer
usz read = reader.read(writer.bytes[writer.index..])!;
writer.index += read;
// Ok, we reached the end.
if (read < len_to_read) return total_read;
// Otherwise go another round
}
}
StreamInterface bytewriter_interface = {
.destroy_fn = fn (s) => ((ByteWriter*)s.data).destroy(),
.len_fn = fn (s) => ((ByteWriter*)s.data).bytes.len,
.write_fn = fn (s, char[] bytes) => ((ByteWriter*)s.data).write(bytes),
.write_byte_fn = fn (s, char c) => ((ByteWriter*)s.data).write_byte(c),
.read_stream_fn = fn (s, reader) => ((ByteWriter*)s.data).read_from(reader),
};

View File

@@ -0,0 +1,14 @@
module std::io;
fn Stream DString.as_stream(DString* dstring)
{
return { .fns = &dstring_interface, .data = dstring };
}
StreamInterface dstring_interface = {
.destroy_fn = fn (s) => ((DString*)s.data).free(),
.len_fn = fn (s) => ((DString*)s.data).len(),
.write_fn = fn (s, char[] bytes) { ((DString*)s.data).append_chars((String)bytes); return bytes.len; },
.write_byte_fn = fn (s, char c) => ((DString*)s.data).append_char(c),
.read_stream_fn = fn (s, reader) => ((DString*)s.data).read_from_stream(reader),
};

View File

@@ -0,0 +1,18 @@
module std::io;
fn Stream File.as_stream(File* file)
{
return { .fns = &filestream_interface, .data = file };
}
StreamInterface filestream_interface = {
.close_fn = fn (s) => ((File*)s.data).close(),
.seek_fn = fn (s, offset, seek) => ((File*)s.data).seek(offset, seek) @inline,
.read_fn = fn (s, char[] bytes) => ((File*)s.data).read(bytes) @inline,
.write_fn = fn (s, char[] bytes) => ((File*)s.data).write(bytes) @inline,
.write_byte_fn = fn (s, char c) => ((File*)s.data).putc(c) @inline,
.read_byte_fn = fn (s) => ((File*)s.data).getc() @inline,
.flush_fn = fn (s) => ((File*)s.data).flush() @inline,
};

View File

@@ -4,9 +4,6 @@
module libc;
// stdlib
// Constants need to be per os/arch
const int EXIT_FAILURE = 1;
const int EXIT_SUCCESS = 0;
@@ -24,8 +21,6 @@ struct LongDivResult
long rem;
}
fn Errno errno()
{
return (Errno)os::errno();
@@ -36,8 +31,12 @@ fn void errno_set(Errno e)
os::errno_set((int)e);
}
define TerminateFunction = fn void();
define CompareFunction = fn int(void*, void*);
def TerminateFunction = fn void();
def CompareFunction = fn int(void*, void*);
def JmpBuf = uptr[$$JMP_BUF_SIZE];
$if env::COMPILER_LIBC_AVAILABLE:
extern fn double atof(char* str);
extern fn int atoi(char* str);
extern fn CLongLong atoll(char* str);
@@ -47,27 +46,26 @@ extern fn CULong stroul(char* str, char** endptr, int base);
extern fn void abort();
extern fn void atexit(TerminateFunction f);
extern fn void exit(int status);
extern fn char* getenv(char* name);
extern fn ZString getenv(ZString name);
extern fn int setenv(ZString name, ZString value, int overwrite);
extern fn int unsetenv(ZString name);
extern fn int system(char* str);
extern fn void bsearch(void* key, void *base, usz items, usz size, CompareFunction compare);
extern fn void qsort(void* base, usz items, usz size, CompareFunction compare);
extern fn int abs(int x);
extern fn DivResult div(int numer, int denom);
extern fn long labs(long x);
extern fn LongDivResult ldiv(long number, long denom);
extern fn int rand();
extern fn void srand(uint seed);
define JmpBuf = CInt[$$JMP_BUF_SIZE];
extern fn void longjmp(JmpBuf* buffer, CInt value);
$if (env::OS_TYPE == OsType.WIN32):
$if env::os_is_win32():
// TODO win32 aarch64
extern fn CInt _setjmp(void* frameptr, JmpBuf* buffer);
macro CInt setjmp(JmpBuf* buffer) = _setjmp($$frameaddress(), buffer);
$else:
macro CInt setjmp(JmpBuf* buffer) => _setjmp($$frameaddress(), buffer);
$else
extern fn CInt setjmp(JmpBuf* buffer);
$endif;
$endif
// MB functions omitted
// string
@@ -75,7 +73,7 @@ extern fn void* memchr(void* str, int c, usz n);
extern fn int memcmp(void* str1, void* str2, usz n);
extern fn void* memcpy(void* dest, void* src, usz n);
extern fn void* memmove(void* dest, void* src, usz n);
extern fn void* memset(void* dest, usz n);
extern fn void* memset(void* dest, CInt value, usz n);
extern fn char* strcat(char* dest, char* src);
extern fn char* strncat(char* dest, char* src, usz n);
extern fn char* strchr(char* str, int c);
@@ -99,23 +97,72 @@ extern fn void* calloc(usz count, usz size);
extern fn void* free(void*);
extern fn void* realloc(void* ptr, usz size);
$else
fn void longjmp(JmpBuf* buffer, CInt value) @weak @extern("longjmp") @nostrip
{
unreachable("longjmp unavailable");
}
fn CInt setjmp(JmpBuf* buffer) @weak @extern("setjmp") @nostrip
{
unreachable("setjmp unavailable");
}
fn void* malloc(usz size) @weak @extern("malloc") @nostrip
{
unreachable("malloc unavailable");
}
fn void* calloc(usz count, usz size) @weak @extern("calloc") @nostrip
{
unreachable("calloc unavailable");
}
fn void* free(void*) @weak @extern("free")
{
unreachable("free unavailable");
}
fn void* realloc(void* ptr, usz size) @weak @extern("realloc") @nostrip
{
unreachable("realloc unavailable");
}
fn void* memcpy(void* dest, void* src, usz n) @weak @extern("memcpy") @nostrip
{
for (usz i = 0; i < n; i++) ((char*)dest)[i] = ((char*)src)[i];
return dest;
}
fn void* memmove(void* dest, void* src, usz n) @weak @extern("memmove") @nostrip
{
return memcpy(dest, src, n) @inline;
}
fn void* memset(void* dest, CInt value, usz n) @weak @extern("memset") @nostrip
{
for (usz i = 0; i < n; i++) ((char*)dest)[i] = (char)value;
return dest;
}
$endif
// stdio
define Fpos = long;
define CFile = void*;
def Fpos = long;
def CFile = void*;
$switch (env::OS_TYPE):
$case OsType.LINUX:
extern CFile __stdin @extname("stdin");
extern CFile __stdout @extname("stdout");
extern CFile __stderr @extname("stderr");
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == LINUX:
extern CFile __stdin @extern("stdin");
extern CFile __stdout @extern("stdout");
extern CFile __stderr @extern("stderr");
extern fn usz malloc_usable_size(void* ptr);
macro usz malloc_size(void* ptr) { return malloc_usable_size(ptr); }
extern fn void* aligned_alloc(usz align, usz size);
macro CFile stdin() { return __stdin; }
macro CFile stdout() { return __stdout; }
macro CFile stderr() { return __stderr; }
$case OsType.MACOSX:
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_darwin():
extern CFile __stdinp;
extern CFile __stdoutp;
extern CFile __stderrp;
@@ -124,7 +171,7 @@ $case OsType.MACOSX:
macro CFile stdin() { return __stdinp; }
macro CFile stdout() { return __stdoutp; }
macro CFile stderr() { return __stderrp; }
$case OsType.WIN32:
$case env::COMPILER_LIBC_AVAILABLE && env::os_is_win32():
extern fn CFile __acrt_iob_func(CInt c);
extern fn usz _msize(void* ptr);
macro usz malloc_size(void* ptr) { return _msize(ptr); }
@@ -135,13 +182,12 @@ $default:
macro CFile stdin() { return (CFile*)(uptr)0; }
macro CFile stdout() { return (CFile*)(uptr)1; }
macro CFile stderr() { return (CFile*)(uptr)2; }
$endswitch;
$endswitch
const HAS_MALLOC_SIZE =
env::OS_TYPE == OsType.LINUX
|| env::OS_TYPE == OsType.WIN32
|| env::OS_TYPE == OsType.MACOSX;
env::OS_TYPE == LINUX
|| env::os_is_win32()
|| env::os_is_darwin();
// The following needs to be set per arch+os
// For now I have simply pulled the defaults from MacOS
@@ -156,8 +202,10 @@ const int EOF = -1;
const int FOPEN_MAX = 20;
const int FILENAME_MAX = 1024;
define Errno = distinct CInt;
define SeekIndex = CLong;
def Errno = distinct CInt;
def SeekIndex = CLong;
$if env::COMPILER_LIBC_AVAILABLE:
extern fn int fclose(CFile stream);
extern fn void clearerr(CFile stream);
@@ -165,9 +213,10 @@ extern fn int feof(CFile stream);
extern fn int ferror(CFile stream);
extern fn int fflush(CFile stream);
extern fn int fgetpos(CFile stream, Fpos* pos);
extern fn CFile fopen(char* filename, char* mode);
extern fn CFile fopen(ZString filename, ZString mode);
extern fn usz fread(void* ptr, usz size, usz nmemb, CFile stream);
extern fn CFile freopen(char* filename, char* mode, CFile stream);
extern fn CFile freopen(ZString filename, ZString mode, CFile stream);
extern fn CFile fmemopen(void* ptr, usz size, ZString mode);
extern fn int fseek(CFile stream, SeekIndex offset, int whence);
extern fn int fsetpos(CFile stream, Fpos* pos);
extern fn SeekIndex ftell(CFile stream);
@@ -190,20 +239,88 @@ extern fn char* fgets(char* str, int n, CFile stream);
extern fn int fputc(int c, CFile stream);
extern fn int getc(CFile stream);
extern fn int getchar();
extern fn int putc(char c, CFile stream);
extern fn int putc(int c, CFile stream);
extern fn int putchar(int c);
extern fn int puts(char* str);
extern fn int ungetc(int c, CFile stream);
extern fn void perror(char* str);
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
$else
fn int fseek(CFile stream, SeekIndex offset, int whence) @weak @extern("fseek") @nostrip
{
unreachable("'fseek' not available.");
}
fn CFile fopen(ZString filename, ZString mode) @weak @extern("fopen") @nostrip
{
unreachable("'fopen' not available.");
}
fn CFile freopen(ZString filename, ZString mode, CFile stream) @weak @extern("fopen") @nostrip
{
unreachable("'freopen' not available.");
}
fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fwrite") @nostrip
{
unreachable("'fwrite' not available.");
}
fn usz fread(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fread") @nostrip
{
unreachable("'fread' not available.");
}
fn CFile fclose(CFile) @weak @extern("fclose") @nostrip
{
unreachable("'fclose' not available.");
}
fn int fflush(CFile stream) @weak @extern("fflush") @nostrip
{
unreachable("'fflush' not available.");
}
fn int fputc(int c, CFile stream) @weak @extern("fputc") @nostrip
{
unreachable("'fputc' not available.");
}
fn char* fgets(ZString str, int n, CFile stream) @weak @extern("fgets") @nostrip
{
unreachable("'fgets' not available.");
}
fn int fgetc(CFile stream) @weak @extern("fgetc") @nostrip
{
unreachable("'fgetc' not available.");
}
fn int feof(CFile stream) @weak @extern("feof") @nostrip
{
unreachable("'feof' not available.");
}
fn int putc(int c, CFile stream) @weak @extern("putc") @nostrip
{
unreachable("'putc' not available.");
}
fn int putchar(int c) @weak @extern("putchar") @nostrip
{
unreachable("'putchar' not available.");
}
fn int puts(ZString str) @weak @extern("puts") @nostrip
{
unreachable("'puts' not available.");
}
$endif
// vsprintf vprintf not supported
// time.h
define TimeOffset = CLong;
struct Tm
struct TmCommon @private
{
int tm_sec; /* seconds after the minute [0-60] */
int tm_min; /* minutes after the hour [0-59] */
@@ -214,35 +331,115 @@ struct Tm
int tm_wday; /* days since Sunday [0-6] */
int tm_yday; /* days since January 1 [0-365] */
int tm_isdst; /* Daylight Savings Time flag */
}
$switch (env::OS_TYPE)
$case WIN32:
def Tm = TmCommon;
$case WASI:
def TimeOffset = int;
struct Tm
{
inline TmCommon common;
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
char *tm_zone; /* timezone abbreviation */
int tm_nsec;
}
$case MACOS:
$case IOS:
$case TVOS:
$case WATCHOS:
$case OPENBSD:
$case FREEBSD:
$default:
def TimeOffset = CLong;
struct Tm
{
inline TmCommon common;
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
char *tm_zone; /* timezone abbreviation */
}
$endswitch
$if env::os_is_win32():
struct TimeSpec
{
Time_t s;
ulong ns;
}
def Time_t = long;
def Clock_t = ulong;
$else
struct TimeSpec
{
Time_t s;
CLong ns;
}
def Time_t = CLong;
def Clock_t = CULong;
$endif
const int TIME_UTC = 1;
extern fn int timespec_get(TimeSpec* ts, int base);
extern fn int nanosleep(TimeSpec* req, TimeSpec* remaining);
// Likely wrong, must be per platform.
const CLOCKS_PER_SEC = 1000000;
// Time also needs to be per platform
define Time = long;
define Clock = ulong;
extern fn char* asctime(Tm *timeptr);
extern fn Clock clock();
extern fn char* ctime(Time *timer);
extern fn double difftime(Time time1, Time time2);
extern fn Tm* gmtime(Time *timer);
extern fn Tm* localtime(Time *timer);
extern fn Time mktime(Tm *timeptr);
extern fn ZString asctime(Tm *timeptr);
extern fn Clock_t clock();
extern fn ZString ctime(Time_t *timer);
extern fn double difftime(Time_t time1, Time_t time2);
extern fn Tm* gmtime(Time_t *timer);
extern fn Tm* localtime(Time_t *timer);
$if env::os_is_win32():
extern fn Tm* _gmtime64_s(Tm* buf, Time_t *timer);
extern fn Tm* _localtime64_s(Tm* buf, Time_t *timer);
extern fn void _get_timezone(CLong *timezone);
macro Tm* gmtime_r(Time_t *timer, Tm* buf) => _gmtime64_s(buf, timer);
macro Tm* localtime_r(Time_t *timer, Tm* buf) => _localtime64_s(buf, timer);
extern fn Time_t mktime(Tm *timeptr) @extern("_mktime64");
extern fn Time_t timegm(Tm *timeptr) @extern("_mkgmtime64");
$else
extern fn Tm* gmtime_r(Time_t *timer, Tm* buf);
extern fn Tm* localtime_r(Time_t *timer, Tm* buf);
extern fn Time_t mktime(Tm *timeptr);
extern fn Time_t timegm(Tm *timeptr);
$endif
extern fn usz strftime(char* str, usz maxsize, char* format, Tm *timeptr);
extern fn Time time(Time *timer);
extern fn Time_t time(Time_t *timer);
// signal
define SignalFunction = fn void(int);
def SignalFunction = fn void(int);
extern fn SignalFunction signal(int sig, SignalFunction function);
// Incomplete
module libc::errno;
const Errno OK = 0;
const Errno EPERM = 1; // Operation not permitted
const Errno ENOENT = 2; // No such file or directory
const Errno ESRCH = 3; // No such process
@@ -254,11 +451,11 @@ const Errno ENOEXEC = 8; // Exec format error
const Errno EBADF = 9; // Bad file number
const Errno ECHILD = 10; // No child processes
$if (env::OS_TYPE == OsType.MACOSX):
$if env::os_is_darwin():
const Errno EAGAIN = 35; // Try again Macos
$else:
$else
const Errno EAGAIN = 11; // Try again
$endif;
$endif
const Errno ENOMEM = 12; // Out of memory
const Errno EACCES = 13; // Permission denied
@@ -284,7 +481,9 @@ const Errno EPIPE = 32; // Broken pipe
const Errno EDOM = 33; // Math argument out of domain of func
const Errno ERANGE = 34; // Math result not representable
$if (env::OS_TYPE == OsType.MACOSX):
$switch (env::OS_TYPE)
$case MACOS:
const Errno EDEADLK = 11; // Resource deadlock would occur MacOS
const Errno ENAMETOOLONG = 63; // File name too long MacOS
const Errno ELOOP = 62; // Too many symbolic links encountered
@@ -293,8 +492,10 @@ const Errno ECONNRESET = 54; // Connection reset by peer Macos
const Errno ENETDOWN = 50; // Network is down MacOS
const Errno ENETUNREACH = 51; // Network is unreachable MacOS
const Errno ENETRESET = 52; // Network dropped connection because of reset MacOS
const Errno EOPNOTSUPP = 45; // Operation not supported on transport endpoint
const Errno ENOTEMPTY = 66; // Directory not empty
$elif (env::OS_TYPE == OsType.WIN32):
$case WIN32:
const Errno EDEADLK = 36; // Resource deadlock would occur Win32
const Errno ENAMETOOLONG = 38; // File name too long Win32
const Errno ELOOP = 114; // Too many symbolic links encountered
@@ -303,8 +504,10 @@ const Errno ENETDOWN = 116; // Network is down
const Errno ECONNRESET = 108; // Connection reset by peer
const Errno ENETUNREACH = 118; // Network is unreachable
const Errno ENETRESET = 117; // Network dropped connection because of reset
const Errno EOPNOTSUPP = 130; // Operation not supported on transport endpoint
const Errno ENOTEMPTY = 41; // Directory not empty
$else:
$default:
const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?)
const Errno ENAMETOOLONG = 36; // File name too long Linux (others?)
const Errno ELOOP = 40; // Too many symbolic links encountered
@@ -313,14 +516,16 @@ const Errno ENETDOWN = 100; // Network is down
const Errno ECONNRESET = 104; // Connection reset by peer
const Errno ENETUNREACH = 101; // Network is unreachable
const Errno ENETRESET = 102; // Network dropped connection because of reset
const Errno EOPNOTSUPP = 95; // Operation not supported on transport endpoint
const Errno ENOTEMPTY = 39; // Directory not empty
$endswitch
$endif;
/*
const Errno ENOLCK = 37; /* No record locks available */
const Errno ENOSYS = 38; /* Function not implemented */
const Errno ENOTEMPTY = 39; /* Directory not empty */
const Errno ENOMSG = 42; /* No message of desired type */
const Errno EIDRM = 43; /* Identifier removed */
@@ -374,7 +579,6 @@ const Errno EPROTOTYPE = 91; /* Protocol wrong type for socket */
const Errno ENOPROTOOPT = 92; /* Protocol not available */
const Errno EPROTONOSUPPORT = 93; /* Protocol not supported */
const Errno ESOCKTNOSUPPORT = 94; /* Socket type not supported */
const Errno EOPNOTSUPP = 95; /* Operation not supported on transport endpoint */
const Errno EPFNOSUPPORT = 96; /* Protocol family not supported */
const Errno EAFNOSUPPORT = 97; /* Address family not supported by protocol */
const Errno EADDRINUSE = 98; /* Address already in use */
@@ -385,26 +589,34 @@ const Errno EISCONN = 106; /* Transport endpoint is already connected */
const Errno ENOTCONN = 107; /* Transport endpoint is not connected */
const Errno ESHUTDOWN = 108; /* Cannot send after transport endpoint shutdown */
const Errno ETOOMANYREFS = 109; /* Too many references: cannot splice */
const Errno ETIMEDOUT = 110; /* Connection timed out */
const Errno ECONNREFUSED = 111; /* Connection refused */
const Errno EHOSTDOWN = 112; /* Host is down */
const Errno EHOSTUNREACH = 113; /* No route to host */
*/
$if (env::OS_TYPE == OsType.MACOSX):
$switch (env::OS_TYPE)
$case MACOS:
const Errno ETIMEDOUT = 60; // Connection timed out
const Errno EINPROGRESS = 36; // Operation now in progress MacOS
const Errno EALREADY = 37; // Operation already in progress MacOS
const Errno EDQUOT = 69; // Quota exceeded, MacOS
const Errno EWOULDBLOCK = 35; // Operation would block
$elif (env::OS_TYPE == OsType.WIN32):
$case WIN32:
const Errno ETIMEDOUT = 138; // Connection timed out
const Errno EALREADY = 103; // Operation already in progress
const Errno EINPROGRESS = 112; // Operation now in progress Win32
const Errno EDQUOT = -122; // Quota exceeded, not in Win32
const Errno EWOULDBLOCK = 140; // Operation would block
$else:
$default:
const Errno ETIMEDOUT = 110; // Connection timed out
const Errno EALREADY = 114; // Operation already in progress
const Errno EINPROGRESS = 115; // Operation now in progress
const Errno EDQUOT = 122; // Quota exceeded
$endif;
const Errno EWOULDBLOCK = 41; // Operation would block
$endswitch
/*
const Errno ESTALE = 116; /* Stale NFS file handle */

View File

@@ -1,21 +1,21 @@
module libc::os;
$switch (env::OS_TYPE):
$case LINUX:
$switch
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == LINUX:
extern fn int* __errno_location();
macro int errno() => *__errno_location();
macro void errno_set(int err) => *(__errno_location()) = err;
macro int errno() = *__errno_location();
macro void errno_set(int err) = *(__errno_location()) = err;
$case MACOSX:
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == MACOS:
extern fn int* __error();
macro int errno() = *__error();
macro void errno_set(int err) = *(__error()) = err;
macro int errno() => *__error();
macro void errno_set(int err) => *(__error()) = err;
$case WIN32:
$case env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == WIN32:
macro int errno()
{
@@ -24,14 +24,15 @@ macro int errno()
return holder;
}
macro void errno_set(int err) = _set_errno(err);
macro void errno_set(int err) => _set_errno(err);
extern fn void _get_errno(int* result);
extern fn void _set_errno(int err);
$default:
macro int errno() = 1;
fn void errno_set(int err) {}
tlocal int _errno_c3 = 0;
fn void errno_set(int err) => _errno_c3 = err;
fn int errno() => _errno_c3;
$endswitch;
$endswitch

View File

@@ -1,169 +0,0 @@
// Copyright (c) 2021 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::array::linkedlist<Type>;
private struct Node
{
Node *next;
Node *prev;
Type value;
}
struct LinkedList
{
usz size;
Node *first;
Node *last;
}
fn void LinkedList.push(LinkedList *list, Type value)
{
list.linkLast(value);
}
private fn void LinkedList.linkFirst(LinkedList *list, Type value)
{
Node *first = list.first;
Node *new_node = mem::alloc(Node);
*new_node = { .next = first, .value = value };
list.first = new_node;
if (!first)
{
list.last = new_node;
}
else
{
first.prev = new_node;
}
list.size++;
}
private fn void LinkedList.linkLast(LinkedList *list, Type value)
{
Node *last = list.last;
Node *new_node = mem::alloc(Node);
*new_node = { .prev = last, .value = value };
list.last = new_node;
if (!last)
{
list.first = new_node;
}
else
{
last.next = new_node;
}
list.size++;
}
fn void LinkedList.free(LinkedList *list)
{
for (Node* node = list.first; node != null;)
{
Node* next = node.next;
free(node);
node = next;
}
list.first = null;
list.last = null;
list.size = 0;
}
fn usz LinkedList.len(LinkedList* list) @inline
{
return list.size;
}
fn Type LinkedList.get(LinkedList* list, usz index)
{
Node* node = list.first;
while (index--)
{
node = node.next;
}
return node.value;
}
/**
* @require succ != null
**/
private fn void LinkedList.linkBefore(LinkedList *list, Node *succ, Type value)
{
Node* pred = succ.prev;
Node* new_node = mem::alloc(Node);
*new_node = { .prev = pred, .next = succ, .value = value };
succ.prev = new_node;
if (!pred)
{
list.first = new_node;
}
else
{
pred.next = new_node;
}
list.size++;
}
/**
* @require f == list.first && f != null
**/
private fn void unlinkFirst(LinkedList* list, Node* f)
{
Node* next = f.next;
free(f);
list.first = next;
if (!next)
{
list.last = null;
}
else
{
next.prev = null;
}
list.size--;
}
/**
* @require l == list.last && l != null
**/
private fn void LinkedList.unlinkLast(LinkedList *list, Node* l)
{
Node* prev = l.prev;
list.last = prev;
free(l);
if (!prev)
{
list.first = null;
}
else
{
prev.next = null;
}
list.size--;
}
/**
* @require x != null
**/
private fn void LinkedList.unlink(LinkedList* list, Node* x)
{
Node* next = x.next;
Node* prev = x.prev;
if (!prev)
{
list.first = next;
}
else
{
prev.next = next;
}
if (!next)
{
list.last = prev;
}
else
{
next.prev = prev;
}
free(x);
list.size--;
}

View File

@@ -1,177 +0,0 @@
// Copyright (c) 2021 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::array::list<Type>;
import std::math;
struct List
{
usz size;
usz capacity;
Allocator *allocator;
Type *entries;
}
/**
* @require allocator != null "A valid allocator must be provided"
**/
fn void List.init(List* list, usz initial_capacity = 16, Allocator* allocator = mem::current_allocator())
{
list.allocator = allocator;
list.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
list.entries = allocator.alloc_aligned(Type.sizeof * initial_capacity, Type[1].alignof)!!;
}
else
{
list.entries = null;
}
list.capacity = initial_capacity;
}
fn void List.tinit(List* list, usz initial_capacity = 16)
{
list.init(initial_capacity, mem::temp_allocator()) @inline;
}
fn void List.push(List* list, Type element) @inline
{
list.append(element);
}
fn void List.append(List* list, Type element)
{
list.ensure_capacity();
list.entries[list.size++] = element;
}
/**
* @require list.size > 0
*/
fn Type List.pop(List* list)
{
return list.entries[--list.size];
}
fn void List.clear(List* list)
{
list.size = 0;
}
/**
* @require list.size > 0
*/
fn Type List.pop_first(List* list)
{
Type value = list.entries[0];
list.remove_at(0);
return value;
}
fn void List.remove_at(List* list, usz index)
{
for (usz i = index + 1; i < list.size; i++)
{
list.entries[i - 1] = list.entries[i];
}
list.size--;
}
fn void List.push_front(List* list, Type type) @inline
{
list.insert_at(0, type);
}
fn void List.insert_at(List* list, usz index, Type type)
{
list.ensure_capacity();
for (usz i = list.size; i > index; i--)
{
list.entries[i] = list.entries[i - 1];
}
list.size++;
list.entries[index] = type;
}
fn void List.remove_last(List* list)
{
list.size--;
}
fn void List.remove_first(List* list)
{
list.remove_at(0);
}
fn Type* List.first(List* list)
{
return list.size ? &list.entries[0] : null;
}
fn Type* List.last(List* list)
{
return list.size ? &list.entries[list.size - 1] : null;
}
fn bool List.is_empty(List* list)
{
return !list.size;
}
fn usz List.len(List* list) @operator(len)
{
return list.size;
}
fn Type List.get(List* list, usz index)
{
return list.entries[index];
}
fn void List.free(List* list)
{
if (!list.allocator) return;
list.allocator.free_aligned(list.entries)!!;
list.capacity = 0;
list.size = 0;
list.entries = null;
}
fn void List.swap(List* list, usz i, usz j)
{
@swap(list.entries[i], list.entries[j]);
}
/**
* Reserve at least min_capacity
**/
fn void List.reserve(List* list, usz min_capacity)
{
if (!min_capacity) return;
if (list.capacity >= min_capacity) return;
if (!list.allocator) list.allocator = mem::temp_allocator();
min_capacity = math::next_power_of_2(min_capacity);
list.entries = list.allocator.realloc_aligned(list.entries, Type.sizeof * min_capacity, Type[1].alignof) ?? null;
list.capacity = min_capacity;
}
macro Type List.@item_at(List &list, usz index) @operator([])
{
return list.entries[index];
}
fn Type* List.get_ref(List* list, usz index) @operator(&[]) @inline
{
return &list.entries[index];
}
private fn void List.ensure_capacity(List* list) @inline
{
if (list.capacity == list.size)
{
list.reserve(list.capacity ? 2 * list.capacity : 16);
}
}

View File

@@ -1,477 +0,0 @@
// Copyright (c) 2021 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::math;
import std::math::complex;
// TODO Define these using quad precision.
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
const LOG10E = 0.434294481903251827651128918916605082; // log10(e)
const LN2 = 0.693147180559945309417232121458176568; // ln(2)
const LN10 = 2.30258509299404568401799145468436421; // ln(10)
const PI = 3.14159265358979323846264338327950288419716939937510; // pi
const PI_2 = 1.57079632679489661923132169163975144; // pi / 2
const PI_4 = 0.785398163397448309615660845819875721; // pi / 4
const DIV_PI = 0.318309886183790671537767526745028724; // 1 / pi
const DIV_2_PI = 0.636619772367581343075535053490057448; // 2 / pi
const DIV_2_SQRTPI = 1.12837916709551257389615890312154517; // 2/sqrt(pi)
const SQRT2 = 1.41421356237309504880168872420969808; // sqrt(2)
const double DIV_1_SQRT2 = 0.707106781186547524400844362104849039; // 1 / sqrt(2)
const HALF_MAX = 6.5504e+4;
const HALF_MIN = 6.103515625e-5;
const HALF_DENORM_MIN = 5.9604644775390625e-8;
const HALF_DIG = 3;
const HALF_DEC_DIGITS = 5;
const HALF_MANT_DIG = 11;
const HALF_MAX_10_EXP = 4;
const HALF_MIN_10_EXP = -4;
const HALF_MAX_EXP = 16;
const HALF_MIN_EXP = -13;
const HALF_EPSILON = 9.765625e-4;
const FLOAT_MAX = 0x1.fffffep+127;
const FLOAT_MIN = 1.17549435e-38;
const FLOAT_DENORM_MIN = 1.40129846432481707092e-45;
const FLOAT_DIG = 6;
const FLOAT_DEC_DIGITS = 9;
const FLOAT_MANT_DIG = 24;
const FLOAT_MAX_10_EXP = 38;
const FLOAT_MIN_10_EXP = -37;
const FLOAT_MAX_EXP = 128;
const FLOAT_MIN_EXP = -125;
const FLOAT_EPSILON = 1.1920928955078125e-07;
const DOUBLE_MAX = 1.79769313486231570815e+308;
const DOUBLE_MIN = 2.2250738585072014e-308;
const DOUBLE_DENORM_MIN = 4.94065645841246544177e-324;
const DOUBLE_DIG = 15;
const DOUBLE_DEC_DIGITS = 17;
const DOUBLE_MANT_DIG = 53;
const DOUBLE_MAX_10_EXP = 308;
const DOUBLE_MIN_10_EXP = -307;
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,
TO_NEAREST,
TOWARD_INFINITY,
TOWARD_NEG_INFINITY
}
define Complex32 = Complex<float>;
define Complex64 = Complex<double>;
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
**/
macro abs(x) = $$abs(x);
/**
* @require values::@is_int(x) `The input must be an integer`
**/
macro sign(x)
{
if (!x) return ($typeof(x))0;
return ($typeof(x))(x < 0 ? -1 : 1);
}
$if (env::COMPILER_LIBC_AVAILABLE):
extern fn double _atan(double x) @extname("atan");
extern fn float _atanf(float x) @extname("atanf");
extern fn double _atan2(double x) @extname("atan2");
extern fn float _atan2f(float x) @extname("atan2f");
macro atan(x)
{
$if ($typeof(x).typeid == float.typeid):
return _atanf(x);
$else:
return _atan(x);
$endif;
}
macro atan2(x, y)
{
$if ($typeof(x).typeid == float.typeid && $typeof(y).typeid == float.typeid):
return _atan2f(x);
$else:
return _atan2(x);
$endif;
}
$endif;
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro ceil(x) = $$ceil(x);
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
* @require types::@has_same(x, lower, upper) `The input types must be equal`
**/
macro clamp(x, lower, upper) = $$max(lower, $$min(x, upper));
/**
* @require values::@is_floatlike(mag) `The input must be a floating point value or float vector`
* @require types::is_same($typeof(mag), $typeof(sgn)) `The input types must be equal`
**/
macro copysign(mag, sgn) = $$copysign(mag, sgn);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro cos(x) = $$cos(x);
macro cosec(x) = 1 / sin(x);
macro cosech(x) = 2 / (exp(x) - exp(-x));
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro cosh(x) = (exp(x) + exp(-x)) / 2.0;
macro cotan(x) = cos(x) / sin(x);
macro cotanh(x) = (exp(2.0 * x) + 1.0) / (exp(2.0 * x) - 1.0);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro exp(x) = $$exp(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro exp2(x) = $$exp2(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro floor(x) = $$floor(x);
/**
* @require values::@is_floatlike(a) `The input must be a floating point value or float vector`
* @require types::@has_same(a, b, c) `The input types must be equal`
**/
macro fma(a, b, c) = $$fma(a, b, c);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
* @require values::@is_floatlike(y) `The input must be a floating point value or float vector`
**/
macro hypot(x, y) = sqrt(sqr(x) + sqr(y));
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro log(x) = $$log(x);
/**
* @require values::is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro log2(x) = $$log2(x);
/**
* @require values::is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro log10(x) = $$log10(x);
/**
* @require types::is_numerical($typeof(x)) `The input must be a floating point value or float vector`
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
**/
macro max(x, y, ...)
{
$if ($vacount == 0):
return $$max(x, y);
$else:
var m = $$max(x, y);
$for (var $i = 0; $i < $vacount; $i++):
m = $$max(m, $vaarg($i));
$endfor;
return m;
$endif;
}
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
**/
macro min(x, y, ...)
{
$if ($vacount == 0):
return $$min(x, y);
$else:
var m = $$min(x, y);
$for (var $i = 0; $i < $vacount; $i++):
m = $$min(m, $vaarg($i));
$endfor;
return m;
$endif;
}
/**
* @require types::@is_float(a) `The input must be a floating point value`
* @require types::@has_same(a, b, c) `The input types must be equal`
**/
macro muladd(a, b, c) = $$fmuladd(a, b, c);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro nearbyint(x) = $$nearbyint(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
* @require values::@convertable_to(exp, x) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
**/
macro pow(x, exp)
{
$if (types::is_floatlike($typeof(exp))):
return $$pow(x, ($typeof(x))exp);
$else:
return $$pow_int(x, exp);
$endif;
}
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro rint(x) = $$rint(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro round(x) = $$round(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro roundeven(x) = $$roundeven(x);
macro sec(x) = 1 / cos(x);
macro sech(x) = 2 / (exp(x) + exp(-x));
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro sin(x) = $$sin(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro sinh(x) = (exp(x) - exp(-x)) / 2.0;
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro sqr(x) = x * x;
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro sqrt(x) = $$sqrt(x);
macro tan(x) = sin(x) / cos(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro tanh(x) = (exp(2.0 * x) - 1.0) / (exp(2.0 * x) + 1.0);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro trunc(x) = $$trunc(x);
macro float float.ceil(float x) = $$ceil(x);
macro float float.clamp(float x, float lower, float upper) = $$max(lower, $$min(x, upper));
macro float float.copysign(float mag, float sgn) = $$copysign(mag, sgn);
macro float float.floor(float x) = $$floor(x);
macro float float.fma(float a, float b, float c) = $$fma(a, b, c);
macro float float.muladd(float a, float b, float c) = $$fmuladd(a, b, c);
macro float float.nearbyint(float x) = $$nearbyint(x);
macro float float.pow(float x, exp) = pow(x, exp);
macro float float.rint(float x) = $$rint(x);
macro float float.round(float x) = $$round(x);
macro float float.roundeven(float x) = $$roundeven(x);
macro float float.trunc(float x) = $$trunc(x);
macro float float[<*>].sum(float[<*>] x, float start = 0.0) = $$reduce_fadd(x, start);
macro float float[<*>].product(float[<*>] x, float start = 1.0) = $$reduce_fmul(x, start);
macro float float[<*>].max(float[<*>] x) = $$reduce_max(x);
macro float float[<*>].min(float[<*>] x) = $$reduce_min(x);
macro float[<*>] float[<*>].ceil(float[<*>] x) = $$ceil(x);
macro float[<*>] float[<*>].clamp(float[<*>] x, float[<*>] lower, float[<*>] upper) = $$max(lower, $$min(x, upper));
macro float[<*>] float[<*>].copysign(float[<*>] mag, float[<*>] sgn) = $$copysign(mag, sgn);
macro float[<*>] float[<*>].fma(float[<*>] a, float[<*>] b, float[<*>] c) = $$fma(a, b, c);
macro float[<*>] float[<*>].floor(float[<*>] x) = $$floor(x);
macro float[<*>] float[<*>].nearbyint(float[<*>] x) = $$nearbyint(x);
macro float[<*>] float[<*>].pow(float[<*>] x, exp) = pow(x, exp);
macro float[<*>] float[<*>].rint(float[<*>] x) = $$rint(x);
macro float[<*>] float[<*>].round(float[<*>] x) = $$round(x);
macro float[<*>] float[<*>].roundeven(float[<*>] x) = $$roundeven(x);
macro float[<*>] float[<*>].trunc(float[<*>] x) = $$trunc(x);
macro double double.ceil(double x) = $$ceil(x);
macro double double.clamp(double x, double lower, double upper) = $$max(lower, $$min(x, upper));
macro double double.copysign(double mag, double sgn) = $$copysign(mag, sgn);
macro double double.floor(double x) = $$floor(x);
macro double double.fma(double a, double b, double c) = $$fma(a, b, c);
macro double double.muladd(double a, double b, double c) = $$fmuladd(a, b, c);
macro double double.nearbyint(double x) = $$nearbyint(x);
macro double double.pow(double x, exp) = pow(x, exp);
macro double double.rint(double x) = $$rint(x);
macro double double.round(double x) = $$round(x);
macro double double.roundeven(double x) = $$roundeven(x);
macro double double.trunc(double x) = $$trunc(x);
macro double double[<*>].sum(double[<*>] x, double start = 0.0) = $$reduce_add(x, start);
macro double double[<*>].product(double[<*>] x, double start = 1.0) = $$reduce_mul(x, start);
macro double double[<*>].max(double[<*>] x) = $$reduce_fmax(x);
macro double double[<*>].min(double[<*>] x) = $$reduce_fmin(x);
macro double[<*>] double[<*>].ceil(double[<*>] x) = $$ceil(x);
macro double[<*>] double[<*>].clamp(double[<*>] x, double[<*>] lower, double[<*>] upper) = $$max(lower, $$min(x, upper));
macro double[<*>] double[<*>].copysign(double[<*>] mag, double[<*>] sgn) = $$copysign(mag, sgn);
macro double[<*>] double[<*>].floor(double[<*>] x) = $$floor(x);
macro double[<*>] double[<*>].fma(double[<*>] a, double[<*>] b, double[<*>] c) = $$fma(a, b, c);
macro double[<*>] double[<*>].nearbyint(double[<*>] x) = $$nearbyint(x);
macro double[<*>] double[<*>].pow(double[<*>] x, exp) = pow(x, exp);
macro double[<*>] double[<*>].rint(double[<*>] x) = $$rint(x);
macro double[<*>] double[<*>].round(double[<*>] x) = $$round(x);
macro double[<*>] double[<*>].roundeven(double[<*>] x) = $$roundeven(x);
macro double[<*>] double[<*>].trunc(double[<*>] x) = $$trunc(x);
macro ichar ichar[<*>].sum(ichar[<*>] x) = $$reduce_add(x);
macro ichar ichar[<*>].product(ichar[<*>] x) = $$reduce_mul(x);
macro ichar ichar[<*>].and(ichar[<*>] x) = $$reduce_and(x);
macro ichar ichar[<*>].or(ichar[<*>] x) = $$reduce_or(x);
macro ichar ichar[<*>].xor(ichar[<*>] x) = $$reduce_xor(x);
macro ichar ichar[<*>].max(ichar[<*>] x) = $$reduce_max(x);
macro ichar ichar[<*>].min(ichar[<*>] x) = $$reduce_min(x);
macro short short[<*>].sum(short[<*>] x) = $$reduce_add(x);
macro short short[<*>].product(short[<*>] x) = $$reduce_mul(x);
macro short short[<*>].and(short[<*>] x) = $$reduce_and(x);
macro short short[<*>].or(short[<*>] x) = $$reduce_or(x);
macro short short[<*>].xor(short[<*>] x) = $$reduce_xor(x);
macro short short[<*>].max(short[<*>] x) = $$reduce_max(x);
macro short short[<*>].min(short[<*>] x) = $$reduce_min(x);
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) = $$veccomplt(x, y);
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) = $$veccomple(x, y);
macro bool[<*>] int[<*>].comp_eq(int[<*>] x, int[<*>] y) = $$veccompeq(x, y);
macro bool[<*>] int[<*>].comp_gt(int[<*>] x, int[<*>] y) = $$veccompgt(x, y);
macro bool[<*>] int[<*>].comp_ge(int[<*>] x, int[<*>] y) = $$veccompge(x, y);
macro bool[<*>] int[<*>].comp_ne(int[<*>] x, int[<*>] y) = $$veccompne(x, y);
macro int int[<*>].sum(int[<*>] x) = $$reduce_add(x);
macro int int[<*>].product(int[<*>] x) = $$reduce_mul(x);
macro int int[<*>].and(int[<*>] x) = $$reduce_and(x);
macro int int[<*>].or(int[<*>] x) = $$reduce_or(x);
macro int int[<*>].xor(int[<*>] x) = $$reduce_xor(x);
macro int int[<*>].max(int[<*>] x) = $$reduce_max(x);
macro int int[<*>].min(int[<*>] x) = $$reduce_min(x);
macro long long[<*>].sum(long[<*>] x) = $$reduce_add(x);
macro long long[<*>].product(long[<*>] x) = $$reduce_mul(x);
macro long long[<*>].and(long[<*>] x) = $$reduce_and(x);
macro long long[<*>].or(long[<*>] x) = $$reduce_or(x);
macro long long[<*>].xor(long[<*>] x) = $$reduce_xor(x);
macro long long[<*>].max(long[<*>] x) = $$reduce_max(x);
macro long long[<*>].min(long[<*>] x) = $$reduce_min(x);
macro int128 int128[<*>].sum(int128[<*>] x) = $$reduce_add(x);
macro int128 int128[<*>].product(int128[<*>] x) = $$reduce_mul(x);
macro int128 int128[<*>].and(int128[<*>] x) = $$reduce_and(x);
macro int128 int128[<*>].or(int128[<*>] x) = $$reduce_or(x);
macro int128 int128[<*>].xor(int128[<*>] x) = $$reduce_xor(x);
macro int128 int128[<*>].max(int128[<*>] x) = $$reduce_max(x);
macro int128 int128[<*>].min(int128[<*>] x) = $$reduce_min(x);
macro bool bool[<*>].sum(bool[<*>] x) = $$reduce_add(x);
macro bool bool[<*>].product(bool[<*>] x) = $$reduce_mul(x);
macro bool bool[<*>].and(bool[<*>] x) = $$reduce_and(x);
macro bool bool[<*>].or(bool[<*>] x) = $$reduce_or(x);
macro bool bool[<*>].xor(bool[<*>] x) = $$reduce_xor(x);
macro bool bool[<*>].max(bool[<*>] x) = $$reduce_max(x);
macro bool bool[<*>].min(bool[<*>] x) = $$reduce_min(x);
macro char char[<*>].sum(char[<*>] x) = $$reduce_add(x);
macro char char[<*>].product(char[<*>] x) = $$reduce_mul(x);
macro char char[<*>].and(char[<*>] x) = $$reduce_and(x);
macro char char[<*>].or(char[<*>] x) = $$reduce_or(x);
macro char char[<*>].xor(char[<*>] x) = $$reduce_xor(x);
macro char char[<*>].max(char[<*>] x) = $$reduce_max(x);
macro char char[<*>].min(char[<*>] x) = $$reduce_min(x);
macro ushort ushort[<*>].sum(ushort[<*>] x) = $$reduce_add(x);
macro ushort ushort[<*>].product(ushort[<*>] x) = $$reduce_mul(x);
macro ushort ushort[<*>].and(ushort[<*>] x) = $$reduce_and(x);
macro ushort ushort[<*>].or(ushort[<*>] x) = $$reduce_or(x);
macro ushort ushort[<*>].xor(ushort[<*>] x) = $$reduce_xor(x);
macro ushort ushort[<*>].max(ushort[<*>] x) = $$reduce_max(x);
macro ushort ushort[<*>].min(ushort[<*>] x) = $$reduce_min(x);
macro uint uint[<*>].sum(uint[<*>] x) = $$reduce_add(x);
macro uint uint[<*>].product(uint[<*>] x) = $$reduce_mul(x);
macro uint uint[<*>].and(uint[<*>] x) = $$reduce_and(x);
macro uint uint[<*>].or(uint[<*>] x) = $$reduce_or(x);
macro uint uint[<*>].xor(uint[<*>] x) = $$reduce_xor(x);
macro uint uint[<*>].max(uint[<*>] x) = $$reduce_max(x);
macro uint uint[<*>].min(uint[<*>] x) = $$reduce_min(x);
macro ulong ulong[<*>].sum(ulong[<*>] x) = $$reduce_add(x);
macro ulong ulong[<*>].product(ulong[<*>] x) = $$reduce_mul(x);
macro ulong ulong[<*>].and(ulong[<*>] x) = $$reduce_and(x);
macro ulong ulong[<*>].or(ulong[<*>] x) = $$reduce_or(x);
macro ulong ulong[<*>].xor(ulong[<*>] x) = $$reduce_xor(x);
macro ulong ulong[<*>].max(ulong[<*>] x) = $$reduce_max(x);
macro ulong ulong[<*>].min(ulong[<*>] x) = $$reduce_min(x);
macro uint128 uint128[<*>].sum(uint128[<*>] x) = $$reduce_add(x);
macro uint128 uint128[<*>].product(uint128[<*>] x) = $$reduce_mul(x);
macro uint128 uint128[<*>].and(uint128[<*>] x) = $$reduce_and(x);
macro uint128 uint128[<*>].or(uint128[<*>] x) = $$reduce_or(x);
macro uint128 uint128[<*>].xor(uint128[<*>] x) = $$reduce_xor(x);
macro uint128 uint128[<*>].max(uint128[<*>] x) = $$reduce_max(x);
macro uint128 uint128[<*>].min(uint128[<*>] x) = $$reduce_min(x);
/**
* @checked x & 1
*/
macro bool is_power_of_2(x)
{
return x != 0 && (x & (x - 1)) == 0;
}
macro next_power_of_2(x)
{
$typeof(x) y = 1;
while (y < x) y += y;
return y;
}

View File

@@ -1,31 +0,0 @@
module std::math::complex<Type>;
union Complex
{
struct
{
Type r, c;
}
Type[<2>] v;
}
macro Complex Complex.add(Complex a, Complex b)
{
return Complex { .v = a.v + b.v };
}
macro Complex Complex.sub(Complex a, Complex b)
{
return Complex { .v = a.v - b.v };
}
macro Complex Complex.mult(Complex a, Complex b)
{
return Complex { .r = a.r * b.r - a.c * b.c, .c = a.r * b.c + b.r * a.c };
}
fn Complex Complex.div(Complex a, Complex b)
{
Type div = b.r * b.r + b.c * b.c;
return Complex { .r = (a.r * b.r + a.c * b.c) / div, .c = (a.c * b.r - a.r * b.c) / div };
}

View File

@@ -1,47 +0,0 @@
module math;
struct SimpleRandom
{
long seed;
}
private const long SIMPLE_RANDOM_MULTIPLIER = 0x5DEECE66D;
private const long SIMPLE_RANDOM_ADDEND = 0xB;
private const long SIMPLE_RANDOM_MASK = (1i64 << 48) - 1;
private fn long simple_random_initial_scramble(long seed)
{
return (seed ^ SIMPLE_RANDOM_MULTIPLIER) & SIMPLE_RANDOM_MASK;
}
private fn int SimpleRandom.next(SimpleRandom* r, int bits)
{
long nextseed = (r.seed * SIMPLE_RANDOM_MULTIPLIER + SIMPLE_RANDOM_ADDEND) & SIMPLE_RANDOM_MASK;
r.seed = nextseed;
return (int)nextseed >> (48 - bits);
}
fn void SimpleRandom.set_seed(SimpleRandom* r, long seed)
{
r.seed = simple_random_initial_scramble(seed);
}
fn int SimpleRandom.next_int(SimpleRandom* r)
{
return r.next(32) @inline;
}
fn bool SimpleRandom.next_bool(SimpleRandom* r)
{
return r.next(1) != 0;
}
fn float SimpleRandom.next_float(SimpleRandom* r)
{
return r.next(24) / (float)(1 << 24);
}
fn double SimpleRandom.next_double(SimpleRandom* r)
{
return (((long)(r.next(26)) << 27) + r.next(27)) * 0x1.0p-53;
}

950
lib/std/math/math.c3 Normal file
View File

@@ -0,0 +1,950 @@
// Copyright (c) 2021 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::math;
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)
const LN2 = 0.693147180559945309417232121458176568; // ln(2)
const LN10 = 2.30258509299404568401799145468436421; // ln(10)
const PI = 3.14159265358979323846264338327950288419716939937510; // pi
const PI_2 = 1.57079632679489661923132169163975144; // pi / 2
const PI_4 = 0.785398163397448309615660845819875721; // pi / 4
const DIV_PI = 0.318309886183790671537767526745028724; // 1 / pi
const DIV_2_PI = 0.636619772367581343075535053490057448; // 2 / pi
const DIV_2_SQRTPI = 1.12837916709551257389615890312154517; // 2/sqrt(pi)
const SQRT2 = 1.41421356237309504880168872420969808; // sqrt(2)
const double DIV_1_SQRT2 = 0.707106781186547524400844362104849039; // 1 / sqrt(2)
const HALF_MAX = 6.5504e+4;
const HALF_MIN = 6.103515625e-5;
const HALF_DENORM_MIN = 5.9604644775390625e-8;
const HALF_DIG = 3;
const HALF_DEC_DIGITS = 5;
const HALF_MANT_DIG = 11;
const HALF_MAX_10_EXP = 4;
const HALF_MIN_10_EXP = -4;
const HALF_MAX_EXP = 16;
const HALF_MIN_EXP = -13;
const HALF_EPSILON = 9.765625e-4;
const FLOAT_MAX = 0x1.fffffep+127;
const FLOAT_MIN = 1.17549435e-38;
const FLOAT_DENORM_MIN = 1.40129846432481707092e-45;
const FLOAT_DIG = 6;
const FLOAT_DEC_DIGITS = 9;
const FLOAT_MANT_DIG = 24;
const FLOAT_MAX_10_EXP = 38;
const FLOAT_MIN_10_EXP = -37;
const FLOAT_MAX_EXP = 128;
const FLOAT_MIN_EXP = -125;
const FLOAT_EPSILON = 1.1920928955078125e-07;
const DOUBLE_MAX = 1.79769313486231570815e+308;
const DOUBLE_MIN = 2.2250738585072014e-308;
const DOUBLE_DENORM_MIN = 4.94065645841246544177e-324;
const DOUBLE_DIG = 15;
const DOUBLE_DEC_DIGITS = 17;
const DOUBLE_MANT_DIG = 53;
const DOUBLE_MAX_10_EXP = 308;
const DOUBLE_MIN_10_EXP = -307;
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,
TO_NEAREST,
TOWARD_INFINITY,
TOWARD_NEG_INFINITY
}
fault MatrixError
{
MATRIX_INVERSE_DOESNT_EXIST,
}
def Complexf = Complex<float>;
def Complex = Complex<double>;
def complexf_identity = complex::identity<float>;
def complex_identity = complex::identity<double>;
def Quaternionf = Quaternion<float>;
def Quaternion = Quaternion<double>;
def quaternionf_identity = quaternion::identity<float>;
def quaternion_identity = quaternion::identity<double>;
def Matrix2f = Matrix2x2<float>;
def Matrix2 = Matrix2x2<double>;
def Matrix3f = Matrix3x3<float>;
def Matrix3 = Matrix3x3<double>;
def Matrix4f = Matrix4x4<float>;
def Matrix4 = Matrix4x4<double>;
def matrix4_ortho = matrix::ortho<double>;
def matrix4_perspective = matrix::perspective<double>;
def matrix4f_ortho = matrix::ortho<float>;
def matrix4f_perspective = matrix::perspective<float>;
def MATRIX2_IDENTITY = matrix::IDENTITY2<double>;
def MATRIX2F_IDENTITY = matrix::IDENTITY2<float>;
def MATRIX3_IDENTITY = matrix::IDENTITY3<double>;
def MATRIX3F_IDENTITY = matrix::IDENTITY3<float>;
def MATRIX4_IDENTITY = matrix::IDENTITY4<double>;
def MATRIX4F_IDENTITY = matrix::IDENTITY4<float>;
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
**/
macro abs(x) => $$abs(x);
/**
* @require values::@is_int(x) `The input must be an integer`
**/
macro sign(x)
{
var $Type = $typeof(x);
$if $Type.kindof == TypeKind.UNSIGNED_INT:
return ($Type)(x > 0);
$else
return ($Type)(x > 0) - ($Type)(x < 0);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
* @checked x + y
**/
macro atan2(x, y)
{
$if @typeis(x, float) && @typeis(y, float):
return _atan2f(x, y);
$else
return _atan2(x, y);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
* @checked (*y)[0] = x, y.len
* @require y.len == 2
**/
macro sincos(x, y)
{
$if $typeof(y[0]).typeid == float.typeid:
return _sincosf(x, y);
$else
return _sincos(x, y);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
* @checked x
**/
macro atan(x)
{
$if $typeof(x).typeid == float.typeid:
return _atanf(x);
$else
return _atan(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro atanh(x)
{
$if $typeof(x).typeid == float.typeid:
return _atanhf(x);
$else
return _atanh(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro acos(x)
{
$if $typeof(x).typeid == float.typeid:
return _acosf(x);
$else
return _acos(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro acosh(x)
{
$if $typeof(x).typeid == float.typeid:
return _acoshf(x);
$else
return _acosh(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro asin(x)
{
$if $typeof(x).typeid == float.typeid:
return _asinf(x);
$else
return _asin(x);
$endif
}
/**
* @require values::@is_int(x) || values::@is_float(x) "Expected an integer or floating point value"
**/
macro asinh(x)
{
$if $typeof(x).typeid == float.typeid:
return _asinhf(x);
$else
return _asinh(x);
$endif
}
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro ceil(x) => $$ceil(x);
/**
* Constrain the value to lie within the given interval.
*
* @param x "the value to clamp, may be a number or a numerical vector."
* @param lower "the lower bounds"
* @param upper "the upper bounds"
* @return "lower if x < lower, upper if x > upper, otherwise return x."
*
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
* @checked $typeof(x) z = lower `The lower bound must be convertable to the value type.`
* @checked $typeof(x) z = upper `The upper bound must be convertable to the value type.`
**/
macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))upper));
/**
* @require values::@is_promotable_to_floatlike(mag) `The input must be a number value or float vector`
* @require @convertable(sgn, $typeof(values::promote_int(mag)))
**/
macro copysign(mag, sgn) => $$copysign(values::promote_int(mag), ($typeof(values::promote_int(mag)))sgn);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cos(x) => $$cos(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cosec(x) => 1 / sin(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cosech(x) => 2 / (exp(x) - exp(-x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cosh(x) => (exp(x) + exp(-x)) / 2.0;
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cotan(x) => cos(x) / sin(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro cotanh(x) => (exp(2.0 * x) + 1.0) / (exp(2.0 * x) - 1.0);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro exp(x) => $$exp(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro exp2(x) => $$exp2(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number value or float vector`
**/
macro floor(x) => $$floor(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(a) `The input must be a number or float vector`
* @require values::@is_promotable_to_floatlike(b) `The input must be a number or float vector`
* @require values::@is_promotable_to_floatlike(c) `The input must be a number or float vector`
* @require types::@is_same_vector_type(a, b) `The input types must be equal`
* @require types::@is_same_vector_type(a, c) `The input types must match`
**/
macro fma(a, b, c) => $$fma(a, b, c);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
* @require values::@is_promotable_to_floatlike(y) `The input must be a number or a float vector`
* @require types::@is_same_vector_type(x, y) `The input types must match`
**/
macro hypot(x, y) => sqrt(sqr(x) + sqr(y));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro log(x) => $$log(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro log2(x) => $$log2(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro log10(x) => $$log10(values::promote_int(x));
/**
* @require types::is_numerical($typeof(x)) `The input must be a floating point value or float vector`
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
**/
macro max(x, y, ...)
{
$if $vacount == 0:
return $$max(x, y);
$else
var m = $$max(x, y);
$for (var $i = 0; $i < $vacount; $i++)
m = $$max(m, $vaarg($i));
$endfor
return m;
$endif
}
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
**/
macro min(x, y, ...)
{
$if $vacount == 0:
return $$min(x, y);
$else
var m = $$min(x, y);
$for (var $i = 0; $i < $vacount; $i++)
m = $$min(m, $vaarg($i));
$endfor
return m;
$endif
}
/**
* @require types::@is_float(a) `The input must be a floating point value`
* @require types::@has_same(a, b, c) `The input types must be equal`
**/
macro muladd(a, b, c) => $$fmuladd(a, b, c);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro nearbyint(x) => $$nearbyint(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
* @require values::@convertable_to(exp, x) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
**/
macro pow(x, exp)
{
$if types::is_floatlike($typeof(exp)):
return $$pow(x, ($typeof(x))exp);
$else
return $$pow_int(x, exp);
$endif
}
/**
* @require values::@is_promotable_to_float(x) : `The input must be integer or floating type`
**/
macro frexp(x, int* e)
{
$switch ($typeof(x))
$case float:
$case float16:
return _frexpf((float)x, e);
$default:
return _frexp((double)x, e);
$endswitch
}
/**
* @require values::@is_promotable_to_float(x) : `The input must be integer or floating type`
**/
macro int signbit(x)
{
$switch ($typeof(x))
$case float:
$case float16:
return bitcast((float)x, uint) >> 31;
$default:
return (int)(bitcast((double)x, ulong) >> 63);
$endswitch
}
/**
* @require values::@is_floatlike(x) `The input must be a number or a float vector`
**/
macro rint(x) => $$rint(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro round(x) => $$round(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro roundeven(x) => $$roundeven(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sec(x) => 1 / cos(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sech(x) => 2 / (exp(x) + exp(-x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sin(x) => $$sin(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sinh(x) => (exp(x) - exp(-x)) / 2.0;
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sqr(x) => values::promote_int(x) * values::promote_int(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro sqrt(x) => $$sqrt(values::promote_int(x));
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro tan(x)
{
var $Type = $typeof(x);
$switch
$case types::is_vector($Type):
return $$sin(x) / $$cos(x);
$case $Type.typeid == float.typeid:
return _tanf(x);
$default:
return _tan(x);
$endswitch
}
/**
* @require values::@is_promotable_to_float(x) `The input must be a float`
**/
macro bool is_finite(x)
{
$switch ($typeof(x))
$case float:
$case float16:
return bitcast((float)x, uint) & 0x7fffffff < 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) < 0x7ffu64 << 52;
$endswitch
}
/**
* @require values::@is_promotable_to_float(x) `The input must be a float`
**/
macro is_nan(x)
{
$switch ($typeof(x))
$case float:
$case float16:
return bitcast((float)x, uint) & 0x7fffffff > 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ffu64 << 52;
$endswitch
}
/**
* @require values::@is_promotable_to_float(x) `The input must be a float`
**/
macro is_inf(x)
{
$switch ($typeof(x))
$case float:
$case float16:
return bitcast((float)x, uint) & 0x7fffffff == 0x7f800000;
$default:
return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ffu64 << 52;
$endswitch
}
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
**/
macro tanh(x) => (exp(2.0 * x) - 1.0) / (exp(2.0 * x) + 1.0);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro trunc(x) => $$trunc(x);
macro lerp(x, y, amount) @private => x + (y - x) * amount;
macro reflect(x, y) @private
{
var dot = x.dot(y);
return x - 2 * y * dot;
}
macro normalize(x) @private
{
var len = x.length();
if (len == 0) return x;
return x * (1 / len);
}
macro float float.ceil(float x) => $$ceil(x);
macro float float.clamp(float x, float lower, float upper) => $$max(lower, $$min(x, upper));
macro float float.copysign(float mag, float sgn) => $$copysign(mag, sgn);
macro float float.floor(float x) => $$floor(x);
macro float float.fma(float a, float b, float c) => $$fma(a, b, c);
macro float float.muladd(float a, float b, float c) => $$fmuladd(a, b, c);
macro float float.nearbyint(float x) => $$nearbyint(x);
macro float float.pow(float x, exp) => pow(x, exp);
macro float float.rint(float x) => $$rint(x);
macro float float.round(float x) => $$round(x);
macro float float.roundeven(float x) => $$roundeven(x);
macro float float.trunc(float x) => $$trunc(x);
macro float float[<*>].sum(float[<*>] x, float start = 0.0) => $$reduce_fadd(x, start);
macro float float[<*>].product(float[<*>] x, float start = 1.0) => $$reduce_fmul(x, start);
macro float float[<*>].max(float[<*>] x) => $$reduce_max(x);
macro float float[<*>].min(float[<*>] x) => $$reduce_min(x);
macro float[<*>] float[<*>].ceil(float[<*>] x) => $$ceil(x);
macro float[<*>] float[<*>].clamp(float[<*>] x, float[<*>] lower, float[<*>] upper) => $$max(lower, $$min(x, upper));
macro float[<*>] float[<*>].copysign(float[<*>] mag, float[<*>] sgn) => $$copysign(mag, sgn);
macro float[<*>] float[<*>].fma(float[<*>] a, float[<*>] b, float[<*>] c) => $$fma(a, b, c);
macro float[<*>] float[<*>].floor(float[<*>] x) => $$floor(x);
macro float[<*>] float[<*>].nearbyint(float[<*>] x) => $$nearbyint(x);
macro float[<*>] float[<*>].pow(float[<*>] x, exp) => pow(x, exp);
macro float[<*>] float[<*>].rint(float[<*>] x) => $$rint(x);
macro float[<*>] float[<*>].round(float[<*>] x) => $$round(x);
macro float[<*>] float[<*>].roundeven(float[<*>] x) => $$roundeven(x);
macro float[<*>] float[<*>].trunc(float[<*>] x) => $$trunc(x);
macro float float[<*>].dot(float[<*>] x, float[<*>] y) => (x * y).sum();
macro float float[<*>].length(float[<*>] x) => $$sqrt(x.dot(x));
macro float float[<*>].distance(float[<*>] x, float[<*>] y) => (x - y).length();
macro float[<*>] float[<*>].normalize(float[<*>] x) => normalize(x);
macro float[<*>] float[<*>].lerp(float[<*>] x, float[<*>] y, float amount) => lerp(x, y, amount);
macro float[<*>] float[<*>].reflect(float[<*>] x, float[<*>] y) => reflect(x, y);
macro bool float[<*>].equals(float[<*>] x, float[<*>] y) => equals_vec(x, y);
macro bool[<*>] float[<*>].comp_lt(float[<*>] x, float[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] float[<*>].comp_le(float[<*>] x, float[<*>] y) => $$veccomple(x, y);
macro bool[<*>] float[<*>].comp_eq(float[<*>] x, float[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] float[<*>].comp_gt(float[<*>] x, float[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] float[<*>].comp_ge(float[<*>] x, float[<*>] y) => $$veccompge(x, y);
macro bool[<*>] float[<*>].comp_ne(float[<*>] x, float[<*>] y) => $$veccompne(x, y);
macro double double.ceil(double x) => $$ceil(x);
macro double double.clamp(double x, double lower, double upper) => $$max(lower, $$min(x, upper));
macro double double.copysign(double mag, double sgn) => $$copysign(mag, sgn);
macro double double.floor(double x) => $$floor(x);
macro double double.fma(double a, double b, double c) => $$fma(a, b, c);
macro double double.muladd(double a, double b, double c) => $$fmuladd(a, b, c);
macro double double.nearbyint(double x) => $$nearbyint(x);
macro double double.pow(double x, exp) => pow(x, exp);
macro double double.rint(double x) => $$rint(x);
macro double double.round(double x) => $$round(x);
macro double double.roundeven(double x) => $$roundeven(x);
macro double double.trunc(double x) => $$trunc(x);
macro double double[<*>].sum(double[<*>] x, double start = 0.0) => $$reduce_fadd(x, start);
macro double double[<*>].product(double[<*>] x, double start = 1.0) => $$reduce_fmul(x, start);
macro double double[<*>].max(double[<*>] x) => $$reduce_fmax(x);
macro double double[<*>].min(double[<*>] x) => $$reduce_fmin(x);
macro double[<*>] double[<*>].ceil(double[<*>] x) => $$ceil(x);
macro double[<*>] double[<*>].clamp(double[<*>] x, double[<*>] lower, double[<*>] upper) => $$max(lower, $$min(x, upper));
macro double[<*>] double[<*>].copysign(double[<*>] mag, double[<*>] sgn) => $$copysign(mag, sgn);
macro double[<*>] double[<*>].floor(double[<*>] x) => $$floor(x);
macro double[<*>] double[<*>].fma(double[<*>] a, double[<*>] b, double[<*>] c) => $$fma(a, b, c);
macro double[<*>] double[<*>].nearbyint(double[<*>] x) => $$nearbyint(x);
macro double[<*>] double[<*>].pow(double[<*>] x, exp) => pow(x, exp);
macro double[<*>] double[<*>].rint(double[<*>] x) => $$rint(x);
macro double[<*>] double[<*>].round(double[<*>] x) => $$round(x);
macro double[<*>] double[<*>].roundeven(double[<*>] x) => $$roundeven(x);
macro double[<*>] double[<*>].trunc(double[<*>] x) => $$trunc(x);
macro double double[<*>].dot(double[<*>] x, double[<*>] y) => (x * y).sum();
macro double double[<*>].length(double[<*>] x) => $$sqrt(x.dot(x));
macro double double[<*>].distance(double[<*>] x, double[<*>] y) => (x - y).length();
macro double[<*>] double[<*>].normalize(double[<*>] x) => normalize(x);
macro double[<*>] double[<*>].reflect(double[<*>] x, double[<*>] y) => reflect(x, y);
macro double[<*>] double[<*>].lerp(double[<*>] x, double[<*>] y, double amount) => lerp(x, y, amount);
macro bool double[<*>].equals(double[<*>] x, double[<*>] y) => equals_vec(x, y);
macro bool[<*>] double[<*>].comp_lt(double[<*>] x, double[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] double[<*>].comp_le(double[<*>] x, double[<*>] y) => $$veccomple(x, y);
macro bool[<*>] double[<*>].comp_eq(double[<*>] x, double[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] double[<*>].comp_gt(double[<*>] x, double[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] double[<*>].comp_ge(double[<*>] x, double[<*>] y) => $$veccompge(x, y);
macro bool[<*>] double[<*>].comp_ne(double[<*>] x, double[<*>] y) => $$veccompne(x, y);
macro bool[<*>] ichar[<*>].comp_lt(ichar[<*>] x, ichar[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ichar[<*>].comp_le(ichar[<*>] x, ichar[<*>] y) => $$veccomple(x, y);
macro bool[<*>] ichar[<*>].comp_eq(ichar[<*>] x, ichar[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] ichar[<*>].comp_gt(ichar[<*>] x, ichar[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] ichar[<*>].comp_ge(ichar[<*>] x, ichar[<*>] y) => $$veccompge(x, y);
macro bool[<*>] ichar[<*>].comp_ne(ichar[<*>] x, ichar[<*>] y) => $$veccompne(x, y);
macro ichar ichar[<*>].sum(ichar[<*>] x) => $$reduce_add(x);
macro ichar ichar[<*>].product(ichar[<*>] x) => $$reduce_mul(x);
macro ichar ichar[<*>].and(ichar[<*>] x) => $$reduce_and(x);
macro ichar ichar[<*>].or(ichar[<*>] x) => $$reduce_or(x);
macro ichar ichar[<*>].xor(ichar[<*>] x) => $$reduce_xor(x);
macro ichar ichar[<*>].max(ichar[<*>] x) => $$reduce_max(x);
macro ichar ichar[<*>].min(ichar[<*>] x) => $$reduce_min(x);
macro bool[<*>] short[<*>].comp_lt(short[<*>] x, short[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] short[<*>].comp_le(short[<*>] x, short[<*>] y) => $$veccomple(x, y);
macro bool[<*>] short[<*>].comp_eq(short[<*>] x, short[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] short[<*>].comp_gt(short[<*>] x, short[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] short[<*>].comp_ge(short[<*>] x, short[<*>] y) => $$veccompge(x, y);
macro bool[<*>] short[<*>].comp_ne(short[<*>] x, short[<*>] y) => $$veccompne(x, y);
macro short short[<*>].sum(short[<*>] x) => $$reduce_add(x);
macro short short[<*>].product(short[<*>] x) => $$reduce_mul(x);
macro short short[<*>].and(short[<*>] x) => $$reduce_and(x);
macro short short[<*>].or(short[<*>] x) => $$reduce_or(x);
macro short short[<*>].xor(short[<*>] x) => $$reduce_xor(x);
macro short short[<*>].max(short[<*>] x) => $$reduce_max(x);
macro short short[<*>].min(short[<*>] x) => $$reduce_min(x);
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) => $$veccomple(x, y);
macro bool[<*>] int[<*>].comp_eq(int[<*>] x, int[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] int[<*>].comp_gt(int[<*>] x, int[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] int[<*>].comp_ge(int[<*>] x, int[<*>] y) => $$veccompge(x, y);
macro bool[<*>] int[<*>].comp_ne(int[<*>] x, int[<*>] y) => $$veccompne(x, y);
macro int int[<*>].sum(int[<*>] x) => $$reduce_add(x);
macro int int[<*>].product(int[<*>] x) => $$reduce_mul(x);
macro int int[<*>].and(int[<*>] x) => $$reduce_and(x);
macro int int[<*>].or(int[<*>] x) => $$reduce_or(x);
macro int int[<*>].xor(int[<*>] x) => $$reduce_xor(x);
macro int int[<*>].max(int[<*>] x) => $$reduce_max(x);
macro int int[<*>].min(int[<*>] x) => $$reduce_min(x);
macro bool[<*>] long[<*>].comp_lt(long[<*>] x, long[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] long[<*>].comp_le(long[<*>] x, long[<*>] y) => $$veccomple(x, y);
macro bool[<*>] long[<*>].comp_eq(long[<*>] x, long[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] long[<*>].comp_gt(long[<*>] x, long[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] long[<*>].comp_ge(long[<*>] x, long[<*>] y) => $$veccompge(x, y);
macro bool[<*>] long[<*>].comp_ne(long[<*>] x, long[<*>] y) => $$veccompne(x, y);
macro long long[<*>].sum(long[<*>] x) => $$reduce_add(x);
macro long long[<*>].product(long[<*>] x) => $$reduce_mul(x);
macro long long[<*>].and(long[<*>] x) => $$reduce_and(x);
macro long long[<*>].or(long[<*>] x) => $$reduce_or(x);
macro long long[<*>].xor(long[<*>] x) => $$reduce_xor(x);
macro long long[<*>].max(long[<*>] x) => $$reduce_max(x);
macro long long[<*>].min(long[<*>] x) => $$reduce_min(x);
macro bool[<*>] int128[<*>].comp_lt(int128[<*>] x, int128[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] int128[<*>].comp_le(int128[<*>] x, int128[<*>] y) => $$veccomple(x, y);
macro bool[<*>] int128[<*>].comp_eq(int128[<*>] x, int128[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] int128[<*>].comp_gt(int128[<*>] x, int128[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] int128[<*>].comp_ge(int128[<*>] x, int128[<*>] y) => $$veccompge(x, y);
macro bool[<*>] int128[<*>].comp_ne(int128[<*>] x, int128[<*>] y) => $$veccompne(x, y);
macro int128 int128[<*>].sum(int128[<*>] x) => $$reduce_add(x);
macro int128 int128[<*>].product(int128[<*>] x) => $$reduce_mul(x);
macro int128 int128[<*>].and(int128[<*>] x) => $$reduce_and(x);
macro int128 int128[<*>].or(int128[<*>] x) => $$reduce_or(x);
macro int128 int128[<*>].xor(int128[<*>] x) => $$reduce_xor(x);
macro int128 int128[<*>].max(int128[<*>] x) => $$reduce_max(x);
macro int128 int128[<*>].min(int128[<*>] x) => $$reduce_min(x);
macro bool[<*>] bool[<*>].comp_lt(bool[<*>] x, bool[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] bool[<*>].comp_le(bool[<*>] x, bool[<*>] y) => $$veccomple(x, y);
macro bool[<*>] bool[<*>].comp_eq(bool[<*>] x, bool[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] bool[<*>].comp_gt(bool[<*>] x, bool[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] bool[<*>].comp_ge(bool[<*>] x, bool[<*>] y) => $$veccompge(x, y);
macro bool[<*>] bool[<*>].comp_ne(bool[<*>] x, bool[<*>] y) => $$veccompne(x, y);
macro bool bool[<*>].sum(bool[<*>] x) => $$reduce_add(x);
macro bool bool[<*>].product(bool[<*>] x) => $$reduce_mul(x);
macro bool bool[<*>].and(bool[<*>] x) => $$reduce_and(x);
macro bool bool[<*>].or(bool[<*>] x) => $$reduce_or(x);
macro bool bool[<*>].xor(bool[<*>] x) => $$reduce_xor(x);
macro bool bool[<*>].max(bool[<*>] x) => $$reduce_max(x);
macro bool bool[<*>].min(bool[<*>] x) => $$reduce_min(x);
macro bool[<*>] char[<*>].comp_lt(char[<*>] x, char[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] char[<*>].comp_le(char[<*>] x, char[<*>] y) => $$veccomple(x, y);
macro bool[<*>] char[<*>].comp_eq(char[<*>] x, char[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] char[<*>].comp_gt(char[<*>] x, char[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] char[<*>].comp_ge(char[<*>] x, char[<*>] y) => $$veccompge(x, y);
macro bool[<*>] char[<*>].comp_ne(char[<*>] x, char[<*>] y) => $$veccompne(x, y);
macro char char[<*>].sum(char[<*>] x) => $$reduce_add(x);
macro char char[<*>].product(char[<*>] x) => $$reduce_mul(x);
macro char char[<*>].and(char[<*>] x) => $$reduce_and(x);
macro char char[<*>].or(char[<*>] x) => $$reduce_or(x);
macro char char[<*>].xor(char[<*>] x) => $$reduce_xor(x);
macro char char[<*>].max(char[<*>] x) => $$reduce_max(x);
macro char char[<*>].min(char[<*>] x) => $$reduce_min(x);
macro bool[<*>] ushort[<*>].comp_lt(ushort[<*>] x, ushort[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ushort[<*>].comp_le(ushort[<*>] x, ushort[<*>] y) => $$veccomple(x, y);
macro bool[<*>] ushort[<*>].comp_eq(ushort[<*>] x, ushort[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] ushort[<*>].comp_gt(ushort[<*>] x, ushort[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] ushort[<*>].comp_ge(ushort[<*>] x, ushort[<*>] y) => $$veccompge(x, y);
macro bool[<*>] ushort[<*>].comp_ne(ushort[<*>] x, ushort[<*>] y) => $$veccompne(x, y);
macro ushort ushort[<*>].sum(ushort[<*>] x) => $$reduce_add(x);
macro ushort ushort[<*>].product(ushort[<*>] x) => $$reduce_mul(x);
macro ushort ushort[<*>].and(ushort[<*>] x) => $$reduce_and(x);
macro ushort ushort[<*>].or(ushort[<*>] x) => $$reduce_or(x);
macro ushort ushort[<*>].xor(ushort[<*>] x) => $$reduce_xor(x);
macro ushort ushort[<*>].max(ushort[<*>] x) => $$reduce_max(x);
macro ushort ushort[<*>].min(ushort[<*>] x) => $$reduce_min(x);
macro bool[<*>] uint[<*>].comp_lt(uint[<*>] x, uint[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] uint[<*>].comp_le(uint[<*>] x, uint[<*>] y) => $$veccomple(x, y);
macro bool[<*>] uint[<*>].comp_eq(uint[<*>] x, uint[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] uint[<*>].comp_gt(uint[<*>] x, uint[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] uint[<*>].comp_ge(uint[<*>] x, uint[<*>] y) => $$veccompge(x, y);
macro bool[<*>] uint[<*>].comp_ne(uint[<*>] x, uint[<*>] y) => $$veccompne(x, y);
macro uint uint[<*>].sum(uint[<*>] x) => $$reduce_add(x);
macro uint uint[<*>].product(uint[<*>] x) => $$reduce_mul(x);
macro uint uint[<*>].and(uint[<*>] x) => $$reduce_and(x);
macro uint uint[<*>].or(uint[<*>] x) => $$reduce_or(x);
macro uint uint[<*>].xor(uint[<*>] x) => $$reduce_xor(x);
macro uint uint[<*>].max(uint[<*>] x) => $$reduce_max(x);
macro uint uint[<*>].min(uint[<*>] x) => $$reduce_min(x);
macro bool[<*>] ulong[<*>].comp_lt(ulong[<*>] x, ulong[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] ulong[<*>].comp_le(ulong[<*>] x, ulong[<*>] y) => $$veccomple(x, y);
macro bool[<*>] ulong[<*>].comp_eq(ulong[<*>] x, ulong[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] ulong[<*>].comp_gt(ulong[<*>] x, ulong[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] ulong[<*>].comp_ge(ulong[<*>] x, ulong[<*>] y) => $$veccompge(x, y);
macro bool[<*>] ulong[<*>].comp_ne(ulong[<*>] x, ulong[<*>] y) => $$veccompne(x, y);
macro ulong ulong[<*>].sum(ulong[<*>] x) => $$reduce_add(x);
macro ulong ulong[<*>].product(ulong[<*>] x) => $$reduce_mul(x);
macro ulong ulong[<*>].and(ulong[<*>] x) => $$reduce_and(x);
macro ulong ulong[<*>].or(ulong[<*>] x) => $$reduce_or(x);
macro ulong ulong[<*>].xor(ulong[<*>] x) => $$reduce_xor(x);
macro ulong ulong[<*>].max(ulong[<*>] x) => $$reduce_max(x);
macro ulong ulong[<*>].min(ulong[<*>] x) => $$reduce_min(x);
macro bool[<*>] uint128[<*>].comp_lt(uint128[<*>] x, uint128[<*>] y) => $$veccomplt(x, y);
macro bool[<*>] uint128[<*>].comp_le(uint128[<*>] x, uint128[<*>] y) => $$veccomple(x, y);
macro bool[<*>] uint128[<*>].comp_eq(uint128[<*>] x, uint128[<*>] y) => $$veccompeq(x, y);
macro bool[<*>] uint128[<*>].comp_gt(uint128[<*>] x, uint128[<*>] y) => $$veccompgt(x, y);
macro bool[<*>] uint128[<*>].comp_ge(uint128[<*>] x, uint128[<*>] y) => $$veccompge(x, y);
macro bool[<*>] uint128[<*>].comp_ne(uint128[<*>] x, uint128[<*>] y) => $$veccompne(x, y);
macro uint128 uint128[<*>].sum(uint128[<*>] x) => $$reduce_add(x);
macro uint128 uint128[<*>].product(uint128[<*>] x) => $$reduce_mul(x);
macro uint128 uint128[<*>].and(uint128[<*>] x) => $$reduce_and(x);
macro uint128 uint128[<*>].or(uint128[<*>] x) => $$reduce_or(x);
macro uint128 uint128[<*>].xor(uint128[<*>] x) => $$reduce_xor(x);
macro uint128 uint128[<*>].max(uint128[<*>] x) => $$reduce_max(x);
macro uint128 uint128[<*>].min(uint128[<*>] x) => $$reduce_min(x);
macro char char.sat_add(char x, char y) => $$sat_add(x, y);
macro char char.sat_sub(char x, char y) => $$sat_sub(x, y);
macro char char.sat_mul(char x, char y) => $$sat_mul(x, y);
macro char char.sat_shl(char x, char y) => $$sat_shl(x, y);
macro ichar ichar.sat_add(ichar x, ichar y) => $$sat_add(x, y);
macro ichar ichar.sat_sub(ichar x, ichar y) => $$sat_sub(x, y);
macro ichar ichar.sat_mul(ichar x, ichar y) => $$sat_mul(x, y);
macro ichar ichar.sat_shl(ichar x, ichar y) => $$sat_shl(x, y);
macro ushort ushort.sat_add(ushort x, ushort y) => $$sat_add(x, y);
macro ushort ushort.sat_sub(ushort x, ushort y) => $$sat_sub(x, y);
macro ushort ushort.sat_mul(ushort x, ushort y) => $$sat_mul(x, y);
macro ushort ushort.sat_shl(ushort x, ushort y) => $$sat_shl(x, y);
macro short short.sat_add(short x, short y) => $$sat_add(x, y);
macro short short.sat_sub(short x, short y) => $$sat_sub(x, y);
macro short short.sat_mul(short x, short y) => $$sat_mul(x, y);
macro short short.sat_shl(short x, short y) => $$sat_shl(x, y);
macro uint uint.sat_add(uint x, uint y) => $$sat_add(x, y);
macro uint uint.sat_sub(uint x, uint y) => $$sat_sub(x, y);
macro uint uint.sat_mul(uint x, uint y) => $$sat_mul(x, y);
macro uint uint.sat_shl(uint x, uint y) => $$sat_shl(x, y);
macro int int.sat_add(int x, int y) => $$sat_add(x, y);
macro int int.sat_sub(int x, int y) => $$sat_sub(x, y);
macro int int.sat_mul(int x, int y) => $$sat_mul(x, y);
macro int int.sat_shl(int x, int y) => $$sat_shl(x, y);
macro ulong ulong.sat_add(ulong x, ulong y) => $$sat_add(x, y);
macro ulong ulong.sat_sub(ulong x, ulong y) => $$sat_sub(x, y);
macro ulong ulong.sat_mul(ulong x, ulong y) => $$sat_mul(x, y);
macro ulong ulong.sat_shl(ulong x, ulong y) => $$sat_shl(x, y);
macro long long.sat_add(long x, long y) => $$sat_add(x, y);
macro long long.sat_sub(long x, long y) => $$sat_sub(x, y);
macro long long.sat_mul(long x, long y) => $$sat_mul(x, y);
macro long long.sat_shl(long x, long y) => $$sat_shl(x, y);
macro uint128 uint128.sat_add(uint128 x, uint128 y) => $$sat_add(x, y);
macro uint128 uint128.sat_sub(uint128 x, uint128 y) => $$sat_sub(x, y);
macro uint128 uint128.sat_mul(uint128 x, uint128 y) => $$sat_mul(x, y);
macro uint128 uint128.sat_shl(uint128 x, uint128 y) => $$sat_shl(x, y);
macro int128 int128.sat_add(int128 x, int128 y) => $$sat_add(x, y);
macro int128 int128.sat_sub(int128 x, int128 y) => $$sat_sub(x, y);
macro int128 int128.sat_mul(int128 x, int128 y) => $$sat_mul(x, y);
macro int128 int128.sat_shl(int128 x, int128 y) => $$sat_shl(x, y);
/**
* @checked x & 1
*/
macro bool is_power_of_2(x)
{
return x != 0 && (x & (x - 1)) == 0;
}
macro next_power_of_2(x)
{
$typeof(x) y = 1;
while (y < x) y += y;
return y;
}
macro equals_vec(v1, v2) @private
{
var $elements = v1.len;
var abs_diff = math::abs(v1 - v2);
var abs_v1 = math::abs(v1);
var abs_v2 = math::abs(v2);
$typeof(abs_v2) eps = 1;
return abs_diff.comp_le(FLOAT_EPSILON * math::max(abs_v1, abs_v2, eps)).and();
}
macro uint double.high_word(double d) => (uint)(bitcast(d, ulong) >> 32);
macro uint double.low_word(double d) => (uint)bitcast(d, ulong);
macro uint float.word(float d) => bitcast(d, uint);
macro double scalbn(double x, int n) => _scalbn(x, n);
extern fn double _atan(double x) @extern("atan");
extern fn float _atanf(float x) @extern("atanf");
extern fn double _atan2(double, double) @extern("atan2");
extern fn float _atan2f(float, float) @extern("atan2f");
extern fn void _sincos(double, double*) @extern("sincos");
extern fn void _sincosf(float, float*) @extern("sincosf");
extern fn double _tan(double x) @extern("tan");
extern fn float _tanf(float x) @extern("tanf");
extern fn double _scalbn(double x, int n) @extern("scalbn");
extern fn double _acos(double x) @extern("acos");
extern fn double _asin(double x) @extern("asin");
extern fn double _acosh(double x) @extern("acosh");
extern fn double _asinh(double x) @extern("asinh");
extern fn double _atanh(double x) @extern("atanh");
extern fn float _acosf(float x) @extern("acosf");
extern fn float _asinf(float x) @extern("asinf");
extern fn float _acoshf(float x) @extern("acoshf");
extern fn float _asinhf(float x) @extern("asinhf");
extern fn float _atanhf(float x) @extern("atanhf");
fn double _frexp(double x, int* e)
{
ulong i = bitcast(x, ulong);
int ee = (int)((i >> 52) & 0x7ff);
switch
{
case !ee:
if (!x)
{
*e = 0;
return x;
}
x = _frexp(x * 0x1p64, e);
*e -= 64;
return x;
case ee == 0x7ff:
return x;
default:
*e = ee - 0x3fe;
i &= 0x800fffffffffffffu64;
i |= 0x3fe0000000000000u64;
return bitcast(i, double);
}
}
fn float _frexpf(float x, int* e)
{
uint i = bitcast(x, uint);
int ee = (i >> 23) & 0xff;
switch
{
case !ee:
if (!x)
{
*e = 0;
return x;
}
x = _frexpf(x * 0x1p64, e);
*e -= 64;
return x;
case ee == 0xff:
return x;
default:
*e = ee - 0x7e;
i &= 0x807fffffu32;
i |= 0x3f000000u32;
return bitcast(i, float);
}
}

View File

@@ -0,0 +1,24 @@
module std::math::complex<Real>;
union Complex
{
struct
{
Real r, c;
}
Real[<2>] v;
}
macro Complex identity() => { 1, 0 };
macro Complex Complex.add(Complex a, Complex b) => Complex { .v = a.v + b.v };
macro Complex Complex.add_each(Complex a, Real b) => Complex { .v = a.v + b };
macro Complex Complex.sub(Complex a, Complex b) => Complex { .v = a.v - b.v };
macro Complex Complex.sub_each(Complex a, Real b) => Complex { .v = a.v - b };
macro Complex Complex.scale(Complex a, Real s) => Complex { .v = a.v * s };
macro Complex Complex.mul(Complex a, Complex b) => { a.r * b.r - a.c * b.c, a.r * b.c + b.r * a.c };
macro Complex Complex.div(Complex a, Complex b)
{
Real div = b.v.dot(b.v);
return Complex{ (a.r * b.r + a.c * b.c) / div, (a.c * b.r - a.r * b.c) / div };
}

View File

@@ -1,9 +1,4 @@
module std::math::matrix;
fault MatrixError
{
MATRIX_INVERSE_DOESNT_EXIST,
}
module std::math::matrix<Real>;
struct Matrix2x2
{
@@ -11,10 +6,10 @@ struct Matrix2x2
{
struct
{
float m00, m01;
float m10, m11;
Real m00, m01;
Real m10, m11;
}
float[4] m;
Real[4] m;
}
}
@@ -24,11 +19,11 @@ struct Matrix3x3
{
struct
{
float m00; float m01; float m02;
float m10; float m11; float m12;
float m20; float m21; float m22;
Real m00, m01, m02;
Real m10, m11, m12;
Real m20, m21, m22;
}
float[9] m;
Real[9] m;
}
}
@@ -38,35 +33,35 @@ struct Matrix4x4
{
struct
{
float m00, m01, m02, m03;
float m10, m11, m12, m13;
float m20, m21, m22, m23;
float m30, m31, m32, m33;
Real m00, m01, m02, m03;
Real m10, m11, m12, m13;
Real m20, m21, m22, m23;
Real m30, m31, m32, m33;
}
float[16] m;
Real[16] m;
}
}
fn float[<2>] Matrix2x2.apply(Matrix2x2* mat, float[<2>] vec)
fn Real[<2>] Matrix2x2.apply(Matrix2x2* mat, Real[<2>] vec)
{
return float[<2>] {
return Real[<2>] {
mat.m00 * vec[0] + mat.m01 * vec[1],
mat.m10 * vec[0] + mat.m11 * vec[1],
};
}
fn float[<3>] Matrix3x3.apply(Matrix3x3* mat, float[<3>] vec)
fn Real[<3>] Matrix3x3.apply(Matrix3x3* mat, Real[<3>] vec)
{
return float[<3>] {
return Real[<3>] {
mat.m00 * vec[0] + mat.m01 * vec[1] + mat.m02 * vec[2],
mat.m10 * vec[0] + mat.m11 * vec[1] + mat.m12 * vec[2],
mat.m20 * vec[0] + mat.m21 * vec[1] + mat.m22 * vec[2],
};
}
fn float[<4>] Matrix4x4.apply(Matrix4x4* mat, float[<4>] vec)
fn Real[<4>] Matrix4x4.apply(Matrix4x4* mat, Real[<4>] vec)
{
return float[<4>] {
return Real[<4>] {
mat.m00 * vec[0] + mat.m01 * vec[1] + mat.m02 * vec[2] + mat.m03 * vec[3],
mat.m10 * vec[0] + mat.m11 * vec[1] + mat.m12 * vec[2] + mat.m13 * vec[3],
mat.m20 * vec[0] + mat.m21 * vec[1] + mat.m22 * vec[2] + mat.m23 * vec[3],
@@ -79,7 +74,7 @@ fn Matrix2x2 Matrix2x2.mul(Matrix2x2* a, Matrix2x2 b)
{
return Matrix2x2 {
a.m00 * b.m00 + a.m01 * b.m10, a.m00 * b.m01 + a.m01 * b.m11,
a.m10 * b.m01 + a.m11 * b.m11, a.m10 * b.m01 + a.m11 * b.m11,
a.m10 * b.m00 + a.m11 * b.m10, a.m10 * b.m01 + a.m11 * b.m11,
};
}
@@ -125,33 +120,18 @@ fn Matrix4x4 Matrix4x4.mul(Matrix4x4* a, Matrix4x4 b)
};
}
fn Matrix2x2 Matrix2x2.component_mul(Matrix2x2* mat, Real s) => matrix_component_mul(mat, s);
fn Matrix3x3 Matrix3x3.component_mul(Matrix3x3* mat, Real s) => matrix_component_mul(mat, s);
fn Matrix4x4 Matrix4x4.component_mul(Matrix4x4* mat, Real s) => matrix_component_mul(mat, s);
fn Matrix2x2 Matrix2x2.component_mul(Matrix2x2* mat, float s)
{
return Matrix2x2 {
mat.m00 * s, mat.m01 * s,
mat.m10 * s, mat.m11 * s,
};
}
fn Matrix2x2 Matrix2x2.add(Matrix2x2* mat, Matrix2x2 mat2) => matrix_add(mat, mat2);
fn Matrix3x3 Matrix3x3.add(Matrix3x3* mat, Matrix3x3 mat2) => matrix_add(mat, mat2);
fn Matrix4x4 Matrix4x4.add(Matrix4x4* mat, Matrix4x4 mat2) => matrix_add(mat, mat2);
fn Matrix3x3 Matrix3x3.component_mul(Matrix3x3* mat, float s)
{
return Matrix3x3 {
mat.m00 * s, mat.m01 * s, mat.m02 * s,
mat.m10 * s, mat.m11 * s, mat.m12 * s,
mat.m20 * s, mat.m21 * s, mat.m22 * s,
};
}
fn Matrix2x2 Matrix2x2.sub(Matrix2x2* mat, Matrix2x2 mat2) => matrix_sub(mat, mat2);
fn Matrix3x3 Matrix3x3.sub(Matrix3x3* mat, Matrix3x3 mat2) => matrix_sub(mat, mat2);
fn Matrix4x4 Matrix4x4.sub(Matrix4x4* mat, Matrix4x4 mat2) => matrix_sub(mat, mat2);
fn Matrix4x4 Matrix4x4.component_mul(Matrix4x4* mat, float s)
{
return Matrix4x4 {
mat.m00 * s, mat.m01 * s, mat.m02 * s, mat.m03 * s,
mat.m10 * s, mat.m11 * s, mat.m12 * s, mat.m13 * s,
mat.m20 * s, mat.m21 * s, mat.m22 * s, mat.m23 * s,
mat.m30 * s, mat.m31 * s, mat.m32 * s, mat.m33 * s,
};
}
fn Matrix2x2 Matrix2x2.transpose(Matrix2x2* mat)
@@ -182,12 +162,12 @@ fn Matrix4x4 Matrix4x4.transpose(Matrix4x4* mat)
}
fn float Matrix2x2.determinant(Matrix2x2* mat)
fn Real Matrix2x2.determinant(Matrix2x2* mat)
{
return mat.m00 * mat.m11 - mat.m01 * mat.m10;
}
fn float Matrix3x3.determinant(Matrix3x3* mat)
fn Real Matrix3x3.determinant(Matrix3x3* mat)
{
return
mat.m00 * (mat.m11 * mat.m22 - mat.m21 * mat.m12) -
@@ -195,7 +175,7 @@ fn float Matrix3x3.determinant(Matrix3x3* mat)
mat.m02 * (mat.m10 * mat.m21 - mat.m20 * mat.m11);
}
fn float Matrix4x4.determinant(Matrix4x4* mat)
fn Real Matrix4x4.determinant(Matrix4x4* mat)
{
return
mat.m00 * (mat.m11 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
@@ -295,30 +275,30 @@ fn Matrix4x4 Matrix4x4.adjoint(Matrix4x4* mat)
fn Matrix2x2! Matrix2x2.inverse(Matrix2x2* m)
{
float det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
Real det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
Matrix2x2 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
fn Matrix3x3! Matrix3x3.inverse(Matrix3x3* m)
{
float det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
Real det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
Matrix3x3 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
fn Matrix4x4! Matrix4x4.inverse(Matrix4x4* m)
{
float det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
Real det = m.determinant();
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST?;
Matrix4x4 adj = m.adjoint();
return adj.component_mul(1 / det).transpose();
}
fn Matrix3x3 Matrix3x3.translate(Matrix3x3* m, float[<2>] v)
fn Matrix3x3 Matrix3x3.translate(Matrix3x3* m, Real[<2>] v)
{
return m.mul(Matrix3x3 {
1, 0, v[0],
@@ -327,7 +307,7 @@ fn Matrix3x3 Matrix3x3.translate(Matrix3x3* m, float[<2>] v)
});
}
fn Matrix4x4 Matrix4x4.translate(Matrix4x4* m, float[<3>] v)
fn Matrix4x4 Matrix4x4.translate(Matrix4x4* m, Real[<3>] v)
{
return m.mul(Matrix4x4 {
1, 0, 0, v[0],
@@ -338,7 +318,7 @@ fn Matrix4x4 Matrix4x4.translate(Matrix4x4* m, float[<3>] v)
}
// r in radians
fn Matrix3x3 Matrix3x3.rotate(Matrix3x3* m, float r)
fn Matrix3x3 Matrix3x3.rotate(Matrix3x3* m, Real r)
{
return m.mul(Matrix3x3 {
math::cos(r), -math::sin(r), 0,
@@ -348,7 +328,7 @@ fn Matrix3x3 Matrix3x3.rotate(Matrix3x3* m, float r)
}
// r in radians
fn Matrix4x4 Matrix4x4.rotate_z(Matrix4x4* m, float r)
fn Matrix4x4 Matrix4x4.rotate_z(Matrix4x4* m, Real r)
{
return m.mul(Matrix4x4 {
math::cos(r), -math::sin(r), 0, 0,
@@ -359,7 +339,7 @@ fn Matrix4x4 Matrix4x4.rotate_z(Matrix4x4* m, float r)
}
// r in radians
fn Matrix4x4 Matrix4x4.rotate_y(Matrix4x4* m, float r)
fn Matrix4x4 Matrix4x4.rotate_y(Matrix4x4* m, Real r)
{
return m.mul(Matrix4x4 {
math::cos(r), 0, -math::sin(r), 0,
@@ -370,7 +350,7 @@ fn Matrix4x4 Matrix4x4.rotate_y(Matrix4x4* m, float r)
}
// r in radians
fn Matrix4x4 Matrix4x4.rotate_x(Matrix4x4* m, float r)
fn Matrix4x4 Matrix4x4.rotate_x(Matrix4x4* m, Real r)
{
return m.mul(Matrix4x4 {
1, 0, 0, 0,
@@ -381,7 +361,7 @@ fn Matrix4x4 Matrix4x4.rotate_x(Matrix4x4* m, float r)
}
fn Matrix3x3 Matrix3x3.scale(Matrix3x3* m, float[<2>] v)
fn Matrix3x3 Matrix3x3.scale(Matrix3x3* m, Real[<2>] v)
{
return m.mul(Matrix3x3 {
v[0], 0, 0,
@@ -390,7 +370,11 @@ fn Matrix3x3 Matrix3x3.scale(Matrix3x3* m, float[<2>] v)
});
}
fn Matrix4x4 Matrix4x4.scale(Matrix4x4* m, float[<3>] v)
fn Real Matrix2x2.trace(Matrix2x2* m) => m.m00 + m.m11;
fn Real Matrix3x3.trace(Matrix3x3* m) => m.m00 + m.m11 + m.m22;
fn Real Matrix4x4.trace(Matrix4x4* m) => m.m00 + m.m11 + m.m22 + m.m33;
fn Matrix4x4 Matrix4x4.scale(Matrix4x4* m, Real[<3>] v)
{
return m.mul(Matrix4x4 {
v[0], 0, 0, 0,
@@ -400,32 +384,51 @@ fn Matrix4x4 Matrix4x4.scale(Matrix4x4* m, float[<3>] v)
});
}
fn Matrix4x4 ortho(float left, float right, float top, float bottom, float near, float far)
fn Matrix4x4 ortho(Real left, Real right, Real top, Real bottom, Real near, Real far)
{
float width = right - left;
float height = top - bottom;
float depth = far - near;
Real width = right - left;
Real height = top - bottom;
Real depth = far - near;
return Matrix4x4 {
2 / width, 0, 0, 0,
0, 2 / height, 0, 0,
0, 0, -2 / depth, 0,
-(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1,
2 / width, 0, 0, 0,
0, 2 / height, 0, 0,
0, 0, -2 / depth, 0,
-(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1
};
}
// fov in radians
fn Matrix4x4 perspective(float fov, float aspect_ratio, float near, float far)
fn Matrix4x4 perspective(Real fov, Real aspect_ratio, Real near, Real far)
{
float top = ((float)math::sin(fov / 2) / (float)math::cos(fov / 2)) * near;
float right = top * aspect_ratio;
float depth = far - near;
Real top = (math::sin(fov / 2) / math::cos(fov / 2)) * near;
Real right = top * aspect_ratio;
Real depth = far - near;
return Matrix4x4 {
1 / right, 0, 0, 0,
0, 1 / top, 0, 0,
0, 0, -2 / depth, 0,
0, 0, - (far + near) / depth, 1,
1 / right, 0, 0, 0,
0, 1 / top, 0, 0,
0, 0, -2 / depth, 0,
0, 0, -(far + near) / depth, 1,
};
}
const Matrix2x2 IDENTITY2 = { .m = { [0] = 1, [3] = 1 } };
const Matrix3x3 IDENTITY3 = { .m = { [0] = 1, [4] = 1, [8] = 1 } };
const Matrix4x4 IDENTITY4 = { .m = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 } };
macro matrix_component_mul(mat, val) @private
{
var $Type = Real[<$typeof(mat.m).len>];
return $typeof(*mat) { .m = val * ($Type)mat.m };
}
macro matrix_add(mat, mat2) @private
{
var $Type = Real[<$typeof(mat.m).len>];
return $typeof(*mat) { .m = ($Type)mat.m + ($Type)mat2.m };
}
macro matrix_sub(mat, mat2) @private
{
var $Type = Real[<$typeof(mat.m).len>];
return $typeof(*mat) { .m = ($Type)mat.m - ($Type)mat2.m };
}

119
lib/std/math/math.random.c3 Normal file
View File

@@ -0,0 +1,119 @@
module std::math;
struct Random
{
RandomInterface fns;
void* state;
}
def RandomSeedFn = fn void(Random*, char[] seed);
def RandomNextBytesFn = fn void(Random*, char[] buffer);
def RandomNextFn = fn uint(Random*, int bits);
struct RandomInterface
{
RandomSeedFn seed_fn;
RandomNextBytesFn next_bytes_fn;
RandomNextFn next_fn;
}
/**
* @param [&inout] random
* @param [inout] buffer
**/
fn void Random.next_bytes(Random* random, char[] buffer)
{
if (!buffer.len) return;
if (RandomNextBytesFn func = random.fns.next_bytes_fn)
{
func(random, buffer);
return;
}
if (RandomNextFn func = random.fns.next_fn)
{
usz current = 0;
while (current < buffer.len)
{
char[4] res = bitcast(func(random, 32), char[4]);
foreach (c : res)
{
buffer[current++] = c;
if (current == buffer.len) return;
}
}
return;
}
unreachable("Invalid Random type.");
}
/**
* @param [&inout] random
* @require bits >= 0 && bits <= 32
**/
fn uint Random.next(Random* random, int bits)
{
if (bits == 0) return 0;
if (RandomNextFn func = random.fns.next_fn) return func(random, bits);
int bytes = (bits + 7) / 8;
char[4] buffer;
char[] b = buffer[:bytes];
assert(random.fns.next_bytes_fn, "Invalid Random");
random.fns.next_bytes_fn(random, b) @inline;
uint next = 0;
foreach (char c : b)
{
next = next << 8 + c;
}
if (bits < 32) next >>= bytes * 8 - bits;
return next;
}
/**
* @param [&inout] random
**/
fn ulong Random.next_long(Random* random)
{
char[8] buffer;
random.next_bytes(&buffer);
return bitcast(buffer, ulong);
}
/**
* @param [&inout] random
**/
fn void Random.set_seed(Random* random, long seed)
{
random.fns.seed_fn(random, &&bitcast(seed, char[8])) @inline;
}
/**
* @param [&inout] random
* @param [in] seed
**/
fn void Random.set_seeds(Random* random, char[] seed)
{
random.fns.seed_fn(random, seed);
}
/**
* @param [&inout] random
**/
fn bool Random.next_bool(Random* random)
{
return random.next(1) != 0;
}
fn float Random.next_float(Random* r)
{
return r.next(24) / (float)(1 << 24);
}
fn double Random.next_double(Random* r)
{
return (((long)(r.next(26)) << 27) + r.next(27)) * 0x1.0p-53;
}
fn uint Random.next_int(Random* r)
{
return r.next(32) @inline;
}

View File

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

View File

@@ -0,0 +1,201 @@
module std::math::easing;
/* Easing equations ported from Robert Penner's equations */
/*
*
* TERMS OF USE - EASING EQUATIONS
*
* Open source under the BSD License.
*
* Copyright © 2001 Robert Penner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Linear Easing functions
fn float linear_none(float t, float b, float c, float d) @inline => c * t / d + b;
fn float linear_in(float t, float b, float c, float d) @inline => c * t / d + b;
fn float linear_out(float t, float b, float c, float d) @inline => c * t / d + b;
fn float linear_inout(float t, float b, float c, float d) @inline => c * t / d + b;
// Sine Easing functions
fn float sine_in(float t, float b, float c, float d) @inline => -c * math::cos(t / d * (float)math::PI_2) + c + b;
fn float sine_out(float t, float b, float c, float d) @inline => c * math::sin(t / d * (float)math::PI_2) + b;
fn float sine_inout(float t, float b, float c, float d) @inline => (-c / 2) * (math::cos((float)math::PI * t / d) - 1) + b;
// Circular Easing functions
fn float circ_in(float t, float b, float c, float d) @inline => -c * (math::sqrt(1 - sq(t / d)) - 1) + b;
fn float circ_out(float t, float b, float c, float d) @inline => c * math::sqrt(1 - sq(t / d - 1)) + b;
fn float circ_inout(float t, float b, float c, float d) @inline
{
t /= d / 2;
return t < 1
? (-c / 2) * (math::sqrt(1 - sq(t)) - 1) + b
: (c / 2) * (math::sqrt(1 - sq(t - 2)) + 1) + b;
}
// Cubic Easing functions
fn float cubic_in(float t, float b, float c, float d) @inline => c * cube(t / d) + b;
fn float cubic_out(float t, float b, float c, float d) @inline => c * (cube(t / d - 1) + 1) + b;
fn float cubic_inout(float t, float b, float c, float d) @inline
{
t /= d / 2;
return t < 1
? (c / 2) * cube(t) + b
: c / 2 * (cube(t - 2) + 2) + b;
}
// Quadratic Easing functions
fn float quad_in(float t, float b, float c, float d) @inline => c * sq(t / d) + b;
fn float quad_out(float t, float b, float c, float d) @inline
{
t /= d;
return -c * t * (t - 2) + b;
}
fn float quad_inout(float t, float b, float c, float d) @inline
{
t /= d / 2;
return t < 1
? (c / 2) * sq(t) + b
: (-c / 2) * ((t - 1) * (t - 3) - 1) + b;
}
// Exponential Easing functions
fn float expo_in(float t, float b, float c, float d) @inline => t ? b : c * math::pow(2.0f, 10 * (t / d - 1)) + b;
fn float expo_out(float t, float b, float c, float d) @inline
{
return (t == d) ? b + c : c * (-math::pow(2.0f, -10 * t / d) + 1) + b;
}
fn float expo_inout(float t, float b, float c, float d) @inline // Ease: Exponential In Out
{
if (t == 0) return b;
if (t == d) return b + c;
t /= d / 2;
return t < 1
? (c / 2) * math::pow(2.0f, 10 * (t - 1)) + b
: (c / 2) * (-math::pow(2.0f, -10 * (t - 1)) + 2) + b;
}
// Back Easing functions
fn float back_in(float t, float b, float c, float d, float s = 1.70158f) @inline
{
t /= d;
return c * sq(t) * ((s + 1) * t - s) + b;
}
fn float back_out(float t, float b, float c, float d, float s = 1.70158f) @inline
{
t = t / d - 1;
return c * (sq(t) * ((s + 1) * t + s) + 1) + b;
}
fn float back_inout(float t, float b, float c, float d, float s = 1.70158f) @inline
{
s *= 1.525f;
t /= d / 2;
if (t < 1)
{
return (c / 2) * sq(t) * ((s + 1) * t - s) + b;
}
t -= 2.0f;
return (c / 2) * (sq(t) * ((s + 1) * t + s) + 2) + b;
}
// Bounce Easing functions
fn float bounce_out(float t, float b, float c, float d) @inline
{
t /= d;
switch
{
case t < 1 / 2.75f:
return c * 7.5625f * sq(t) + b;
case t < 2 / 2.75f:
t -= 1.5f / 2.75f;
return c * (7.5625f * sq(t) + 0.75f) + b;
case t < 2.5f / 2.75f:
t -= 2.25f / 2.75f;
return c * (7.5625f * sq(t) + 0.9375f) + b;
default:
t -= 2.625f / 2.75f;
return c * (7.5625f * sq(t) + 0.984375f) + b;
}
}
fn float bounce_in(float t, float b, float c, float d) @inline => c - bounce_out(d - t, 0, c, d) + b;
fn float bounce_inout(float t, float b, float c, float d) @inline
{
return t < d / 2
? bounce_in(t * 2, 0, c, d) * 0.5f + b
: bounce_out(t * 2 - d, 0, c, d) * 0.5f + b;
}
// Elastic Easing functions
fn float elastic_in(float t, float b, float c, float d) @inline
{
if (t == 0) return b;
t /= d;
if (t == 1) return b + c;
float p = d * 0.3f;
float a = c;
float s = p / 4;
t -= 1;
return -a * math::pow(2.0f, 10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) + b;
}
fn float elastic_out(float t, float b, float c, float d) @inline
{
if (t == 0) return b;
t /= d;
if (t == 1) return b + c;
float p = d * 0.3f;
float a = c;
float s = p / 4;
return a * math::pow(2.0f, -10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) + c + b;
}
fn float elastic_inout(float t, float b, float c, float d) @inline
{
if (t == 0) return b;
t /= d / 2;
if (t == 2) return b + c;
float p = d * (0.3f * 1.5f);
float a = c;
float s = p / 4;
t -= 1;
return t < 0
? -0.5f * a * math::pow(2.0f, 10 * t) * math::sin((t * d - s) * (2 * (float)math::PI)/p) + b
: a * math::pow(2.0f, -10 * t) * math::sin((t * d - s) * (2 * (float)math::PI) / p) * 0.5f + c + b;
}
macro sq(x) @private => x * x;
macro cube(x) @private => x * x * x;

View File

@@ -1,6 +1,6 @@
module std::math;
fn int128 __divti3(int128 a, int128 b) @extname("__divti3") @weak
fn int128 __divti3(int128 a, int128 b) @extern("__divti3") @weak @nostrip
{
int128 sign_a = a >> 127; // -1 : 0
int128 sign_b = b >> 127; // -1 : 0
@@ -10,7 +10,7 @@ fn int128 __divti3(int128 a, int128 b) @extname("__divti3") @weak
return __udivti3(unsigned_a, unsigned_b) @inline ^ sign_a + (-sign_a);
}
fn uint128 __umodti3(uint128 n, uint128 d) @extname("__umodti3") @weak
fn uint128 __umodti3(uint128 n, uint128 d) @extern("__umodti3") @weak @nostrip
{
// Ignore d = 0
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
@@ -34,7 +34,7 @@ fn uint128 __umodti3(uint128 n, uint128 d) @extname("__umodti3") @weak
return r;
}
fn uint128 __udivti3(uint128 n, uint128 d) @extname("__udivti3") @weak
fn uint128 __udivti3(uint128 n, uint128 d) @extern("__udivti3") @weak @nostrip
{
// Ignore d = 0
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
@@ -60,7 +60,7 @@ fn uint128 __udivti3(uint128 n, uint128 d) @extname("__udivti3") @weak
return n;
}
fn int128 __modti3(int128 a, int128 b) @extname("__modti3") @weak
fn int128 __modti3(int128 a, int128 b) @extern("__modti3") @weak @nostrip
{
int128 sign = b >> 127;
uint128 unsigned_b = (uint128)(b ^ sign) + (-sign);
@@ -70,20 +70,117 @@ fn int128 __modti3(int128 a, int128 b) @extname("__modti3") @weak
return __umodti3(unsigned_a, unsigned_b) ^ sign + (-sign);
}
fn float __floattisf(int128 a) @extname("__floattisf") @weak = float_from_i128(float, a);
fn double __floattidf(int128 a) @extname("__floattidf") @weak = float_from_i128(double, a);
fn float __floatuntisf(uint128 a) @extname("__floatuntisf") @weak = float_from_u128(float, a);
fn double __floatuntidf(uint128 a) @extname("__floatuntidf") @weak = float_from_u128(double, a);
fn uint128 __fixunsdfti(double a) @weak @extname("__fixunsdfti") = fixuint(a);
fn uint128 __fixunssfti(float a) @weak @extname("__fixunssfti") = fixuint(a);
fn int128 __fixdfti(double a) @weak @extname("__fixdfti") = fixint(a);
fn int128 __fixsfti(float a) @weak @extname("__fixsfti") = fixint(a);
union Int128bits @private
{
struct
{
ulong ulow, uhigh;
}
struct
{
long ilow, ihigh;
}
uint128 all;
}
fn uint128 __lshrti3(uint128 a, uint b) @extern("__lshrti3") @weak @nostrip
{
Int128bits result;
result.all = a;
if (b >= 64)
{
result.ulow = result.uhigh >> (b - 64);
result.uhigh = 0;
}
else
{
if (b == 0) return a;
result.ulow = (result.uhigh << (64 - b)) | (result.ulow >> b);
result.uhigh = result.uhigh >> b;
}
return result.all;
}
fn int128 __ashrti3(int128 a, uint b) @extern("__ashrti3") @weak @nostrip
{
Int128bits result;
result.all = a;
if (b >= 64)
{
result.ilow = result.ihigh >> (b - 64);
result.ihigh = result.ihigh >> 63;
}
else
{
if (b == 0) return a;
result.ilow = result.ihigh << (64 - b) | (result.ilow >> b);
result.ihigh = result.ihigh >> b;
}
return result.all;
}
fn int128 __ashlti3(int128 a, uint b) @extern("__ashlti3") @weak @nostrip
{
Int128bits result;
result.all = a;
if (b >= 64)
{
result.ulow = 0;
result.uhigh = result.ulow << (b - 64);
}
else
{
if (b == 0) return a;
result.uhigh = (result.uhigh << b) | (result.ulow >> (64 - b));
result.ulow = result.ulow << b;
}
return result.all;
}
// Returns: a * b
fn int128 __mulddi3(ulong a, ulong b) @private
{
Int128bits r;
const ulong LOWER_MASK = 0xffff_ffff;
r.ulow = (a & LOWER_MASK) * (b & LOWER_MASK);
ulong t = r.ulow >> 32;
r.ulow &= LOWER_MASK;
t += (a >> 32) * (b & LOWER_MASK);
r.ulow += (t & LOWER_MASK) << 32;
r.uhigh = t >> 32;
t = r.ulow >> 32;
r.ulow &= LOWER_MASK;
t += (b >> 32) * (a & LOWER_MASK);
r.ulow += (t & LOWER_MASK) << 32;
r.uhigh += t >> 32;
r.uhigh += (a >> 32) * (b >> 32);
return r.all;
}
fn int128 __multi3(int128 a, int128 b) @extern("__multi3") @weak @nostrip
{
Int128bits x = { .all = a };
Int128bits y = { .all = b };
Int128bits r = { .all = __mulddi3(x.ulow, y.ulow) };
r.uhigh += x.uhigh * y.ulow + x.ulow * y.uhigh;
return r.all;
}
fn float __floattisf(int128 a) @extern("__floattisf") @weak @nostrip => float_from_i128(float, a);
fn double __floattidf(int128 a) @extern("__floattidf") @weak @nostrip => float_from_i128(double, a);
fn float __floatuntisf(uint128 a) @extern("__floatuntisf") @weak @nostrip => float_from_u128(float, a);
fn double __floatuntidf(uint128 a) @extern("__floatuntidf") @weak @nostrip => float_from_u128(double, a);
fn uint128 __fixunsdfti(double a) @weak @extern("__fixunsdfti") @nostrip => fixuint(a);
fn uint128 __fixunssfti(float a) @weak @extern("__fixunssfti") @nostrip => fixuint(a);
fn int128 __fixdfti(double a) @weak @extern("__fixdfti") @nostrip => fixint(a);
fn int128 __fixsfti(float a) @weak @extern("__fixsfti") @nostrip => fixint(a);
private macro float_from_i128($Type, a)
macro float_from_i128($Type, a) @private
{
var $Rep;
$switch ($Type):
$switch ($Type)
$case double:
$Rep = ulong;
const MANT_DIG = DOUBLE_MANT_DIG;
@@ -104,7 +201,7 @@ private macro float_from_i128($Type, a)
$case float128:
$Rep = uint128;
const MANT_DIG = QUAD_MANT_DIG;
$endswitch;
$endswitch
if (a == 0) return ($Type)0;
// Grab and remove sign.
int128 sign = a >> 127;
@@ -136,13 +233,13 @@ private macro float_from_i128($Type, a)
{
a <<= (MANT_DIG - sd);
}
return bitcast(((($Rep)sign & SIGN_BIT) | ((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS)) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
return bitcast((($Rep)sign & SIGN_BIT) | ((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
}
private macro float_from_u128($Type, a)
macro float_from_u128($Type, a) @private
{
var $Rep;
$switch ($Type):
$switch ($Type)
$case double:
$Rep = ulong;
const MANT_DIG = DOUBLE_MANT_DIG;
@@ -161,7 +258,7 @@ private macro float_from_u128($Type, a)
$case float128:
$Rep = uint128;
const MANT_DIG = QUAD_MANT_DIG;
$endswitch;
$endswitch
if (a == 0) return ($Type)0;
int sd = 128 - (int)$$clz(a); // digits
int e = sd - 1; // exponent
@@ -194,10 +291,10 @@ private macro float_from_u128($Type, a)
}
private macro fixuint(a)
macro fixuint(a) @private
{
var $Rep;
$switch ($typeof(a)):
$switch ($typeof(a))
$case double:
$Rep = ulong;
const EXPONENT_BITS = 11;
@@ -214,7 +311,7 @@ private macro fixuint(a)
$Rep = uint128;
const EXPONENT_BITS = 15;
const SIGNIFICANT_BITS = 112;
$endswitch;
$endswitch
const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u;
const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u;
const $Rep ONE_REP =EXPONENT_BIAS << SIGNIFICANT_BITS;
@@ -238,10 +335,10 @@ private macro fixuint(a)
return (uint128)significand << (exponent - SIGNIFICANT_BITS);
}
private macro fixint(a)
macro fixint(a) @private
{
var $Rep;
$switch ($typeof(a)):
$switch ($typeof(a))
$case double:
$Rep = ulong;
const EXPONENT_BITS = 11;
@@ -258,7 +355,7 @@ private macro fixint(a)
$Rep = uint128;
const EXPONENT_BITS = 15;
const SIGNIFICANT_BITS = 112;
$endswitch;
$endswitch
const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u;
const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u;
const $Rep ONE_REP = EXPONENT_BIAS << SIGNIFICANT_BITS;

47
lib/std/math/math_libc.c3 Normal file
View File

@@ -0,0 +1,47 @@
/* 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

@@ -0,0 +1,34 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_cos.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
fn double __cos(double x, double y) @extern("__cos") @weak @nostrip
{
const C1 = 4.16666666666666019037e-02; /* 0x3FA55555, 0x5555554C */
const C2 = -1.38888888888741095749e-03; /* 0xBF56C16C, 0x16C15177 */
const C3 = 2.48015872894767294178e-05; /* 0x3EFA01A0, 0x19CB1590 */
const C4 = -2.75573143513906633035e-07; /* 0xBE927E4F, 0x809C52AD */
const C5 = 2.08757232129817482790e-09; /* 0x3E21EE9E, 0xBDB4B1C4 */
const C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */
double z = x * x;
double w = z * z;
double r = z * (C1 + z * (C2 + z * C3)) + w * w * (C4 + z * (C5 + z * C6));
double hz = 0.5 * z;
w = 1.0 - hz;
return w + (((1.0 - w) - hz) + (z * r - x * y));
}
$endif

View File

@@ -0,0 +1,36 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_cosf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
* Debugged and optimized by Bruce D. Evans.
*/
/*
* ====================================================
* 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.
* ====================================================
*/
/* |cos(x) - c(x)| < 2**-34.1 (~[-5.37e-11, 5.295e-11]). */
const double C0 @private = -0x1ffffffd0c5e81.0p-54; /* -0.499999997251031003120 */
const double C1 @private = 0x155553e1053a42.0p-57; /* 0.0416666233237390631894 */
const double C2 @private = -0x16c087e80f1e27.0p-62; /* -0.00138867637746099294692 */
const double C3 @private = 0x199342e0ee5069.0p-68; /* 0.0000243904487962774090654 */
fn float __cosdf(double x) @extern("__cosdf") @weak @nostrip
{
/* Try to optimize for parallel evaluation as in __tandf.c. */
double z = x * x;
double w = z * z;
double r = C2 + z * C3;
return (float)(((1.0f + z * C0) + w * C1) + (w * z) * r);
}
$endif

View File

@@ -0,0 +1,35 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_sin.c */
/*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunSoft, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
fn double __sin(double x, double y, int iy) @extern("__sin") @weak @nostrip
{
const S1 = -1.66666666666666324348e-01; /* 0xBFC55555, 0x55555549 */
const S2 = 8.33333333332248946124e-03; /* 0x3F811111, 0x1110F8A6 */
const S3 = -1.98412698298579493134e-04; /* 0xBF2A01A0, 0x19C161D5 */
const S4 = 2.75573137070700676789e-06; /* 0x3EC71DE3, 0x57B1FE7D */
const S5 = -2.50507602534068634195e-08; /* 0xBE5AE5E6, 0x8A2B9CEB */
const S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */
double z = x * x;
double w = z * z;
double r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6);
double v = z * x;
return iy == 0
? x + v * (S1 + z * r)
: x - ((z * (0.5 * y - v * r) - y) - v * S1);
}
$endif

View File

@@ -0,0 +1,34 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_sinf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
* Optimized by Bruce D. Evans.
*/
/*
* ====================================================
* 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.
* ====================================================
*/
// |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]).
fn float __sindf(double x) @extern("__sindf") @weak @nostrip
{
const S1F = -0x15555554cbac77.0p-55; /* -0.166666666416265235595 */
const S2F = 0x111110896efbb2.0p-59; /* 0.0083333293858894631756 */
const S3F = -0x1a00f9e2cae774.0p-65; /* -0.000198393348360966317347 */
const S4F = 0x16cd878c3b46a7.0p-71; /* 0.0000027183114939898219064 */
double z = x * x;
double w = z * z;
double r = S3F + z * S4F;
double s = z * x;
return (float)((x + s * (S1F + z * S2F)) + s * w * r);
}
$endif

View File

@@ -0,0 +1,83 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_tan.c */
/*
* ====================================================
* Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
const double[*] TAN_T = {
3.33333333333334091986e-01, /* 3FD55555, 55555563 */
1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */
5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */
2.18694882948595424599e-02, /* 3F9664F4, 8406D637 */
8.86323982359930005737e-03, /* 3F8226E3, E96E8493 */
3.59207910759131235356e-03, /* 3F6D6D22, C9560328 */
1.45620945432529025516e-03, /* 3F57DBC8, FEE08315 */
5.88041240820264096874e-04, /* 3F4344D8, F2F26501 */
2.46463134818469906812e-04, /* 3F3026F7, 1A8D1068 */
7.81794442939557092300e-05, /* 3F147E88, A03792A6 */
7.14072491382608190305e-05, /* 3F12B80F, 32F0A7E9 */
-1.85586374855275456654e-05, /* BEF375CB, DB605373 */
2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */
};
fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
{
const double PIO4 = 7.85398163397448278999e-01; /* 3FE921FB, 54442D18 */
const double PIO4LO = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */
uint hx = (uint)(bitcast(x, ulong) >> 32);
bool big = (hx &0x7fffffff) >= 0x3FE59428; // |x| >= 0.6744
int sign @noinit;
if (big)
{
sign = hx >> 31;
if (sign)
{
x = -x;
y = -y;
}
x = (PIO4 - x) + (PIO4LO - y);
y = 0.0;
}
double z = x * x;
double w = z * z;
/*
* Break x^5*(T[1]+x^2*T[2]+...) into
* x^5(T[1]+x^4*T[3]+...+x^20*T[11]) +
* x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12]))
*/
double r = TAN_T[1] + w * (TAN_T[3] + w * (TAN_T[5] + w * (TAN_T[7] + w * (TAN_T[9] + w * TAN_T[11]))));
double v = z * (TAN_T[2] + w * (TAN_T[4] + w * (TAN_T[6] + w * (TAN_T[8] + w * (TAN_T[10] + w * TAN_T[12])))));
double s = z * x;
r = y + z * (s * (r + v) + y) + s * TAN_T[0];
w = x + r;
if (big)
{
s = 1 - 2 * odd;
v = s - 2.0 * (x + (r - w*w/(w + s)));
return sign ? -v : v;
}
if (!odd) return w;
// -1.0/(x+r) has up to 2ulp error, so compute it accurately
// Clear low word
ulong d = bitcast(w, ulong) & 0xFFFF_FFFF_0000_0000;
double w0 = bitcast(d, double);
v = r - (w0 - x); // w0+v = r+x
double a = -1.0 / w;
d = bitcast(a, ulong) & 0xFFFF_FFFF_0000_0000;
double a0 = bitcast(d, double);
return a0 + a * (1.0 + a0 * w0 + a0 * v);
}
$endif

View File

@@ -0,0 +1,56 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
/* origin: FreeBSD /usr/src/lib/msun/src/k_tanf.c */
/*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
* Optimized by Bruce D. Evans.
*/
/*
* ====================================================
* Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
// |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]).
const double[*] TANDF = {
0x15554d3418c99f.0p-54, /* 0.333331395030791399758 */
0x1112fd38999f72.0p-55, /* 0.133392002712976742718 */
0x1b54c91d865afe.0p-57, /* 0.0533812378445670393523 */
0x191df3908c33ce.0p-58, /* 0.0245283181166547278873 */
0x185dadfcecf44e.0p-61, /* 0.00297435743359967304927 */
0x1362b9bf971bcd.0p-59, /* 0.00946564784943673166728 */
};
fn float __tandf(double x, int odd) @extern("__tandf") @weak @nostrip
{
double z = x * x;
/*
* Split up the polynomial into small independent terms to give
* opportunities for parallel evaluation. The chosen splitting is
* micro-optimized for Athlons (XP, X64). It costs 2 multiplications
* relative to Horner's method on sequential machines.
*
* We add the small terms from lowest degree up for efficiency on
* non-sequential machines (the lowest degree terms tend to be ready
* earlier). Apart from this, we don't care about order of
* operations, and don't need to to care since we have precision to
* spare. However, the chosen splitting is good for accuracy too,
* and would give results as accurate as Horner's method if the
* small terms were added from highest degree down.
*/
double r = TANDF[4] + z * TANDF[5];
double t = TANDF[2] + z * TANDF[3];
double w = z * z;
double s = z * x;
double u = TANDF[0] + z * TANDF[1];
r = (x + s * u) + (s * w) * (t + w * r);
return (float)(odd ? -1.0 / r : r);
}
$endif

View File

@@ -0,0 +1,324 @@
module std::math::nolibc::atan;
$if !env::COMPILER_LIBC_AVAILABLE:
const double[*] ATANHI @private = {
4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */
7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */
9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */
1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */
};
const double[*] ATANLO @private = {
2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */
3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */
1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */
6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */
};
const double[*] AT @private = {
3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */
-1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */
1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */
-1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */
9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */
-7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */
6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */
-5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */
4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */
-3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */
1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */
};
fn double _atan(double x) @weak @extern("atan") @nostrip
{
int id @noinit;
uint ix = x.high_word();
uint sign = ix >> 31;
ix &= 0x7fffffff;
switch
{
case ix >= 0x44100000:
/* if |x| >= 2^66 */
if (math::is_nan(x)) return x;
double z = ATANHI[3] + 0x1p-120f;
return sign ? -z : z;
case ix < 0x3fdc0000:
/* |x| < 0.4375 */
if (ix < 0x3e400000)
{
/* |x| < 2^-27 */
if (ix < 0x00100000)
{
/* raise underflow for subnormal x */
(float)@volatile_load(x);
}
return x;
}
id = -1;
case ix < 0x3ff30000:
/* |x| < 1.1875 */
x = math::abs(x);
if (ix < 0x3fe60000)
{
/* 7/16 <= |x| < 11/16 */
id = 0;
x = (2 * x - 1) / (2 + x);
}
else
{ /* 11/16 <= |x| < 19/16 */
id = 1;
x = (x - 1) / (x + 1);
}
case ix < 0x40038000:
x = math::abs(x);
/* |x| < 2.4375 */
id = 2;
x = (x - 1.5) / (1 + 1.5 * x);
default:
/* 2.4375 <= |x| < 2^66 */
id = 3;
x = -1 / math::abs(x);
}
/* end of argument reduction */
double z = x * x;
double w = z * z;
/* break sum from i=0 to 10 AT[i]z**(i+1) into odd and even poly */
double s1 = z * (AT[0] + w * (AT[2] + w * (AT[4] + w * (AT[6] + w * (AT[8] + w * AT[10])))));
double s2 = w * (AT[1] + w * (AT[3] + w * (AT[5] + w * (AT[7] + w * AT[9]))));
if (id < 0) return x - x * (s1 + s2);
z = ATANHI[id] - (x * (s1 + s2) - ATANLO[id] - x);
return sign ? -z : z;
}
const float[*] ATANHIF @private = {
4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */
7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */
9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */
1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */
};
const float[*] ATANLOF @private = {
5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */
3.7748947079e-08, /* atan(1.0)lo 0x33222168 */
3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */
7.5497894159e-08, /* atan(inf)lo 0x33a22168 */
};
const float[*] ATF @private = {
3.3333328366e-01,
-1.9999158382e-01,
1.4253635705e-01,
-1.0648017377e-01,
6.1687607318e-02,
};
fn float _atanf(float x) @weak @extern("atanf") @nostrip
{
int id @noinit;
uint ix = x.word();
uint sign = ix >> 31;
ix &= 0x7fffffff;
if (ix >= 0x4c800000)
{
/* if |x| >= 2**26 */
if (math::is_nan(x)) return x;
float z = ATANHIF[3] + 0x1p-120f;
return sign ? -z : z;
}
switch
{
case ix < 0x3ee00000:
/* |x| < 0.4375 */
if (ix < 0x39800000)
{
/* |x| < 2**-12 */
if (ix < 0x00800000)
{
/* raise underflow for subnormal x */
float f = @volatile_load(x);
f = f * f;
}
return x;
}
id = -1;
case ix < 0x3f980000:
/* |x| < 1.1875 */
x = math::abs(x);
if (ix < 0x3f300000)
{
/* 7/16 <= |x| < 11/16 */
id = 0;
x = (2.0f * x - 1.0f) / (2.0f + x);
break;
}
/* 11/16 <= |x| < 19/16 */
id = 1;
x = (x - 1.0f) / (x + 1.0f);
case ix < 0x401c0000:
x = math::abs(x);
/* |x| < 2.4375 */
id = 2;
x = (x - 1.5f) / (1.0f + 1.5f * x);
default:
/* 2.4375 <= |x| < 2**26 */
x = math::abs(x);
id = 3;
x = -1.0f / x;
}
/* end of argument reduction */
float z = x * x;
float w = z * z;
/* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
float s1 = z * (ATF[0] + w * (ATF[2] + w * ATF[4]));
float s2 = w * (ATF[1] + w * ATF[3]);
if (id < 0) return x - x * (s1 + s2) * 10000;
z = ATANHIF[id] - ((x * (s1 + s2) - ATANLOF[id]) - x);
return sign ? -z : z;
}
const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
macro void extract_words(double d, uint* hi, uint* lo) @private
{
ulong rep = bitcast(d, ulong);
*hi = (uint)(rep >> 32);
*lo = (uint)rep;
}
fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
{
if (math::is_nan(x) || math::is_nan(y)) return x + y;
uint lx @noinit;
uint ix @noinit;
extract_words(x, &ix, &lx);
uint ly @noinit;
uint iy @noinit;
extract_words(y, &iy, &ly);
// x = 1.0
if ((ix - 0x3ff00000) | lx == 0) return _atan(y);
// 2*sign(x) + sign(y)
uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2);
ix = ix & 0x7fffffff;
iy = iy & 0x7fffffff;
// when y = 0
if (iy | ly == 0)
{
switch (m)
{
case 0:
case 1: return y; /* atan(+-0,+anything)=+-0 */
case 2: return math::PI; /* atan(+0,-anything) = pi */
case 3: return -math::PI; /* atan(-0,-anything) =-pi */
}
}
// when x = 0 */
if (ix | lx == 0) return m & 1 ? -math::PI_2 : math::PI_2;
/* when x is INF */
if (ix == 0x7ff00000)
{
if (iy == 0x7ff00000)
{
switch (m)
{
case 0: return math::PI_4; /* atan(+INF,+INF) */
case 1: return -math::PI_4; /* atan(-INF,+INF) */
case 2: return math::PI_4 + math::PI_2; /* atan(+INF,-INF) */
case 3: return - (math::PI_4 + math::PI_2); /* atan(-INF,-INF) */
}
unreachable();
}
switch (m)
{
case 0: return 0.0; /* atan(+...,+INF) */
case 1: return -0.0; /* atan(-...,+INF) */
case 2: return math::PI; /* atan(+...,-INF) */
case 3: return -math::PI; /* atan(-...,-INF) */
}
unreachable();
}
/* |y/x| > 0x1p64 */
if (ix + (64 << 20) < iy || iy == 0x7ff00000) return m & 1 ? -math::PI_2 : math::PI_2;
/* z = atan(|y/x|) without spurious underflow */
double z = ((m & 2) && iy + (64 << 20) < ix) ? 0 : _atan(math::abs(y/x));
switch (m)
{
case 0: return z; /* atan(+,+) */
case 1: return -z; /* atan(-,+) */
case 2: return math::PI - (z - PI_LO); /* atan(+,-) */
default: return (z - PI_LO) - math::PI; /* atan(-,-) */
}
}
const float PI_F @private = 3.1415927410e+00; /* 0x40490fdb */
const float PI_LO_F @private = -8.7422776573e-08; /* 0xb3bbbd2e */
fn float _atan2f(float y, float x) @weak @extern("atan2f") @nostrip
{
if (math::is_nan(x) || math::is_nan(y)) return x + y;
uint ix = bitcast(x, uint);
uint iy = bitcast(y, uint);
/* x=1.0 */
if (ix == 0x3f800000) return _atanf(y);
/* 2*sign(x)+sign(y) */
uint m = ((iy >> 31) & 1) | ((ix >> 30) & 2);
ix &= 0x7fffffff;
iy &= 0x7fffffff;
/* when y = 0 */
if (iy == 0)
{
switch (m)
{
case 0:
case 1: return y; /* atan(+-0,+anything)=+-0 */
case 2: return PI_F; /* atan(+0,-anything) = pi */
case 3: return -PI_F; /* atan(-0,-anything) =-pi */
}
unreachable();
}
/* when x = 0 */
if (ix == 0) return m & 1 ? -(float)math::PI_2 : (float)math::PI_2;
/* when x is INF */
if (ix == 0x7f800000)
{
if (iy == 0x7f800000)
{
switch (m)
{
case 0: return (float)math::PI_4; /* atan(+INF,+INF) */
case 1: return (float)-math::PI_4; /* atan(-INF,+INF) */
case 2: return (float)(math::PI_4 + math::PI_2); /*atan(+INF,-INF)*/
case 3: return -(float)(math::PI_4 + math::PI_2); /*atan(-INF,-INF)*/
}
unreachable();
}
switch (m)
{
case 0: return 0.0f; /* atan(+...,+INF) */
case 1: return -0.0f; /* atan(-...,+INF) */
case 2: return PI_F; /* atan(+...,-INF) */
case 3: return -PI_F; /* atan(-...,-INF) */
}
unreachable();
}
/* |y/x| > 0x1p26 */
if (ix + 26 << 23 < iy || iy == 0x7f800000) return m & 1 ? -(float)math::PI_2 : (float)math::PI_2;
/* z = atan(|y/x|) with correct underflow */
/*|y/x| < 0x1p-26, x < 0 */
float z = (m & 2 && iy + 26 << 23 < ix) ? 0.0f : _atanf(math::abs(y / x));
switch (m)
{
case 0: return z; /* atan(+,+) */
case 1: return -z; /* atan(-,+) */
case 2: return PI_F - (z - PI_LO_F); /* atan(+,-) */
default: return (z - PI_LO_F) - PI_F; /* atan(-,-) */
}
}
$endif

View File

@@ -0,0 +1,45 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
fn double _ceil(double x) @weak @extern("ceil") @nostrip
{
ulong ui = bitcast(x, ulong);
int e = (int)((ui >> 52) & 0x7ff);
if (e >= 0x3ff + 52 || x == 0) return x;
// y = int(x) - x, where int(x) is an integer neighbor of x
double y = ui >> 63 ? (x - TOINT) + TOINT - x : (x + TOINT) - TOINT - x;
// special case because of non-nearest rounding modes
if (e <= 0x3ff - 1)
{
force_eval_add(y, 0);
return ui >> 63 ? -0.0 : 1;
}
return y < 0 ? x + y + 1 : x + y;
}
fn float _ceilf(float x) @weak @extern("ceilf") @nostrip
{
uint u = bitcast(x, uint);
int e = (int)((u >> 23) & 0xff) - 0x7f;
switch
{
case e >= 23:
return x;
case e >= 0:
uint m = 0x007fffff >> e;
if (u & m == 0) return x;
force_eval_add(x, 0x1p120f);
if (u >> 31 == 0) u += m;
u &= ~m;
case u >> 31 != 0:
force_eval_add(x, 0x1p120f);
return -0.0f;
case u << 1 != 0:
return 1;
}
return bitcast(u, float);
}
$endif

View File

@@ -0,0 +1,91 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
fn float _cosf(float x) @extern("cosf") @weak @nostrip
{
uint ix = bitcast(x, uint);
uint sign = ix >> 31;
ix &= 0x7fffffff;
switch
{
case (ix <= 0x3f490fda):
// |x| < 2**-12
if (ix < 0x39800000)
{
/* raise inexact if x != 0 */
force_eval_add(x, 0x1p120f);
return 1.0f;
}
return __cosdf(x);
// |x| ~<= 5*pi/4
case (ix <= 0x407b53d1):
// |x| ~> 3*pi/4
if (ix > 0x4016cbe3) return -__cosdf(sign ? x + S2PI2 : x - S2PI2);
return sign ? __sindf(x + S1PI2) : __sindf(S1PI2 - x);
// |x| ~<= 9*pi/4
case (ix <= 0x40e231d5):
if (ix > 0x40afeddf) return __cosdf(sign ? x + S4PI2 : x - S4PI2);
return sign ? __sindf((double)(-x) - S3PI2) : __sindf(x - S3PI2);
}
// general argument reduction needed
double y @noinit;
switch (__rem_pio2f(x, &y) & 3)
{
case 0: return __cosdf(y);
case 1: return __sindf(-y);
case 2: return -__cosdf(y);
default: return __sindf(y);
}
unreachable();
}
/* origin: FreeBSD /usr/src/lib/msun/src/s_cos.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.
* ====================================================
*/
fn double _cos(double x) @extern("cos") @weak @nostrip
{
// High word of x.
uint ix = (uint)(bitcast(x, ulong) >> 32);
ix &= 0x7fffffff;
switch
{
// |x| ~< pi/4
case ix <= 0x3fe921fb:
if (ix < 0x3e46a09e)
{
// |x| < 2**-27 * sqrt(2)
// raise inexact if x!=0
force_eval_add(x, 0x1p120f);
return 1.0;
}
return __cos(x, 0);
case ix >= 0x7ff00000:
// cos(Inf or NaN) is NaN
return x - x;
default:
// argument reduction
double[2] y @noinit;
switch (__rem_pio2(x, &y) & 3)
{
case 0: return __cos(y[0], y[1]);
case 1: return -__sin(y[0], y[1], 1);
case 2: return -__cos(y[0], y[1]);
default: return __sin(y[0], y[1], 1);
}
}
}
$endif

View File

@@ -0,0 +1,144 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
macro uint _top12f(float x) @private => bitcast(x, uint) >> 20;
fn float _exp2f(float x) @extern("exp2f") @weak @nostrip
{
double xd = x;
uint abstop = _top12f(x) & 0x7ff;
if (abstop >= _top12f(128.0f)) /* @unlikely */
{
switch
{
// |x| >= 128 or x is nan.
case bitcast(x, uint) == bitcast(-float.inf, uint):
return 0;
case abstop >= _top12f(float.inf):
return x + x;
case x > 0:
return __math_oflowf(0);
case x <= -150.0f:
return __math_uflowf(0);
}
}
const SHIFT = __EXP2F_DATA.shift_scaled;
const uint N = 1U << EXP2F_TABLE_BITS;
// x = k/N + r with r in [-1/(2N), 1/(2N)] and int k.
double kd = xd + SHIFT;
ulong ki = bitcast(kd, ulong);
kd -= SHIFT; /* k/N for int k. */
double r = xd - kd;
// exp2(x) = 2^(k/N) * 2^r ~= s * (C0*r^3 + C1*r^2 + C2*r + 1)
ulong t = __EXP2F_DATA.tab[ki % N];
t += ki << (52 - EXP2F_TABLE_BITS);
double s = bitcast(t, double);
double z = __EXP2F_DATA.poly[0] * r + __EXP2F_DATA.poly[1];
double r2 = r * r;
double y = __EXP2F_DATA.poly[2] * r + 1;
y = z * r2 + y;
y = y * s;
return (float)y;
}
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;
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;
double scale = bitcast(sbits, double);
double y = scale + scale * tmp;
if (y >= 1.0)
{
return 0x1p-1022 * y;
}
// Round y to the right precision before scaling it into the subnormal
// range to avoid double rounding that can cause 0.5+E/2 ulp error where
// E is the worst-case ulp error outside the subnormal range. So this
// is only useful if the goal is better than 1 ulp worst-case error.
double lo = scale - y + scale * tmp;
double hi = 1.0 + y;
lo = 1.0 - hi + y + lo;
y = hi + lo - 1.0;
/* Avoid -0.0 with downward rounding. */
if (WANT_ROUNDING && y == 0.0) y = 0.0;
/* The underflow exception needs to be signaled explicitly. */
return __math_xflow(0, 0x1p-1022);
}
macro uint _top12d(double x) @private
{
return (uint)(bitcast(x, ulong) >> 52);
}
fn double _exp2(double x) @extern("exp2") @weak @nostrip
{
uint abstop = _top12d(x) & 0x7ff;
ulong u = bitcast(x, ulong);
if (abstop - _top12d(0x1p-54) >= _top12d(512.0) - _top12d(0x1p-54)) /* @unlikely */
{
if (abstop - _top12d(0x1p-54) >= 0x80000000)
{
// Avoid spurious underflow for tiny x. */
// Note: 0 is common input. */
return WANT_ROUNDING ? 1.0 + x : 1.0;
}
if (abstop >= _top12d(1024.0))
{
switch
{
case u == bitcast(-double.inf, ulong):
return 0.0;
case abstop >= _top12d(double.inf):
return 1.0 + x;
case !(u >> 63):
return __math_oflow(0);
case u >= bitcast(-1075.0, ulong):
return __math_uflow(0);
}
}
// Large x is special cased below.
if (2 * u > 2 * bitcast(928.0, ulong)) abstop = 0;
}
const SHIFT = __EXP2_DATA.exp2_shift;
// exp2(x) = 2^(k/N) * 2^r, with 2^r in [2^(-1/2N),2^(1/2N)]. */
// x = k/N + r, with int k and r in [-1/2N, 1/2N]. */
double kd = x + SHIFT;
ulong ki = bitcast(kd, ulong); /* k. */
kd -= SHIFT; /* k/N for int k. */
double r = x - kd;
/* 2^(k/N) ~= scale * (1 + tail). */
ulong idx = 2 * (ki % EXP_DATA_WIDTH);
ulong top = ki << (52 - EXP_TABLE_BITS);
double tail = __EXP2_DATA.tab[idx];
/* This is only a valid scale when -1023*N < k < 1024*N. */
ulong sbits = __EXP2_DATA.tab[idx + 1] + top;
/* exp2(x) = 2^(k/N) * 2^r ~= scale + scale * (tail + 2^r - 1). */
/* Evaluation is optimized assuming superscalar pipelined execution. */
double r2 = r * r;
const C1 = __EXP2_DATA.exp2_poly[0];
const C2 = __EXP2_DATA.exp2_poly[1];
const C3 = __EXP2_DATA.exp2_poly[2];
const C4 = __EXP2_DATA.exp2_poly[3];
const C5 = __EXP2_DATA.exp2_poly[4];
/* Without fma the worst case error is 0.5/N ulp larger. */
/* Worst case error is less than 0.5+0.86/N+(abs poly error * 2^53) ulp. */
double tmp = tail + r * C1 + r2 * (C2 + r * C3) + r2 * r2 * (C4 + r * C5);
if (abstop == 0 /* @unlikely */) return _exp2_specialcase(tmp, sbits, ki);
double scale = bitcast(sbits, double);
/* Note: tmp == 0 or |tmp| > 2^-65 and scale > 2^-928, so there
is no spurious underflow here even without fma. */
return scale + scale * tmp;
}
$endif

View File

@@ -0,0 +1,45 @@
module std::math::nolibc;
$if !env::COMPILER_LIBC_AVAILABLE:
fn double _floor(double x) @weak @extern("floor") @nostrip
{
ulong ui = bitcast(x, ulong);
int e = (int)((ui >> 52) & 0x7ff);
if (e >= 0x3ff + 52 || x == 0) return x;
// y = int(x) - x, where int(x) is an integer neighbor of x
double y = ui >> 63 ? (x - TOINT) + TOINT - x : (x + TOINT) - TOINT - x;
// special case because of non-nearest rounding modes
if (e <= 0x3ff - 1)
{
force_eval_add(y, 0);
return ui >> 63 ? -1 : 0;
}
return y > 0 ? x + y - 1 : x + y;
}
fn float _floorf(float x) @weak @extern("floorf") @nostrip
{
uint u = bitcast(x, uint);
int e = (int)((u >> 23) & 0xff) - 0x7f;
switch
{
case e >= 23:
return x;
case e >= 0:
uint m = 0x007fffff >> e;
if (u & m == 0) return x;
force_eval_add(x, 0x1p120f);
if (u >> 31) u += m;
u &= ~m;
case u >> 31 == 0:
force_eval_add(x, 0x1p120f);
u = 0;
case (u << 1 != 0):
return -1;
}
return bitcast(u, float);
}
$endif

View File

@@ -0,0 +1,256 @@
module std::math::nolibc;
const double TOINT = 1 / math::DOUBLE_EPSILON;
const double TOINT15 = 1.5 / math::DOUBLE_EPSILON;
const float TOINTF = (float)(1 / math::FLOAT_EPSILON);
const double S1PI2 @private = math::PI_2; /* 0x3FF921FB, 0x54442D18 */
const double S2PI2 @private = math::PI; /* 0x400921FB, 0x54442D18 */
const double S3PI2 @private = math::PI + math::PI_2; /* 0x4012D97C, 0x7F3321D2 */
const double S4PI2 @private = math::PI + math::PI; /* 0x401921FB, 0x54442D18 */
// Shared between expf, exp2f and powf.
const EXP2F_TABLE_BITS @private = 5;
const EXP2F_POLY_ORDER @private = 3;
struct Exp2fData @private
{
ulong[1 << EXP2F_TABLE_BITS] tab;
double shift_scaled;
double[EXP2F_POLY_ORDER] poly;
double shift;
double invln2_scaled;
double[EXP2F_POLY_ORDER] poly_scaled;
}
const Exp2fData __EXP2F_DATA @private = {
.tab = {
0x3ff0000000000000, 0x3fefd9b0d3158574, 0x3fefb5586cf9890f, 0x3fef9301d0125b51,
0x3fef72b83c7d517b, 0x3fef54873168b9aa, 0x3fef387a6e756238, 0x3fef1e9df51fdee1,
0x3fef06fe0a31b715, 0x3feef1a7373aa9cb, 0x3feedea64c123422, 0x3feece086061892d,
0x3feebfdad5362a27, 0x3feeb42b569d4f82, 0x3feeab07dd485429, 0x3feea47eb03a5585,
0x3feea09e667f3bcd, 0x3fee9f75e8ec5f74, 0x3feea11473eb0187, 0x3feea589994cce13,
0x3feeace5422aa0db, 0x3feeb737b0cdc5e5, 0x3feec49182a3f090, 0x3feed503b23e255d,
0x3feee89f995ad3ad, 0x3feeff76f2fb5e47, 0x3fef199bdd85529c, 0x3fef3720dcef9069,
0x3fef5818dcfba487, 0x3fef7c97337b9b5f, 0x3fefa4afa2a490da, 0x3fefd0765b6e4540,
},
.shift_scaled = 0x1.8p+52 / (1 << 5),
.poly = {
0x1.c6af84b912394p-5, 0x1.ebfce50fac4f3p-3, 0x1.62e42ff0c52d6p-1,
},
.shift = 0x1.8p+52,
.invln2_scaled = 0x1.71547652b82fep+0 * (1 << 5),
.poly_scaled = {
0x1.c6af84b912394p-5 / (1 << 5) / (1 << 5) / (1 << 5),
0x1.ebfce50fac4f3p-3 / (1 << 5) / (1 << 5),
0x1.62e42ff0c52d6p-1 / (1 << 5),
},
};
const EXP_TABLE_BITS = 7;
const EXP_POLY_ORDER = 5;
const EXP2_POLY_ORDER = 5;
const EXP_DATA_WIDTH = 1 << EXP_TABLE_BITS;
struct Exp2Data @private
{
double invln2N;
double shift;
double negln2hiN;
double negln2loN;
double[4] poly; // Last four coefficients.
double exp2_shift;
double[EXP2_POLY_ORDER] exp2_poly;
ulong[2 * EXP_DATA_WIDTH] tab;
}
/*
* Shared data between exp, exp2 and pow.
*
* Copyright (c) 2018, Arm Limited.
* SPDX-License-Identifier: MIT OR Apache-2.0 WITH LLVM-exception
*/
const Exp2Data __EXP2_DATA = {
// N/ln2
.invln2N = 0x1.71547652b82fep0 * EXP_DATA_WIDTH,
// -ln2/N
.negln2hiN = -0x1.62e42fefa0000p-8,
.negln2loN = -0x1.cf79abc9e3b3ap-47,
.shift = 0x1.8p52,
// exp polynomial coefficients.
.poly = {
// abs error: 1.555*2^-66
// ulp error: 0.509 (0.511 without fma)
// if |x| < ln2/256+eps
// abs error if |x| < ln2/256+0x1p-15: 1.09*2^-65
// abs error if |x| < ln2/128: 1.7145*2^-56
0x1.ffffffffffdbdp-2,
0x1.555555555543cp-3,
0x1.55555cf172b91p-5,
0x1.1111167a4d017p-7,
},
.exp2_shift = 0x1.8p52 / EXP_DATA_WIDTH,
// exp2 polynomial coefficients.
.exp2_poly = {
// abs error: 1.2195*2^-65
// ulp error: 0.507 (0.511 without fma)
// if |x| < 1/256
// abs error if |x| < 1/128: 1.9941*2^-56
0x1.62e42fefa39efp-1,
0x1.ebfbdff82c424p-3,
0x1.c6b08d70cf4b5p-5,
0x1.3b2abd24650ccp-7,
0x1.5d7e09b4e3a84p-10,
},
// 2^(k/N) ~= H[k]*(1 + T[k]) for int k in [0,N)
// tab[2*k] = asuint64(T[k])
// tab[2*k+1] = asuint64(H[k]) - (k << 52)/N
.tab = {
0x0, 0x3ff0000000000000,
0x3c9b3b4f1a88bf6e, 0x3feff63da9fb3335,
0xbc7160139cd8dc5d, 0x3fefec9a3e778061,
0xbc905e7a108766d1, 0x3fefe315e86e7f85,
0x3c8cd2523567f613, 0x3fefd9b0d3158574,
0xbc8bce8023f98efa, 0x3fefd06b29ddf6de,
0x3c60f74e61e6c861, 0x3fefc74518759bc8,
0x3c90a3e45b33d399, 0x3fefbe3ecac6f383,
0x3c979aa65d837b6d, 0x3fefb5586cf9890f,
0x3c8eb51a92fdeffc, 0x3fefac922b7247f7,
0x3c3ebe3d702f9cd1, 0x3fefa3ec32d3d1a2,
0xbc6a033489906e0b, 0x3fef9b66affed31b,
0xbc9556522a2fbd0e, 0x3fef9301d0125b51,
0xbc5080ef8c4eea55, 0x3fef8abdc06c31cc,
0xbc91c923b9d5f416, 0x3fef829aaea92de0,
0x3c80d3e3e95c55af, 0x3fef7a98c8a58e51,
0xbc801b15eaa59348, 0x3fef72b83c7d517b,
0xbc8f1ff055de323d, 0x3fef6af9388c8dea,
0x3c8b898c3f1353bf, 0x3fef635beb6fcb75,
0xbc96d99c7611eb26, 0x3fef5be084045cd4,
0x3c9aecf73e3a2f60, 0x3fef54873168b9aa,
0xbc8fe782cb86389d, 0x3fef4d5022fcd91d,
0x3c8a6f4144a6c38d, 0x3fef463b88628cd6,
0x3c807a05b0e4047d, 0x3fef3f49917ddc96,
0x3c968efde3a8a894, 0x3fef387a6e756238,
0x3c875e18f274487d, 0x3fef31ce4fb2a63f,
0x3c80472b981fe7f2, 0x3fef2b4565e27cdd,
0xbc96b87b3f71085e, 0x3fef24dfe1f56381,
0x3c82f7e16d09ab31, 0x3fef1e9df51fdee1,
0xbc3d219b1a6fbffa, 0x3fef187fd0dad990,
0x3c8b3782720c0ab4, 0x3fef1285a6e4030b,
0x3c6e149289cecb8f, 0x3fef0cafa93e2f56,
0x3c834d754db0abb6, 0x3fef06fe0a31b715,
0x3c864201e2ac744c, 0x3fef0170fc4cd831,
0x3c8fdd395dd3f84a, 0x3feefc08b26416ff,
0xbc86a3803b8e5b04, 0x3feef6c55f929ff1,
0xbc924aedcc4b5068, 0x3feef1a7373aa9cb,
0xbc9907f81b512d8e, 0x3feeecae6d05d866,
0xbc71d1e83e9436d2, 0x3feee7db34e59ff7,
0xbc991919b3ce1b15, 0x3feee32dc313a8e5,
0x3c859f48a72a4c6d, 0x3feedea64c123422,
0xbc9312607a28698a, 0x3feeda4504ac801c,
0xbc58a78f4817895b, 0x3feed60a21f72e2a,
0xbc7c2c9b67499a1b, 0x3feed1f5d950a897,
0x3c4363ed60c2ac11, 0x3feece086061892d,
0x3c9666093b0664ef, 0x3feeca41ed1d0057,
0x3c6ecce1daa10379, 0x3feec6a2b5c13cd0,
0x3c93ff8e3f0f1230, 0x3feec32af0d7d3de,
0x3c7690cebb7aafb0, 0x3feebfdad5362a27,
0x3c931dbdeb54e077, 0x3feebcb299fddd0d,
0xbc8f94340071a38e, 0x3feeb9b2769d2ca7,
0xbc87deccdc93a349, 0x3feeb6daa2cf6642,
0xbc78dec6bd0f385f, 0x3feeb42b569d4f82,
0xbc861246ec7b5cf6, 0x3feeb1a4ca5d920f,
0x3c93350518fdd78e, 0x3feeaf4736b527da,
0x3c7b98b72f8a9b05, 0x3feead12d497c7fd,
0x3c9063e1e21c5409, 0x3feeab07dd485429,
0x3c34c7855019c6ea, 0x3feea9268a5946b7,
0x3c9432e62b64c035, 0x3feea76f15ad2148,
0xbc8ce44a6199769f, 0x3feea5e1b976dc09,
0xbc8c33c53bef4da8, 0x3feea47eb03a5585,
0xbc845378892be9ae, 0x3feea34634ccc320,
0xbc93cedd78565858, 0x3feea23882552225,
0x3c5710aa807e1964, 0x3feea155d44ca973,
0xbc93b3efbf5e2228, 0x3feea09e667f3bcd,
0xbc6a12ad8734b982, 0x3feea012750bdabf,
0xbc6367efb86da9ee, 0x3fee9fb23c651a2f,
0xbc80dc3d54e08851, 0x3fee9f7df9519484,
0xbc781f647e5a3ecf, 0x3fee9f75e8ec5f74,
0xbc86ee4ac08b7db0, 0x3fee9f9a48a58174,
0xbc8619321e55e68a, 0x3fee9feb564267c9,
0x3c909ccb5e09d4d3, 0x3feea0694fde5d3f,
0xbc7b32dcb94da51d, 0x3feea11473eb0187,
0x3c94ecfd5467c06b, 0x3feea1ed0130c132,
0x3c65ebe1abd66c55, 0x3feea2f336cf4e62,
0xbc88a1c52fb3cf42, 0x3feea427543e1a12,
0xbc9369b6f13b3734, 0x3feea589994cce13,
0xbc805e843a19ff1e, 0x3feea71a4623c7ad,
0xbc94d450d872576e, 0x3feea8d99b4492ed,
0x3c90ad675b0e8a00, 0x3feeaac7d98a6699,
0x3c8db72fc1f0eab4, 0x3feeace5422aa0db,
0xbc65b6609cc5e7ff, 0x3feeaf3216b5448c,
0x3c7bf68359f35f44, 0x3feeb1ae99157736,
0xbc93091fa71e3d83, 0x3feeb45b0b91ffc6,
0xbc5da9b88b6c1e29, 0x3feeb737b0cdc5e5,
0xbc6c23f97c90b959, 0x3feeba44cbc8520f,
0xbc92434322f4f9aa, 0x3feebd829fde4e50,
0xbc85ca6cd7668e4b, 0x3feec0f170ca07ba,
0x3c71affc2b91ce27, 0x3feec49182a3f090,
0x3c6dd235e10a73bb, 0x3feec86319e32323,
0xbc87c50422622263, 0x3feecc667b5de565,
0x3c8b1c86e3e231d5, 0x3feed09bec4a2d33,
0xbc91bbd1d3bcbb15, 0x3feed503b23e255d,
0x3c90cc319cee31d2, 0x3feed99e1330b358,
0x3c8469846e735ab3, 0x3feede6b5579fdbf,
0xbc82dfcd978e9db4, 0x3feee36bbfd3f37a,
0x3c8c1a7792cb3387, 0x3feee89f995ad3ad,
0xbc907b8f4ad1d9fa, 0x3feeee07298db666,
0xbc55c3d956dcaeba, 0x3feef3a2b84f15fb,
0xbc90a40e3da6f640, 0x3feef9728de5593a,
0xbc68d6f438ad9334, 0x3feeff76f2fb5e47,
0xbc91eee26b588a35, 0x3fef05b030a1064a,
0x3c74ffd70a5fddcd, 0x3fef0c1e904bc1d2,
0xbc91bdfbfa9298ac, 0x3fef12c25bd71e09,
0x3c736eae30af0cb3, 0x3fef199bdd85529c,
0x3c8ee3325c9ffd94, 0x3fef20ab5fffd07a,
0x3c84e08fd10959ac, 0x3fef27f12e57d14b,
0x3c63cdaf384e1a67, 0x3fef2f6d9406e7b5,
0x3c676b2c6c921968, 0x3fef3720dcef9069,
0xbc808a1883ccb5d2, 0x3fef3f0b555dc3fa,
0xbc8fad5d3ffffa6f, 0x3fef472d4a07897c,
0xbc900dae3875a949, 0x3fef4f87080d89f2,
0x3c74a385a63d07a7, 0x3fef5818dcfba487,
0xbc82919e2040220f, 0x3fef60e316c98398,
0x3c8e5a50d5c192ac, 0x3fef69e603db3285,
0x3c843a59ac016b4b, 0x3fef7321f301b460,
0xbc82d52107b43e1f, 0x3fef7c97337b9b5f,
0xbc892ab93b470dc9, 0x3fef864614f5a129,
0x3c74b604603a88d3, 0x3fef902ee78b3ff6,
0x3c83c5ec519d7271, 0x3fef9a51fbc74c83,
0xbc8ff7128fd391f0, 0x3fefa4afa2a490da,
0xbc8dae98e223747d, 0x3fefaf482d8e67f1,
0x3c8ec3bc41aa2008, 0x3fefba1bee615a27,
0x3c842b94c3a9eb32, 0x3fefc52b376bba97,
0x3c8a64a931d185ee, 0x3fefd0765b6e4540,
0xbc8e37bae43be3ed, 0x3fefdbfdad9cbe14,
0x3c77893b4d91cd9d, 0x3fefe7c1819e90d8,
0x3c5305c14160cc89, 0x3feff3c22b8f71f1,
}
};
const bool WANT_ROUNDING = true;
macro float __math_uflowf(uint sign) => __math_xflow(sign, 0x1p97f);
macro double __math_uflow(ulong sign) => __math_xflow(sign, 0x1p769);
macro float __math_oflowf(uint sign) => __math_xflow(sign, 0x1p-95f);
macro double __math_oflow(ulong sign) => __math_xflow(sign, 0x1p-767);
macro __math_xflow(sign, v)
{
$typeof(v) temp;
@volatile_store(temp, (sign ? -v : v) * v);
return temp;
}
macro force_eval_add(x, v)
{
$typeof(x) temp;
@volatile_store(temp, x + v);
}

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