mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Compare commits
163 Commits
latest-0.7
...
v0.7.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e348d1e71 | ||
|
|
d697b910ba | ||
|
|
4d848f1707 | ||
|
|
6377f0573d | ||
|
|
c3d2b2824c | ||
|
|
18e408ead4 | ||
|
|
08c63108a1 | ||
|
|
da25a411f9 | ||
|
|
e685414829 | ||
|
|
ae5a74bc41 | ||
|
|
76374d31c4 | ||
|
|
ffd7a5e483 | ||
|
|
d143ec227c | ||
|
|
f2703508f2 | ||
|
|
bb96dc931e | ||
|
|
a5a2b00ec8 | ||
|
|
00f1206f3c | ||
|
|
349d9ef3cf | ||
|
|
9f30b56e13 | ||
|
|
83d6b35afe | ||
|
|
f4b9f375e0 | ||
|
|
be3f9007c9 | ||
|
|
b665e2cbe5 | ||
|
|
0ed68f94cf | ||
|
|
966e8107f8 | ||
|
|
61a4dcc807 | ||
|
|
52541a03eb | ||
|
|
972c84b65b | ||
|
|
f668b96cc9 | ||
|
|
9461873b4c | ||
|
|
8d563eba7a | ||
|
|
fe98225f0a | ||
|
|
bae3e59217 | ||
|
|
b5ddc36d7f | ||
|
|
c2c0ecded8 | ||
|
|
9d5b31dad5 | ||
|
|
6c0e94cad9 | ||
|
|
84aee6a25b | ||
|
|
71a765c66e | ||
|
|
5c3b637cf6 | ||
|
|
bd1de1e7dc | ||
|
|
3cd2267b0a | ||
|
|
7fcc91edc8 | ||
|
|
9052f07c19 | ||
|
|
c7f0d54328 | ||
|
|
498803e9ba | ||
|
|
082457c5fb | ||
|
|
23897bc9a4 | ||
|
|
8ada2a70d9 | ||
|
|
a91330b7d1 | ||
|
|
2f3954a7d9 | ||
|
|
b7ae5dce8b | ||
|
|
91db6ceeda | ||
|
|
fc2f718d9e | ||
|
|
64ef3fc756 | ||
|
|
93dd432b62 | ||
|
|
6c822e5aa3 | ||
|
|
8c741c617c | ||
|
|
b83e57b952 | ||
|
|
24ebe975d8 | ||
|
|
511ae0da00 | ||
|
|
36eb650228 | ||
|
|
50b4d7aa35 | ||
|
|
abe4727c3a | ||
|
|
c528f53d58 | ||
|
|
83955ea5b5 | ||
|
|
fc5c70a628 | ||
|
|
5287640140 | ||
|
|
634438eb82 | ||
|
|
164c901ae6 | ||
|
|
54e70cae0f | ||
|
|
30ec200492 | ||
|
|
584a8a2e60 | ||
|
|
3f07d1c7b8 | ||
|
|
125436d23e | ||
|
|
900365c25e | ||
|
|
d313afa487 | ||
|
|
a411f20762 | ||
|
|
8a0907cb70 | ||
|
|
8a09b2e5f7 | ||
|
|
bfccc303d1 | ||
|
|
0d3299f267 | ||
|
|
11bb8b49da | ||
|
|
f0d2b0eff0 | ||
|
|
005cc08118 | ||
|
|
c5494a23ce | ||
|
|
5dcc67aa1b | ||
|
|
335f53fb64 | ||
|
|
3636898ac0 | ||
|
|
5ba24e05d0 | ||
|
|
0ada5504af | ||
|
|
8ac02a28cc | ||
|
|
246957b8bd | ||
|
|
0595270d9a | ||
|
|
8b86d1461d | ||
|
|
0129308bf3 | ||
|
|
05094b4f47 | ||
|
|
9a59cd164d | ||
|
|
3eecaf9e29 | ||
|
|
8b47673524 | ||
|
|
8b29e4780d | ||
|
|
fd2a81afb1 | ||
|
|
a0d4df2272 | ||
|
|
e39c7cae8d | ||
|
|
8a2907806b | ||
|
|
f778e75757 | ||
|
|
6ab7953706 | ||
|
|
42e4370994 | ||
|
|
9a1fdbbca0 | ||
|
|
434a0e8e4b | ||
|
|
946c167bf1 | ||
|
|
ba10c8953d | ||
|
|
72d7813c20 | ||
|
|
1083de1f81 | ||
|
|
b4b6cba301 | ||
|
|
3244898610 | ||
|
|
6454856fdb | ||
|
|
5cf48ad730 | ||
|
|
b5d0739de0 | ||
|
|
f6e130ad3c | ||
|
|
f9e62b80ea | ||
|
|
debbae594c | ||
|
|
37ffd92f7b | ||
|
|
a44e932806 | ||
|
|
668175851b | ||
|
|
e7c9ec0938 | ||
|
|
d6fa9cd50b | ||
|
|
41e173d255 | ||
|
|
fde2bb2a7e | ||
|
|
0a9bb2e8e0 | ||
|
|
b64dcde21d | ||
|
|
eade5fa57a | ||
|
|
f85198e3ee | ||
|
|
dca805bd8a | ||
|
|
3888fcb182 | ||
|
|
de73265d28 | ||
|
|
cb895754c8 | ||
|
|
6e42bfef3b | ||
|
|
01357ef6d7 | ||
|
|
89d205258e | ||
|
|
0f2d425297 | ||
|
|
28fc03c376 | ||
|
|
1290906d66 | ||
|
|
25d416aca1 | ||
|
|
8cce7f6836 | ||
|
|
4c26adb376 | ||
|
|
94b8330ac5 | ||
|
|
3cb5df5639 | ||
|
|
ded5fde2d5 | ||
|
|
65fb977e89 | ||
|
|
e3f3b6f5f1 | ||
|
|
ab4ed9472a | ||
|
|
1668999f90 | ||
|
|
e828d9a05a | ||
|
|
47447dc069 | ||
|
|
39a59c929f | ||
|
|
f355738dda | ||
|
|
87e254e4b1 | ||
|
|
561a683230 | ||
|
|
63e5aa58c5 | ||
|
|
2be3071bdb | ||
|
|
d3e81b193a | ||
|
|
586d191585 |
48
.github/workflows/main.yml
vendored
48
.github/workflows/main.yml
vendored
@@ -10,7 +10,7 @@ env:
|
||||
LLVM_RELEASE_VERSION_WINDOWS: 18
|
||||
LLVM_RELEASE_VERSION_MAC: 17
|
||||
LLVM_RELEASE_VERSION_LINUX: 17
|
||||
LLVM_RELEASE_VERSION_UBUNTU20: 17
|
||||
LLVM_RELEASE_VERSION_UBUNTU22: 17
|
||||
LLVM_DEV_VERSION: 21
|
||||
jobs:
|
||||
|
||||
@@ -77,15 +77,15 @@ jobs:
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55_v7
|
||||
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55
|
||||
|
||||
- name: Try raylib5
|
||||
run: |
|
||||
cd resources
|
||||
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55_v7
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_arkanoid.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_snake.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_tetris.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_arkanoid.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_snake.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_tetris.c3
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib55_v7
|
||||
./build/c3c vendor-fetch raylib55
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
@@ -401,8 +401,8 @@ jobs:
|
||||
name: c3-linux-${{matrix.build_type}}
|
||||
path: c3-linux-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-linux-ubuntu20:
|
||||
runs-on: ubuntu-20.04
|
||||
build-linux-ubuntu22:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
@@ -507,7 +507,7 @@ jobs:
|
||||
../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU22
|
||||
run: |
|
||||
mkdir c3
|
||||
cp -r lib c3
|
||||
@@ -515,15 +515,15 @@ jobs:
|
||||
cp releasenotes.md c3
|
||||
cp msvc_build_libraries.py c3
|
||||
cp build/c3c c3
|
||||
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz c3
|
||||
tar czf c3-ubuntu-22-${{matrix.build_type}}.tar.gz c3
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU22
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: c3-ubuntu-20-${{matrix.build_type}}
|
||||
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
|
||||
|
||||
name: c3-ubuntu-22-${{matrix.build_type}}
|
||||
path: c3-ubuntu-22-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-with-docker:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
@@ -642,7 +642,7 @@ jobs:
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib55_v7
|
||||
./build/c3c vendor-fetch raylib55
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
@@ -749,7 +749,7 @@ jobs:
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu20]
|
||||
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu22]
|
||||
if: github.ref == 'refs/heads/master'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -769,19 +769,19 @@ jobs:
|
||||
- run: zip -r c3-windows-debug.zip c3-windows-Debug
|
||||
- run: mv c3-linux-Release/c3-linux-Release.tar.gz c3-linux-Release/c3-linux.tar.gz
|
||||
- run: mv c3-linux-Debug/c3-linux-Debug.tar.gz c3-linux-Debug/c3-linux-debug.tar.gz
|
||||
- run: mv c3-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
|
||||
- run: mv c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
|
||||
- run: mv c3-ubuntu-22-Release/c3-ubuntu-22-Release.tar.gz c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz
|
||||
- run: mv c3-ubuntu-22-Debug/c3-ubuntu-22-Debug.tar.gz c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
|
||||
- run: mv c3-macos-Release/c3-macos-Release.zip c3-macos-Release/c3-macos.zip
|
||||
- run: mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-Debug/c3-macos-debug.zip
|
||||
- run: gh release delete latest-0.7.0-prerelease --cleanup-tag -y || true
|
||||
- run: echo "RELEASE_NAME=latest-0.7.0-rc-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
|
||||
- run: gh release delete latest-prerelease --cleanup-tag -y || true
|
||||
- run: echo "RELEASE_NAME=latest-prerelease-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
|
||||
|
||||
- id: create_release
|
||||
uses: softprops/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: latest-0.7.0-prerelease
|
||||
tag_name: latest-prerelease
|
||||
name: ${{ env.RELEASE_NAME }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
@@ -790,7 +790,7 @@ jobs:
|
||||
c3-windows-debug.zip
|
||||
c3-linux-Release/c3-linux.tar.gz
|
||||
c3-linux-Debug/c3-linux-debug.tar.gz
|
||||
c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
|
||||
c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
|
||||
c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz
|
||||
c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
|
||||
c3-macos-Release/c3-macos.zip
|
||||
c3-macos-Debug/c3-macos-debug.zip
|
||||
|
||||
@@ -144,6 +144,20 @@ if(C3_WITH_LLVM)
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
find_package(LLD REQUIRED CONFIG)
|
||||
else()
|
||||
# Add paths for LLVM CMake files of version 19 and higher as they follow a new installation
|
||||
# layout and are now in /usr/lib/llvm/*/lib/cmake/llvm/ rather than /usr/lib/cmake/llvm/
|
||||
#
|
||||
# Because of CMAKE_FIND_PACKAGE_SORT_ORDER CMAKE_FIND_PACKAGE_SORT_DIRECTION,
|
||||
# the newest version will always be found first.
|
||||
message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}")
|
||||
if (DEFINED LLVM_DIR)
|
||||
message(STATUS "Looking for LLVM CMake files in user-specified directory ${LLVM_DIR}")
|
||||
else()
|
||||
file (GLOB LLVM_CMAKE_PATHS "/usr/lib/llvm/*/lib/cmake/llvm/")
|
||||
list (APPEND CMAKE_PREFIX_PATH ${LLVM_CMAKE_PATHS} "/usr/lib/")
|
||||
message(STATUS "No LLVM_DIR specified, searching default directories ${CMAKE_PREFIX_PATH}")
|
||||
endif()
|
||||
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
|
||||
else()
|
||||
@@ -151,6 +165,10 @@ if(C3_WITH_LLVM)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EXISTS /opt/homebrew/lib)
|
||||
list(APPEND LLVM_LIBRARY_DIRS /opt/homebrew/lib)
|
||||
endif()
|
||||
|
||||
if (EXISTS /usr/lib)
|
||||
# Some systems (such as Alpine Linux) seem to put some of the relevant
|
||||
# LLVM files in /usr/lib, but this doesn't seem to be included in the
|
||||
|
||||
@@ -151,8 +151,9 @@ one with related functions when working on temporary strings.
|
||||
|
||||
# C3 Standard library style guide.
|
||||
|
||||
When contributing to the standard librairy please to your best to follow the following style requirements
|
||||
as to ensure a consistent style in the stdlib and also make accepting PRs more quickly.
|
||||
When contributing to the standard library please try your best to adhere to the
|
||||
following style requirements to ensure a consistent style in the stdlib and to
|
||||
facilitate accepting PRs more quickly.
|
||||
|
||||
### Braces are placed on the next line
|
||||
|
||||
@@ -256,4 +257,4 @@ argument.
|
||||
## Add tests to your changes
|
||||
|
||||
If you add or fix things, then there should always be tests in `test/unit/stdlib` to verify
|
||||
the functionality.
|
||||
the functionality.
|
||||
|
||||
70
CONTRIBUTING.md
Normal file
70
CONTRIBUTING.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# How to contribute to C3
|
||||
|
||||
The C3 project consists of
|
||||
|
||||
1. The C3 language itself.
|
||||
2. The C3 compiler, called c3c.
|
||||
3. The C3 standard library
|
||||
4. Various tools, such as the editor plugins
|
||||
|
||||
## 1. How to contribute to the C3 language
|
||||
|
||||
The C3 language is essentially the language specification. You can contribute to the language by:
|
||||
|
||||
1. Filing enhancement requests for changes to the language.
|
||||
2. Offering feedback on existing features, on Discord or by filing issues.
|
||||
3. Help working on the language specification.
|
||||
4. Help working on the grammar.
|
||||
|
||||
## 2. How to contribute to the C3 compiler
|
||||
|
||||
The C3 compiler consists for the compiler itself + test suites for testing the compiler.
|
||||
You can contribute by:
|
||||
|
||||
1. File bugs (by far the most important thing).
|
||||
2. Suggest improved diagnostics / error messages.
|
||||
3. Refactoring existing code (needs deep understanding of the compiler).
|
||||
4. Add support for more architectures.
|
||||
5. Add support for more backends.
|
||||
|
||||
## 3. How to contribute to the standard library
|
||||
|
||||
The standard library is the library itself + test suites for testing the standard library.
|
||||
You can contribute by:
|
||||
|
||||
1. Filing bugs on the standard library.
|
||||
2. Write additional unit tests.
|
||||
3. Suggest new functionality by filing an issue.
|
||||
4. Work on stdlib additions.
|
||||
5. Fix bugs in the stdlib
|
||||
6. Maintain a section of the standard library
|
||||
|
||||
### How to work on small stdlib additions
|
||||
|
||||
If there is just a matter of adding a function or two to an existing module, a pull request
|
||||
is sufficient. However, please make sure that:
|
||||
|
||||
1. It follows the guidelines for the code to ensure a uniform experience (naming standard, indentation, braces etc).
|
||||
2. Add a line in the release notes about the change.
|
||||
3. Make sure it has unit tests.
|
||||
|
||||
### How to work on non-trivial additions to the stdlib
|
||||
|
||||
Regardless whether an addition is approved for inclusion or not, it needs to incubate:
|
||||
|
||||
1. First implement it standalone, showing that it’s working well and has a solid design. This has the advantage of people being able to contribute or even create competing implementations
|
||||
2. Once it is considered finished it can be proposed for inclusion.
|
||||
|
||||
This will greatly help improving the quality of additions.
|
||||
|
||||
Note that any new addition needs a full set of unit tests before being included into the standard library.
|
||||
|
||||
### Maintain a part of the standard library
|
||||
|
||||
A single maintainer is insufficient for a standard library, instead we need one or more maintainer
|
||||
for each module. The maintainer(s) will review pull requests and actively work on making the module
|
||||
pristine with the highest possible quality.
|
||||
|
||||
## 4. How to contribute to various tools
|
||||
|
||||
In general, file a pull request. Depending on who maintains it, rules may differ.
|
||||
40
README.md
40
README.md
@@ -8,16 +8,18 @@ for programmers who like C.
|
||||
|
||||
Precompiled binaries for the following operating systems are available:
|
||||
|
||||
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
|
||||
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
|
||||
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
|
||||
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
|
||||
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
|
||||
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
|
||||
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
|
||||
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
|
||||
|
||||
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
|
||||
|
||||

|
||||
|
||||
Thanks to full ABI compatibility with C, it's possible to mix C and C3 in the same project with no effort. As a demonstration, vkQuake was compiled with a small portion of the code converted to C3 and compiled with the c3c compiler. (The fork can be found at https://github.com/c3lang/vkQuake)
|
||||
Thanks to full ABI compatibility with C, it's possible to mix C and C3 in the same project with no effort. As a demonstration, vkQuake was compiled with a small portion of the code converted to C3 and compiled with the c3c compiler. (The aging fork can be found at https://github.com/c3lang/vkQuake)
|
||||
|
||||
A non-curated list of user written projects and other resources can be found [here](https://github.com/c3lang/c3-showcase).
|
||||
|
||||
### Design Principles
|
||||
- Procedural "get things done"-type of language.
|
||||
@@ -33,7 +35,7 @@ whole new language.
|
||||
|
||||
### Example code
|
||||
|
||||
The following code shows [generic modules](https://c3-lang.org/references/docs/generics/) (more examples can be found at https://c3-lang.org/references/docs/examples/).
|
||||
The following code shows [generic modules](https://c3-lang.org/generic-programming/generics/) (more examples can be found at https://c3-lang.org/language-overview/examples/).
|
||||
|
||||
```cpp
|
||||
module stack {Type};
|
||||
@@ -124,6 +126,7 @@ fn void main()
|
||||
- New semantic macro system
|
||||
- Module based name spacing
|
||||
- Slices
|
||||
- Operator overloading
|
||||
- Compile time reflection
|
||||
- Enhanced compile time execution
|
||||
- Generics based on generic modules
|
||||
@@ -138,9 +141,10 @@ fn void main()
|
||||
|
||||
### Current status
|
||||
|
||||
The current stable version of the compiler is **version 0.6.8**.
|
||||
The current stable version of the compiler is **version 0.7.2**.
|
||||
|
||||
The the next version is 0.7.0 which will be a breaking release.
|
||||
The upcoming 0.7.x releases will focus on expanding the standard library,
|
||||
fixing bugs and improving compile time analysis.
|
||||
Follow the issues [here](https://github.com/c3lang/c3c/issues).
|
||||
|
||||
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
|
||||
@@ -193,29 +197,31 @@ More platforms will be supported in the future.
|
||||
|
||||
### Installing
|
||||
|
||||
This installs the latest prerelease build, as opposed to the latest released version.
|
||||
|
||||
#### Installing on Windows with precompiled binaries
|
||||
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-windows-debug.zip))
|
||||
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows-debug.zip))
|
||||
2. Unzip exe and standard lib.
|
||||
3. If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows.
|
||||
4. Run `c3c.exe`.
|
||||
|
||||
#### Installing on Debian with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-linux-debug.tar.gz))
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
#### Installing on Ubuntu with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20-debug.tar.gz))
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
#### Installing on MacOS with precompiled binaries
|
||||
1. Make sure you have XCode with command line tools installed.
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-macos-debug.zip))
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos-debug.zip))
|
||||
3. Unzip executable and standard lib.
|
||||
4. Run `./c3c`.
|
||||
|
||||
@@ -406,4 +412,4 @@ A special thank you to sponsors [Caleb-o](https://github.com/Caleb-o) and [devda
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#c3lang/c3c&Date)
|
||||
[](https://www.star-history.com/#c3lang/c3c&Date)
|
||||
|
||||
@@ -1,77 +1,42 @@
|
||||
<* This module is scheduled for removal, use std::core::ascii *>
|
||||
module std::ascii;
|
||||
|
||||
macro bool in_range_m(c, start, len) => (uint)(c - start) < len;
|
||||
macro bool is_lower_m(c) => in_range_m(c, 0x61, 26);
|
||||
macro bool is_upper_m(c) => in_range_m(c, 0x41, 26);
|
||||
macro bool is_digit_m(c) => in_range_m(c, 0x30, 10);
|
||||
macro bool is_lower_m(c) => in_range_m(c, 0x61, 26);
|
||||
macro bool is_upper_m(c) => in_range_m(c, 0x41, 26);
|
||||
macro bool is_digit_m(c) => in_range_m(c, 0x30, 10);
|
||||
macro bool is_bdigit_m(c) => in_range_m(c, 0x30, 2);
|
||||
macro bool is_odigit_m(c) => in_range_m(c, 0x30, 8);
|
||||
macro bool is_xdigit_m(c) => in_range_m(c | 32, 0x61, 6) || is_digit_m(c);
|
||||
macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26);
|
||||
macro bool is_print_m(c) => in_range_m(c, 0x20, 95);
|
||||
macro bool is_graph_m(c) => in_range_m(c, 0x21, 94);
|
||||
macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20;
|
||||
macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c);
|
||||
macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c);
|
||||
macro bool is_blank_m(c) => c == 0x20 || c == 0x9;
|
||||
macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f;
|
||||
macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26);
|
||||
macro bool is_print_m(c) => in_range_m(c, 0x20, 95);
|
||||
macro bool is_graph_m(c) => in_range_m(c, 0x21, 94);
|
||||
macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20;
|
||||
macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c);
|
||||
macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c);
|
||||
macro bool is_blank_m(c) => c == 0x20 || c == 0x9;
|
||||
macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f;
|
||||
macro to_lower_m(c) => is_upper_m(c) ? c + 0x20 : c;
|
||||
macro to_upper_m(c) => is_lower_m(c) ? c - 0x20 : c;
|
||||
|
||||
fn bool in_range(char c, char start, char len) => in_range_m(c, start, len);
|
||||
fn bool is_lower(char c) => is_lower_m(c);
|
||||
fn bool is_upper(char c) => is_upper_m(c);
|
||||
fn bool is_digit(char c) => is_digit_m(c);
|
||||
fn bool is_bdigit(char c) => is_bdigit_m(c);
|
||||
fn bool is_odigit(char c) => is_odigit_m(c);
|
||||
fn bool is_xdigit(char c) => is_xdigit_m(c);
|
||||
fn bool is_alpha(char c) => is_alpha_m(c);
|
||||
fn bool is_print(char c) => is_print_m(c);
|
||||
fn bool is_graph(char c) => is_graph_m(c);
|
||||
fn bool is_space(char c) => is_space_m(c);
|
||||
fn bool is_alnum(char c) => is_alnum_m(c);
|
||||
fn bool is_punct(char c) => is_punct_m(c);
|
||||
fn bool is_blank(char c) => is_blank_m(c);
|
||||
fn bool is_cntrl(char c) => is_cntrl_m(c);
|
||||
fn char to_lower(char c) => (char)to_lower_m(c);
|
||||
fn char to_upper(char c) => (char)to_upper_m(c);
|
||||
|
||||
fn bool char.in_range(char c, char start, char len) => in_range_m(c, start, len);
|
||||
fn bool char.is_lower(char c) => is_lower_m(c);
|
||||
fn bool char.is_upper(char c) => is_upper_m(c);
|
||||
fn bool char.is_digit(char c) => is_digit_m(c);
|
||||
fn bool char.is_bdigit(char c) => is_bdigit_m(c);
|
||||
fn bool char.is_odigit(char c) => is_odigit_m(c);
|
||||
fn bool char.is_xdigit(char c) => is_xdigit_m(c);
|
||||
fn bool char.is_alpha(char c) => is_alpha_m(c);
|
||||
fn bool char.is_print(char c) => is_print_m(c);
|
||||
fn bool char.is_graph(char c) => is_graph_m(c);
|
||||
fn bool char.is_space(char c) => is_space_m(c);
|
||||
fn bool char.is_alnum(char c) => is_alnum_m(c);
|
||||
fn bool char.is_punct(char c) => is_punct_m(c);
|
||||
fn bool char.is_blank(char c) => is_blank_m(c);
|
||||
fn bool char.is_cntrl(char c) => is_cntrl_m(c);
|
||||
fn char char.to_lower(char c) => (char)to_lower_m(c);
|
||||
fn char char.to_upper(char c) => (char)to_upper_m(c);
|
||||
<*
|
||||
@require c.is_xdigit()
|
||||
*>
|
||||
fn char char.from_hex(char c) => c.is_digit() ? c - '0' : 10 + (c | 0x20) - 'a';
|
||||
|
||||
fn bool uint.in_range(uint c, uint start, uint len) => in_range_m(c, start, len);
|
||||
fn bool uint.is_lower(uint c) => is_lower_m(c);
|
||||
fn bool uint.is_upper(uint c) => is_upper_m(c);
|
||||
fn bool uint.is_digit(uint c) => is_digit_m(c);
|
||||
fn bool uint.is_bdigit(uint c) => is_bdigit_m(c);
|
||||
fn bool uint.is_odigit(uint c) => is_odigit_m(c);
|
||||
fn bool uint.is_xdigit(uint c) => is_xdigit_m(c);
|
||||
fn bool uint.is_alpha(uint c) => is_alpha_m(c);
|
||||
fn bool uint.is_print(uint c) => is_print_m(c);
|
||||
fn bool uint.is_graph(uint c) => is_graph_m(c);
|
||||
fn bool uint.is_space(uint c) => is_space_m(c);
|
||||
fn bool uint.is_alnum(uint c) => is_alnum_m(c);
|
||||
fn bool uint.is_punct(uint c) => is_punct_m(c);
|
||||
fn bool uint.is_blank(uint c) => is_blank_m(c);
|
||||
fn bool uint.is_cntrl(uint c) => is_cntrl_m(c);
|
||||
fn uint uint.to_lower(uint c) => (uint)to_lower_m(c);
|
||||
fn uint uint.to_upper(uint c) => (uint)to_upper_m(c);
|
||||
fn bool uint.is_lower(uint c) @deprecated => is_lower_m(c);
|
||||
fn bool uint.is_upper(uint c) @deprecated => is_upper_m(c);
|
||||
fn bool uint.is_digit(uint c) @deprecated => is_digit_m(c);
|
||||
fn bool uint.is_bdigit(uint c) @deprecated => is_bdigit_m(c);
|
||||
fn bool uint.is_odigit(uint c) @deprecated => is_odigit_m(c);
|
||||
fn bool uint.is_xdigit(uint c) @deprecated => is_xdigit_m(c);
|
||||
fn bool uint.is_alpha(uint c) @deprecated => is_alpha_m(c);
|
||||
fn bool uint.is_print(uint c) @deprecated => is_print_m(c);
|
||||
fn bool uint.is_graph(uint c) @deprecated => is_graph_m(c);
|
||||
fn bool uint.is_space(uint c) @deprecated => is_space_m(c);
|
||||
fn bool uint.is_alnum(uint c) @deprecated => is_alnum_m(c);
|
||||
fn bool uint.is_punct(uint c) @deprecated => is_punct_m(c);
|
||||
fn bool uint.is_blank(uint c) @deprecated => is_blank_m(c);
|
||||
fn bool uint.is_cntrl(uint c) @deprecated => is_cntrl_m(c);
|
||||
fn uint uint.to_lower(uint c) @deprecated => (uint)to_lower_m(c);
|
||||
fn uint uint.to_upper(uint c) @deprecated => (uint)to_upper_m(c);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2023 Eduardo José Gómez Hernández. All rights reserved.
|
||||
// Copyright (c) 2023-2025 Eduardo José Gómez Hernández. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::atomic::types{Type};
|
||||
@@ -183,7 +183,7 @@ macro bool is_native_atomic_type($Type)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to be added to ptr."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -203,7 +203,7 @@ macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to be subtracted from ptr."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -223,7 +223,7 @@ macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to be multiplied with ptr."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -263,7 +263,7 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to divide ptr by."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -303,7 +303,7 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to perform a bitwise or with."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -320,7 +320,7 @@ macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to perform a bitwise xor with."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -337,7 +337,7 @@ macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to perform a bitwise and with."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -354,7 +354,7 @@ macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to shift ptr by."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -396,7 +396,7 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to shift ptr by."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -438,7 +438,7 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@@ -466,7 +466,7 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
|
||||
@@ -7,6 +7,17 @@ import std::io,std::math;
|
||||
alias AnyPredicate = fn bool(any value);
|
||||
alias AnyTest = fn bool(any type, any context);
|
||||
|
||||
<*
|
||||
The AnyList contains a heterogenous set of types. Anything placed in the
|
||||
list will shallowly copied in order to be stored as an `any`. This means
|
||||
that the list will copy and free its elements.
|
||||
|
||||
However, because we're getting `any` values back when we pop, those operations
|
||||
need to take an allocator, as we can only copy then pop then return the copy.
|
||||
|
||||
If we're not doing pop, then things are easier, since we can just hand over
|
||||
the existing any.
|
||||
*>
|
||||
struct AnyList (Printable)
|
||||
{
|
||||
usz size;
|
||||
@@ -17,8 +28,11 @@ struct AnyList (Printable)
|
||||
|
||||
|
||||
<*
|
||||
Initialize the list. If not initialized then it will use the temp allocator
|
||||
when something is pushed to it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param initial_capacity : "The initial capacity to reserve"
|
||||
@param initial_capacity : "The initial capacity to reserve, defaults to 16"
|
||||
*>
|
||||
fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
|
||||
{
|
||||
@@ -49,6 +63,361 @@ fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16)
|
||||
|
||||
fn bool AnyList.is_initialized(&self) @inline => self.allocator != null;
|
||||
|
||||
<*
|
||||
Push an element on the list by cloning it.
|
||||
*>
|
||||
macro void AnyList.push(&self, element)
|
||||
{
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
self._append(allocator::clone(self.allocator, element));
|
||||
}
|
||||
|
||||
<*
|
||||
Free a retained element removed using *_retained.
|
||||
*>
|
||||
fn void AnyList.free_element(&self, any element) @inline
|
||||
{
|
||||
allocator::free(self.allocator, element.ptr);
|
||||
}
|
||||
|
||||
<*
|
||||
Pop a value who's type is known. If the type is incorrect, this
|
||||
will still pop the element.
|
||||
|
||||
@param $Type : "The type we assume the value has"
|
||||
@return "The last value as the type given"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.pop(&self, $Type)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return *anycast(self.entries[--self.size], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Copy the last value, pop it and return the copy of it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use for copying"
|
||||
@return "A copy of the last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.copy_pop(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return allocator::clone_any(allocator, self.entries[--self.size]);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Copy the last value, pop it and return the copy of it.
|
||||
|
||||
@return "A temp copy of the last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.tcopy_pop(&self) => self.copy_pop(tmem);
|
||||
|
||||
|
||||
<*
|
||||
Pop the last value. It must later be released using `list.free_element()`.
|
||||
|
||||
@return "The last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.pop_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
return self.entries[--self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Remove all elements in the list.
|
||||
*>
|
||||
fn void AnyList.clear(&self)
|
||||
{
|
||||
for (usz i = 0; i < self.size; i++)
|
||||
{
|
||||
self.free_element(self.entries[i]);
|
||||
}
|
||||
self.size = 0;
|
||||
}
|
||||
|
||||
<*
|
||||
Pop a value who's type is known. If the type is incorrect, this
|
||||
will still pop the element.
|
||||
|
||||
@param $Type : "The type we assume the value has"
|
||||
@return "The first value as the type given"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.pop_first(&self, $Type)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return *anycast(self.entries[0], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Pop the first value. It must later be released using `list.free_element()`.
|
||||
|
||||
@return "The first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.pop_first_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Copy the first value, pop it and return the copy of it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use for copying"
|
||||
@return "A copy of the first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.copy_pop_first(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
defer self.remove_at(0);
|
||||
return allocator::clone_any(allocator, self.entries[0]);
|
||||
}
|
||||
|
||||
<*
|
||||
Copy the first value, pop it and return the temp copy of it.
|
||||
|
||||
@return "A temp copy of the first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
|
||||
|
||||
<*
|
||||
Remove the element at the particular index.
|
||||
|
||||
@param index : "The index of the element to remove"
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList.remove_at(&self, usz index)
|
||||
{
|
||||
if (!--self.size || index == self.size) return;
|
||||
self.free_element(self.entries[index]);
|
||||
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Add all the elements in another AnyList.
|
||||
|
||||
@param [&in] other_list : "The list to add"
|
||||
*>
|
||||
fn void AnyList.add_all(&self, AnyList* other_list)
|
||||
{
|
||||
if (!other_list.size) return;
|
||||
self.reserve(other_list.size);
|
||||
foreach (value : other_list)
|
||||
{
|
||||
self.entries[self.size++] = allocator::clone_any(self.allocator, value);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Reverse the order of the elements in the list.
|
||||
*>
|
||||
fn void AnyList.reverse(&self)
|
||||
{
|
||||
if (self.size < 2) return;
|
||||
usz half = self.size / 2U;
|
||||
usz end = self.size - 1;
|
||||
for (usz i = 0; i < half; i++)
|
||||
{
|
||||
self.swap(i, end - i);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Return a view of the data as a slice.
|
||||
|
||||
@return "The slice view"
|
||||
*>
|
||||
fn any[] AnyList.array_view(&self)
|
||||
{
|
||||
return self.entries[:self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Push an element to the front of the list.
|
||||
|
||||
@param value : "The value to push to the list"
|
||||
*>
|
||||
macro void AnyList.push_front(&self, value)
|
||||
{
|
||||
self.insert_at(0, value);
|
||||
}
|
||||
|
||||
<*
|
||||
Insert an element at a particular index.
|
||||
|
||||
@param index : "the index where the element should be inserted"
|
||||
@param type : "the value to insert"
|
||||
@require index <= self.size : "The index is out of bounds"
|
||||
*>
|
||||
macro void AnyList.insert_at(&self, usz index, type)
|
||||
{
|
||||
if (index == self.size)
|
||||
{
|
||||
self.push(type);
|
||||
return;
|
||||
}
|
||||
any value = allocator::copy(self.allocator, type);
|
||||
self._insert_at(self, index, value);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove the last element in the list. The list may not be empty.
|
||||
|
||||
@require self.size > 0 : "The list was already empty"
|
||||
*>
|
||||
fn void AnyList.remove_last(&self)
|
||||
{
|
||||
self.free_element(self.entries[--self.size]);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove the first element in the list, the list may not be empty.
|
||||
|
||||
@require self.size > 0
|
||||
*>
|
||||
fn void AnyList.remove_first(&self)
|
||||
{
|
||||
self.remove_at(0);
|
||||
}
|
||||
|
||||
<*
|
||||
Return the first element by value, assuming it is the given type.
|
||||
|
||||
@param $Type : "The type of the first element"
|
||||
@return "The first element"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.first(&self, $Type)
|
||||
{
|
||||
return *anycast(self.first_any(), $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Return the first element
|
||||
|
||||
@return "The first element"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.first_any(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
<*
|
||||
Return the last element by value, assuming it is the given type.
|
||||
|
||||
@param $Type : "The type of the last element"
|
||||
@return "The last element"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.last(&self, $Type)
|
||||
{
|
||||
return *anycast(self.last_any(), $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Return the last element
|
||||
|
||||
@return "The last element"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.last_any(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
<*
|
||||
Return whether the list is empty.
|
||||
|
||||
@return "True if the list is empty"
|
||||
*>
|
||||
fn bool AnyList.is_empty(&self) @inline
|
||||
{
|
||||
return !self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
Return the length of the list.
|
||||
|
||||
@return "The number of elements in the list"
|
||||
*>
|
||||
fn usz AnyList.len(&self) @operator(len) @inline
|
||||
{
|
||||
return self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
Return an element in the list by value, assuming it is the given type.
|
||||
|
||||
@param index : "The index of the element to retrieve"
|
||||
@param $Type : "The type of the element"
|
||||
@return "The element at the index"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
@require index < self.size : "Index out of range"
|
||||
*>
|
||||
macro AnyList.get(&self, usz index, $Type)
|
||||
{
|
||||
return *anycast(self.entries[index], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Return an element in the list.
|
||||
|
||||
@param index : "The index of the element to retrieve"
|
||||
@return "The element at the index"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
@require index < self.size : "Index out of range"
|
||||
*>
|
||||
fn any AnyList.get_any(&self, usz index) @inline @operator([])
|
||||
{
|
||||
return self.entries[index];
|
||||
}
|
||||
|
||||
<*
|
||||
Completely free and clear a list.
|
||||
*>
|
||||
fn void AnyList.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
self.clear();
|
||||
allocator::free(self.allocator, self.entries);
|
||||
self.capacity = 0;
|
||||
self.entries = null;
|
||||
}
|
||||
|
||||
<*
|
||||
Swap two elements in a list.
|
||||
|
||||
@param i : "Index of one of the elements"
|
||||
@param j : "Index of the other element"
|
||||
@require i < self.size : "The first index is out of range"
|
||||
@require j < self.size : "The second index is out of range"
|
||||
*>
|
||||
fn void AnyList.swap(&self, usz i, usz j)
|
||||
{
|
||||
any temp = self.entries[i];
|
||||
self.entries[i] = self.entries[j];
|
||||
self.entries[j] = temp;
|
||||
}
|
||||
|
||||
<*
|
||||
Print the list to a formatter.
|
||||
*>
|
||||
fn usz? AnyList.to_format(&self, Formatter* formatter) @dynamic
|
||||
{
|
||||
switch (self.size)
|
||||
@@ -70,265 +439,8 @@ fn usz? AnyList.to_format(&self, Formatter* formatter) @dynamic
|
||||
}
|
||||
|
||||
<*
|
||||
Push an element on the list by cloning it.
|
||||
*>
|
||||
macro void AnyList.push(&self, element)
|
||||
{
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
self.append_internal(allocator::clone(self.allocator, element));
|
||||
}
|
||||
Remove any elements matching the predicate.
|
||||
|
||||
fn void AnyList.append_internal(&self, any element) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
<*
|
||||
Free a retained element removed using *_retained.
|
||||
*>
|
||||
fn void AnyList.free_element(&self, any element) @inline
|
||||
{
|
||||
allocator::free(self.allocator, element.ptr);
|
||||
}
|
||||
|
||||
<*
|
||||
Pop a value who's type is known. If the type is incorrect, this
|
||||
will still pop the element.
|
||||
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.pop(&self, $Type)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return *anycast(self.entries[--self.size], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Pop the last value and allocate the copy using the given allocator.
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.copy_pop(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return allocator::clone_any(allocator, self.entries[--self.size]);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Pop the last value and allocate the copy using the temp allocator
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.tcopy_pop(&self) => self.copy_pop(tmem);
|
||||
|
||||
<*
|
||||
Pop the last value. It must later be released using list.free_element()
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.pop_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
return self.entries[--self.size];
|
||||
}
|
||||
|
||||
fn void AnyList.clear(&self)
|
||||
{
|
||||
for (usz i = 0; i < self.size; i++)
|
||||
{
|
||||
self.free_element(self.entries[i]);
|
||||
}
|
||||
self.size = 0;
|
||||
}
|
||||
|
||||
<*
|
||||
Same as pop() but pops the first value instead.
|
||||
*>
|
||||
macro AnyList.pop_first(&self, $Type)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return *anycast(self.entries[0], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Same as pop_retained() but pops the first value instead.
|
||||
*>
|
||||
fn any? AnyList.pop_first_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Same as copy_pop() but pops the first value instead.
|
||||
*>
|
||||
fn any? AnyList.copy_pop_first(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
defer self.remove_at(0);
|
||||
return allocator::clone_any(allocator, self.entries[0]);
|
||||
}
|
||||
|
||||
<*
|
||||
Same as temp_pop() but pops the first value instead.
|
||||
*>
|
||||
fn any? AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList.remove_at(&self, usz index)
|
||||
{
|
||||
if (!--self.size || index == self.size) return;
|
||||
self.free_element(self.entries[index]);
|
||||
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
|
||||
}
|
||||
|
||||
fn void AnyList.add_all(&self, AnyList* other_list)
|
||||
{
|
||||
if (!other_list.size) return;
|
||||
self.reserve(other_list.size);
|
||||
foreach (value : other_list)
|
||||
{
|
||||
self.entries[self.size++] = allocator::clone_any(self.allocator, value);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Reverse the elements in a list.
|
||||
*>
|
||||
fn void AnyList.reverse(&self)
|
||||
{
|
||||
if (self.size < 2) return;
|
||||
usz half = self.size / 2U;
|
||||
usz end = self.size - 1;
|
||||
for (usz i = 0; i < half; i++)
|
||||
{
|
||||
self.swap(i, end - i);
|
||||
}
|
||||
}
|
||||
|
||||
fn any[] AnyList.array_view(&self)
|
||||
{
|
||||
return self.entries[:self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Push an element to the front of the list.
|
||||
*>
|
||||
macro void AnyList.push_front(&self, type)
|
||||
{
|
||||
self.insert_at(0, type);
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
macro void AnyList.insert_at(&self, usz index, type) @local
|
||||
{
|
||||
any value = allocator::copy(self.allocator, type);
|
||||
self.insert_at_internal(self, index, value);
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList.insert_at_internal(&self, usz index, any value) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
for (usz i = self.size; i > index; i--)
|
||||
{
|
||||
self.entries[i] = self.entries[i - 1];
|
||||
}
|
||||
self.size++;
|
||||
self.entries[index] = value;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@require self.size > 0
|
||||
*>
|
||||
fn void AnyList.remove_last(&self)
|
||||
{
|
||||
self.free_element(self.entries[--self.size]);
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.size > 0
|
||||
*>
|
||||
fn void AnyList.remove_first(&self)
|
||||
{
|
||||
self.remove_at(0);
|
||||
}
|
||||
|
||||
macro AnyList.first(&self, $Type)
|
||||
{
|
||||
return *anycast(self.first_any(), $Type);
|
||||
}
|
||||
|
||||
fn any? AnyList.first_any(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
macro AnyList.last(&self, $Type)
|
||||
{
|
||||
return *anycast(self.last_any(), $Type);
|
||||
}
|
||||
|
||||
fn any? AnyList.last_any(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
fn bool AnyList.is_empty(&self) @inline
|
||||
{
|
||||
return !self.size;
|
||||
}
|
||||
|
||||
fn usz AnyList.len(&self) @operator(len) @inline
|
||||
{
|
||||
return self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size : "Index out of range"
|
||||
*>
|
||||
macro AnyList.get(&self, usz index, $Type)
|
||||
{
|
||||
return *anycast(self.entries[index], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size : "Index out of range"
|
||||
*>
|
||||
fn any AnyList.get_any(&self, usz index) @inline
|
||||
{
|
||||
return self.entries[index];
|
||||
}
|
||||
|
||||
fn void AnyList.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
self.clear();
|
||||
allocator::free(self.allocator, self.entries);
|
||||
self.capacity = 0;
|
||||
self.entries = null;
|
||||
}
|
||||
|
||||
fn void AnyList.swap(&self, usz i, usz j)
|
||||
{
|
||||
any temp = self.entries[i];
|
||||
self.entries[i] = self.entries[j];
|
||||
self.entries[j] = temp;
|
||||
}
|
||||
|
||||
<*
|
||||
@param filter : "The function to determine if it should be removed or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
@@ -338,6 +450,8 @@ fn usz AnyList.remove_if(&self, AnyPredicate filter)
|
||||
}
|
||||
|
||||
<*
|
||||
Retain the elements matching the predicate.
|
||||
|
||||
@param selection : "The function to determine if it should be kept or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
@@ -346,40 +460,95 @@ fn usz AnyList.retain_if(&self, AnyPredicate selection)
|
||||
return self._remove_if(selection, true);
|
||||
}
|
||||
|
||||
macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local
|
||||
{
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
{
|
||||
// Find last index of item to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && !filter(&self.entries[i - 1])) i--;
|
||||
$else
|
||||
while (i > 0 && filter(&self.entries[i - 1])) i--;
|
||||
$endif
|
||||
// Remove the items from this index up to the one not to be deleted.
|
||||
usz n = self.size - k;
|
||||
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
|
||||
self.entries[i:n] = self.entries[k:n];
|
||||
self.size -= k - i;
|
||||
// Find last index of item not to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && filter(&self.entries[i - 1])) i--;
|
||||
$else
|
||||
while (i > 0 && !filter(&self.entries[i - 1])) i--;
|
||||
$endif
|
||||
}
|
||||
return size - self.size;
|
||||
}
|
||||
<*
|
||||
Remove any elements matching the predicate.
|
||||
|
||||
@param filter : "The function to determine if it should be removed or not"
|
||||
@param context : "The context to the function"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz AnyList.remove_using_test(&self, AnyTest filter, any context)
|
||||
{
|
||||
return self._remove_using_test(filter, false, context);
|
||||
}
|
||||
|
||||
fn usz AnyList.retain_using_test(&self, AnyTest filter, any context)
|
||||
<*
|
||||
Retain any elements matching the predicate.
|
||||
|
||||
@param selection : "The function to determine if it should be retained or not"
|
||||
@param context : "The context to the function"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz AnyList.retain_using_test(&self, AnyTest selection, any context)
|
||||
{
|
||||
return self._remove_using_test(filter, true, context);
|
||||
return self._remove_using_test(selection, true, context);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Reserve memory so that at least the `min_capacity` exists.
|
||||
|
||||
@param min_capacity : "The min capacity to hold"
|
||||
*>
|
||||
fn void AnyList.reserve(&self, usz min_capacity)
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (self.capacity >= min_capacity) return;
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
|
||||
self.capacity = min_capacity;
|
||||
}
|
||||
|
||||
<*
|
||||
Set the element at any index.
|
||||
|
||||
@param index : "The index where to set the value."
|
||||
@param value : "The value to set"
|
||||
@require index <= self.size : "Index out of range"
|
||||
*>
|
||||
macro void AnyList.set(&self, usz index, value)
|
||||
{
|
||||
if (index == self.size)
|
||||
{
|
||||
self.push(value);
|
||||
return;
|
||||
}
|
||||
self.free_element(self.entries[index]);
|
||||
self.entries[index] = allocator::copy(self.allocator, value);
|
||||
}
|
||||
|
||||
// -- private
|
||||
|
||||
fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private
|
||||
{
|
||||
usz new_size = self.size + added;
|
||||
if (self.capacity >= new_size) return;
|
||||
|
||||
assert(new_size < usz.max / 2U);
|
||||
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
|
||||
while (new_capacity < new_size) new_capacity *= 2U;
|
||||
self.reserve(new_capacity);
|
||||
}
|
||||
|
||||
fn void AnyList._append(&self, any element) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList._insert_at(&self, usz index, any value) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
for (usz i = self.size; i > index; i--)
|
||||
{
|
||||
self.entries[i] = self.entries[i - 1];
|
||||
}
|
||||
self.size++;
|
||||
self.entries[index] = value;
|
||||
}
|
||||
|
||||
macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @local
|
||||
@@ -408,45 +577,28 @@ macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @
|
||||
return size - self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
Reserve at least min_capacity
|
||||
*>
|
||||
fn void AnyList.reserve(&self, usz min_capacity)
|
||||
macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (self.capacity >= min_capacity) return;
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
|
||||
self.capacity = min_capacity;
|
||||
}
|
||||
|
||||
macro any AnyList.@item_at(&self, usz index) @operator([])
|
||||
{
|
||||
return self.entries[index];
|
||||
}
|
||||
|
||||
<*
|
||||
@require index <= self.size : "Index out of range"
|
||||
*>
|
||||
macro void AnyList.set(&self, usz index, value)
|
||||
{
|
||||
if (index == self.size)
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
{
|
||||
self.push(value);
|
||||
return;
|
||||
// Find last index of item to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && !filter(&self.entries[i - 1])) i--;
|
||||
$else
|
||||
while (i > 0 && filter(&self.entries[i - 1])) i--;
|
||||
$endif
|
||||
// Remove the items from this index up to the one not to be deleted.
|
||||
usz n = self.size - k;
|
||||
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
|
||||
self.entries[i:n] = self.entries[k:n];
|
||||
self.size -= k - i;
|
||||
// Find last index of item not to be deleted.
|
||||
$if $invert:
|
||||
while (i > 0 && filter(&self.entries[i - 1])) i--;
|
||||
$else
|
||||
while (i > 0 && !filter(&self.entries[i - 1])) i--;
|
||||
$endif
|
||||
}
|
||||
self.free_element(self.entries[index]);
|
||||
self.entries[index] = allocator::copy(self.allocator, value);
|
||||
}
|
||||
|
||||
fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private
|
||||
{
|
||||
usz new_size = self.size + added;
|
||||
if (self.capacity >= new_size) return;
|
||||
|
||||
assert(new_size < usz.max / 2U);
|
||||
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
|
||||
while (new_capacity < new_size) new_capacity *= 2U;
|
||||
self.reserve(new_capacity);
|
||||
return size - self.size;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
// Copyright (c) 2023-2025 C3 team. All rights reserved.
|
||||
// Use of self source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
<*
|
||||
@require SIZE > 0
|
||||
@require SIZE > 0 : "The size of the bitset in bits must be at least 1"
|
||||
*>
|
||||
module std::collections::bitset{SIZE};
|
||||
module std::collections::bitset {SIZE};
|
||||
|
||||
alias Type = uint;
|
||||
|
||||
const BITS = Type.sizeof * 8;
|
||||
const BITS = uint.sizeof * 8;
|
||||
const SZ = (SIZE + BITS - 1) / BITS;
|
||||
|
||||
struct BitSet
|
||||
{
|
||||
Type[SZ] data;
|
||||
uint[SZ] data;
|
||||
}
|
||||
|
||||
<*
|
||||
@return "The number of bits set"
|
||||
*>
|
||||
fn usz BitSet.cardinality(&self)
|
||||
{
|
||||
usz n;
|
||||
@@ -24,7 +28,11 @@ fn usz BitSet.cardinality(&self)
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Set a bit in the bitset.
|
||||
|
||||
@param i : "The index to set"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn void BitSet.set(&self, usz i)
|
||||
{
|
||||
@@ -34,7 +42,86 @@ fn void BitSet.set(&self, usz i)
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Perform xor over all bits, mutating itself
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
macro BitSet BitSet.xor_self(&self, BitSet set) @operator(^=)
|
||||
{
|
||||
foreach (i, &x : self.data) *x ^= set.data[i];
|
||||
return *self;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform xor over all bits, returning a new bit set.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
fn BitSet BitSet.xor(&self, BitSet set) @operator(^)
|
||||
{
|
||||
BitSet new_set @noinit;
|
||||
foreach (i, x : self.data) new_set.data[i] = x ^ set.data[i];
|
||||
return new_set;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform or over all bits, returning a new bit set.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
fn BitSet BitSet.or(&self, BitSet set) @operator(|)
|
||||
{
|
||||
BitSet new_set @noinit;
|
||||
foreach (i, x : self.data) new_set.data[i] = x | set.data[i];
|
||||
return new_set;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform or over all bits, mutating itself
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
macro BitSet BitSet.or_self(&self, BitSet set) @operator(|=)
|
||||
{
|
||||
foreach (i, &x : self.data) *x |= set.data[i];
|
||||
return *self;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform & over all bits, returning a new bit set.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
fn BitSet BitSet.and(&self, BitSet set) @operator(&)
|
||||
{
|
||||
BitSet new_set @noinit;
|
||||
foreach (i, x : self.data) new_set.data[i] = x & set.data[i];
|
||||
return new_set;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform & over all bits, mutating itself.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
macro BitSet BitSet.and_self(&self, BitSet set) @operator(&=)
|
||||
{
|
||||
foreach (i, &x : self.data) *x &= set.data[i];
|
||||
return *self;
|
||||
}
|
||||
|
||||
<*
|
||||
Unset (clear) a bit in the bitset.
|
||||
|
||||
@param i : "The index to set"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn void BitSet.unset(&self, usz i)
|
||||
{
|
||||
@@ -44,7 +131,11 @@ fn void BitSet.unset(&self, usz i)
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Get a particular bit in the bitset
|
||||
|
||||
@param i : "The index of the bit"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn bool BitSet.get(&self, usz i) @operator([]) @inline
|
||||
{
|
||||
@@ -59,7 +150,12 @@ fn usz BitSet.len(&self) @operator(len) @inline
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Change a particular bit in the bitset
|
||||
|
||||
@param i : "The index of the bit"
|
||||
@param value : "The value to set the bit to"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
|
||||
{
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
*>
|
||||
module std::collections::enummap{Enum, ValueType};
|
||||
import std::io;
|
||||
|
||||
struct EnumMap (Printable)
|
||||
{
|
||||
ValueType[Enum.len] values;
|
||||
ValueType[Enum.values.len] values;
|
||||
}
|
||||
|
||||
fn void EnumMap.init(&self, ValueType init_value)
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
module std::collections::enumset{Enum};
|
||||
import std::io;
|
||||
|
||||
alias EnumSetType @private = $typefrom(type_for_enum_elements(Enum.elements));
|
||||
const ENUM_COUNT @private = Enum.values.len;
|
||||
alias EnumSetType @private = $typefrom(type_for_enum_elements(ENUM_COUNT));
|
||||
|
||||
const IS_CHAR_ARRAY = Enum.elements > 128;
|
||||
const IS_CHAR_ARRAY = ENUM_COUNT > 128;
|
||||
typedef EnumSet (Printable) = EnumSetType;
|
||||
|
||||
fn void EnumSet.add(&self, Enum v)
|
||||
|
||||
@@ -34,3 +34,12 @@ macro Type? Maybe.get(self)
|
||||
{
|
||||
return self.has_value ? self.value : NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn bool Maybe.equals(self, Maybe other) @operator(==) @if(types::is_equatable_type(Type))
|
||||
{
|
||||
if (self.has_value)
|
||||
{
|
||||
return other.has_value && equals(self.value, other.value);
|
||||
}
|
||||
return !other.has_value;
|
||||
}
|
||||
|
||||
@@ -186,6 +186,10 @@ fn void Object.set_object(&self, String key, Object* new_object) @private
|
||||
self.map.set(key, new_object);
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.allocator != null : "This object is not properly initialized, was it really created using 'new'"
|
||||
@require !@typeis(value, void*) ||| value == null : "void pointers cannot be stored in an object"
|
||||
*>
|
||||
macro Object* Object.object_from_value(&self, value) @private
|
||||
{
|
||||
var $Type = $typeof(value);
|
||||
@@ -201,7 +205,6 @@ macro Object* Object.object_from_value(&self, value) @private
|
||||
$case $Type.typeid == Object*.typeid:
|
||||
return value;
|
||||
$case $Type.typeid == void*.typeid:
|
||||
if (value != null) return TYPE_MISMATCH?;
|
||||
return &NULL_OBJECT;
|
||||
$case $assignable(value, String):
|
||||
return new_string(value, self.allocator);
|
||||
|
||||
@@ -248,7 +248,7 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
|
||||
}
|
||||
|
||||
// write end of stream
|
||||
output[pos:END_OF_STREAM.len] = END_OF_STREAM;
|
||||
output[pos:END_OF_STREAM.len] = END_OF_STREAM[..];
|
||||
pos += END_OF_STREAM.len;
|
||||
|
||||
return output[:pos];
|
||||
@@ -364,7 +364,7 @@ fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels c
|
||||
}
|
||||
|
||||
// draw the pixel
|
||||
if (channels == RGBA) { image[loc:4] = p.rgba; } else { image[loc:3] = p.rgb; }
|
||||
if (channels == RGBA) { image[loc:4] = p.rgba[..]; } else { image[loc:3] = p.rgb[..]; }
|
||||
}
|
||||
|
||||
return image;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2023-2025 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::mem::allocator;
|
||||
import std::math;
|
||||
|
||||
// The arena allocator allocates up to its maximum data
|
||||
// and then fails to allocate more, returning out of memory.
|
||||
// It supports mark and reset to mark.
|
||||
|
||||
struct ArenaAllocator (Allocator)
|
||||
{
|
||||
char[] data;
|
||||
@@ -12,6 +16,8 @@ struct ArenaAllocator (Allocator)
|
||||
|
||||
<*
|
||||
Initialize a memory arena for use using the provided bytes.
|
||||
|
||||
@param [inout] data : "The memory to use for the arena."
|
||||
*>
|
||||
fn ArenaAllocator* ArenaAllocator.init(&self, char[] data)
|
||||
{
|
||||
@@ -20,23 +26,44 @@ fn ArenaAllocator* ArenaAllocator.init(&self, char[] data)
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
Reset the usage completely.
|
||||
*>
|
||||
fn void ArenaAllocator.clear(&self)
|
||||
{
|
||||
self.used = 0;
|
||||
}
|
||||
|
||||
struct ArenaAllocatorHeader @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
<*
|
||||
Given some memory, create an arena allocator on the stack for it.
|
||||
|
||||
@param [inout] bytes : `The bytes to use, may be empty.`
|
||||
|
||||
@return `An arena allocator using the bytes`
|
||||
*>
|
||||
macro ArenaAllocator* wrap(char[] bytes)
|
||||
{
|
||||
return (ArenaAllocator){}.init(bytes);
|
||||
}
|
||||
|
||||
<*
|
||||
"Mark" the current state of the arena allocator by returning the use count.
|
||||
|
||||
@return `The value to pass to 'reset' in order to reset to the current use.`
|
||||
*>
|
||||
fn usz ArenaAllocator.mark(&self) => self.used;
|
||||
|
||||
<*
|
||||
Reset to a previous mark.
|
||||
|
||||
@param mark : `The previous mark.`
|
||||
@require mark <= self.used : "Invalid mark - out of range"
|
||||
*>
|
||||
fn void ArenaAllocator.reset(&self, usz mark) => self.used = mark;
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require ptr != null
|
||||
*>
|
||||
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
@@ -50,10 +77,10 @@ fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
}
|
||||
}
|
||||
|
||||
fn usz ArenaAllocator.mark(&self) => self.used;
|
||||
fn void ArenaAllocator.reset(&self, usz mark) => self.used = mark;
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require size > 0
|
||||
@@ -77,6 +104,8 @@ fn void*? ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz a
|
||||
}
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require old_pointer != null
|
||||
@@ -111,4 +140,12 @@ fn void*? ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
|
||||
void* mem = self.acquire(size, NO_ZERO, alignment)!;
|
||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
// Internal data
|
||||
|
||||
struct ArenaAllocatorHeader @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::io, std::math;
|
||||
|
||||
struct AllocChunk @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
<*
|
||||
The backed arena allocator provides an allocator that will allocate from a pre-allocated chunk of memory
|
||||
provided by it's backing allocator. The allocator supports mark / reset operations, so it can be used
|
||||
as a stack (push-pop) allocator. If the initial memory is used up, it will fall back to regular allocations,
|
||||
that will be safely freed on `reset`.
|
||||
|
||||
While this allocator is similar to the dynamic arena, it supports multiple "save points", which the dynamic arena
|
||||
doesn't.
|
||||
*>
|
||||
struct BackedArenaAllocator (Allocator)
|
||||
{
|
||||
Allocator backing_allocator;
|
||||
@@ -16,6 +19,12 @@ struct BackedArenaAllocator (Allocator)
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
struct AllocChunk @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
const usz PAGE_IS_ALIGNED @local = (usz)isz.max + 1u;
|
||||
|
||||
struct ExtraPage @local
|
||||
|
||||
@@ -4,6 +4,17 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::math;
|
||||
|
||||
<*
|
||||
The dynamic arena allocator is an arena allocator that can grow by adding additional arena "pages".
|
||||
It only supports reset, at which point all pages except the first one is released to the backing
|
||||
allocator.
|
||||
|
||||
If you want multiple save points, use the BackedArenaAllocator instead.
|
||||
|
||||
The advantage over the BackedArenaAllocator, is that when allocating beyond the first "page", it will
|
||||
retain the characteristics of an arena allocator (allocating a large piece of memory then handing off
|
||||
memory from that memory), wheras the BackedArenaAllocator will have heap allocator characteristics.
|
||||
*>
|
||||
struct DynamicArenaAllocator (Allocator)
|
||||
{
|
||||
Allocator backing_allocator;
|
||||
|
||||
@@ -5,6 +5,13 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::math;
|
||||
|
||||
<*
|
||||
The SimpleHeapAllocator implements a simple heap allocator on top of an allocator function.
|
||||
|
||||
It uses the given allocator function to allocate memory from some source, but never frees it.
|
||||
This allocator is intended to be used in environments where there isn't any native libc malloc,
|
||||
and it has to be emulated from a memory region, or wrapping linear memory as is the case for plain WASM.
|
||||
*>
|
||||
struct SimpleHeapAllocator (Allocator)
|
||||
{
|
||||
MemoryAllocFn alloc_fn;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2021-2025 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
|
||||
module std::core::mem::allocator @if(env::LIBC);
|
||||
import std::io;
|
||||
import libc;
|
||||
|
||||
const LibcAllocator LIBC_ALLOCATOR = {};
|
||||
<*
|
||||
The LibcAllocator is a wrapper around malloc to conform to the Allocator interface.
|
||||
*>
|
||||
typedef LibcAllocator (Allocator, Printable) = uptr;
|
||||
const LibcAllocator LIBC_ALLOCATOR = {};
|
||||
|
||||
fn String LibcAllocator.to_string(&self, Allocator allocator) @dynamic => "Libc allocator".copy(allocator);
|
||||
fn usz? LibcAllocator.to_format(&self, Formatter *format) @dynamic => format.print("Libc allocator");
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
module std::core::mem::allocator;
|
||||
|
||||
<*
|
||||
The OnStackAllocator is similar to the ArenaAllocator: it allocates from a chunk of memory
|
||||
given to it.
|
||||
|
||||
The difference is that when it runs out of memory it will go directly to its backing allocator
|
||||
rather than failing.
|
||||
|
||||
It is utilized by the @stack_mem macro as an alternative to the temp allocator.
|
||||
*>
|
||||
struct OnStackAllocator (Allocator)
|
||||
{
|
||||
Allocator backing_allocator;
|
||||
@@ -8,7 +17,6 @@ struct OnStackAllocator (Allocator)
|
||||
OnStackAllocatorExtraChunk* chunk;
|
||||
}
|
||||
|
||||
|
||||
struct OnStackAllocatorExtraChunk @local
|
||||
{
|
||||
bool is_aligned;
|
||||
|
||||
@@ -2,12 +2,32 @@ module std::core::mem::allocator;
|
||||
import std::io, std::math;
|
||||
import std::core::sanitizer::asan;
|
||||
|
||||
|
||||
struct TempAllocatorChunk @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
// This implements the temp allocator.
|
||||
// The temp allocator is a specialized allocator only intended for use where
|
||||
// the allocation is strictly stack-like.
|
||||
//
|
||||
// It is *not* thread-safe: you cannot safely use the
|
||||
// temp allocator on a thread and pass it to another thread.
|
||||
//
|
||||
// Fundamentally the temp allocator is a thread local arena allocator
|
||||
// but the stack-like behaviour puts additional constraints to it.
|
||||
//
|
||||
// It works great for allocating temporary data in a scope then dropping
|
||||
// that data, however you should not be storing temporary data in globals
|
||||
// or locals that have a lifetime outside of the current temp allocator scope.
|
||||
//
|
||||
// Furthermore, note that the temp allocator is bounded, with additional
|
||||
// allocations on top of that causing heap allocations. Such heap allocations
|
||||
// will be cleaned up but is undesirable from a performance standpoint.
|
||||
//
|
||||
// If you want customizable behaviour similar to the temp allocator, consider
|
||||
// the ArenaAllocator, BackedArenaAllocator or the DynamicArenaAllocator.
|
||||
//
|
||||
// Experimenting with the temp allocator to make it work outside of its
|
||||
// standard usage patterns will invariably end in tears and frustrated
|
||||
// hair pulling.
|
||||
//
|
||||
// Use one of the ArenaAllocators instead.
|
||||
|
||||
struct TempAllocator (Allocator)
|
||||
{
|
||||
@@ -21,8 +41,13 @@ struct TempAllocator (Allocator)
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
|
||||
struct TempAllocatorChunk @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
|
||||
|
||||
struct TempAllocatorPage
|
||||
{
|
||||
@@ -82,6 +107,9 @@ fn TempAllocator*? TempAllocator.derive_allocator(&self, usz min_size, usz buffe
|
||||
return temp;
|
||||
}
|
||||
|
||||
<*
|
||||
Reset the entire temp allocator, which will merge all the children into it.
|
||||
*>
|
||||
fn void TempAllocator.reset(&self)
|
||||
{
|
||||
TempAllocator* child = self.derived;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2021-2025 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
|
||||
@@ -18,6 +18,10 @@ alias AllocMap = HashMap { uptr, Allocation };
|
||||
// A simple tracking allocator.
|
||||
// It tracks allocations using a hash map but
|
||||
// is not compatible with allocators that uses mark()
|
||||
//
|
||||
// It is also embarassingly single-threaded, so
|
||||
// do not use it to track allocations that cross threads.
|
||||
|
||||
struct TrackingAllocator (Allocator)
|
||||
{
|
||||
Allocator inner_allocator;
|
||||
|
||||
@@ -2,6 +2,25 @@ module std::core::array;
|
||||
import std::core::array::slice;
|
||||
|
||||
<*
|
||||
Returns true if the array contains at least one element, else false
|
||||
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@require @typekind(array) == SLICE || @typekind(array) == ARRAY
|
||||
@require @typeis(array[0], $typeof(element)) : "array and element must have the same type"
|
||||
*>
|
||||
macro bool contains(array, element)
|
||||
{
|
||||
foreach (&item : array)
|
||||
{
|
||||
if (*item == element) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
<*
|
||||
Return the first index of element found in the array, searching from the start.
|
||||
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@return "the first index of the element"
|
||||
@@ -17,6 +36,14 @@ macro index_of(array, element)
|
||||
}
|
||||
|
||||
<*
|
||||
Slice a 2d array and create a Slice2d from it.
|
||||
|
||||
@param array_ptr : "the pointer to create a slice from"
|
||||
@param x : "The starting position of the slice x, optional"
|
||||
@param y : "The starting position of the slice y, optional"
|
||||
@param xlen : "The length of the slice in x, defaults to the length of the array"
|
||||
@param ylen : "The length of the slice in y, defaults to the length of the array"
|
||||
@return "A Slice2d from the array"
|
||||
@require @typekind(array_ptr) == POINTER
|
||||
@require @typekind(*array_ptr) == VECTOR || @typekind(*array_ptr) == ARRAY
|
||||
@require @typekind((*array_ptr)[0]) == VECTOR || @typekind((*array_ptr)[0]) == ARRAY
|
||||
@@ -26,11 +53,13 @@ macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
|
||||
if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
|
||||
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
|
||||
var $ElementType = $typeof((*array_ptr)[0][0]);
|
||||
return Slice2d{$ElementType} { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
|
||||
return (Slice2d{$ElementType}) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Return the first index of element found in the array, searching in reverse from the end.
|
||||
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@return "the last index of the element"
|
||||
@@ -81,97 +110,4 @@ macro concat(Allocator allocator, arr1, arr2) @nodiscard
|
||||
@require @typeis(arr1[0], $typeof(arr2[0])) : "Arrays must have the same type"
|
||||
@ensure return.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
|
||||
|
||||
module std::core::array::slice{Type};
|
||||
|
||||
struct Slice2d
|
||||
{
|
||||
Type* ptr;
|
||||
usz inner_len;
|
||||
usz ystart;
|
||||
usz ylen;
|
||||
usz xstart;
|
||||
usz xlen;
|
||||
}
|
||||
|
||||
fn usz Slice2d.len(&self) @operator(len)
|
||||
{
|
||||
return self.ylen;
|
||||
}
|
||||
|
||||
fn usz Slice2d.count(&self)
|
||||
{
|
||||
return self.ylen * self.xlen;
|
||||
}
|
||||
|
||||
macro void Slice2d.@each(&self; @body(usz[<2>], Type))
|
||||
{
|
||||
foreach (y, line : *self)
|
||||
{
|
||||
foreach (x, val : line)
|
||||
{
|
||||
@body({ x, y }, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
|
||||
{
|
||||
foreach (y, line : *self)
|
||||
{
|
||||
foreach (x, &val : line)
|
||||
{
|
||||
@body({ x, y }, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require idy >= 0 && idy < self.ylen
|
||||
*>
|
||||
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
|
||||
{
|
||||
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_coord(self, usz[<2>] coord)
|
||||
{
|
||||
return *self.get_coord_ref(coord);
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_xy(self, x, y)
|
||||
{
|
||||
return *self.get_xy_ref(x, y);
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_xy_ref(self, x, y)
|
||||
{
|
||||
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
|
||||
{
|
||||
return self.get_xy_ref(coord.x, coord.y);
|
||||
}
|
||||
|
||||
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
|
||||
{
|
||||
*self.get_coord_ref(coord) = value;
|
||||
}
|
||||
|
||||
macro void Slice2d.set_xy(self, x, y, Type value)
|
||||
{
|
||||
*self.get_xy_ref(x, y) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
@require y >= 0 && y < self.ylen
|
||||
@require x >= 0 && x < self.xlen
|
||||
*>
|
||||
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
|
||||
{
|
||||
if (xlen < 1) xlen = self.xlen + xlen;
|
||||
if (ylen < 1) ylen = self.ylen + ylen;
|
||||
return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen };
|
||||
}
|
||||
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
|
||||
114
lib/std/core/ascii.c3
Normal file
114
lib/std/core/ascii.c3
Normal file
@@ -0,0 +1,114 @@
|
||||
<*
|
||||
This module contains utils for handling ASCII characters. They only operate on
|
||||
characters corresponding to 0-127.
|
||||
*>
|
||||
module std::core::ascii;
|
||||
|
||||
macro bool @is_lower(c) => ASCII_LOOKUP[c].lower; // Is a-z
|
||||
macro bool @is_upper(c) => ASCII_LOOKUP[c].upper; // Is A-Z
|
||||
macro bool @is_digit(c) => ASCII_LOOKUP[c].digit; // Is 0-9
|
||||
macro bool @is_bdigit(c) => ASCII_LOOKUP[c].bin_digit; // Is 0-1
|
||||
macro bool @is_odigit(c) => ASCII_LOOKUP[c].oct_digit; // Is 0-7
|
||||
macro bool @is_xdigit(c) => ASCII_LOOKUP[c].hex_digit; // Is 0-9 or a-f or A-F
|
||||
macro bool @is_alpha(c) => ASCII_LOOKUP[c].alpha; // Is a-z or A-Z
|
||||
macro bool @is_print(c) => ASCII_LOOKUP[c].printable; // Is a printable character (space or higher and < 127
|
||||
macro bool @is_graph(c) => ASCII_LOOKUP[c].graph; // Does it show any graphics (printable but not space)
|
||||
macro bool @is_space(c) => ASCII_LOOKUP[c].space; // Is it a space character: space, tab, linefeed etc
|
||||
macro bool @is_alnum(c) => ASCII_LOOKUP[c].alphanum; // Is it alpha or digit
|
||||
macro bool @is_punct(c) => ASCII_LOOKUP[c].punct; // Is it "graph" but not digit or letter
|
||||
macro bool @is_blank(c) => ASCII_LOOKUP[c].blank; // Is it a blank space: space or tab
|
||||
macro bool @is_cntrl(c) => ASCII_LOOKUP[c].control; // Is it a control character: before space or 127
|
||||
macro char @to_lower(c) => c + TO_LOWER[c]; // Convert A-Z to a-z if found
|
||||
macro char @to_upper(c) => c - TO_UPPER[c]; // Convert a-z to A-Z if found
|
||||
|
||||
fn bool is_lower(char c) => @is_lower(c); // Is a-z
|
||||
fn bool is_upper(char c) => @is_upper(c); // Is A-Z
|
||||
fn bool is_digit(char c) => @is_digit(c); // Is 0-9
|
||||
fn bool is_bdigit(char c) => @is_bdigit(c); // Is 0-1
|
||||
fn bool is_odigit(char c) => @is_odigit(c); // Is 0-7
|
||||
fn bool is_xdigit(char c) => @is_xdigit(c); // Is 0-9 or a-f or A-F
|
||||
fn bool is_alpha(char c) => @is_alpha(c); // Is a-z or A-Z
|
||||
fn bool is_print(char c) => @is_print(c); // Is a printable character (space or higher and < 127
|
||||
fn bool is_graph(char c) => @is_graph(c); // Does it show any graphics (printable but not space)
|
||||
fn bool is_space(char c) => @is_space(c); // Is it a space character: space, tab, linefeed etc
|
||||
fn bool is_alnum(char c) => @is_alnum(c); // Is it alpha or digit
|
||||
fn bool is_punct(char c) => @is_punct(c); // Is it "graph" but not digit or letter
|
||||
fn bool is_blank(char c) => @is_blank(c); // Is it a blank space: space or tab
|
||||
fn bool is_cntrl(char c) => @is_cntrl(c); // Is it a control character: before space or 127
|
||||
fn char to_lower(char c) => @to_lower(c); // Convert A-Z to a-z if found
|
||||
fn char to_upper(char c) => @to_upper(c); // Convert a-z to A-Z if found
|
||||
|
||||
// The following methods are macro methods for the same functions
|
||||
macro bool char.is_lower(char c) => @is_lower(c);
|
||||
macro bool char.is_upper(char c) => @is_upper(c);
|
||||
macro bool char.is_digit(char c) => @is_digit(c);
|
||||
macro bool char.is_bdigit(char c) => @is_bdigit(c);
|
||||
macro bool char.is_odigit(char c) => @is_odigit(c);
|
||||
macro bool char.is_xdigit(char c) => @is_xdigit(c);
|
||||
macro bool char.is_alpha(char c) => @is_alpha(c);
|
||||
macro bool char.is_print(char c) => @is_print(c);
|
||||
macro bool char.is_graph(char c) => @is_graph(c);
|
||||
macro bool char.is_space(char c) => @is_space(c);
|
||||
macro bool char.is_alnum(char c) => @is_alnum(c);
|
||||
macro bool char.is_punct(char c) => @is_punct(c);
|
||||
macro bool char.is_blank(char c) => @is_blank(c);
|
||||
macro bool char.is_cntrl(char c) => @is_cntrl(c);
|
||||
macro char char.to_lower(char c) => @to_lower(c);
|
||||
macro char char.to_upper(char c) => @to_upper(c);
|
||||
|
||||
<*
|
||||
Convert a-f/A-F/0-9 to the appropriate hex value.
|
||||
|
||||
@require c.is_xdigit()
|
||||
@ensure return >= 0 && return <= 15
|
||||
*>
|
||||
macro char char.from_hex(char c) => HEX_VALUE[c];
|
||||
|
||||
<*
|
||||
Bitstruct containing the different properties of a character
|
||||
*>
|
||||
bitstruct CharType : ushort @private
|
||||
{
|
||||
bool lower;
|
||||
bool upper;
|
||||
bool digit;
|
||||
bool bin_digit;
|
||||
bool hex_digit;
|
||||
bool oct_digit;
|
||||
bool alpha;
|
||||
bool alphanum;
|
||||
bool space;
|
||||
bool printable;
|
||||
bool blank;
|
||||
bool punct;
|
||||
bool control;
|
||||
bool graph;
|
||||
}
|
||||
|
||||
const CharType[256] ASCII_LOOKUP @private = {
|
||||
[0..31] = { .control },
|
||||
[9..13] = { .control, .space },
|
||||
['\t'] = { .control, .space, .blank },
|
||||
[' '] = { .space, .printable, .blank },
|
||||
[33..126] = { .printable, .graph, .punct },
|
||||
['0'..'9'] = { .printable, .graph, .alphanum, .hex_digit, .digit },
|
||||
['2'..'7'] = { .printable, .graph, .alphanum, .hex_digit, .digit, .oct_digit },
|
||||
['0'..'1'] = { .printable, .graph, .alphanum, .hex_digit, .digit, .oct_digit, .bin_digit },
|
||||
['A'..'Z'] = { .printable, .graph, .alphanum, .alpha, .upper },
|
||||
['A'..'F'] = { .printable, .graph, .alphanum, .alpha, .upper, .hex_digit },
|
||||
['a'..'z'] = { .printable, .graph, .alphanum, .alpha, .lower },
|
||||
['a'..'f'] = { .printable, .graph, .alphanum, .alpha, .lower, .hex_digit },
|
||||
[127] = { .control },
|
||||
};
|
||||
|
||||
const char[256] HEX_VALUE = {
|
||||
['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4,
|
||||
['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9,
|
||||
['A'] = 10, ['B'] = 11, ['C'] = 12, ['D'] = 13, ['E'] = 14,
|
||||
['F'] = 15, ['a'] = 10, ['b'] = 11, ['c'] = 12, ['d'] = 13,
|
||||
['e'] = 14, ['f'] = 15
|
||||
};
|
||||
|
||||
const char[256] TO_UPPER @private = { ['a'..'z'] = 'a' - 'A' };
|
||||
const char[256] TO_LOWER @private = { ['A'..'Z'] = 'a' - 'A' };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno and contributors. All rights reserved.
|
||||
// Copyright (c) 2023-2025 Christoffer Lerno and contributors. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::bitorder;
|
||||
|
||||
@@ -30,6 +30,14 @@ typedef EmptySlot = void*;
|
||||
macro @is_empty_macro_slot(#arg) @const @builtin => @typeis(#arg, EmptySlot);
|
||||
macro @is_valid_macro_slot(#arg) @const @builtin => !@typeis(#arg, EmptySlot);
|
||||
|
||||
<*
|
||||
Returns a random value at compile time.
|
||||
|
||||
@ensure return >= 0.0 && return < 1.0
|
||||
@return "A compile time random"
|
||||
*>
|
||||
macro @rnd() @const @builtin => $$rnd();
|
||||
|
||||
/*
|
||||
Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
*/
|
||||
@@ -278,7 +286,6 @@ macro enum_by_name($Type, String enum_name) @builtin
|
||||
*>
|
||||
macro @enum_from_value($Type, #value, value) @builtin
|
||||
{
|
||||
usz elements = $Type.elements;
|
||||
foreach (e : $Type.values)
|
||||
{
|
||||
if (e.#value == value) return e;
|
||||
@@ -431,22 +438,63 @@ macro @generic_hash(value)
|
||||
return h;
|
||||
}
|
||||
|
||||
macro uint int.hash(int i) => @generic_hash(i);
|
||||
macro uint uint.hash(uint i) => @generic_hash(i);
|
||||
macro uint short.hash(short s) => @generic_hash(s);
|
||||
macro uint ushort.hash(ushort s) => @generic_hash(s);
|
||||
macro uint char.hash(char c) => @generic_hash(c);
|
||||
macro uint ichar.hash(ichar c) => @generic_hash(c);
|
||||
macro uint long.hash(long i) => @generic_hash(i);
|
||||
macro uint ulong.hash(ulong i) => @generic_hash(i);
|
||||
macro uint int128.hash(int128 i) => @generic_hash(i);
|
||||
macro uint uint128.hash(uint128 i) => @generic_hash(i);
|
||||
macro uint bool.hash(bool b) => @generic_hash(b);
|
||||
|
||||
macro uint int128.hash(self) => @generic_hash(self);
|
||||
macro uint uint128.hash(self) => @generic_hash(self);
|
||||
macro uint long.hash(self) => @generic_hash(self);
|
||||
macro uint ulong.hash(self) => @generic_hash(self);
|
||||
macro uint int.hash(self) => @generic_hash(self);
|
||||
macro uint uint.hash(self) => @generic_hash(self);
|
||||
macro uint short.hash(self) => @generic_hash(self);
|
||||
macro uint ushort.hash(self) => @generic_hash(self);
|
||||
macro uint ichar.hash(self) => @generic_hash(self);
|
||||
macro uint char.hash(self) => @generic_hash(self);
|
||||
macro uint bool.hash(self) => @generic_hash(self);
|
||||
|
||||
macro uint int128[*].hash(&self) => hash_array(self);
|
||||
macro uint uint128[*].hash(&self) => hash_array(self);
|
||||
macro uint long[*].hash(&self) => hash_array(self);
|
||||
macro uint ulong[*].hash(&self) => hash_array(self);
|
||||
macro uint int[*].hash(&self) => hash_array(self);
|
||||
macro uint uint[*].hash(&self) => hash_array(self);
|
||||
macro uint short[*].hash(&self) => hash_array(self);
|
||||
macro uint ushort[*].hash(&self) => hash_array(self);
|
||||
macro uint char[*].hash(&self) => hash_array(self);
|
||||
macro uint ichar[*].hash(&self) => hash_array(self);
|
||||
macro uint bool[*].hash(&self) => hash_array(self);
|
||||
|
||||
macro uint int128[<*>].hash(self) => hash_vec(self);
|
||||
macro uint uint128[<*>].hash(self) => hash_vec(self);
|
||||
macro uint long[<*>].hash(self) => hash_vec(self);
|
||||
macro uint ulong[<*>].hash(self) => hash_vec(self);
|
||||
macro uint int[<*>].hash(self) => hash_vec(self);
|
||||
macro uint uint[<*>].hash(self) => hash_vec(self);
|
||||
macro uint short[<*>].hash(self) => hash_vec(self);
|
||||
macro uint ushort[<*>].hash(self) => hash_vec(self);
|
||||
macro uint char[<*>].hash(self) => hash_vec(self);
|
||||
macro uint ichar[<*>].hash(self) => hash_vec(self);
|
||||
macro uint bool[<*>].hash(self) => hash_vec(self);
|
||||
|
||||
macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t));
|
||||
macro uint String.hash(String c) => (uint)fnv32a::hash(c);
|
||||
macro uint char[].hash(char[] c) => (uint)fnv32a::hash(c);
|
||||
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
|
||||
|
||||
<*
|
||||
@require @typekind(array_ptr) == POINTER &&& @typekind(*array_ptr) == ARRAY
|
||||
*>
|
||||
macro uint hash_array(array_ptr) @local
|
||||
{
|
||||
return (uint)fnv32a::hash(((char*)array_ptr)[:$sizeof(*array_ptr)]);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @typekind(vec) == VECTOR
|
||||
*>
|
||||
macro uint hash_vec(vec) @local
|
||||
{
|
||||
return (uint)fnv32a::hash(((char*)&&vec)[:$sizeof(vec.len * $typeof(vec).inner.sizeof)]);
|
||||
}
|
||||
|
||||
const MAX_FRAMEADDRESS = 128;
|
||||
<*
|
||||
@@ -731,7 +779,7 @@ macro void* get_returnaddress(int n)
|
||||
}
|
||||
}
|
||||
|
||||
module std::core::builtin @if((env::LINUX || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
|
||||
module std::core::builtin @if((env::LINUX || env::ANDROID || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
|
||||
import libc, std::io;
|
||||
|
||||
fn void sig_panic(String message)
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
module std::core::dstring;
|
||||
import std::io;
|
||||
|
||||
<*
|
||||
The DString offers a dynamic string builder.
|
||||
*>
|
||||
typedef DString (OutStream) = DStringOpaque*;
|
||||
typedef DStringOpaque = void;
|
||||
|
||||
const usz MIN_CAPACITY @private = 16;
|
||||
|
||||
<*
|
||||
Initialize the DString with a particular allocator.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param capacity : "Starting capacity, defaults to MIN_CAPACITY and cannot be smaller"
|
||||
@return "Return the DString itself"
|
||||
@require !self.data() : "String already initialized"
|
||||
*>
|
||||
fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
|
||||
@@ -20,6 +28,11 @@ fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize the DString with the temp allocator. Note that if the dstring is never
|
||||
initialized, this is the allocator it will default to.
|
||||
|
||||
@param capacity : "Starting capacity, defaults to MIN_CAPACITY and cannot be smaller"
|
||||
@return "Return the DString itself"
|
||||
@require !self.data() : "String already initialized"
|
||||
*>
|
||||
fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY)
|
||||
@@ -99,7 +112,7 @@ fn void DString.replace(&self, String needle, String replacement)
|
||||
};
|
||||
}
|
||||
|
||||
fn DString DString.concat(self, Allocator allocator, DString b)
|
||||
fn DString DString.concat(self, Allocator allocator, DString b) @nodiscard
|
||||
{
|
||||
DString string;
|
||||
string.init(allocator, self.len() + b.len());
|
||||
@@ -220,7 +233,7 @@ fn usz DString.append_char32(&self, Char32 c)
|
||||
|
||||
fn DString DString.tcopy(&self) => self.copy(tmem);
|
||||
|
||||
fn DString DString.copy(self, Allocator allocator)
|
||||
fn DString DString.copy(self, Allocator allocator) @nodiscard
|
||||
{
|
||||
if (!self) return new(allocator);
|
||||
StringData* data = self.data();
|
||||
@@ -229,7 +242,7 @@ fn DString DString.copy(self, Allocator allocator)
|
||||
return new_string;
|
||||
}
|
||||
|
||||
fn ZString DString.copy_zstr(self, Allocator allocator)
|
||||
fn ZString DString.copy_zstr(self, Allocator allocator) @nodiscard
|
||||
{
|
||||
usz str_len = self.len();
|
||||
if (!str_len)
|
||||
@@ -243,12 +256,12 @@ fn ZString DString.copy_zstr(self, Allocator allocator)
|
||||
return (ZString)zstr;
|
||||
}
|
||||
|
||||
fn String DString.copy_str(self, Allocator allocator)
|
||||
fn String DString.copy_str(self, Allocator allocator) @nodiscard
|
||||
{
|
||||
return (String)self.copy_zstr(allocator)[:self.len()];
|
||||
}
|
||||
|
||||
fn String DString.tcopy_str(self) => self.copy_str(tmem) @inline;
|
||||
fn String DString.tcopy_str(self) @nodiscard => self.copy_str(tmem) @inline;
|
||||
|
||||
fn bool DString.equals(self, DString other_string)
|
||||
{
|
||||
@@ -558,7 +571,7 @@ fn usz? DString.appendfn(&self, String format, args...) @maydiscard
|
||||
};
|
||||
}
|
||||
|
||||
fn DString join(Allocator allocator, String[] s, String joiner)
|
||||
fn DString join(Allocator allocator, String[] s, String joiner) @nodiscard
|
||||
{
|
||||
if (!s.len) return new(allocator);
|
||||
usz total_size = joiner.len * s.len;
|
||||
@@ -568,10 +581,10 @@ fn DString join(Allocator allocator, String[] s, String joiner)
|
||||
}
|
||||
DString res = new_with_capacity(allocator, total_size);
|
||||
res.append(s[0]);
|
||||
foreach (String* &str : s[1..])
|
||||
foreach (String str : s[1..])
|
||||
{
|
||||
res.append(joiner);
|
||||
res.append(*str);
|
||||
res.append(str);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ const String COMPILER_BUILD_HASH = $$BUILD_HASH;
|
||||
const String COMPILER_BUILD_DATE = $$BUILD_DATE;
|
||||
const OsType OS_TYPE = OsType.from_ordinal($$OS_TYPE);
|
||||
const ArchType ARCH_TYPE = ArchType.from_ordinal($$ARCH_TYPE);
|
||||
const usz MAX_VECTOR_SIZE = $$MAX_VECTOR_SIZE;
|
||||
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
|
||||
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
|
||||
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
|
||||
|
||||
@@ -342,7 +342,7 @@ macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_alig
|
||||
@param $src_align : "the alignment of the destination if different from the default, 0 assumes the default"
|
||||
@param $is_volatile : "True if this copy should be treated as volatile, i.e. it can't be optimized away."
|
||||
|
||||
@require src != null || len == 0 : "Copying a null with non-zero length is invalid"
|
||||
@require src != null || $len == 0 : "Copying a null with non-zero length is invalid"
|
||||
@require $len == 0 || dst + $len <= src || src + $len <= dst : "Ranges may not overlap"
|
||||
*>
|
||||
macro void copy_inline(void* dst, void* src, usz $len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
|
||||
@@ -627,14 +627,36 @@ macro TrackingEnv* get_tracking_env()
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value"
|
||||
@require $alignof(value) <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'clone_aligned' instead"
|
||||
*>
|
||||
macro @clone(value) @builtin @nodiscard
|
||||
{
|
||||
return allocator::clone(mem, value);
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value, which must be released using free_aligned"
|
||||
*>
|
||||
macro @clone_aligned(value) @builtin @nodiscard
|
||||
{
|
||||
return allocator::clone_aligned(mem, value);
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value"
|
||||
*>
|
||||
macro @tclone(value) @builtin @nodiscard
|
||||
{
|
||||
return tnew($typeof(value), value);
|
||||
$if $alignof(value) <= mem::DEFAULT_MEM_ALIGNMENT:
|
||||
return tnew($typeof(value), value);
|
||||
$else
|
||||
return allocator::clone_aligned(tmem, value);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void* malloc(usz size) @builtin @inline @nodiscard
|
||||
@@ -738,9 +760,9 @@ macro alloc_aligned($Type) @nodiscard
|
||||
macro tnew($Type, ...) @nodiscard
|
||||
{
|
||||
$if $vacount == 0:
|
||||
return ($Type*)tcalloc($Type.sizeof) @inline;
|
||||
return ($Type*)tcalloc($Type.sizeof, $Type.alignof) @inline;
|
||||
$else
|
||||
$Type* val = tmalloc($Type.sizeof) @inline;
|
||||
$Type* val = tmalloc($Type.sizeof, $Type.alignof) @inline;
|
||||
*val = $vaexpr[0];
|
||||
return val;
|
||||
$endif
|
||||
@@ -753,9 +775,9 @@ macro tnew($Type, ...) @nodiscard
|
||||
macro temp_with_padding($Type, usz padding, ...) @nodiscard
|
||||
{
|
||||
$if $vacount == 0:
|
||||
return ($Type*)tcalloc($Type.sizeof + padding) @inline;
|
||||
return ($Type*)tcalloc($Type.sizeof + padding, $Type.alignof) @inline;
|
||||
$else
|
||||
$Type* val = tmalloc($Type.sizeof + padding) @inline;
|
||||
$Type* val = tmalloc($Type.sizeof + padding, $Type.alignof) @inline;
|
||||
*val = $vaexpr[0];
|
||||
return val;
|
||||
$endif
|
||||
@@ -763,12 +785,12 @@ macro temp_with_padding($Type, usz padding, ...) @nodiscard
|
||||
|
||||
macro talloc($Type) @nodiscard
|
||||
{
|
||||
return tmalloc($Type.sizeof);
|
||||
return tmalloc($Type.sizeof, $Type.alignof);
|
||||
}
|
||||
|
||||
macro talloc_with_padding($Type, usz padding) @nodiscard
|
||||
{
|
||||
return tmalloc($Type.sizeof + padding);
|
||||
return tmalloc($Type.sizeof + padding, $Type.alignof);
|
||||
}
|
||||
|
||||
<*
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::math;
|
||||
|
||||
// C3 has multiple different allocators available:
|
||||
//
|
||||
// Name Arena Uses buffer OOM Fallback? Mark? Reset?
|
||||
// ArenaAllocator Yes Yes No Yes Yes
|
||||
// BackedArenaAllocator Yes No Yes Yes Yes
|
||||
// DynamicArenaAllocator Yes No Yes No Yes
|
||||
// HeapAllocator No No No No No *Note: Not for normal use
|
||||
// LibcAllocator No No No No No *Note: Wraps malloc
|
||||
// OnStackAllocator Yes Yes Yes No No *Note: Used by @stack_mem
|
||||
// TempAllocator Yes No Yes No* No* *Note: Mark/reset using @pool
|
||||
// TrackingAllocator No No N/A No No *Note: Wraps other heap allocator
|
||||
|
||||
const DEFAULT_SIZE_PREFIX = usz.sizeof;
|
||||
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
|
||||
|
||||
@@ -20,13 +32,18 @@ enum AllocInitType
|
||||
interface Allocator
|
||||
{
|
||||
<*
|
||||
Acquire memory from the allocator, with the given alignment and initialization type.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require size > 0
|
||||
@require size > 0 : "The size must be 1 or more"
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? acquire(usz size, AllocInitType init_type, usz alignment = 0);
|
||||
|
||||
<*
|
||||
Resize acquired memory from the allocator, with the given new size and alignment.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require ptr != null
|
||||
@@ -34,8 +51,11 @@ interface Allocator
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? resize(void* ptr, usz new_size, usz alignment = 0);
|
||||
|
||||
<*
|
||||
@require ptr != null
|
||||
Release memory acquired using `acquire` or `resize`.
|
||||
|
||||
@require ptr != null : "Empty pointers should never be released"
|
||||
*>
|
||||
fn void release(void* ptr, bool aligned);
|
||||
}
|
||||
@@ -283,11 +303,31 @@ macro alloc_array_try(Allocator allocator, $Type, usz elements) @nodiscard
|
||||
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
|
||||
}
|
||||
|
||||
<*
|
||||
Clone a value.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use to clone"
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value"
|
||||
@require $alignof(value) <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'clone_aligned' instead"
|
||||
*>
|
||||
macro clone(Allocator allocator, value) @nodiscard
|
||||
{
|
||||
return new(allocator, $typeof(value), value);
|
||||
}
|
||||
|
||||
<*
|
||||
Clone overaligned values. Must be released using free_aligned.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use to clone"
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value"
|
||||
*>
|
||||
macro clone_aligned(Allocator allocator, value) @nodiscard
|
||||
{
|
||||
return new_aligned(allocator, $typeof(value), value)!!;
|
||||
}
|
||||
|
||||
fn any clone_any(Allocator allocator, any value) @nodiscard
|
||||
{
|
||||
usz size = value.type.sizeof;
|
||||
@@ -435,7 +475,7 @@ fn Allocator create_temp_allocator_on_demand() @private
|
||||
auto_create_temp = true;
|
||||
abort("Use '@pool_init()' to enable the temp allocator on a new thread. A temp allocator is only implicitly created on the main thread.");
|
||||
}
|
||||
return create_temp_allocator(base_allocator(), temp_allocator_size());
|
||||
return create_temp_allocator(temp_base_allocator, temp_allocator_size());
|
||||
}
|
||||
<*
|
||||
@require !top_temp : "This should never be called when temp already exists"
|
||||
@@ -452,7 +492,7 @@ macro Allocator temp()
|
||||
|
||||
alias tmem @builtin = current_temp;
|
||||
|
||||
fn void allow_implicit_temp_allocator_on_load_thread() @init(1) @local @if(env::LIBC)
|
||||
fn void allow_implicit_temp_allocator_on_load_thread() @init(1) @local @if(env::LIBC || env::WASM_NOLIBC)
|
||||
{
|
||||
auto_create_temp = true;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ uint128 x86_features;
|
||||
|
||||
fn void add_feature_if_bit(X86Feature feature, uint register, int bit)
|
||||
{
|
||||
if (register & 1U << bit) x86_features |= 1u128 << feature.ordinal;
|
||||
if (register & 1U << bit) x86_features |= 1ULL << feature.ordinal;
|
||||
}
|
||||
|
||||
fn void x86_initialize_cpu_features()
|
||||
|
||||
@@ -22,6 +22,14 @@ struct SliceRaw
|
||||
usz len;
|
||||
}
|
||||
|
||||
macro @enum_lookup($Type, #value, value)
|
||||
{
|
||||
$foreach $val : $Type.values:
|
||||
if ($val.#value == value) return $val;
|
||||
$endforeach
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
|
||||
module std::core::runtime @if(WASM_NOLIBC);
|
||||
|
||||
|
||||
169
lib/std/core/slice2d.c3
Normal file
169
lib/std/core/slice2d.c3
Normal file
@@ -0,0 +1,169 @@
|
||||
module std::core::array::slice {Type};
|
||||
|
||||
<*
|
||||
A slice2d allows slicing an array like int[10][10] into an arbitrary "int[][]"-like counterpart
|
||||
Typically you'd use array::slice2d(...) to create one.
|
||||
*>
|
||||
struct Slice2d
|
||||
{
|
||||
Type* ptr;
|
||||
usz inner_len;
|
||||
usz ystart;
|
||||
usz ylen;
|
||||
usz xstart;
|
||||
usz xlen;
|
||||
}
|
||||
|
||||
<*
|
||||
@return `The length of the "outer" slice`
|
||||
*>
|
||||
fn usz Slice2d.len(&self) @operator(len)
|
||||
{
|
||||
return self.ylen;
|
||||
}
|
||||
|
||||
<*
|
||||
@return `The total number of elements.`
|
||||
*>
|
||||
fn usz Slice2d.count(&self)
|
||||
{
|
||||
return self.ylen * self.xlen;
|
||||
}
|
||||
|
||||
<*
|
||||
Step through each element of the slice.
|
||||
*>
|
||||
macro void Slice2d.@each(&self; @body(usz[<2>], Type))
|
||||
{
|
||||
foreach (y, line : *self)
|
||||
{
|
||||
foreach (x, val : line)
|
||||
{
|
||||
@body({ x, y }, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Step through each element of the slice *by reference*
|
||||
*>
|
||||
macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
|
||||
{
|
||||
foreach (y, line : *self)
|
||||
{
|
||||
foreach (x, &val : line)
|
||||
{
|
||||
@body({ x, y }, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Return a row as a slice.
|
||||
|
||||
@param idy : "The row to return"
|
||||
@return "The slice for the particular row"
|
||||
@require idy >= 0 && idy < self.ylen
|
||||
*>
|
||||
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
|
||||
{
|
||||
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value at a particular x/y position in the slice.
|
||||
|
||||
@param coord : "The xy coordinate"
|
||||
@return "The value at that coordinate"
|
||||
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
|
||||
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type Slice2d.get_coord(self, usz[<2>] coord)
|
||||
{
|
||||
return *self.get_coord_ref(coord);
|
||||
}
|
||||
|
||||
<*
|
||||
Get a pointer to the value at a particular x/y position in the slice.
|
||||
|
||||
@param coord : "The xy coordinate"
|
||||
@return "A pointer to the value at that coordinate"
|
||||
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
|
||||
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
|
||||
{
|
||||
return self.get_xy_ref(coord.x, coord.y);
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value at a particular x/y position in the slice.
|
||||
|
||||
@param x : "The x coordinate"
|
||||
@param y : "The x coordinate"
|
||||
@return "The value at that coordinate"
|
||||
@require y >= 0 && y < self.ylen : "y value out of range"
|
||||
@require x >= 0 && x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type Slice2d.get_xy(self, x, y)
|
||||
{
|
||||
return *self.get_xy_ref(x, y);
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value at a particular x/y position in the slice by reference.
|
||||
|
||||
@param x : "The x coordinate"
|
||||
@param y : "The y coordinate"
|
||||
@return "A pointer to the value at that coordinate"
|
||||
@require y >= 0 && y < self.ylen : "y value out of range"
|
||||
@require x >= 0 && x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type* Slice2d.get_xy_ref(self, x, y)
|
||||
{
|
||||
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
|
||||
}
|
||||
|
||||
<*
|
||||
Set the ´value at a particular x/y position in the slice.
|
||||
|
||||
@param coord : "The xy coordinate"
|
||||
@param value : "The new value"
|
||||
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
|
||||
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
|
||||
{
|
||||
*self.get_coord_ref(coord) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
Set the value at a particular x/y position in the slice.
|
||||
|
||||
@param x : "The x coordinate"
|
||||
@param y : "The y coordinate"
|
||||
@param value : "The new value"
|
||||
@require y >= 0 && y < self.ylen : "y value out of range"
|
||||
@require x >= 0 && x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro void Slice2d.set_xy(self, x, y, Type value)
|
||||
{
|
||||
*self.get_xy_ref(x, y) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
Reslice a slice2d returning a new slice.
|
||||
|
||||
@param x : "The starting x"
|
||||
@param xlen : "The length along x"
|
||||
@param y : "The starting y"
|
||||
@param ylen : "The length along y"
|
||||
@require y >= 0 && y < self.ylen
|
||||
@require x >= 0 && x < self.xlen
|
||||
*>
|
||||
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
|
||||
{
|
||||
if (xlen < 1) xlen = self.xlen + xlen;
|
||||
if (ylen < 1) ylen = self.ylen + ylen;
|
||||
return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen };
|
||||
}
|
||||
@@ -1,41 +1,78 @@
|
||||
module std::core::string;
|
||||
import std::ascii;
|
||||
import std::io;
|
||||
|
||||
typedef String @if(!$defined(String)) = inline char[];
|
||||
<*
|
||||
ZString is a pointer to a zero terminated array of chars.
|
||||
|
||||
Use ZString when you need to interop with C zero terminated strings.
|
||||
*>
|
||||
typedef ZString = inline char*;
|
||||
|
||||
<*
|
||||
WString is a pointer to a zero terminated array of Char16.
|
||||
|
||||
Depending on the platform, this may or may not correspond to wchar_t.
|
||||
For Windows, wchar_t is generally 16 bits, on MacOS it is 32 bits.
|
||||
However, for both MacOS and Linux, regular C strings (ZString)
|
||||
will be UTF-8 encoded, so there is no need to use the wchar_t versions
|
||||
of functions outside of encoding functions.
|
||||
*>
|
||||
typedef WString = inline Char16*;
|
||||
|
||||
<*
|
||||
Char32 is a UTF32 codepoint
|
||||
*>
|
||||
alias Char32 = uint;
|
||||
|
||||
<*
|
||||
Char16 is a UTF16 "character"
|
||||
*>
|
||||
alias Char16 = ushort;
|
||||
|
||||
<*
|
||||
Common faults used with strings
|
||||
*>
|
||||
faultdef INVALID_UTF8, INVALID_UTF16, CONVERSION_FAILED,
|
||||
EMPTY_STRING, NEGATIVE_VALUE, MALFORMED_INTEGER,
|
||||
INTEGER_OVERFLOW, MALFORMED_FLOAT, FLOAT_OUT_OF_RANGE;
|
||||
|
||||
const uint SURROGATE_OFFSET @private = 0x10000;
|
||||
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
|
||||
const uint SURROGATE_MASK @private = 0xFC00;
|
||||
const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF;
|
||||
const uint SURROGATE_BITS @private = 10;
|
||||
const uint SURROGATE_LOW_VALUE @private = 0xDC00;
|
||||
const uint SURROGATE_HIGH_VALUE @private = 0xD800;
|
||||
|
||||
<*
|
||||
Create a pointer to an UTF32 encoded string at compile time.
|
||||
|
||||
@param $string : "The string to encode"
|
||||
*>
|
||||
macro Char32* @wstring32(String $string) @builtin
|
||||
{
|
||||
return (Char32*)&&$$wstr32($string);
|
||||
}
|
||||
|
||||
<*
|
||||
Create a slice of an UTF32 encoded string at compile time.
|
||||
|
||||
@param $string : "The string to encode"
|
||||
*>
|
||||
macro Char32[] @char32(String $string) @builtin
|
||||
{
|
||||
return $$wstr32($string)[..^2];
|
||||
}
|
||||
|
||||
<*
|
||||
Create a WString (an UTF16 encoded string) at compile time.
|
||||
|
||||
@param $string : "The string to encode"
|
||||
*>
|
||||
macro WString @wstring(String $string) @builtin
|
||||
{
|
||||
return (WString)&&$$wstr16($string);
|
||||
}
|
||||
|
||||
<*
|
||||
Create a slice of an UTF32 encoded string at compile time.
|
||||
|
||||
@param $string : "The string to encode"
|
||||
*>
|
||||
macro Char16[] @char16(String $string) @builtin
|
||||
{
|
||||
return $$wstr16($string)[..^2];
|
||||
@@ -117,6 +154,39 @@ fn String join(Allocator allocator, String[] s, String joiner)
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Replace all instances of one substring with a different string.
|
||||
|
||||
@param [in] self
|
||||
@param [in] needle : `The string to be replaced`
|
||||
@param [in] new_str : `The replacement string`
|
||||
@param [&inout] allocator : `The allocator to use for the String`
|
||||
@return "The new string with the elements replaced"
|
||||
*>
|
||||
fn String String.replace(self, Allocator allocator, String needle, String new_str) @nodiscard
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
String[] split = self.tsplit(needle);
|
||||
return dstring::join(tmem, split, new_str).copy_str(mem);
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Replace all instances of one substring with a different string, allocating the new string on the temp allocator.
|
||||
|
||||
@param [in] self
|
||||
@param [in] needle : `The string to be replaced`
|
||||
@param [in] new_str : `The replacement string`
|
||||
@return "The new string with the elements replaced"
|
||||
*>
|
||||
fn String String.treplace(self, String needle, String new_str)
|
||||
{
|
||||
String[] split = self.tsplit(needle);
|
||||
return dstring::join(tmem, split, new_str).str_view();
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Remove characters from the front and end of a string.
|
||||
|
||||
@@ -231,7 +301,7 @@ fn String String.strip_end(self, String suffix)
|
||||
@param [&inout] allocator : "The allocator to use for the String[]"
|
||||
|
||||
@require delimiter.len > 0 : "The delimiter must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
@ensure return.len > 0 || skip_empty
|
||||
*>
|
||||
fn String[] String.split(self, Allocator allocator, String delimiter, usz max = 0, bool skip_empty = false)
|
||||
{
|
||||
@@ -290,7 +360,7 @@ faultdef BUFFER_EXCEEDED;
|
||||
@param [inout] buffer
|
||||
@param max : "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@require delimiter.len > 0 : "The delimiter must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
@ensure return.len > 0 || skip_empty
|
||||
@return? BUFFER_EXCEEDED : `If there are more elements than would fit the buffer`
|
||||
*>
|
||||
fn String[]? String.split_to_buffer(s, String delimiter, String[] buffer, usz max = 0, bool skip_empty = false)
|
||||
@@ -338,6 +408,38 @@ fn bool String.contains(s, String substr)
|
||||
return @ok(s.index_of(substr));
|
||||
}
|
||||
|
||||
<*
|
||||
Check how many non-overlapping instances of a substring there is.
|
||||
|
||||
If the substring has zero length, the number of matches is zero.
|
||||
|
||||
@param [in] self : "The string to search"
|
||||
@param [in] substr : "The string to look for."
|
||||
@pure
|
||||
@return "The number of times matched"
|
||||
*>
|
||||
fn usz String.count(self, String substr)
|
||||
{
|
||||
usz count = 0;
|
||||
usz needed = substr.len;
|
||||
if (needed == 0) return 0;
|
||||
char first = substr[0];
|
||||
while OUTER: (self.len >= needed)
|
||||
{
|
||||
foreach (i, c: self[..^needed])
|
||||
{
|
||||
if (c == first && self[i : needed] == substr)
|
||||
{
|
||||
count++;
|
||||
self = self[i + needed..];
|
||||
continue OUTER;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
<*
|
||||
Find the index of the first incidence of a string.
|
||||
|
||||
@@ -692,16 +794,64 @@ fn usz String.utf8_codepoints(s)
|
||||
return len;
|
||||
}
|
||||
|
||||
<*
|
||||
Determine whether the current string actually points to a ZString-like string.
|
||||
|
||||
This is done by looking at the byte one step after the end of the string. If this
|
||||
is zero, it is considered zero terminated.
|
||||
|
||||
This function can safely be used with data pointing to null. However, it will not
|
||||
work correctly if the pointer is invalid, for example it is already freed.
|
||||
*>
|
||||
fn bool String.is_zstr(self)
|
||||
{
|
||||
return self.ptr && *(self.ptr + self.len) == 0;
|
||||
}
|
||||
|
||||
<*
|
||||
@require (base <= 10 && base > 1) || base == 16 : "Unsupported base"
|
||||
Return a pointer to the string *iff* it is a pointer
|
||||
to a zero terminated string, otherwise return a temp allocated zstring copy.
|
||||
|
||||
This function is suitable if you are converting strings to ZString on the temp
|
||||
allocator, but suspect that the String might actually already point to zero
|
||||
terminated data.
|
||||
|
||||
The function looks one step beyond the end of the slice to determine this,
|
||||
which means that if that data is then modified after this call, this function
|
||||
might behave incorrectly.
|
||||
|
||||
For this reason, try to ensure that the resulting ZString is immediately used.
|
||||
|
||||
@ensure return[self.len] == 0
|
||||
*>
|
||||
fn ZString String.quick_zstr(self)
|
||||
{
|
||||
if (self.is_zstr() @inline) return (ZString)self.ptr;
|
||||
return self.zstr_tcopy();
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Convert a number to a given base. If the base is not given, then
|
||||
it will be inferred from the number if the string starts with 0x 0o or 0b and the
|
||||
base is given as 10.
|
||||
|
||||
Furthermore it will skip any spaces before and after the number.
|
||||
|
||||
@param $Type : "The type to convert to"
|
||||
@param base : "The base to convert to"
|
||||
@require base > 0 && base <= 16 : "Unsupported base"
|
||||
@return? MALFORMED_INTEGER : "When the value has some illegal character"
|
||||
@return? INTEGER_OVERFLOW : "If the value does not fit in the given type"
|
||||
@return? EMPTY_STRING : "If the string was empty"
|
||||
@return? NEGATIVE_VALUE : "If the type was unsigned, and the value had a - prefix"
|
||||
*>
|
||||
macro String.to_integer(self, $Type, int base = 10)
|
||||
{
|
||||
usz len = self.len;
|
||||
usz index = 0;
|
||||
char* ptr = self.ptr;
|
||||
while (index < len && ascii::is_blank_m(ptr[index])) index++;
|
||||
while (index < len && ptr[index].is_blank()) index++;
|
||||
if (len == index) return EMPTY_STRING?;
|
||||
bool is_negative;
|
||||
switch (self[index])
|
||||
@@ -746,7 +896,7 @@ macro String.to_integer(self, $Type, int base = 10)
|
||||
char c = self[index++];
|
||||
switch
|
||||
{
|
||||
case base_used != 16 || c < 'A': c -= '0';
|
||||
case base_used < 10 || c < 'A': c -= '0';
|
||||
case c <= 'F': c -= 'A' - 10;
|
||||
case c < 'a' || c > 'f': return MALFORMED_INTEGER?;
|
||||
default: c -= 'a' - 10;
|
||||
@@ -784,22 +934,91 @@ fn char? String.to_uchar(self, int base = 10) => self.to_integer(char, base);
|
||||
fn double? String.to_double(self) => self.to_real(double);
|
||||
fn float? String.to_float(self) => self.to_real(float);
|
||||
|
||||
fn Splitter String.splitter(self, String split)
|
||||
{
|
||||
return { .string = self, .split = split };
|
||||
}
|
||||
|
||||
<*
|
||||
Create a Splitter to track tokenizing of a string. Tokenize will turn "foo:bar::baz" into
|
||||
"foo", "bar" and "baz", if you want the empty string to be present, use `tokenize_all`
|
||||
instead.
|
||||
|
||||
@param [in] split : "The string to use for splitting"
|
||||
@return "A Splitter to track the state"
|
||||
*>
|
||||
fn Splitter String.tokenize(self, String split)
|
||||
{
|
||||
return { .string = self, .split = split, .tokenize = true };
|
||||
return { .string = self, .split = split, .type = TOKENIZE };
|
||||
}
|
||||
|
||||
<*
|
||||
Create a Splitter to track tokenizing of a string. Tokenize will turn "foo:bar::baz" into
|
||||
"foo", "bar" and "baz", if you want the empty string to be present, use `tokenize_all`
|
||||
instead.
|
||||
|
||||
@param [in] split : "The string to use for splitting"
|
||||
@param skip_last : "Set to true to not include the last empty token if present (default: false)"
|
||||
@return "A Splitter to track the state"
|
||||
*>
|
||||
fn Splitter String.tokenize_all(self, String split, bool skip_last = false)
|
||||
{
|
||||
return {
|
||||
.string = self,
|
||||
.split = split,
|
||||
.type = skip_last ? TOKENIZE_ALL_SKIP_LAST : TOKENIZE_ALL
|
||||
};
|
||||
}
|
||||
|
||||
fn Splitter String.splitter(self, String split) @deprecated("Use tokenize_all instead")
|
||||
{
|
||||
return self.tokenize_all(split, skip_last: true);
|
||||
}
|
||||
|
||||
<*
|
||||
This macro will create a string description of a struct.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param x : "The struct to create a description of"
|
||||
*>
|
||||
macro String from_struct(Allocator allocator, x)
|
||||
{
|
||||
DString s;
|
||||
@stack_mem(512; Allocator mem)
|
||||
{
|
||||
s.init(allocator: mem);
|
||||
io::fprint(&s, x)!!;
|
||||
return s.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
This macro will create a temporary string description of a struct.
|
||||
|
||||
@param x : "The struct to create a description of"
|
||||
*>
|
||||
macro String tfrom_struct(x) => from_struct(tmem, x);
|
||||
|
||||
const uint SURROGATE_OFFSET @private = 0x10000;
|
||||
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
|
||||
const uint SURROGATE_MASK @private = 0xFC00;
|
||||
const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF;
|
||||
const uint SURROGATE_BITS @private = 10;
|
||||
const uint SURROGATE_LOW_VALUE @private = 0xDC00;
|
||||
const uint SURROGATE_HIGH_VALUE @private = 0xD800;
|
||||
|
||||
enum SplitterType
|
||||
{
|
||||
TOKENIZE,
|
||||
TOKENIZE_ALL,
|
||||
TOKENIZE_ALL_SKIP_LAST
|
||||
}
|
||||
|
||||
<*
|
||||
Splitter is handles tokenizing strings.
|
||||
*>
|
||||
struct Splitter
|
||||
{
|
||||
String string;
|
||||
String split;
|
||||
usz current;
|
||||
bool tokenize;
|
||||
SplitterType type;
|
||||
int last_index;
|
||||
}
|
||||
|
||||
@@ -814,29 +1033,22 @@ fn String? Splitter.next(&self)
|
||||
{
|
||||
usz len = self.string.len;
|
||||
usz current = self.current;
|
||||
if (current >= len) return NO_MORE_ELEMENT?;
|
||||
if (current > len) return NO_MORE_ELEMENT?;
|
||||
if (current == len)
|
||||
{
|
||||
if (self.type != TOKENIZE_ALL) return NO_MORE_ELEMENT?;
|
||||
self.current++;
|
||||
return self.string[current - 1:0];
|
||||
}
|
||||
String remaining = self.string[current..];
|
||||
usz? next = remaining.index_of(self.split);
|
||||
if (try next)
|
||||
{
|
||||
self.current = current + next + self.split.len;
|
||||
if (!next && self.tokenize) continue;
|
||||
if (!next && self.type == TOKENIZE) continue;
|
||||
return remaining[:next];
|
||||
}
|
||||
self.current = len;
|
||||
return remaining;
|
||||
}
|
||||
}
|
||||
|
||||
macro String from_struct(Allocator allocator, x)
|
||||
{
|
||||
DString s;
|
||||
@stack_mem(512; Allocator mem)
|
||||
{
|
||||
s.init(allocator: mem);
|
||||
io::fprint(&s, x)!!;
|
||||
return s.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
macro String tfrom_struct(x) => from_struct(tmem, x);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::encoding::json;
|
||||
import std::io;
|
||||
import std::ascii;
|
||||
import std::collections::object;
|
||||
|
||||
faultdef UNEXPECTED_CHARACTER, INVALID_ESCAPE_SEQUENCE, DUPLICATE_MEMBERS, INVALID_NUMBER;
|
||||
|
||||
@@ -93,7 +93,7 @@ macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[]
|
||||
UIntBE be = { (uint)index };
|
||||
hmac.update(&&bitcast(be, char[4]));
|
||||
tmp = hmac.final();
|
||||
out[..] = tmp;
|
||||
out[..] = tmp[..];
|
||||
for (int it = 1; it < iterations; it++)
|
||||
{
|
||||
hmac = *hmac_start;
|
||||
|
||||
@@ -89,10 +89,10 @@ fn char[HASH_BYTES] Md5.final(&ctx)
|
||||
body(ctx, &ctx.buffer, 64);
|
||||
|
||||
char[16] res @noinit;
|
||||
res[0:4] = bitcast(ctx.a, char[4]);
|
||||
res[4:4] = bitcast(ctx.b, char[4]);
|
||||
res[8:4] = bitcast(ctx.c, char[4]);
|
||||
res[12:4] = bitcast(ctx.d, char[4]);
|
||||
res[0:4] = bitcast(ctx.a, char[4])[..];
|
||||
res[4:4] = bitcast(ctx.b, char[4])[..];
|
||||
res[8:4] = bitcast(ctx.c, char[4])[..];
|
||||
res[12:4] = bitcast(ctx.d, char[4])[..];
|
||||
*ctx = {};
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ fn usz? Formatter.print_with_function(&self, Printable arg)
|
||||
|
||||
fn usz? Formatter.out_unknown(&self, String category, any arg) @private
|
||||
{
|
||||
return self.out_substr("[") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr("]");
|
||||
return self.out_substr("<") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr(">");
|
||||
}
|
||||
fn usz? Formatter.out_str(&self, any arg) @private
|
||||
{
|
||||
@@ -196,7 +196,15 @@ fn usz? Formatter.out_str(&self, any arg) @private
|
||||
case BITSTRUCT:
|
||||
return self.out_unknown("bitstruct", arg);
|
||||
case FUNC:
|
||||
return self.out_unknown("function", arg);
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.width = 0;
|
||||
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
|
||||
case DISTINCT:
|
||||
if (arg.type == String.typeid)
|
||||
{
|
||||
|
||||
@@ -39,10 +39,11 @@ fn uint128? int_from_any(any arg, bool *is_neg) @private
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case TypeKind.POINTER:
|
||||
case FUNC:
|
||||
case POINTER:
|
||||
*is_neg = false;
|
||||
return (uint128)(uptr)*(void**)arg.ptr;
|
||||
case TypeKind.DISTINCT:
|
||||
case DISTINCT:
|
||||
return int_from_any(arg.as_inner(), is_neg);
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module std::io::os;
|
||||
import libc, std::os, std::io;
|
||||
|
||||
fn void? native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::BSD_FAMILY) => @pool()
|
||||
fn void? native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY) => @pool()
|
||||
{
|
||||
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
|
||||
$if env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY:
|
||||
int res = libc::stat(path.zstr_tcopy(), stat);
|
||||
$else
|
||||
unreachable("Stat unimplemented");
|
||||
@@ -69,6 +69,7 @@ fn bool native_file_or_dir_exists(String path)
|
||||
$case env::NETBSD:
|
||||
$case env::OPENBSD:
|
||||
$case env::LINUX:
|
||||
$case env::ANDROID:
|
||||
Stat stat;
|
||||
return @ok(native_stat(&stat, path));
|
||||
$case env::WIN32:
|
||||
@@ -94,6 +95,7 @@ fn bool native_is_file(String path)
|
||||
$case env::NETBSD:
|
||||
$case env::OPENBSD:
|
||||
$case env::LINUX:
|
||||
$case env::ANDROID:
|
||||
Stat stat;
|
||||
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFREG);
|
||||
$default:
|
||||
@@ -105,7 +107,7 @@ fn bool native_is_file(String path)
|
||||
|
||||
fn bool native_is_dir(String path)
|
||||
{
|
||||
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
|
||||
$if env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY:
|
||||
Stat stat;
|
||||
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFDIR);
|
||||
$else
|
||||
|
||||
@@ -125,6 +125,7 @@ extern fn ZString getenv(ZString name);
|
||||
extern fn ZString gets(char* buffer);
|
||||
extern fn Tm* gmtime(Time_t* timer);
|
||||
extern fn Tm* gmtime_r(Time_t* timer, Tm* buf) @if(!env::WIN32);
|
||||
extern fn CInt ioctl(CInt fd, ulong request, ...);
|
||||
extern fn CInt isatty(Fd fd) @if(!env::WIN32);
|
||||
extern fn CLong labs(CLong x);
|
||||
extern fn LongDivResult ldiv(CLong number, CLong denom);
|
||||
@@ -373,7 +374,7 @@ module libc;
|
||||
alias CFile = void*;
|
||||
|
||||
|
||||
const HAS_MALLOC_SIZE = env::LINUX || env::WIN32 || env::DARWIN;
|
||||
const HAS_MALLOC_SIZE = env::LINUX || env::ANDROID || env::WIN32 || env::DARWIN;
|
||||
|
||||
// The following needs to be set per arch+os
|
||||
// For now I have simply pulled the defaults from MacOS
|
||||
|
||||
@@ -127,13 +127,13 @@ fn bool BigInt.is_negative(&self)
|
||||
return self.data[MAX_LEN - 1] & 0x80000000 != 0;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.add(self, BigInt other)
|
||||
fn BigInt BigInt.add(self, BigInt other) @operator(+)
|
||||
{
|
||||
self.add_this(other);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.add_this(&self, BigInt other)
|
||||
fn void BigInt.add_this(&self, BigInt other) @operator(+=)
|
||||
{
|
||||
bool sign = self.is_negative();
|
||||
bool sign_arg = other.is_negative();
|
||||
@@ -172,13 +172,13 @@ macro uint find_length(uint* data, uint length)
|
||||
return length;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.mult(self, BigInt bi2)
|
||||
fn BigInt BigInt.mult(self, BigInt bi2) @operator(*)
|
||||
{
|
||||
self.mult_this(bi2);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.mult_this(&self, BigInt bi2)
|
||||
fn void BigInt.mult_this(&self, BigInt bi2) @operator(*=)
|
||||
{
|
||||
if (bi2.is_zero())
|
||||
{
|
||||
@@ -270,13 +270,13 @@ fn void BigInt.negate(&self)
|
||||
|
||||
macro bool BigInt.is_zero(&self) => self.len == 1 && self.data[0] == 0;
|
||||
|
||||
fn BigInt BigInt.sub(self, BigInt other)
|
||||
fn BigInt BigInt.sub(self, BigInt other) @operator(-)
|
||||
{
|
||||
self.sub_this(other);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn BigInt* BigInt.sub_this(&self, BigInt other)
|
||||
fn BigInt* BigInt.sub_this(&self, BigInt other) @operator(-=)
|
||||
{
|
||||
self.len = max(self.len, other.len);
|
||||
|
||||
@@ -325,7 +325,7 @@ fn int BigInt.bitcount(&self)
|
||||
return bits;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.unary_minus(&self)
|
||||
fn BigInt BigInt.unary_minus(&self) @operator(-)
|
||||
{
|
||||
if (self.is_zero()) return *self;
|
||||
BigInt result = *self;
|
||||
@@ -334,13 +334,13 @@ fn BigInt BigInt.unary_minus(&self)
|
||||
}
|
||||
|
||||
|
||||
macro BigInt BigInt.div(self, BigInt other)
|
||||
macro BigInt BigInt.div(self, BigInt other) @operator(/)
|
||||
{
|
||||
self.div_this(other);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.div_this(&self, BigInt other)
|
||||
fn void BigInt.div_this(&self, BigInt other) @operator(/=)
|
||||
{
|
||||
bool negate_answer = self.is_negative();
|
||||
|
||||
@@ -377,13 +377,13 @@ fn void BigInt.div_this(&self, BigInt other)
|
||||
*self = quotient;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.mod(self, BigInt bi2)
|
||||
fn BigInt BigInt.mod(self, BigInt bi2) @operator(%)
|
||||
{
|
||||
self.mod_this(bi2);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.mod_this(&self, BigInt bi2)
|
||||
fn void BigInt.mod_this(&self, BigInt bi2) @operator(%=)
|
||||
{
|
||||
if (bi2.is_negative())
|
||||
{
|
||||
@@ -428,30 +428,30 @@ fn void BigInt.bit_negate_this(&self)
|
||||
self.reduce_len();
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_negate(self)
|
||||
fn BigInt BigInt.bit_negate(self) @operator(~)
|
||||
{
|
||||
self.bit_negate_this();
|
||||
return self;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.shr(self, int shift)
|
||||
fn BigInt BigInt.shr(self, int shift) @operator(>>)
|
||||
{
|
||||
self.shr_this(shift);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.shr_this(self, int shift)
|
||||
fn void BigInt.shr_this(self, int shift) @operator(>>=)
|
||||
{
|
||||
self.len = shift_right(&self.data, self.len, shift);
|
||||
}
|
||||
|
||||
fn BigInt BigInt.shl(self, int shift)
|
||||
fn BigInt BigInt.shl(self, int shift) @operator(<<)
|
||||
{
|
||||
self.shl_this(shift);
|
||||
return self;
|
||||
}
|
||||
|
||||
macro bool BigInt.equals(&self, BigInt other)
|
||||
macro bool BigInt.equals(&self, BigInt other) @operator(==)
|
||||
{
|
||||
if (self.len != other.len) return false;
|
||||
return self.data[:self.len] == other.data[:self.len];
|
||||
@@ -764,7 +764,7 @@ fn BigInt BigInt.sqrt(&self)
|
||||
return result;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_and(self, BigInt bi2)
|
||||
fn BigInt BigInt.bit_and(self, BigInt bi2) @operator(&)
|
||||
{
|
||||
self.bit_and_this(bi2);
|
||||
return self;
|
||||
@@ -782,7 +782,7 @@ fn void BigInt.bit_and_this(&self, BigInt bi2)
|
||||
self.reduce_len();
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_or(self, BigInt bi2)
|
||||
fn BigInt BigInt.bit_or(self, BigInt bi2) @operator(|)
|
||||
{
|
||||
self.bit_or_this(bi2);
|
||||
return self;
|
||||
@@ -800,7 +800,7 @@ fn void BigInt.bit_or_this(&self, BigInt bi2)
|
||||
self.reduce_len();
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_xor(self, BigInt bi2)
|
||||
fn BigInt BigInt.bit_xor(self, BigInt bi2) @operator(^)
|
||||
{
|
||||
self.bit_xor_this(bi2);
|
||||
return self;
|
||||
@@ -818,7 +818,7 @@ fn void BigInt.bit_xor_this(&self, BigInt bi2)
|
||||
self.reduce_len();
|
||||
}
|
||||
|
||||
fn void BigInt.shl_this(&self, int shift)
|
||||
fn void BigInt.shl_this(&self, int shift) @operator(<<=)
|
||||
{
|
||||
self.len = shift_left(&self.data, self.len, shift);
|
||||
}
|
||||
|
||||
66
lib/std/math/complex.c3
Normal file
66
lib/std/math/complex.c3
Normal file
@@ -0,0 +1,66 @@
|
||||
module std::math;
|
||||
|
||||
// Complex number aliases.
|
||||
|
||||
alias Complexf = Complex {float};
|
||||
alias Complex = Complex {double};
|
||||
alias COMPLEX_IDENTITY @builtin = complex::IDENTITY {double};
|
||||
alias COMPLEXF_IDENTITY @builtin = complex::IDENTITY {float};
|
||||
alias IMAGINARY @builtin @deprecated("Use I") = complex::IMAGINARY { double };
|
||||
alias IMAGINARYF @builtin @deprecated("Use I_F") = complex::IMAGINARY { float };
|
||||
alias I @builtin = complex::IMAGINARY { double };
|
||||
alias I_F @builtin = complex::IMAGINARY { float };
|
||||
|
||||
<*
|
||||
The generic complex number module, for float or double based complex number definitions.
|
||||
|
||||
@require Real.kindof == FLOAT : "A complex number must use a floating type"
|
||||
*>
|
||||
module std::math::complex {Real};
|
||||
import std::io;
|
||||
|
||||
union Complex (Printable)
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real r, c;
|
||||
}
|
||||
Real[<2>] v;
|
||||
}
|
||||
|
||||
const Complex IDENTITY = { 1, 0 };
|
||||
const Complex IMAGINARY = { 0, 1 };
|
||||
|
||||
macro Complex Complex.add(self, Complex b) @operator(+) => { .v = self.v + b.v };
|
||||
macro Complex Complex.add_this(&self, Complex b) @operator(+=) => { .v = self.v += b.v };
|
||||
macro Complex Complex.add_real(self, Real r) @operator_s(+) => { .v = self.v + (Real[<2>]) { r, 0 } };
|
||||
macro Complex Complex.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro Complex Complex.sub(self, Complex b) @operator(-) => { .v = self.v - b.v };
|
||||
macro Complex Complex.sub_this(&self, Complex b) @operator(-=) => { .v = self.v -= b.v };
|
||||
macro Complex Complex.sub_real(self, Real r) @operator(-) => { .v = self.v - (Real[<2>]) { r, 0 } };
|
||||
macro Complex Complex.sub_real_inverse(self, Real r) @operator_r(-) => { .v = (Real[<2>]) { r, 0 } - self.v };
|
||||
macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro Complex Complex.scale(self, Real r) @operator_s(*) => { .v = self.v * r };
|
||||
macro Complex Complex.mul(self, Complex b)@operator(*) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
|
||||
macro Complex Complex.div_real(self, Real r) @operator(/) => { .v = self.v / r };
|
||||
macro Complex Complex.div_real_inverse(Complex c, Real r) @operator_r(/) => ((Complex) { .r = self }).div(c);
|
||||
macro Complex Complex.div(self, Complex b) @operator(/)
|
||||
{
|
||||
Real div = b.v.dot(b.v);
|
||||
return { (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
|
||||
}
|
||||
macro Complex Complex.inverse(self)
|
||||
{
|
||||
Real sqr = self.v.dot(self.v);
|
||||
return { self.r / sqr, -self.c / sqr };
|
||||
}
|
||||
macro Complex Complex.conjugate(self) => { .r = self.r, .c = -self.c };
|
||||
macro Complex Complex.negate(self) @operator(-) => { .v = -self.v };
|
||||
macro bool Complex.equals(self, Complex b) @operator(==) => self.v == b.v;
|
||||
macro bool Complex.equals_real(self, Real r) @operator_s(==) => self.v == { r, 0 };
|
||||
macro bool Complex.not_equals(self, Complex b) @operator(!=) => self.v != b.v;
|
||||
|
||||
fn usz? Complex.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
return f.printf("%g%+gi", self.r, self.c);
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import std::math::complex;
|
||||
import std::math::matrix;
|
||||
import std::math::quaternion;
|
||||
|
||||
// TODO Define these using quad precision.
|
||||
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
|
||||
const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
|
||||
const LOG10E = 0.434294481903251827651128918916605082; // log10(e)
|
||||
@@ -57,21 +56,6 @@ const DOUBLE_MAX_EXP = 1024;
|
||||
const DOUBLE_MIN_EXP = -1021;
|
||||
const DOUBLE_EPSILON = 2.22044604925031308085e-16;
|
||||
|
||||
const QUAD_MANT_DIG = 113;
|
||||
|
||||
/*
|
||||
const QUAD_MAX = 1.18973149535723176508575932662800702e+4932;
|
||||
const QUAD_MIN = 3.36210314311209350626267781732175260e-4932;
|
||||
const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966;
|
||||
const QUAD_DIG = 33;
|
||||
const QUAD_DEC_DIGITS = 36;
|
||||
const QUAD_MAX_10_EXP = 4932;
|
||||
const QUAD_MIN_10_EXP = -4931;
|
||||
const QUAD_MAX_EXP = 16384;
|
||||
const QUAD_MIN_EXP = -16481;
|
||||
const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34;
|
||||
*/
|
||||
|
||||
enum RoundingMode : int
|
||||
{
|
||||
TOWARD_ZERO,
|
||||
@@ -82,35 +66,6 @@ enum RoundingMode : int
|
||||
|
||||
faultdef OVERFLOW, MATRIX_INVERSE_DOESNT_EXIST;
|
||||
|
||||
alias Complexf = Complex {float};
|
||||
alias Complex = Complex {double};
|
||||
alias COMPLEX_IDENTITY @builtin = complex::IDENTITY {double};
|
||||
alias COMPLEXF_IDENTITY @builtin = complex::IDENTITY {float};
|
||||
|
||||
alias Quaternionf = Quaternion {float};
|
||||
alias Quaternion = Quaternion {double};
|
||||
alias QUATERNION_IDENTITY @builtin = quaternion::IDENTITY {double};
|
||||
alias QUATERNIONF_IDENTITY @builtin = quaternion::IDENTITY {float};
|
||||
|
||||
alias Matrix2f = Matrix2x2 {float};
|
||||
alias Matrix2 = Matrix2x2 {double};
|
||||
alias Matrix3f = Matrix3x3 {float};
|
||||
alias Matrix3 = Matrix3x3 {double};
|
||||
alias Matrix4f = Matrix4x4 {float};
|
||||
alias Matrix4 = Matrix4x4 {double};
|
||||
alias matrix4_ortho @builtin = matrix::ortho {double};
|
||||
alias matrix4f_ortho @builtin = matrix::ortho {float};
|
||||
alias matrix4_perspective @builtin = matrix::perspective {double};
|
||||
alias matrix4f_perspective @builtin = matrix::perspective {float};
|
||||
|
||||
alias MATRIX2_IDENTITY @builtin = matrix::IDENTITY2 {double};
|
||||
alias MATRIX2F_IDENTITY @builtin = matrix::IDENTITY2 {float};
|
||||
alias MATRIX3_IDENTITY @builtin = matrix::IDENTITY3 {double};
|
||||
alias MATRIX3F_IDENTITY @builtin = matrix::IDENTITY3 {float};
|
||||
alias MATRIX4_IDENTITY @builtin = matrix::IDENTITY4 {double};
|
||||
alias MATRIX4F_IDENTITY @builtin = matrix::IDENTITY4 {float};
|
||||
|
||||
|
||||
<*
|
||||
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
|
||||
*>
|
||||
@@ -129,7 +84,7 @@ macro is_approx(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
return abs(x-y) <= eps;
|
||||
return abs(x - y) <= eps;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -140,7 +95,7 @@ macro is_approx_rel(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
return abs(x-y) <= eps * max(abs(x), abs(y));
|
||||
return abs(x - y) <= eps * max(abs(x), abs(y));
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -149,7 +104,7 @@ macro is_approx_rel(x, y, eps)
|
||||
macro sign(x)
|
||||
{
|
||||
var $Type = $typeof(x);
|
||||
$if $Type.kindof == TypeKind.UNSIGNED_INT:
|
||||
$if $Type.kindof == UNSIGNED_INT:
|
||||
return ($Type)(x > 0);
|
||||
$else
|
||||
return ($Type)(x > 0) - ($Type)(x < 0);
|
||||
@@ -177,7 +132,7 @@ macro atan2(x, y)
|
||||
*>
|
||||
macro sincos_ref(x, sinp, cosp)
|
||||
{
|
||||
$if @typeid(*sinp) == float.typeid:
|
||||
$if @typeis(sinp, float*.typeid):
|
||||
return _sincosf(x, sinp, cosp);
|
||||
$else
|
||||
return _sincos(x, sinp, cosp);
|
||||
@@ -192,7 +147,7 @@ macro sincos_ref(x, sinp, cosp)
|
||||
*>
|
||||
macro sincos(x)
|
||||
{
|
||||
$if @typeid(x) == float.typeid:
|
||||
$if @typeis(x, float):
|
||||
float[<2>] v @noinit;
|
||||
_sincosf(x, &v[0], &v[1]);
|
||||
$else
|
||||
@@ -279,6 +234,13 @@ macro asinh(x)
|
||||
*>
|
||||
macro ceil(x) => $$ceil(x);
|
||||
|
||||
<*
|
||||
Ceil for compile time evaluation.
|
||||
|
||||
@require @typeis($input, double) || @typeis($input, float) : "Only float and double may be used"
|
||||
*>
|
||||
macro @ceil($input) @const => $$ceil($input);
|
||||
|
||||
<*
|
||||
Constrain the value to lie within the given interval.
|
||||
|
||||
@@ -552,7 +514,7 @@ macro bool is_finite(x)
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff < 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) < 0x7ffu64 << 52;
|
||||
return bitcast((double)x, ulong) & (~0UL >> 1) < 0x7ffUL << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
@@ -566,7 +528,7 @@ macro is_nan(x)
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff > 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ffu64 << 52;
|
||||
return bitcast((double)x, ulong) & (~0UL >> 1) > 0x7ffUL << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
@@ -580,7 +542,7 @@ macro is_inf(x)
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff == 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ffu64 << 52;
|
||||
return bitcast((double)x, ulong) & (~0UL >> 1) == 0x7ffUL << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
@@ -1054,10 +1016,16 @@ extern fn double _atan(double x) @extern("atan");
|
||||
extern fn float _atanf(float x) @extern("atanf");
|
||||
extern fn double _atan2(double, double) @extern("atan2");
|
||||
extern fn float _atan2f(float, float) @extern("atan2f");
|
||||
|
||||
extern fn void _sincos(double, double*, double*) @extern("__sincos") @link("m") @if(env::DARWIN);
|
||||
extern fn void _sincosf(float, float*, float*) @extern("__sincosf") @link("m") @if(env::DARWIN);
|
||||
extern fn void _sincos(double, double*, double*) @extern("sincos") @link("m") @if(!env::DARWIN);
|
||||
extern fn void _sincosf(float, float*, float*) @extern("sincosf") @link("m") @if(!env::DARWIN);
|
||||
|
||||
extern fn void _sincos(double, double*, double*) @extern("sincos") @link("m") @if(!env::DARWIN && !env::WIN32);
|
||||
extern fn void _sincosf(float, float*, float*) @extern("sincosf") @link("m") @if(!env::DARWIN && !env::WIN32);
|
||||
|
||||
fn void _sincos(double a, double* s, double* c) @extern("sincos") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
|
||||
fn void _sincosf(float a, float* s, float* c) @extern("sincosf") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
|
||||
|
||||
extern fn double _tan(double x) @extern("tan");
|
||||
extern fn float _tanf(float x) @extern("tanf");
|
||||
extern fn double _scalbn(double x, int n) @extern("scalbn");
|
||||
@@ -1092,8 +1060,8 @@ fn double _frexp(double x, int* e)
|
||||
return x;
|
||||
default:
|
||||
*e = ee - 0x3fe;
|
||||
i &= 0x800fffffffffffffu64;
|
||||
i |= 0x3fe0000000000000u64;
|
||||
i &= 0x800fffffffffffffUL;
|
||||
i |= 0x3fe0000000000000UL;
|
||||
return bitcast(i, double);
|
||||
}
|
||||
}
|
||||
@@ -1118,8 +1086,8 @@ fn float _frexpf(float x, int* e)
|
||||
return x;
|
||||
default:
|
||||
*e = ee - 0x7e;
|
||||
i &= 0x807fffffu32;
|
||||
i |= 0x3f000000u32;
|
||||
i &= 0x807fffffU;
|
||||
i |= 0x3f000000U;
|
||||
return bitcast(i, float);
|
||||
}
|
||||
}
|
||||
@@ -1145,6 +1113,33 @@ macro overflow_mul_helper(x, y) @local
|
||||
return res;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&out] out : "Where the result of the addition is stored"
|
||||
@return "Whether the addition resulted in an integer overflow"
|
||||
@require values::@is_same_type(a, b) : "a and b must be the same type"
|
||||
@require values::@is_int(a) &&& values::@is_int(b) : "a and b must both be integers"
|
||||
@require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
|
||||
*>
|
||||
macro bool overflow_add(a, b, out) => $$overflow_add(a, b, out);
|
||||
|
||||
<*
|
||||
@param [&out] out : "Where the result of the subtraction is stored"
|
||||
@return "Whether the subtraction resulted in an integer overflow"
|
||||
@require values::@is_same_type(a, b) : "a and b must be the same type"
|
||||
@require values::@is_int(a) &&& values::@is_int(b) : "a and b must both be integers"
|
||||
@require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
|
||||
*>
|
||||
macro bool overflow_sub(a, b, out) => $$overflow_sub(a, b, out);
|
||||
|
||||
<*
|
||||
@param [&out] out : "Where the result of the multiplication is stored"
|
||||
@return "Whether the multiplication resulted in an integer overflow"
|
||||
@require values::@is_same_type(a, b) : "a and b must be the same type"
|
||||
@require values::@is_int(a) &&& values::@is_int(b) : "a and b must both be integers"
|
||||
@require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
|
||||
*>
|
||||
macro bool overflow_mul(a, b, out) => $$overflow_mul(a, b, out);
|
||||
|
||||
<*
|
||||
@require types::is_vector($Type) || ($Type.kindof == ARRAY &&& types::is_numerical($typefrom($Type.inner)))
|
||||
*>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
module std::math::complex{Real};
|
||||
|
||||
union Complex
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real r, c;
|
||||
}
|
||||
Real[<2>] v;
|
||||
}
|
||||
|
||||
const Complex IDENTITY = { 1, 0 };
|
||||
const Complex IMAGINARY = { 0, 1 };
|
||||
macro Complex Complex.add(self, Complex b) => { .v = self.v + b.v };
|
||||
macro Complex Complex.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro Complex Complex.sub(self, Complex b) => { .v = self.v - b.v };
|
||||
macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro Complex Complex.scale(self, Real s) => { .v = self.v * s };
|
||||
macro Complex Complex.mul(self, Complex b) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
|
||||
macro Complex Complex.div(self, Complex b)
|
||||
{
|
||||
Real div = b.v.dot(b.v);
|
||||
return { (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
|
||||
}
|
||||
macro Complex Complex.inverse(self)
|
||||
{
|
||||
Real sqr = self.v.dot(self.v);
|
||||
return { self.r / sqr, -self.c / sqr };
|
||||
}
|
||||
macro Complex Complex.conjugate(self) => { .r = self.r, .c = -self.c };
|
||||
macro bool Complex.equals(self, Complex b) => self.v == b.v;
|
||||
@@ -1,47 +0,0 @@
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
/* atan(x)
|
||||
* Method
|
||||
* 1. Reduce x to positive by atan(x) = -atan(-x).
|
||||
* 2. According to the integer k=4t+0.25 chopped, t=x, the argument
|
||||
* is further reduced to one of the following intervals and the
|
||||
* arctangent of t is evaluated by the corresponding formula:
|
||||
*
|
||||
* [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...)
|
||||
* [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) )
|
||||
* [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) )
|
||||
* [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) )
|
||||
* [39/16,INF] atan(x) = atan(INF) + atan( -1/t )
|
||||
*
|
||||
* Constants:
|
||||
* The hexadecimal values are the intended ones for the following
|
||||
* constants. The decimal values may be used, provided that the
|
||||
* compiler will convert from decimal to binary accurately enough
|
||||
* to produce the hexadecimal values shown.
|
||||
*/
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
module std::math;
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ fn double _atanh(double x) @weak @extern("atanh") @nostrip
|
||||
}
|
||||
return double.nan;
|
||||
/* x<2**-28 */
|
||||
case ix < 0x3e300000 && (1e300 + x) > 0.:
|
||||
case ix < 0x3e300000 && (1e300 + x) > 0.0:
|
||||
return x;
|
||||
}
|
||||
x.set_high_word(ix);
|
||||
@@ -37,11 +37,11 @@ fn double _atanh(double x) @weak @extern("atanh") @nostrip
|
||||
if (ix < 0x3fe00000)
|
||||
{
|
||||
t = x + x;
|
||||
t = 0.5 * _log1p(t + t * x / (1. - x));
|
||||
t = 0.5 * _log1p(t + t * x / (1.0 - x));
|
||||
}
|
||||
else
|
||||
{
|
||||
t = 0.5 * _log1p((x + x) / (1. - x));
|
||||
t = 0.5 * _log1p((x + x) / (1.0 - x));
|
||||
}
|
||||
return sign ? -t : t;
|
||||
}
|
||||
|
||||
@@ -47,13 +47,13 @@ fn double _exp2_specialcase(double tmp, ulong sbits, ulong ki) @private
|
||||
if (ki & 0x80000000 == 0)
|
||||
{
|
||||
// k > 0, the exponent of scale might have overflowed by 1.
|
||||
sbits -= 1u64 << 52;
|
||||
sbits -= 1UL << 52;
|
||||
double scale = bitcast(sbits, double);
|
||||
double y = 2 * (scale + scale * tmp);
|
||||
return y;
|
||||
}
|
||||
// k < 0, need special care in the subnormal range.
|
||||
sbits += 1022u64 << 52;
|
||||
sbits += 1022UL << 52;
|
||||
double scale = bitcast(sbits, double);
|
||||
double y = scale + scale * tmp;
|
||||
if (y >= 1.0)
|
||||
|
||||
@@ -90,7 +90,7 @@ fn double _log1p(double x) @weak @extern("log1p") @nostrip
|
||||
/* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
|
||||
if (k < 54)
|
||||
{
|
||||
c = (k >= 2) ? 1. - (u - x) : x - (u - 1.);
|
||||
c = (k >= 2) ? 1.0 - (u - x) : x - (u - 1.0);
|
||||
c /= u;
|
||||
}
|
||||
else
|
||||
@@ -100,10 +100,10 @@ fn double _log1p(double x) @weak @extern("log1p") @nostrip
|
||||
/* reduce u into [sqrt(2)/2, sqrt(2)] */
|
||||
hu = (hu & 0x000fffff) + 0x3fe6a09e;
|
||||
u = bitcast(((ulong)hu << 32) | (bitcast(u, ulong) & 0xffffffff) , double);
|
||||
f = u - 1.;
|
||||
f = u - 1.0;
|
||||
}
|
||||
double hfsq = 0.5 * f * f;
|
||||
double s = f / (2. + f);
|
||||
double s = f / (2.0 + f);
|
||||
double z = s * s;
|
||||
double w = z * z;
|
||||
double t1 = w * (LG2 + w * (LG4 + w * LG6));
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
module std::math::quaternion{Real};
|
||||
import std::math::vector;
|
||||
union Quaternion
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real i, j, k, l;
|
||||
}
|
||||
Real[<4>] v;
|
||||
}
|
||||
|
||||
const Quaternion IDENTITY = { 0, 0, 0, 1 };
|
||||
|
||||
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => { .v = a.v + b.v };
|
||||
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => { .v = a.v + b };
|
||||
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => { .v = a.v - b.v };
|
||||
macro Quaternion Quaternion.sub_each(Quaternion a, Real b) => { .v = a.v - b };
|
||||
macro Quaternion Quaternion.scale(Quaternion a, Real s) => { .v = a.v * s };
|
||||
macro Quaternion Quaternion.normalize(Quaternion q) => { .v = q.v.normalize() };
|
||||
macro Real Quaternion.length(Quaternion q) => q.v.length();
|
||||
macro Quaternion Quaternion.lerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount) };
|
||||
macro Matrix4f Quaternion.to_matrixf(Quaternion* q) => into_matrix(q, Matrix4f);
|
||||
macro Matrix4 Quaternion.to_matrix(Quaternion* q) => into_matrix(q, Matrix4);
|
||||
fn Quaternion Quaternion.nlerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount).normalize() };
|
||||
|
||||
fn Quaternion Quaternion.invert(q)
|
||||
{
|
||||
Real length_sq = q.v.dot(q.v);
|
||||
if (length_sq <= 0) return q;
|
||||
Real inv_length = 1 / length_sq;
|
||||
return { q.v[0] * -inv_length, q.v[1] * -inv_length, q.v[2] * -inv_length, q.v[3] * inv_length };
|
||||
}
|
||||
|
||||
fn Quaternion Quaternion.slerp(q1, Quaternion q2, Real amount)
|
||||
{
|
||||
Quaternion result = {};
|
||||
|
||||
Real[<4>] q2v = q2.v;
|
||||
Real cos_half_theta = q1.v.dot(q2v);
|
||||
|
||||
if (cos_half_theta < 0)
|
||||
{
|
||||
q2v = -q2v;
|
||||
cos_half_theta = -cos_half_theta;
|
||||
}
|
||||
|
||||
if (cos_half_theta >= 1) return q1;
|
||||
|
||||
Real[<4>] q1v = q1.v;
|
||||
if (cos_half_theta > 0.95f) return { .v = q1v.lerp(q2v, amount) };
|
||||
|
||||
Real half_theta = math::cos(cos_half_theta);
|
||||
Real sin_half_theta = math::sqrt(1 - cos_half_theta * cos_half_theta);
|
||||
if (math::abs(sin_half_theta) < 0.001f)
|
||||
{
|
||||
return { .v = (q1v + q2v) * 0.5f };
|
||||
}
|
||||
Real ratio_a = math::sin((1 - amount) * half_theta) / sin_half_theta;
|
||||
Real ratio_b = math::sin(amount * half_theta) / sin_half_theta;
|
||||
return { .v = q1v * ratio_a + q2v * ratio_b };
|
||||
}
|
||||
|
||||
fn Quaternion Quaternion.mul(a, Quaternion b)
|
||||
{
|
||||
return { a.i * b.l + a.l * b.i + a.j * b.k - a.k * b.j,
|
||||
a.j * b.l + a.l * b.j + a.k * b.i - a.i * b.k,
|
||||
a.k * b.l + a.l * b.k + a.i * b.j - a.j * b.i,
|
||||
a.l * b.l - a.i * b.i - a.j * a.j - a.k * a.k };
|
||||
}
|
||||
|
||||
macro into_matrix(Quaternion* q, $Type) @private
|
||||
{
|
||||
Quaternion rotation = q.normalize();
|
||||
var x = rotation.i;
|
||||
var y = rotation.j;
|
||||
var z = rotation.k;
|
||||
var w = rotation.l;
|
||||
|
||||
return ($Type) {
|
||||
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
|
||||
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
|
||||
2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0,
|
||||
0.0, 0.0, 0.0, 1.0,
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,32 @@
|
||||
module std::math::matrix{Real};
|
||||
module std::math;
|
||||
|
||||
// Predefined matrix types
|
||||
alias Matrix2f = Matrix2x2 {float};
|
||||
alias Matrix2 = Matrix2x2 {double};
|
||||
alias Matrix3f = Matrix3x3 {float};
|
||||
alias Matrix3 = Matrix3x3 {double};
|
||||
alias Matrix4f = Matrix4x4 {float};
|
||||
alias Matrix4 = Matrix4x4 {double};
|
||||
|
||||
// Predefined matrix functions
|
||||
alias matrix4_ortho @builtin = matrix::ortho {double};
|
||||
alias matrix4f_ortho @builtin = matrix::ortho {float};
|
||||
alias matrix4_perspective @builtin = matrix::perspective {double};
|
||||
alias matrix4f_perspective @builtin = matrix::perspective {float};
|
||||
|
||||
alias MATRIX2_IDENTITY @builtin = matrix::IDENTITY2 {double};
|
||||
alias MATRIX2F_IDENTITY @builtin = matrix::IDENTITY2 {float};
|
||||
alias MATRIX3_IDENTITY @builtin = matrix::IDENTITY3 {double};
|
||||
alias MATRIX3F_IDENTITY @builtin = matrix::IDENTITY3 {float};
|
||||
alias MATRIX4_IDENTITY @builtin = matrix::IDENTITY4 {double};
|
||||
alias MATRIX4F_IDENTITY @builtin = matrix::IDENTITY4 {float};
|
||||
|
||||
<*
|
||||
The generic matrix module, for float or double based matrix definitions.
|
||||
|
||||
@require Real.kindof == FLOAT : "A matrix must use a floating type"
|
||||
*>
|
||||
module std::math::matrix {Real};
|
||||
import std::math::vector;
|
||||
|
||||
struct Matrix2x2
|
||||
@@ -43,7 +71,7 @@ struct Matrix4x4
|
||||
}
|
||||
}
|
||||
|
||||
fn Real[<2>] Matrix2x2.apply(&self, Real[<2>] vec)
|
||||
fn Real[<2>] Matrix2x2.apply(&self, Real[<2>] vec) @operator(*)
|
||||
{
|
||||
return {
|
||||
self.m00 * vec[0] + self.m01 * vec[1],
|
||||
@@ -51,7 +79,7 @@ fn Real[<2>] Matrix2x2.apply(&self, Real[<2>] vec)
|
||||
};
|
||||
}
|
||||
|
||||
fn Real[<3>] Matrix3x3.apply(&self, Real[<3>] vec)
|
||||
fn Real[<3>] Matrix3x3.apply(&self, Real[<3>] vec) @operator(*)
|
||||
{
|
||||
return {
|
||||
self.m00 * vec[0] + self.m01 * vec[1] + self.m02 * vec[2],
|
||||
@@ -60,7 +88,7 @@ fn Real[<3>] Matrix3x3.apply(&self, Real[<3>] vec)
|
||||
};
|
||||
}
|
||||
|
||||
fn Real[<4>] Matrix4x4.apply(&self, Real[<4>] vec)
|
||||
fn Real[<4>] Matrix4x4.apply(&self, Real[<4>] vec) @operator(*)
|
||||
{
|
||||
return {
|
||||
self.m00 * vec[0] + self.m01 * vec[1] + self.m02 * vec[2] + self.m03 * vec[3],
|
||||
@@ -71,7 +99,7 @@ fn Real[<4>] Matrix4x4.apply(&self, Real[<4>] vec)
|
||||
}
|
||||
|
||||
|
||||
fn Matrix2x2 Matrix2x2.mul(&self, Matrix2x2 b)
|
||||
fn Matrix2x2 Matrix2x2.mul(&self, Matrix2x2 b) @operator(*)
|
||||
{
|
||||
return {
|
||||
self.m00 * b.m00 + self.m01 * b.m10, self.m00 * b.m01 + self.m01 * b.m11,
|
||||
@@ -79,7 +107,7 @@ fn Matrix2x2 Matrix2x2.mul(&self, Matrix2x2 b)
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.mul(&self, Matrix3x3 b)
|
||||
fn Matrix3x3 Matrix3x3.mul(&self, Matrix3x3 b) @operator(*)
|
||||
{
|
||||
return {
|
||||
self.m00 * b.m00 + self.m01 * b.m10 + self.m02 * b.m20,
|
||||
@@ -96,28 +124,28 @@ fn Matrix3x3 Matrix3x3.mul(&self, Matrix3x3 b)
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.mul(Matrix4x4* a, Matrix4x4 b)
|
||||
fn Matrix4x4 Matrix4x4.mul(Matrix4x4* self, Matrix4x4 b) @operator(*)
|
||||
{
|
||||
return {
|
||||
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30,
|
||||
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31,
|
||||
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32,
|
||||
a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33,
|
||||
self.m00 * b.m00 + self.m01 * b.m10 + self.m02 * b.m20 + self.m03 * b.m30,
|
||||
self.m00 * b.m01 + self.m01 * b.m11 + self.m02 * b.m21 + self.m03 * b.m31,
|
||||
self.m00 * b.m02 + self.m01 * b.m12 + self.m02 * b.m22 + self.m03 * b.m32,
|
||||
self.m00 * b.m03 + self.m01 * b.m13 + self.m02 * b.m23 + self.m03 * b.m33,
|
||||
|
||||
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30,
|
||||
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31,
|
||||
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32,
|
||||
a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33,
|
||||
self.m10 * b.m00 + self.m11 * b.m10 + self.m12 * b.m20 + self.m13 * b.m30,
|
||||
self.m10 * b.m01 + self.m11 * b.m11 + self.m12 * b.m21 + self.m13 * b.m31,
|
||||
self.m10 * b.m02 + self.m11 * b.m12 + self.m12 * b.m22 + self.m13 * b.m32,
|
||||
self.m10 * b.m03 + self.m11 * b.m13 + self.m12 * b.m23 + self.m13 * b.m33,
|
||||
|
||||
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30,
|
||||
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31,
|
||||
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32,
|
||||
a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33,
|
||||
self.m20 * b.m00 + self.m21 * b.m10 + self.m22 * b.m20 + self.m23 * b.m30,
|
||||
self.m20 * b.m01 + self.m21 * b.m11 + self.m22 * b.m21 + self.m23 * b.m31,
|
||||
self.m20 * b.m02 + self.m21 * b.m12 + self.m22 * b.m22 + self.m23 * b.m32,
|
||||
self.m20 * b.m03 + self.m21 * b.m13 + self.m22 * b.m23 + self.m23 * b.m33,
|
||||
|
||||
a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30,
|
||||
a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31,
|
||||
a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32,
|
||||
a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33,
|
||||
self.m30 * b.m00 + self.m31 * b.m10 + self.m32 * b.m20 + self.m33 * b.m30,
|
||||
self.m30 * b.m01 + self.m31 * b.m11 + self.m32 * b.m21 + self.m33 * b.m31,
|
||||
self.m30 * b.m02 + self.m31 * b.m12 + self.m32 * b.m22 + self.m33 * b.m32,
|
||||
self.m30 * b.m03 + self.m31 * b.m13 + self.m32 * b.m23 + self.m33 * b.m33,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -125,13 +153,25 @@ fn Matrix2x2 Matrix2x2.component_mul(&self, Real s) => matrix_component_mul(self
|
||||
fn Matrix3x3 Matrix3x3.component_mul(&self, Real s) => matrix_component_mul(self, s);
|
||||
fn Matrix4x4 Matrix4x4.component_mul(&self, Real s) => matrix_component_mul(self, s);
|
||||
|
||||
fn Matrix2x2 Matrix2x2.add(&self, Matrix2x2 mat2) => matrix_add(self, mat2);
|
||||
fn Matrix3x3 Matrix3x3.add(&self, Matrix3x3 mat2) => matrix_add(self, mat2);
|
||||
fn Matrix4x4 Matrix4x4.add(&self, Matrix4x4 mat2) => matrix_add(self, mat2);
|
||||
fn Matrix2x2 Matrix2x2.add(&self, Matrix2x2 mat2) @operator(+) => matrix_add(self, mat2);
|
||||
fn Matrix3x3 Matrix3x3.add(&self, Matrix3x3 mat2) @operator(+) => matrix_add(self, mat2);
|
||||
fn Matrix4x4 Matrix4x4.add(&self, Matrix4x4 mat2) @operator(+) => matrix_add(self, mat2);
|
||||
|
||||
fn Matrix2x2 Matrix2x2.sub(&self, Matrix2x2 mat2) => matrix_sub(self, mat2);
|
||||
fn Matrix3x3 Matrix3x3.sub(&self, Matrix3x3 mat2) => matrix_sub(self, mat2);
|
||||
fn Matrix4x4 Matrix4x4.sub(&self, Matrix4x4 mat2) => matrix_sub(self, mat2);
|
||||
fn Matrix2x2 Matrix2x2.sub(&self, Matrix2x2 mat2) @operator(-) => matrix_sub(self, mat2);
|
||||
fn Matrix3x3 Matrix3x3.sub(&self, Matrix3x3 mat2) @operator(-) => matrix_sub(self, mat2);
|
||||
fn Matrix4x4 Matrix4x4.sub(&self, Matrix4x4 mat2) @operator(-) => matrix_sub(self, mat2);
|
||||
|
||||
fn Matrix2x2 Matrix2x2.negate(&self) @operator(-) => { .m = (Real[<4>])self.m };
|
||||
fn Matrix3x3 Matrix3x3.negate(&self) @operator(-) => { .m = (Real[<9>])self.m };
|
||||
fn Matrix4x4 Matrix4x4.negate(&self) @operator(-) => { .m = (Real[<16>])self.m };
|
||||
|
||||
fn bool Matrix2x2.eq(&self, Matrix2x2 mat2) @operator(==) => (Real[<4>])self.m == (Real[<4>])mat2.m;
|
||||
fn bool Matrix3x3.eq(&self, Matrix3x3 mat2) @operator(==) => (Real[<9>])self.m == (Real[<9>])mat2.m;
|
||||
fn bool Matrix4x4.eq(&self, Matrix4x4 mat2) @operator(==) => (Real[<16>])self.m == (Real[<16>])mat2.m;
|
||||
|
||||
fn bool Matrix2x2.neq(&self, Matrix2x2 mat2) @operator(!=) => (Real[<4>])self.m != (Real[<4>])mat2.m;
|
||||
fn bool Matrix3x3.neq(&self, Matrix3x3 mat2) @operator(!=) => (Real[<9>])self.m != (Real[<9>])mat2.m;
|
||||
fn bool Matrix4x4.neq(&self, Matrix4x4 mat2) @operator(!=) => (Real[<16>])self.m != (Real[<16>])mat2.m;
|
||||
|
||||
fn Matrix4x4 look_at(Real[<3>] eye, Real[<3>] target, Real[<3>] up) => matrix_look_at(Matrix4x4, eye, target, up);
|
||||
|
||||
101
lib/std/math/quaternion.c3
Normal file
101
lib/std/math/quaternion.c3
Normal file
@@ -0,0 +1,101 @@
|
||||
module std::math;
|
||||
|
||||
// Predefined quaternion aliases.
|
||||
|
||||
alias Quaternionf = Quaternion {float};
|
||||
alias Quaternion = Quaternion {double};
|
||||
alias QUATERNION_IDENTITY @builtin = quaternion::IDENTITY {double};
|
||||
alias QUATERNIONF_IDENTITY @builtin = quaternion::IDENTITY {float};
|
||||
|
||||
<*
|
||||
The generic quaternion module, for float or double based quaternion definitions.
|
||||
|
||||
@require Real.kindof == FLOAT : "A quaternion must use a floating type"
|
||||
*>
|
||||
|
||||
module std::math::quaternion {Real};
|
||||
import std::math::vector;
|
||||
union Quaternion
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real i, j, k, l;
|
||||
}
|
||||
Real[<4>] v;
|
||||
}
|
||||
|
||||
const Quaternion IDENTITY = { 0, 0, 0, 1 };
|
||||
|
||||
macro Quaternion Quaternion.add(self, Quaternion b) @operator(+) => { .v = self.v + b.v };
|
||||
macro Quaternion Quaternion.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro Quaternion Quaternion.sub(self, Quaternion b) @operator(-) => { .v = self.v - b.v };
|
||||
macro Quaternion Quaternion.negate(self) @operator(-) => { .v = -self.v };
|
||||
macro Quaternion Quaternion.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro Quaternion Quaternion.scale(self, Real s) @operator_s(*) => { .v = self.v * s };
|
||||
macro Quaternion Quaternion.normalize(self) => { .v = self.v.normalize() };
|
||||
macro Real Quaternion.length(self) => self.v.length();
|
||||
macro Quaternion Quaternion.lerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount) };
|
||||
macro Matrix4f Quaternion.to_matrixf(&self) => into_matrix(self, Matrix4f);
|
||||
macro Matrix4 Quaternion.to_matrix(&self) => into_matrix(self, Matrix4);
|
||||
fn Quaternion Quaternion.nlerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount).normalize() };
|
||||
|
||||
fn Quaternion Quaternion.invert(self)
|
||||
{
|
||||
Real length_sq = self.v.dot(self.v);
|
||||
if (length_sq <= 0) return self;
|
||||
Real inv_length = 1 / length_sq;
|
||||
return { self.v[0] * -inv_length, self.v[1] * -inv_length, self.v[2] * -inv_length, self.v[3] * inv_length };
|
||||
}
|
||||
|
||||
fn Quaternion Quaternion.slerp(self, Quaternion q2, Real amount)
|
||||
{
|
||||
Quaternion result = {};
|
||||
|
||||
Real[<4>] q2v = q2.v;
|
||||
Real cos_half_theta = self.v.dot(q2v);
|
||||
|
||||
if (cos_half_theta < 0)
|
||||
{
|
||||
q2v = -q2v;
|
||||
cos_half_theta = -cos_half_theta;
|
||||
}
|
||||
|
||||
if (cos_half_theta >= 1) return self;
|
||||
|
||||
Real[<4>] q1v = self.v;
|
||||
if (cos_half_theta > 0.95f) return { .v = q1v.lerp(q2v, amount) };
|
||||
|
||||
Real half_theta = math::cos(cos_half_theta);
|
||||
Real sin_half_theta = math::sqrt(1 - cos_half_theta * cos_half_theta);
|
||||
if (math::abs(sin_half_theta) < 0.001f)
|
||||
{
|
||||
return { .v = (q1v + q2v) * 0.5f };
|
||||
}
|
||||
Real ratio_a = math::sin((1 - amount) * half_theta) / sin_half_theta;
|
||||
Real ratio_b = math::sin(amount * half_theta) / sin_half_theta;
|
||||
return { .v = q1v * ratio_a + q2v * ratio_b };
|
||||
}
|
||||
|
||||
fn Quaternion Quaternion.mul(self, Quaternion b) @operator(*)
|
||||
{
|
||||
return { self.i * b.l + self.l * b.i + self.j * b.k - self.k * b.j,
|
||||
self.j * b.l + self.l * b.j + self.k * b.i - self.i * b.k,
|
||||
self.k * b.l + self.l * b.k + self.i * b.j - self.j * b.i,
|
||||
self.l * b.l - self.i * b.i - self.j * self.j - self.k * self.k };
|
||||
}
|
||||
|
||||
macro into_matrix(Quaternion* q, $Type) @private
|
||||
{
|
||||
Quaternion rotation = q.normalize();
|
||||
var x = rotation.i;
|
||||
var y = rotation.j;
|
||||
var z = rotation.k;
|
||||
var w = rotation.l;
|
||||
|
||||
return ($Type) {
|
||||
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
|
||||
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
|
||||
2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0,
|
||||
0.0, 0.0, 0.0, 1.0,
|
||||
};
|
||||
}
|
||||
@@ -31,4 +31,4 @@ fn char SimpleRandom.next_byte(&self) @dynamic => (char)self.next_int();
|
||||
|
||||
const long SIMPLE_RANDOM_MULTIPLIER @local = 0x5DEECE66D;
|
||||
const long SIMPLE_RANDOM_ADDEND @local = 0xB;
|
||||
const long SIMPLE_RANDOM_MASK @local = (1u64 << 48) - 1;
|
||||
const long SIMPLE_RANDOM_MASK @local = (1UL << 48) - 1;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math;
|
||||
module std::math::math_rt;
|
||||
|
||||
fn int128 __divti3(int128 a, int128 b) @extern("__divti3") @weak @nostrip
|
||||
{
|
||||
@@ -312,21 +312,21 @@ macro float_from_i128($Type, a) @private
|
||||
$switch $Type:
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const MANT_DIG = DOUBLE_MANT_DIG;
|
||||
const MANT_DIG = math::DOUBLE_MANT_DIG;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
const EXP_BIAS = 1023;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
|
||||
const SIGN_BIT = 1u64 << 63;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFUL;
|
||||
const SIGN_BIT = 1UL << 63;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const MANT_DIG = FLOAT_MANT_DIG;
|
||||
const MANT_DIG = math::FLOAT_MANT_DIG;
|
||||
const EXP_BIAS = 127;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
const MANTISSA_MASK = 0x7F_FFFFu32;
|
||||
const SIGN_BIT = 1u32 << 31;
|
||||
const MANTISSA_MASK = 0x7F_FFFFU;
|
||||
const SIGN_BIT = 1U << 31;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const MANT_DIG = HALF_MANT_DIG;
|
||||
const MANT_DIG = math::HALF_MANT_DIG;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const MANT_DIG = QUAD_MANT_DIG;
|
||||
@@ -352,7 +352,7 @@ macro float_from_i128($Type, a) @private
|
||||
a |= (uint128)((a & 4) != 0);
|
||||
a++;
|
||||
a >>= 2;
|
||||
if (a & (1i128 << MANT_DIG))
|
||||
if (a & (1LL << MANT_DIG))
|
||||
{
|
||||
a >>= 1;
|
||||
e++;
|
||||
@@ -371,22 +371,22 @@ macro float_from_u128($Type, a) @private
|
||||
$switch $Type:
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const MANT_DIG = DOUBLE_MANT_DIG;
|
||||
const MANT_DIG = math::DOUBLE_MANT_DIG;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
const EXP_BIAS = 1023;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFUL;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const MANT_DIG = FLOAT_MANT_DIG;
|
||||
const MANT_DIG = math::FLOAT_MANT_DIG;
|
||||
const EXP_BIAS = 127;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
const MANTISSA_MASK = 0x7F_FFFFu32;
|
||||
const MANTISSA_MASK = 0x7F_FFFFU;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const MANT_DIG = HALF_MANT_DIG;
|
||||
const MANT_DIG = math::HALF_MANT_DIG;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const MANT_DIG = QUAD_MANT_DIG;
|
||||
const MANT_DIG = math::QUAD_MANT_DIG;
|
||||
$endswitch
|
||||
if (a == 0) return ($Type)0;
|
||||
int sd = 128 - (int)$$clz(a); // digits
|
||||
@@ -406,7 +406,7 @@ macro float_from_u128($Type, a) @private
|
||||
a |= (uint128)((a & 4) != 0);
|
||||
a++;
|
||||
a >>= 2;
|
||||
if (a & (1i128 << MANT_DIG))
|
||||
if (a & (1LL << MANT_DIG))
|
||||
{
|
||||
a >>= 1;
|
||||
e++;
|
||||
@@ -458,8 +458,8 @@ macro fixuint(a) @private
|
||||
int sign = rep & SIGN_BIT ? -1 : 1;
|
||||
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
|
||||
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
|
||||
if (sign == -1 || exponent < 0) return 0u128;
|
||||
if ((uint)exponent >= uint128.sizeof * 8) return ~0u128;
|
||||
if (sign == -1 || exponent < 0) return 0ULL;
|
||||
if ((uint)exponent >= uint128.sizeof * 8) return ~0ULL;
|
||||
if (exponent < SIGNIFICANT_BITS) return (uint128)significand >> (SIGNIFICANT_BITS - exponent);
|
||||
return (uint128)significand << (exponent - SIGNIFICANT_BITS);
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
module std::math;
|
||||
module std::math::math_rt;
|
||||
|
||||
fn float __roundevenf(float f) @extern("roundevenf") @weak @nostrip
|
||||
{
|
||||
// Slow implementation
|
||||
return round(f / 2) * 2;
|
||||
return math::round(f / 2) * 2;
|
||||
}
|
||||
|
||||
fn double __roundeven(double d) @extern("roundeven") @weak @nostrip
|
||||
{
|
||||
// Slow implementation
|
||||
return round(d / 2) * 2;
|
||||
return math::round(d / 2) * 2;
|
||||
}
|
||||
|
||||
fn double __powidf2(double a, int b) @extern("__powidf2") @weak @nostrip
|
||||
@@ -1,3 +1,5 @@
|
||||
// Vector supplemental methods
|
||||
|
||||
module std::math::vector;
|
||||
import std::math;
|
||||
|
||||
@@ -51,6 +53,8 @@ fn double[<3>] double[<3>].unproject(self, Matrix4 projection, Matrix4 view) =>
|
||||
fn void ortho_normalize(float[<3>]* v1, float[<3>]* v2) => ortho_normalize3(v1, v2);
|
||||
fn void ortho_normalized(double[<3>]* v1, double[<3>]* v2) => ortho_normalize3(v1, v2);
|
||||
|
||||
// -- private helpers
|
||||
|
||||
macro towards(v, target, max_distance) @private
|
||||
{
|
||||
var delta = target - v;
|
||||
@@ -80,7 +84,7 @@ macro rotate(v, angle) @private
|
||||
{
|
||||
var c = math::cos(angle);
|
||||
var s = math::sin(angle);
|
||||
return $typeof(v) { v[0] * c - v[1] * s, v[0] * s + v[1] * c };
|
||||
return ($typeof(v)) { v[0] * c - v[1] * s, v[0] * s + v[1] * c };
|
||||
}
|
||||
|
||||
macro perpendicular3(v) @private
|
||||
@@ -111,7 +115,7 @@ macro cross3(v1, v2) @private
|
||||
|
||||
macro transform2(v, mat) @private
|
||||
{
|
||||
return $typeof(v) {
|
||||
return ($typeof(v)) {
|
||||
mat.m00 * v[0] + mat.m10 * v[1] + mat.m30 ,
|
||||
mat.m01 * v[0] + mat.m11 * v[1] + mat.m31 };
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
module std::net::http;
|
||||
/*
|
||||
enum HttpStatus
|
||||
{
|
||||
PENDING,
|
||||
COMPLETED,
|
||||
FAILED
|
||||
}
|
||||
|
||||
struct Http
|
||||
{
|
||||
HttpStatus status;
|
||||
int status_code;
|
||||
String reason;
|
||||
String content_type;
|
||||
String response_data;
|
||||
}
|
||||
|
||||
fn Http* http_get(String url, Allocator using = allocator::temp())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
fn Http* http_post(String url, Allocator using = allocator::temp())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
fn void Http.destroy(Http* this)
|
||||
{
|
||||
}
|
||||
|
||||
fn HttpStatus Http.process(Http* this)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
// Common across implementations
|
||||
|
||||
struct HttpInternal @private
|
||||
{
|
||||
inline Http http;
|
||||
Allocator allocator;
|
||||
int connect_pending;
|
||||
int request_sent;
|
||||
char[256] address;
|
||||
char[256] request_header;
|
||||
char* request_header_large;
|
||||
String request_data;
|
||||
char[1024] reason_phrase;
|
||||
char[256] content_type;
|
||||
|
||||
usz data_size;
|
||||
usz data_capacity;
|
||||
void* data;
|
||||
}
|
||||
|
||||
fn String? parse_url(String url, String* port, String* resource) @private
|
||||
{
|
||||
if (url[:7] != "http://") return NetError.INVALID_URL?;
|
||||
url = url[7..];
|
||||
|
||||
usz end_index = url.index_of(":") ?? url.index_of("/") ?? url.len;
|
||||
String address = url[:end_index];
|
||||
String end_part = url[end_index..];
|
||||
if (!end_part.len)
|
||||
{
|
||||
*port = "80";
|
||||
*resource = {};
|
||||
return address;
|
||||
}
|
||||
switch (end_part[0])
|
||||
{
|
||||
case ':':
|
||||
end_index = end_part.index_of("/") ?? end_part.len;
|
||||
end_part[:end_index].to_uint() ?? NetError.INVALID_URL?!;
|
||||
*port = end_part[:end_index];
|
||||
*resource = url[end_index..];
|
||||
case '/':
|
||||
*port = "80";
|
||||
*resource = end_part;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
fn Socket? http_internal_connect(String address, uint port) @private
|
||||
{
|
||||
return tcp::connect_async(address, port)!;
|
||||
}
|
||||
|
||||
fn HttpInternal* http_internal_create(usz request_data_size, Allocator allocator) @private
|
||||
{
|
||||
HttpInternal* internal = allocator.alloc(HttpInternal.sizeof + request_data_size)!!;
|
||||
internal.status = PENDING;
|
||||
internal.status_code = 0;
|
||||
internal.response_data = {};
|
||||
internal.allocator = allocator;
|
||||
internal.connect_pending = 1;
|
||||
internal.request_sent = 0;
|
||||
// internal.reason = "";
|
||||
// internal.content_type = "";
|
||||
internal.data_size = 0;
|
||||
internal.data_capacity = 64 * 1024;
|
||||
internal.data = allocator.alloc(internal.data_capacity)!!;
|
||||
internal.request_data = {};
|
||||
return internal;
|
||||
}
|
||||
|
||||
fn Http*? http_get(String url, Allocator allocator = allocator::temp())
|
||||
{
|
||||
$if env::WIN32:
|
||||
int[1024] wsa_data;
|
||||
if (_wsa_startup(1, &wsa_data) != 0) return NetError.GENERAL_ERROR?;
|
||||
$endif
|
||||
uint port;
|
||||
String resource;
|
||||
String address = parse_url(url, &port, &resource)?;
|
||||
Socket socket = tcp::connect(address, port)?;
|
||||
HttpInternal* internal = http_internal_create(0, allocator);
|
||||
internal.socket = socket;
|
||||
|
||||
char* request_header;
|
||||
usz request_header_len = 64 + resource.len + address.len + port.len;
|
||||
if (request_header_len < sizeof(internal.request_header))
|
||||
{
|
||||
internal.request_header_large = null;
|
||||
request_header = internal.request_header;
|
||||
}
|
||||
else
|
||||
{
|
||||
internal.request_header_large = (char*)allocator.malloc(request_header_len + 1);
|
||||
request_header = internal.request_header_large;
|
||||
}
|
||||
int default_http_port = port == "80";
|
||||
sprintf( request_header, "GET %s HTTP/1.0\r\nHost: %s%s%s\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port );
|
||||
|
||||
return internal;
|
||||
}
|
||||
|
||||
fn Http*? http_post(String url, Allocator allocator = allocator::temp())
|
||||
{
|
||||
$if env::OS_TYPE == OsType::WIN32:
|
||||
int[1024] wsa_data;
|
||||
if (_wsa_startup(1, &wsa_data) != 0) return NetError.GENERAL_ERROR?;
|
||||
$endif
|
||||
String port;
|
||||
String resource;
|
||||
String address = parse_url(url, &port, &resource)?;
|
||||
Socket socket = http_internal_connect(address, port)?;
|
||||
HttpInternal* internal = http_internal_create(0, allocator);
|
||||
internal.socket = socket;
|
||||
|
||||
char* request_header;
|
||||
uz request_header_len = 64 + resource.len + address.len + port.len;
|
||||
if (request_header_len < sizeof(internal.request_header))
|
||||
{
|
||||
internal.request_header_large = null;
|
||||
request_header = internal.request_header;
|
||||
}
|
||||
else
|
||||
{
|
||||
internal.request_header_large = (char*)allocator.malloc(request_header_len + 1);
|
||||
request_header = internal.request_header_large;
|
||||
}
|
||||
int default_http_port = port == "80";
|
||||
sprintf( request_header, "POST %s HTTP/1.0\r\nHost: %s%s%s\r\nContent-Length: %d\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port,
|
||||
(int) size );
|
||||
|
||||
internal->request_data_size = size;
|
||||
internal->request_data = ( internal + 1 );
|
||||
memcpy( internal->request_data, data, size );
|
||||
|
||||
return internal;
|
||||
}
|
||||
|
||||
fn HttpStatus Http.process(Http* http)
|
||||
{
|
||||
HttpInternal* internal = (HttpInternal*)http;
|
||||
if (http.status == HttpStatus.FAILED) return http.status;
|
||||
if (internal.connect_pending)
|
||||
{
|
||||
fd_set sockets_to_check;
|
||||
FD_ZERO(&sockets_to_check);
|
||||
FD_SET( internal->socket, &sockets_to_check );
|
||||
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
|
||||
// check if socket is ready for send
|
||||
if( select( (int)( internal->socket + 1 ), NULL, &sockets_to_check, NULL, &timeout ) == 1 )
|
||||
{
|
||||
int opt = -1;
|
||||
socklen_t len = sizeof( opt );
|
||||
if( getsockopt( internal->socket, SOL_SOCKET, SO_ERROR, (char*)( &opt ), &len) >= 0 && opt == 0 )
|
||||
{
|
||||
internal->connect_pending = 0; // if it is, we're connected
|
||||
}
|
||||
}
|
||||
}
|
||||
if (internal.connect_pending) retur http.status;
|
||||
if (!internal.request_sent)
|
||||
{
|
||||
char* request_header = internal->request_header_large ?
|
||||
internal.request_header_large : internal.request_header;
|
||||
if (send(internal.socket, request_header, (int) strlen( request_header ), 0) == -1)
|
||||
{
|
||||
return http.status = FAILED;
|
||||
}
|
||||
if (internal.request_data_size)
|
||||
{
|
||||
int res = send(internal.socket, (char const*)internal->request_data, (int) internal->request_data_size, 0 );
|
||||
if (res == -1)
|
||||
{
|
||||
http.status = HTTP_STATUS_FAILED;
|
||||
return http.status;
|
||||
}
|
||||
}
|
||||
internal.request_sent = 1;
|
||||
return http.status;
|
||||
}
|
||||
// check if socket is ready for recv
|
||||
fd_set sockets_to_check;
|
||||
FD_ZERO( &sockets_to_check );
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
|
||||
FD_SET( internal->socket, &sockets_to_check );
|
||||
#pragma warning( pop )
|
||||
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
|
||||
}
|
||||
http_status_t http_process( http_t* http )
|
||||
{
|
||||
|
||||
|
||||
while( select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 )
|
||||
{
|
||||
char buffer[ 4096 ];
|
||||
int size = recv( internal->socket, buffer, sizeof( buffer ), 0 );
|
||||
if( size == -1 )
|
||||
{
|
||||
http->status = HTTP_STATUS_FAILED;
|
||||
return http->status;
|
||||
}
|
||||
else if( size > 0 )
|
||||
{
|
||||
size_t min_size = internal->data_size + size + 1;
|
||||
if( internal->data_capacity < min_size )
|
||||
{
|
||||
internal->data_capacity *= 2;
|
||||
if( internal->data_capacity < min_size ) internal->data_capacity = min_size;
|
||||
void* new_data = HTTP_MALLOC( memctx, internal->data_capacity );
|
||||
memcpy( new_data, internal->data, internal->data_size );
|
||||
HTTP_FREE( memctx, internal->data );
|
||||
internal->data = new_data;
|
||||
}
|
||||
memcpy( (void*)( ( (uintptr_t) internal->data ) + internal->data_size ), buffer, (size_t) size );
|
||||
internal->data_size += size;
|
||||
}
|
||||
else if( size == 0 )
|
||||
{
|
||||
char const* status_line = (char const*) internal->data;
|
||||
|
||||
int header_size = 0;
|
||||
char const* header_end = strstr( status_line, "\r\n\r\n" );
|
||||
if( header_end )
|
||||
{
|
||||
header_end += 4;
|
||||
header_size = (int)( header_end - status_line );
|
||||
}
|
||||
else
|
||||
{
|
||||
http->status = HTTP_STATUS_FAILED;
|
||||
return http->status;
|
||||
}
|
||||
|
||||
// skip http version
|
||||
status_line = strchr( status_line, ' ' );
|
||||
if( !status_line )
|
||||
{
|
||||
http->status = HTTP_STATUS_FAILED;
|
||||
return http->status;
|
||||
}
|
||||
++status_line;
|
||||
|
||||
// extract status code
|
||||
char status_code[ 16 ];
|
||||
char const* status_code_end = strchr( status_line, ' ' );
|
||||
if( !status_code_end )
|
||||
{
|
||||
http->status = HTTP_STATUS_FAILED;
|
||||
return http->status;
|
||||
}
|
||||
memcpy( status_code, status_line, (size_t)( status_code_end - status_line ) );
|
||||
status_code[ status_code_end - status_line ] = 0;
|
||||
status_line = status_code_end + 1;
|
||||
http->status_code = atoi( status_code );
|
||||
|
||||
// extract reason phrase
|
||||
char const* reason_phrase_end = strstr( status_line, "\r\n" );
|
||||
if( !reason_phrase_end )
|
||||
{
|
||||
http->status = HTTP_STATUS_FAILED;
|
||||
return http->status;
|
||||
}
|
||||
size_t reason_phrase_len = (size_t)( reason_phrase_end - status_line );
|
||||
if( reason_phrase_len >= sizeof( internal->reason_phrase ) )
|
||||
reason_phrase_len = sizeof( internal->reason_phrase ) - 1;
|
||||
memcpy( internal->reason_phrase, status_line, reason_phrase_len );
|
||||
internal->reason_phrase[ reason_phrase_len ] = 0;
|
||||
status_line = reason_phrase_end + 1;
|
||||
|
||||
// extract content type
|
||||
char const* content_type_start = strstr( status_line, "Content-Type: " );
|
||||
if( content_type_start )
|
||||
{
|
||||
content_type_start += strlen( "Content-Type: " );
|
||||
char const* content_type_end = strstr( content_type_start, "\r\n" );
|
||||
if( content_type_end )
|
||||
{
|
||||
size_t content_type_len = (size_t)( content_type_end - content_type_start );
|
||||
if( content_type_len >= sizeof( internal->content_type ) )
|
||||
content_type_len = sizeof( internal->content_type ) - 1;
|
||||
memcpy( internal->content_type, content_type_start, content_type_len );
|
||||
internal->content_type[ content_type_len ] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
http->status = http->status_code < 300 ? HTTP_STATUS_COMPLETED : HTTP_STATUS_FAILED;
|
||||
http->response_data = (void*)( ( (uintptr_t) internal->data ) + header_size );
|
||||
http->response_size = internal->data_size - header_size;
|
||||
|
||||
// add an extra zero after the received data, but don't modify the size, so ascii results can be used as
|
||||
// a zero terminated string. the size returned will be the string without this extra zero terminator.
|
||||
( (char*)http->response_data )[ http->response_size ] = 0;
|
||||
return http->status;
|
||||
}
|
||||
}
|
||||
|
||||
return http->status;
|
||||
}
|
||||
|
||||
|
||||
void http_release( http_t* http )
|
||||
{
|
||||
http_internal_t* internal = (http_internal_t*) http;
|
||||
#ifdef _WIN32
|
||||
closesocket( internal->socket );
|
||||
#else
|
||||
close( internal->socket );
|
||||
#endif
|
||||
|
||||
if( internal->request_header_large) HTTP_FREE( memctx, internal->request_header_large );
|
||||
HTTP_FREE( memctx, internal->data );
|
||||
HTTP_FREE( memctx, internal );
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#endif /* HTTP_IMPLEMENTATION */
|
||||
|
||||
/*
|
||||
revision history:
|
||||
1.0 first released version
|
||||
*/
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses - you may choose the one you like.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2016 Mattias Gustavsson
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/*/
|
||||
@@ -1,6 +1,5 @@
|
||||
module std::net;
|
||||
import std::io;
|
||||
import std::ascii;
|
||||
|
||||
enum IpProtocol : char (AIFamily ai_family)
|
||||
{
|
||||
|
||||
92
lib/std/net/os/android.c3
Normal file
92
lib/std/net/os/android.c3
Normal file
@@ -0,0 +1,92 @@
|
||||
module std::net::os @if(env::ANDROID);
|
||||
import libc;
|
||||
|
||||
const AIFamily PLATFORM_AF_AX25 = 3;
|
||||
const AIFamily PLATFORM_AF_IPX = 4;
|
||||
const AIFamily PLATFORM_AF_APPLETALK = 5;
|
||||
const AIFamily PLATFORM_AF_NETROM = 6;
|
||||
const AIFamily PLATFORM_AF_BRIDGE = 7;
|
||||
const AIFamily PLATFORM_AF_AAL5 = 8;
|
||||
const AIFamily PLATFORM_AF_X25 = 9;
|
||||
const AIFamily PLATFORM_AF_INET6 = 10;
|
||||
|
||||
const PLATFORM_O_NONBLOCK = 0o4000;
|
||||
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/socket.h
|
||||
const int SOL_SOCKET = 1;
|
||||
|
||||
const int SO_DEBUG = 1; // turn on debugging info recording
|
||||
const int SO_REUSEADDR = 2; // allow local address reuse
|
||||
const int SO_TYPE = 3;
|
||||
const int SO_ERROR = 4;
|
||||
const int SO_DONTROUTE = 5; // just use interface addresses
|
||||
const int SO_BROADCAST = 6; // permit sending of broadcast msgs
|
||||
const int SO_SNDBUF = 7; // Send buffer size
|
||||
const int SO_RCVBUF = 8; // Receive buffer size
|
||||
const int SO_KEEPALIVE = 9; // keep connections alive
|
||||
const int SO_OOBINLINE = 10; // leave received OOB data in line
|
||||
const int SO_NO_CHECK = 11;
|
||||
const int SO_PRIORITY = 12;
|
||||
const int SO_LINGER = 13; // linger on close if data present (in ticks)
|
||||
const int SO_BSDCOMPAT = 14;
|
||||
const int SO_REUSEPORT = 15; // allow local address & port reuse
|
||||
const int SO_RCVLOWAT = 18;
|
||||
const int SO_SNDLOWAT = 19;
|
||||
const int SO_RCVTIMEO = 20; // IMPORTANT Verify before use
|
||||
const int SO_SNDTIMEO = 21; // IMPORTANT Verify before use
|
||||
const int SO_BINDTODEVICE = 25;
|
||||
const int SO_ATTACH_FILTER = 26;
|
||||
const int SO_DETACH_FILTER = 27;
|
||||
const int SO_PEERNAME = 28;
|
||||
const int SO_TIMESTAMP = 29; // IMPORTANT Verify before use timestamp received dgram traffic
|
||||
const int SO_ACCEPTCONN = 30;
|
||||
const int SO_PEERSEC = 31;
|
||||
const int SO_SNDBUFFORCE = 32;
|
||||
const int SO_RCVBUFFORCE = 33;
|
||||
const int SO_PASSSEC = 34;
|
||||
const int SO_MARK = 36;
|
||||
const int SO_PROTOCOL = 38;
|
||||
const int SO_DOMAIN = 39;
|
||||
const int SO_RXQ_OVFL = 40;
|
||||
const int SO_WIFI_STATUS = 41;
|
||||
const int SO_PEEK_OFF = 42;
|
||||
const int SO_NOFCS = 43;
|
||||
const int SO_LOCK_FILTER = 44;
|
||||
const int SO_SELECT_ERR_QUEUE = 45;
|
||||
const int SO_BUSY_POLL = 46;
|
||||
const int SO_MAX_PACING_RATE = 47;
|
||||
const int SO_BPF_EXTENSIONS = 48;
|
||||
const int SO_INCOMING_CPU = 49;
|
||||
const int SO_ATTACH_BPF = 50;
|
||||
const int SO_ATTACH_REUSEPORT_CBPF = 51;
|
||||
const int SO_ATTACH_REUSEPORT_EBPF = 52;
|
||||
const int SO_CNX_ADVICE = 53;
|
||||
const int SO_MEMINFO = 55;
|
||||
const int SO_INCOMING_NAPI_ID = 56;
|
||||
const int SO_COOKIE = 57;
|
||||
const int SO_PEERGROUPS = 59;
|
||||
const int SO_ZEROCOPY = 60;
|
||||
const int SO_TXTIME = 61;
|
||||
const int SO_BINDTOIFINDEX = 62;
|
||||
const int SO_DETACH_REUSEPORT_BPF = 68;
|
||||
const int SO_PREFER_BUSY_POLL = 69;
|
||||
const int SO_BUSY_POLL_BUDGET = 70;
|
||||
const int SO_NETNS_COOKIE = 71;
|
||||
const int SO_BUF_LOCK = 72;
|
||||
const int SO_RESERVE_MEM = 73;
|
||||
const int SO_TXREHASH = 74;
|
||||
const int SO_RCVMARK = 75;
|
||||
const int SO_PASSPIDFD = 76;
|
||||
const int SO_PEERPIDFD = 77;
|
||||
|
||||
const CUShort POLLRDNORM = 0x0040;
|
||||
const CUShort POLLRDBAND = 0x0080;
|
||||
const CUShort POLLWRNORM = 0x0100;
|
||||
const CUShort POLLWRBAND = 0x0200;
|
||||
const CUShort POLLMSG = 0x0400;
|
||||
const CUShort POLLREMOVE = 0x1000;
|
||||
const CUShort POLLRDHUP = 0x2000;
|
||||
const CUShort POLLFREE = 0x4000;
|
||||
const CUShort POLL_BUSY_LOOP = 0x8000;
|
||||
|
||||
const CInt MSG_PEEK = 0x0002;
|
||||
@@ -1,5 +1,5 @@
|
||||
module std::net::os;
|
||||
const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX);
|
||||
const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID);
|
||||
|
||||
typedef AIFamily = CInt;
|
||||
typedef AIProtocol = CInt;
|
||||
@@ -18,7 +18,7 @@ struct AddrInfo
|
||||
AISockType ai_socktype;
|
||||
AIProtocol ai_protocol;
|
||||
Socklen_t ai_addrlen;
|
||||
struct @if(env::WIN32 || env::DARWIN)
|
||||
struct @if(env::WIN32 || env::DARWIN || env::ANDROID)
|
||||
{
|
||||
ZString ai_canonname;
|
||||
SockAddrPtr ai_addr;
|
||||
@@ -58,7 +58,7 @@ extern fn void freeaddrinfo(AddrInfo* res) @if(SUPPORTS_INET);
|
||||
extern fn CInt setsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET);
|
||||
extern fn CInt getsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET);
|
||||
|
||||
module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX));
|
||||
module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID));
|
||||
|
||||
const AIFamily PLATFORM_AF_INET6 = 0;
|
||||
const AIFamily PLATFORM_AF_IPX = 0;
|
||||
|
||||
@@ -88,7 +88,7 @@ fn fault convert_error(WSAError error)
|
||||
|
||||
fn fault socket_error()
|
||||
{
|
||||
return convert_error(win32_WSAGetLastError());
|
||||
return convert_error(win32::wsaGetLastError());
|
||||
}
|
||||
|
||||
const CUShort POLLIN = win32::POLLIN;
|
||||
|
||||
@@ -69,7 +69,7 @@ fn ulong? poll_ms(Poll[] polls, long timeout_ms)
|
||||
{
|
||||
if (timeout_ms > CInt.max) timeout_ms = CInt.max;
|
||||
$if env::WIN32:
|
||||
CInt result = win32_WSAPoll((Win32_LPWSAPOLLFD)polls.ptr, (Win32_ULONG)polls.len, (CInt)timeout_ms);
|
||||
CInt result = win32::wsaPoll((Win32_LPWSAPOLLFD)polls.ptr, (Win32_ULONG)polls.len, (CInt)timeout_ms);
|
||||
$else
|
||||
CInt result = os::poll((Posix_pollfd*)polls.ptr, (Posix_nfds_t)polls.len, (CInt)timeout_ms);
|
||||
$endif
|
||||
|
||||
@@ -23,7 +23,7 @@ fn bool last_error_is_delayed_connect()
|
||||
{
|
||||
$switch:
|
||||
$case env::WIN32:
|
||||
switch (win32_WSAGetLastError())
|
||||
switch (win32::wsaGetLastError())
|
||||
{
|
||||
case wsa::EWOULDBLOCK:
|
||||
case wsa::EINPROGRESS: return true;
|
||||
|
||||
2
lib/std/os/freebsd/general.c3
Normal file
2
lib/std/os/freebsd/general.c3
Normal file
@@ -0,0 +1,2 @@
|
||||
module std::os::freebsd @if(env::FREEBSD);
|
||||
|
||||
@@ -67,6 +67,17 @@ struct Darwin_segment_command_64
|
||||
uint flags; /* flags */
|
||||
}
|
||||
|
||||
struct Darwin_mach_timebase_info
|
||||
{
|
||||
uint numer;
|
||||
uint denom;
|
||||
}
|
||||
|
||||
alias Darwin_mach_timebase_info_t = Darwin_mach_timebase_info;
|
||||
alias Darwin_mach_timebase_info_data_t = Darwin_mach_timebase_info;
|
||||
|
||||
extern fn void mach_timebase_info(Darwin_mach_timebase_info_data_t* timebase);
|
||||
extern fn ulong mach_absolute_time();
|
||||
|
||||
fn String? executable_path(Allocator allocator)
|
||||
{
|
||||
@@ -155,3 +166,4 @@ fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
|
||||
};
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
2
lib/std/os/macos/general.c3
Normal file
2
lib/std/os/macos/general.c3
Normal file
@@ -0,0 +1,2 @@
|
||||
module std::os::darwin @if(env::DARWIN);
|
||||
|
||||
1
lib/std/os/netbsd/general.c3
Normal file
1
lib/std/os/netbsd/general.c3
Normal file
@@ -0,0 +1 @@
|
||||
module std::os::netbsd @if(env::NETBSD);
|
||||
2
lib/std/os/openbsd/general.c3
Normal file
2
lib/std/os/openbsd/general.c3
Normal file
@@ -0,0 +1,2 @@
|
||||
module std::os::openbsd @if(env::OPENBSD);
|
||||
|
||||
@@ -4,7 +4,7 @@ import libc;
|
||||
<*
|
||||
Exit the process with a given exit code. This will typically call 'exit' in LibC
|
||||
*>
|
||||
fn void exit(int result, bool cleanup = true) @weak
|
||||
fn void exit(int result) @weak @noreturn
|
||||
{
|
||||
$if env::LIBC:
|
||||
libc::exit(result);
|
||||
@@ -17,7 +17,7 @@ fn void exit(int result, bool cleanup = true) @weak
|
||||
Exit the process with a given exit code. This will typically call '_Exit' in LibC
|
||||
usually bypassing '@finalizer' functions.
|
||||
*>
|
||||
fn void fastexit(int result, bool cleanup = true) @weak
|
||||
fn void fastexit(int result) @weak @noreturn
|
||||
{
|
||||
$if env::LIBC:
|
||||
libc::_exit(result);
|
||||
|
||||
68
lib/std/os/posix/clock.c3
Normal file
68
lib/std/os/posix/clock.c3
Normal file
@@ -0,0 +1,68 @@
|
||||
module std::os::posix @if(env::POSIX);
|
||||
import libc;
|
||||
|
||||
extern fn CInt clock_gettime(int type, TimeSpec *time);
|
||||
|
||||
module std::os::posix @if(env::OPENBSD);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 2;
|
||||
const CLOCK_MONOTONIC = 3;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 4;
|
||||
const CLOCK_UPTIME = 5;
|
||||
const CLOCK_BOOTTIME = 6;
|
||||
|
||||
module std::os::posix @if(env::FREEBSD);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_VIRTUAL = 1;
|
||||
const CLOCK_PROF = 2;
|
||||
const CLOCK_MONOTONIC = 4;
|
||||
const CLOCK_UPTIME = 5;
|
||||
const CLOCK_UPTIME_PRECISE = 7;
|
||||
const CLOCK_UPTIME_FAST = 8;
|
||||
const CLOCK_REALTIME_PRECISE = 9;
|
||||
const CLOCK_REALTIME_FAST = 10;
|
||||
const CLOCK_MONOTONIC_PRECISE = 11;
|
||||
const CLOCK_MONOTONIC_FAST = 12;
|
||||
const CLOCK_SECOND = 13;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 14;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 15;
|
||||
const CLOCK_BOOTTIME = CLOCK_UPTIME;
|
||||
const CLOCK_REALTIME_COARSE = CLOCK_REALTIME_FAST;
|
||||
const CLOCK_MONOTONIC_COARSE = CLOCK_MONOTONIC_FAST;
|
||||
|
||||
module std::os::posix @if(env::NETBSD);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_VIRTUAL = 1;
|
||||
const CLOCK_PROF = 2;
|
||||
const CLOCK_MONOTONIC = 3;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 0x20000000;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 0x40000000;
|
||||
|
||||
module std::os::posix @if(env::WASI);
|
||||
// Not implemented
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 0;
|
||||
|
||||
module std::os::posix @if(env::DARWIN);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 6;
|
||||
const CLOCK_MONOTONIC_RAW = 4;
|
||||
const CLOCK_MONOTONIC_RAW_APPROX = 5;
|
||||
const CLOCK_UPTIME_RAW = 8;
|
||||
const CLOCK_UPTIME_RAW_APPROX = 9;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 12;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 16;
|
||||
|
||||
module std::os::posix @if(env::LINUX || env::ANDROID);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 1;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 2;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 3;
|
||||
const CLOCK_MONOTONIC_RAW = 4;
|
||||
const CLOCK_REALTIME_COARSE = 5;
|
||||
const CLOCK_MONOTONIC_COARSE = 6;
|
||||
const CLOCK_BOOTTIME = 7;
|
||||
const CLOCK_REALTIME_ALARM = 8;
|
||||
const CLOCK_BOOTTIME_ALARM = 9;
|
||||
const CLOCK_TAI = 11;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
module std::os::posix;
|
||||
module std::os::posix @if(env::POSIX);
|
||||
|
||||
extern ZString* environ;
|
||||
|
||||
|
||||
1
lib/std/os/posix/net.c3
Normal file
1
lib/std/os/posix/net.c3
Normal file
@@ -0,0 +1 @@
|
||||
module std::os::posix @if(env::POSIX);
|
||||
@@ -45,6 +45,8 @@ bitstruct SubProcessOptions : int
|
||||
// Note: this will **not** search for paths in any provided custom environment
|
||||
// and instead uses the PATH of the spawning process.
|
||||
bool search_user_path;
|
||||
// Inherit the parent's stdin, stdout, and stderr handles instead of creating pipes
|
||||
bool inherit_stdio;
|
||||
}
|
||||
|
||||
fn void? create_named_pipe_helper(void** rd, void **wr) @local @if(env::WIN32)
|
||||
@@ -115,22 +117,32 @@ fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WI
|
||||
*>
|
||||
fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, String[] environment = {}) @if(env::WIN32)
|
||||
{
|
||||
void* rd, wr;
|
||||
Win32_DWORD flags = win32::CREATE_UNICODE_ENVIRONMENT;
|
||||
Win32_PROCESS_INFORMATION process_info;
|
||||
Win32_SECURITY_ATTRIBUTES sa_attr = { Win32_SECURITY_ATTRIBUTES.sizeof, null, 1 };
|
||||
Win32_STARTUPINFOW start_info = {
|
||||
.cb = Win32_STARTUPINFOW.sizeof,
|
||||
.dwFlags = win32::STARTF_USESTDHANDLES
|
||||
};
|
||||
|
||||
if (options.no_window) flags |= win32::CREATE_NO_WINDOW;
|
||||
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
// TODO defer catch
|
||||
if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
|
||||
// Only set STARTF_USESTDHANDLES if we're not inheriting stdio
|
||||
if (!options.inherit_stdio) start_info.dwFlags = win32::STARTF_USESTDHANDLES;
|
||||
|
||||
CFile stdin;
|
||||
CFile stdout;
|
||||
CFile stderr;
|
||||
void* rd = null;
|
||||
void* wr = null;
|
||||
|
||||
// Only create pipes if not inheriting stdio
|
||||
if (!options.inherit_stdio)
|
||||
{
|
||||
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
// TODO defer catch
|
||||
if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
}
|
||||
|
||||
@pool()
|
||||
{
|
||||
WString used_environment = null;
|
||||
@@ -149,39 +161,19 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
env.append("\0");
|
||||
used_environment = env.str_view().to_temp_wstring()!;
|
||||
}
|
||||
int fd = win32::_open_osfhandle((iptr)wr, 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
stdin = win32::_fdopen(fd, "wb");
|
||||
if (!stdin) return FAILED_TO_OPEN_STDIN?;
|
||||
}
|
||||
start_info.hStdInput = rd;
|
||||
if (options.read_async)
|
||||
{
|
||||
create_named_pipe_helper(&rd, &wr)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
}
|
||||
if (!win32::setHandleInformation(rd, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
fd = win32::_open_osfhandle((iptr)rd, 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
stdout = win32::_fdopen(fd, "rb");
|
||||
if (!stdout) return FAILED_TO_OPEN_STDOUT?;
|
||||
}
|
||||
|
||||
start_info.hStdOutput = wr;
|
||||
|
||||
do
|
||||
// Handle stdin pipe if not inheriting
|
||||
if (!options.inherit_stdio)
|
||||
{
|
||||
if (options.combined_stdout_stderr)
|
||||
int fd = win32::_open_osfhandle((iptr)wr, 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
stderr = stdout;
|
||||
start_info.hStdError = start_info.hStdOutput;
|
||||
break;
|
||||
stdin = win32::_fdopen(fd, "wb");
|
||||
if (!stdin) return FAILED_TO_OPEN_STDIN?;
|
||||
}
|
||||
start_info.hStdInput = rd;
|
||||
|
||||
// Handle stdout pipe
|
||||
if (options.read_async)
|
||||
{
|
||||
create_named_pipe_helper(&rd, &wr)!;
|
||||
@@ -195,18 +187,49 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
fd = win32::_open_osfhandle((iptr)rd, 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
stderr = win32::_fdopen(fd, "rb");
|
||||
if (!stderr) return FAILED_TO_OPEN_STDERR?;
|
||||
stdout = win32::_fdopen(fd, "rb");
|
||||
if (!stdout) return FAILED_TO_OPEN_STDOUT?;
|
||||
}
|
||||
start_info.hStdError = wr;
|
||||
};
|
||||
void *event_output;
|
||||
void *event_error;
|
||||
if (options.read_async)
|
||||
|
||||
start_info.hStdOutput = wr;
|
||||
|
||||
// Handle stderr pipe or combine with stdout
|
||||
do
|
||||
{
|
||||
if (options.combined_stdout_stderr)
|
||||
{
|
||||
stderr = stdout;
|
||||
start_info.hStdError = start_info.hStdOutput;
|
||||
break;
|
||||
}
|
||||
if (options.read_async)
|
||||
{
|
||||
create_named_pipe_helper(&rd, &wr)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
}
|
||||
if (!win32::setHandleInformation(rd, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
|
||||
fd = win32::_open_osfhandle((iptr)rd, 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
stderr = win32::_fdopen(fd, "rb");
|
||||
if (!stderr) return FAILED_TO_OPEN_STDERR?;
|
||||
}
|
||||
start_info.hStdError = wr;
|
||||
};
|
||||
}
|
||||
|
||||
void *event_output = null;
|
||||
void *event_error = null;
|
||||
if (!options.inherit_stdio && options.read_async)
|
||||
{
|
||||
event_output = win32::createEventA(&sa_attr, 1, 1, null);
|
||||
event_error = win32::createEventA(&sa_attr, 1, 1, null);
|
||||
}
|
||||
|
||||
if (!win32::createProcessW(
|
||||
null,
|
||||
convert_command_line_win32(command_line),
|
||||
@@ -214,14 +237,17 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
null, // primary thread security attributes
|
||||
1, // handles are inherited
|
||||
flags, // creation flags
|
||||
used_environment, // environment
|
||||
used_environment, // environment
|
||||
null, // use parent dir
|
||||
&start_info, // startup info ptr
|
||||
&process_info)) return FAILED_TO_START_PROCESS?;
|
||||
};
|
||||
|
||||
// We don't need the handle of the primary thread in the called process.
|
||||
win32::closeHandle(process_info.hThread);
|
||||
if (start_info.hStdOutput)
|
||||
|
||||
// Close handles if not inheriting stdio
|
||||
if (!options.inherit_stdio && start_info.hStdOutput)
|
||||
{
|
||||
win32::closeHandle(start_info.hStdOutput);
|
||||
if (start_info.hStdOutput != start_info.hStdError) win32::closeHandle(start_info.hStdError);
|
||||
@@ -229,7 +255,7 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
|
||||
return {
|
||||
.hProcess = process_info.hProcess,
|
||||
.hStdInput = start_info.hStdInput,
|
||||
.hStdInput = options.inherit_stdio ? null : start_info.hStdInput,
|
||||
.stdin_file = stdin,
|
||||
.stdout_file = stdout,
|
||||
.stderr_file = stderr,
|
||||
@@ -280,27 +306,37 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
CInt[2] stdinfd;
|
||||
CInt[2] stdoutfd;
|
||||
CInt[2] stderrfd;
|
||||
|
||||
if (posix::pipe(&stdinfd)) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::pipe(&stdoutfd)) return FAILED_TO_OPEN_STDOUT?;
|
||||
if (!options.combined_stdout_stderr && posix::pipe(&stderrfd)) return FAILED_TO_OPEN_STDERR?;
|
||||
CFile stdin = null;
|
||||
CFile stdout = null;
|
||||
CFile stderr = null;
|
||||
|
||||
Posix_spawn_file_actions_t actions;
|
||||
if (posix::spawn_file_actions_init(&actions)) return FAILED_TO_INITIALIZE_ACTIONS?;
|
||||
defer posix::spawn_file_actions_destroy(&actions);
|
||||
if (posix::spawn_file_actions_addclose(&actions, stdinfd[1])) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stdinfd[0], libc::STDIN_FD)) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::spawn_file_actions_addclose(&actions, stdoutfd[0])) return FAILED_TO_OPEN_STDOUT?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stdoutfd[1], libc::STDOUT_FD)) return FAILED_TO_OPEN_STDOUT?;
|
||||
if (options.combined_stdout_stderr)
|
||||
|
||||
// Only set up pipes if not inheriting stdio
|
||||
if (!options.inherit_stdio)
|
||||
{
|
||||
if (posix::spawn_file_actions_adddup2(&actions, libc::STDOUT_FD, libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (posix::spawn_file_actions_addclose(&actions, stderrfd[0])) return FAILED_TO_OPEN_STDERR?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stderrfd[1], libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
|
||||
if (posix::pipe(&stdinfd)) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::pipe(&stdoutfd)) return FAILED_TO_OPEN_STDOUT?;
|
||||
if (!options.combined_stdout_stderr && posix::pipe(&stderrfd)) return FAILED_TO_OPEN_STDERR?;
|
||||
|
||||
if (posix::spawn_file_actions_addclose(&actions, stdinfd[1])) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stdinfd[0], libc::STDIN_FD)) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::spawn_file_actions_addclose(&actions, stdoutfd[0])) return FAILED_TO_OPEN_STDOUT?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stdoutfd[1], libc::STDOUT_FD)) return FAILED_TO_OPEN_STDOUT?;
|
||||
|
||||
if (options.combined_stdout_stderr)
|
||||
{
|
||||
if (posix::spawn_file_actions_adddup2(&actions, libc::STDOUT_FD, libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (posix::spawn_file_actions_addclose(&actions, stderrfd[0])) return FAILED_TO_OPEN_STDERR?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stderrfd[1], libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
|
||||
}
|
||||
}
|
||||
|
||||
Pid_t child;
|
||||
@pool()
|
||||
{
|
||||
@@ -315,21 +351,27 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return FAILED_TO_START_PROCESS?;
|
||||
}
|
||||
};
|
||||
libc::close(stdinfd[0]);
|
||||
CFile stdin = libc::fdopen(stdinfd[1], "wb");
|
||||
libc::close(stdoutfd[1]);
|
||||
CFile stdout = libc::fdopen(stdoutfd[0], "rb");
|
||||
CFile stderr @noinit;
|
||||
do
|
||||
|
||||
// Only set up file handles if not inheriting stdio
|
||||
if (!options.inherit_stdio)
|
||||
{
|
||||
if (options.combined_stdout_stderr)
|
||||
libc::close(stdinfd[0]);
|
||||
stdin = libc::fdopen(stdinfd[1], "wb");
|
||||
libc::close(stdoutfd[1]);
|
||||
stdout = libc::fdopen(stdoutfd[0], "rb");
|
||||
|
||||
do
|
||||
{
|
||||
stderr = stdout;
|
||||
break;
|
||||
}
|
||||
libc::close(stderrfd[1]);
|
||||
stderr = libc::fdopen(stderrfd[0], "rb");
|
||||
};
|
||||
if (options.combined_stdout_stderr)
|
||||
{
|
||||
stderr = stdout;
|
||||
break;
|
||||
}
|
||||
libc::close(stderrfd[1]);
|
||||
stderr = libc::fdopen(stderrfd[0], "rb");
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
.stdin_file = stdin,
|
||||
.stdout_file = stdout,
|
||||
@@ -357,11 +399,13 @@ fn CInt? SubProcess.join(&self) @if(env::POSIX)
|
||||
|
||||
fn File SubProcess.stdout(&self)
|
||||
{
|
||||
if (!self.stdout_file) return (File){}; // Return an empty File struct
|
||||
return file::from_handle(self.stdout_file);
|
||||
}
|
||||
|
||||
fn File SubProcess.stderr(&self)
|
||||
{
|
||||
if (!self.stderr_file) return (File){}; // Return an empty File struct
|
||||
return file::from_handle(self.stderr_file);
|
||||
}
|
||||
|
||||
@@ -450,6 +494,8 @@ fn usz? read_from_file_posix(CFile file, char* buffer, usz size) @if(env::POSIX)
|
||||
|
||||
fn usz? SubProcess.read_stdout(&self, char* buffer, usz size)
|
||||
{
|
||||
if (!self.stdout_file) return 0; // No output available when inheriting stdio
|
||||
|
||||
$if env::WIN32:
|
||||
return read_from_file_win32(self.stdout_file, self.hEventOutput, buffer, size);
|
||||
$else
|
||||
@@ -457,8 +503,11 @@ fn usz? SubProcess.read_stdout(&self, char* buffer, usz size)
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
fn usz? SubProcess.read_stderr(&self, char* buffer, usz size)
|
||||
{
|
||||
if (!self.stderr_file) return 0; // No output available when inheriting stdio
|
||||
|
||||
$if env::WIN32:
|
||||
return read_from_file_win32(self.stderr_file, self.hEventError, buffer, size);
|
||||
$else
|
||||
|
||||
6
lib/std/os/win32/clock.c3
Normal file
6
lib/std/os/win32/clock.c3
Normal file
@@ -0,0 +1,6 @@
|
||||
module std::os::win32 @if(env::WIN32);
|
||||
import std::math;
|
||||
|
||||
extern fn void getSystemTimeAsFileTime(Win32_FILETIME* time) @extern("GetSystemTimeAsFileTime");
|
||||
extern fn Win32_BOOL queryPerformanceFrequency(Win32_LARGE_INTEGER* lpFrequency) @extern("QueryPerformanceFrequency");
|
||||
extern fn Win32_BOOL queryPerformanceCounter(Win32_LARGE_INTEGER* lpPerformanceCount) @extern("QueryPerformanceCounter");
|
||||
@@ -1,5 +1,6 @@
|
||||
module std::os::win32 @if(env::WIN32);
|
||||
import libc;
|
||||
|
||||
enum Win32_GET_FILEEX_INFO_LEVELS
|
||||
{
|
||||
STANDARD,
|
||||
|
||||
@@ -222,4 +222,5 @@ const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D;
|
||||
const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E;
|
||||
const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F;
|
||||
const Win32_DWORD ERROR_IO_INCOMPLETE = 0x3E4;
|
||||
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;
|
||||
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;
|
||||
const Win32_DWORD ERROR_TIMEOUT = 0x5B4;
|
||||
@@ -46,6 +46,8 @@ const IMAGE_FILE_MACHINE_ARM64 = 0xAA64;
|
||||
const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
|
||||
const UNDNAME_COMPLETE = 0x0000;
|
||||
|
||||
alias Win32_INIT_ONCE_FN = fn Win32_BOOL(Win32_INIT_ONCE* initOnce, void* parameter, void** context);
|
||||
|
||||
extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection");
|
||||
extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection");
|
||||
extern fn Win32_HANDLE createMutex(void*, Win32_BOOL, void*) @extern("CreateMutexA");
|
||||
@@ -53,6 +55,19 @@ extern fn Win32_BOOL releaseMutex(Win32_HANDLE) @extern("ReleaseMutex");
|
||||
extern fn void enterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection");
|
||||
extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection");
|
||||
extern fn Win32_BOOL tryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection");
|
||||
extern fn void initializeSRWLock(Win32_SRWLOCK* lock) @extern("InitializeSRWLock");
|
||||
extern fn void acquireSRWLockExclusive(Win32_SRWLOCK* lock) @extern("AcquireSRWLockExclusive");
|
||||
extern fn void acquireSRWLockShared(Win32_SRWLOCK* lock) @extern("AcquireSRWLockShared");
|
||||
extern fn void releaseSRWLockExclusive(Win32_SRWLOCK* lock) @extern("ReleaseSRWLockExclusive");
|
||||
extern fn void releaseSRWLockShared(Win32_SRWLOCK* lock) @extern("ReleaseSRWLockShared");
|
||||
extern fn Win32_BOOL tryAcquireSRWLockExclusive(Win32_SRWLOCK* lock) @extern("TryAcquireSRWLockExclusive");
|
||||
extern fn Win32_BOOL tryAcquireSRWLockShared(Win32_SRWLOCK* lock) @extern("TryAcquireSRWLockShared");
|
||||
extern fn void initializeConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("InitializeConditionVariable");
|
||||
extern fn void wakeConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("WakeConditionVariable");
|
||||
extern fn void wakeAllConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("WakeAllConditionVariable");
|
||||
extern fn Win32_BOOL sleepConditionVariableCS(Win32_CONDITION_VARIABLE* conditionVariable, Win32_CRITICAL_SECTION* section, Win32_DWORD dwMilliseconds) @extern("SleepConditionVariableCS");
|
||||
extern fn Win32_BOOL sleepConditionVariableSRW(Win32_CONDITION_VARIABLE* conditionVariable, Win32_SRWLOCK* lock, Win32_DWORD dwMilliseconds, Win32_ULONG flags) @extern("SleepConditionVariableSRW");
|
||||
extern fn Win32_BOOL initOnceExecuteOnce(Win32_INIT_ONCE* initOnce, Win32_INIT_ONCE_FN initFn, void* parameter, void** context) @extern("InitOnceExecuteOnce");
|
||||
extern fn Win32_DWORD waitForSingleObject(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds) @extern("WaitForSingleObject");
|
||||
extern fn Win32_DWORD waitForSingleObjectEx(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds, Win32_BOOL bAlertable) @extern("WaitForSingleObjectEx");
|
||||
extern fn Win32_DWORD waitForMultipleObjects(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds) @extern("WaitForMultipleObjects");
|
||||
|
||||
@@ -190,6 +190,9 @@ union Win32_LARGE_INTEGER
|
||||
}
|
||||
|
||||
typedef Win32_CRITICAL_SECTION = ulong[5];
|
||||
typedef Win32_CONDITION_VARIABLE = void*;
|
||||
typedef Win32_SRWLOCK = void*;
|
||||
typedef Win32_INIT_ONCE = void*;
|
||||
|
||||
struct Win32_SECURITY_ATTRIBUTES
|
||||
{
|
||||
@@ -694,4 +697,3 @@ alias Win32_LPSTACKFRAME64 = Win32_STACKFRAME64*;
|
||||
alias Win32_PMODLOAD_DATA = Win32_MODLOAD_DATA*;
|
||||
alias Win32_PIMAGEHLP_LINE64 = Win32_IMAGEHLP_LINE64*;
|
||||
alias Win32_LPMODULEINFO = Win32_MODULEINFO*;
|
||||
|
||||
|
||||
@@ -122,11 +122,11 @@ const SD_RECEIVE = 0x00;
|
||||
const SD_SEND = 0x01;
|
||||
const SD_BOTH = 0x02;
|
||||
|
||||
extern fn CInt win32_WSAPoll(Win32_LPWSAPOLLFD fdArray, Win32_ULONG fds, Win32_INT timeout) @extern("WSAPoll") @builtin;
|
||||
extern fn WSAError win32_WSAGetLastError() @extern("WSAGetLastError") @builtin;
|
||||
extern fn void win32_WSASetLastError(WSAError error) @extern("WSASetLastError") @builtin;
|
||||
extern fn CInt win32_WSAStartup(Win32_WORD, void*) @extern("WSAStartup") @builtin;
|
||||
extern fn CInt win32_WSACleanup() @extern("WSACleanup") @builtin;
|
||||
extern fn CInt wsaPoll(Win32_LPWSAPOLLFD fdArray, Win32_ULONG fds, Win32_INT timeout) @extern("WSAPoll");
|
||||
extern fn WSAError wsaGetLastError() @extern("WSAGetLastError");
|
||||
extern fn void wsaSetLastError(WSAError error) @extern("WSASetLastError");
|
||||
extern fn CInt wsaStartup(Win32_WORD, void*) @extern("WSAStartup");
|
||||
extern fn CInt wsaCleanup() @extern("WSACleanup");
|
||||
|
||||
const int FIONBIO = -2147195266;
|
||||
const int FIONREAD = 1074030207;
|
||||
@@ -230,3 +230,4 @@ const WSAError QOS_EPSFILTERSPEC = 11028;
|
||||
const WSAError QOS_ESDMODEOBJ = 11029;
|
||||
const WSAError QOS_ESHAPERATEOBJ = 11030;
|
||||
const WSAError QOS_RESERVED_PETYPE = 11031;
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*module std::text::i18n;
|
||||
import std::collections::map;
|
||||
import std::hash::fnv32a;
|
||||
typedef Language = char[];
|
||||
|
||||
const Language EN = "en";
|
||||
|
||||
alias TranslationMap = HashMap{String, String};
|
||||
fn uint Language.hash(self) => fnv32a::encode((char[])self);
|
||||
HashMap{Language, TranslationMap*} language_map @private;
|
||||
TranslationMap? current_map;
|
||||
|
||||
macro String @localized(String string) @builtin
|
||||
{
|
||||
return current_map[string] ?? string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module std::thread::os @if (!env::POSIX && !env::WIN32);
|
||||
|
||||
typedef NativeMutex = int;
|
||||
typedef NativeTimedMutex = int;
|
||||
typedef NativeConditionVariable = int;
|
||||
typedef NativeOnceFlag = int;
|
||||
typedef NativeThread = int;
|
||||
@@ -7,6 +7,8 @@ struct NativeMutex
|
||||
bool initialized;
|
||||
}
|
||||
|
||||
alias NativeTimedMutex = NativeMutex;
|
||||
|
||||
alias NativeConditionVariable = Pthread_cond_t;
|
||||
|
||||
struct NativeThread
|
||||
@@ -28,7 +30,7 @@ fn void? NativeMutex.init(&self, MutexType type)
|
||||
if (posix::pthread_mutexattr_init(&attr)) return thread::INIT_FAILED?;
|
||||
defer posix::pthread_mutexattr_destroy(&attr);
|
||||
// TODO: make a fine grained error instead
|
||||
if (type & thread::MUTEX_RECURSIVE)
|
||||
if (type.recursive)
|
||||
{
|
||||
if (posix::pthread_mutexattr_settype(&attr, posix::PTHREAD_MUTEX_RECURSIVE)) return thread::INIT_FAILED?;
|
||||
}
|
||||
|
||||
@@ -5,239 +5,313 @@ typedef NativeThread = inline Win32_HANDLE;
|
||||
|
||||
struct NativeMutex
|
||||
{
|
||||
union
|
||||
Win32_SRWLOCK srw_lock;
|
||||
Win32_DWORD owner_thread;
|
||||
bitstruct : uint
|
||||
{
|
||||
Win32_CRITICAL_SECTION critical_section;
|
||||
Win32_HANDLE handle;
|
||||
bool initialized : 0;
|
||||
bool recursive : 1;
|
||||
uint locks : 2..31;
|
||||
}
|
||||
// Size is less than a Win32_HANDLE so due to alignment
|
||||
// there is no benefit to pack these into a bitstruct.
|
||||
uint locks;
|
||||
bool recursive;
|
||||
bool timed;
|
||||
}
|
||||
|
||||
struct NativeOnceFlag
|
||||
struct NativeTimedMutex
|
||||
{
|
||||
int status;
|
||||
Win32_CRITICAL_SECTION lock;
|
||||
Win32_SRWLOCK srw_lock;
|
||||
Win32_CONDITION_VARIABLE cond_var;
|
||||
Win32_DWORD owner_thread;
|
||||
bitstruct : uint
|
||||
{
|
||||
bool initialized : 0;
|
||||
bool recursive : 1;
|
||||
uint locks : 2..31;
|
||||
}
|
||||
}
|
||||
|
||||
struct NativeConditionVariable
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
Win32_HANDLE event_one;
|
||||
Win32_HANDLE event_all;
|
||||
}
|
||||
Win32_HANDLE[2] events;
|
||||
}
|
||||
uint waiters_count;
|
||||
Win32_CRITICAL_SECTION waiters_count_lock;
|
||||
Win32_CONDITION_VARIABLE cond_var;
|
||||
}
|
||||
|
||||
fn void? NativeMutex.init(&mtx, MutexType type)
|
||||
struct NativeOnceFlag
|
||||
{
|
||||
mtx.locks = 0;
|
||||
mtx.recursive = (bool)(type & thread::MUTEX_RECURSIVE);
|
||||
mtx.timed = (bool)(type & thread::MUTEX_TIMED);
|
||||
if (!mtx.timed)
|
||||
{
|
||||
win32::initializeCriticalSection(&(mtx.critical_section));
|
||||
return;
|
||||
}
|
||||
if (!(mtx.handle = win32::createMutex(null, 0, null))) return thread::INIT_FAILED?;
|
||||
Win32_INIT_ONCE init_once;
|
||||
}
|
||||
|
||||
fn void? NativeMutex.destroy(&mtx)
|
||||
{
|
||||
mtx.locks = 0;
|
||||
if (!mtx.timed)
|
||||
{
|
||||
win32::deleteCriticalSection(&mtx.critical_section);
|
||||
return;
|
||||
}
|
||||
if (!win32::closeHandle(mtx.handle)) return thread::DESTROY_FAILED?;
|
||||
}
|
||||
|
||||
fn void? NativeMutex.lock(&mtx)
|
||||
{
|
||||
if (!mtx.timed)
|
||||
{
|
||||
win32::enterCriticalSection(&mtx.critical_section);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (win32::waitForSingleObject(mtx.handle, win32::INFINITE))
|
||||
{
|
||||
case win32::WAIT_OBJECT_0:
|
||||
break;
|
||||
case win32::WAIT_ABANDONED:
|
||||
default:
|
||||
return thread::LOCK_FAILED?;
|
||||
|
||||
}
|
||||
}
|
||||
if (!mtx.recursive && mtx.locks)
|
||||
{
|
||||
return thread::LOCK_FAILED?;
|
||||
}
|
||||
mtx.locks++;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@require mtx.timed : "Only available for timed locks"
|
||||
@require !mtx.initialized : "Mutex is already initialized"
|
||||
@require !type.timed
|
||||
@ensure mtx.initialized
|
||||
*>
|
||||
fn void? NativeMutex.lock_timeout(&mtx, ulong ms)
|
||||
fn void? NativeMutex.init(&mtx, MutexType type)
|
||||
{
|
||||
if (ms > uint.max) ms = uint.max;
|
||||
switch (win32::waitForSingleObject(mtx.handle, (uint)ms))
|
||||
{
|
||||
case win32::WAIT_OBJECT_0:
|
||||
break;
|
||||
case win32::WAIT_TIMEOUT:
|
||||
return thread::LOCK_TIMEOUT?;
|
||||
case win32::WAIT_ABANDONED:
|
||||
default:
|
||||
return thread::LOCK_FAILED?;
|
||||
}
|
||||
if (!mtx.recursive && mtx.locks)
|
||||
{
|
||||
return thread::LOCK_FAILED?;
|
||||
}
|
||||
mtx.locks++;
|
||||
*mtx = {
|
||||
.initialized = true,
|
||||
.recursive = type.recursive,
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
@require mtx.owner_thread != win32::getCurrentThreadId() : "Mutex was not unlocked before destroying it"
|
||||
@ensure !mtx.initialized
|
||||
*>
|
||||
fn void? NativeMutex.destroy(&mtx)
|
||||
{
|
||||
*mtx = {};
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn void? NativeMutex.lock(&mtx)
|
||||
{
|
||||
Win32_DWORD current_thread = win32::getCurrentThreadId();
|
||||
if (mtx.owner_thread == current_thread)
|
||||
{
|
||||
if (!mtx.recursive) return thread::LOCK_FAILED?;
|
||||
mtx.locks++;
|
||||
return;
|
||||
}
|
||||
|
||||
win32::acquireSRWLockExclusive(&mtx.srw_lock);
|
||||
mtx.owner_thread = current_thread;
|
||||
mtx.locks = 1;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn bool NativeMutex.try_lock(&mtx)
|
||||
{
|
||||
bool success = mtx.timed
|
||||
? win32::waitForSingleObject(mtx.handle, 0) == win32::WAIT_OBJECT_0
|
||||
: (bool)win32::tryEnterCriticalSection(&mtx.critical_section);
|
||||
|
||||
if (!success) return false;
|
||||
if (!mtx.recursive)
|
||||
Win32_DWORD current_thread = win32::getCurrentThreadId();
|
||||
if (mtx.owner_thread == current_thread)
|
||||
{
|
||||
if (mtx.locks)
|
||||
{
|
||||
if (mtx.timed)
|
||||
{
|
||||
win32::releaseMutex(mtx.handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
win32::leaveCriticalSection(&mtx.critical_section);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!mtx.recursive) return false;
|
||||
mtx.locks++;
|
||||
return true;
|
||||
}
|
||||
mtx.locks++;
|
||||
|
||||
if (!win32::tryAcquireSRWLockExclusive(&mtx.srw_lock)) return false;
|
||||
|
||||
mtx.owner_thread = current_thread;
|
||||
mtx.locks = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn void? NativeMutex.unlock(&mtx)
|
||||
{
|
||||
if (!mtx.locks) return thread::UNLOCK_FAILED?;
|
||||
mtx.locks--;
|
||||
if (!mtx.timed)
|
||||
$if env::COMPILER_SAFE_MODE:
|
||||
if (mtx.owner_thread != win32::getCurrentThreadId()) return thread::UNLOCK_FAILED?; // Mutex was not locked by the current thread
|
||||
$endif
|
||||
if (--mtx.locks == 0)
|
||||
{
|
||||
win32::leaveCriticalSection(&mtx.critical_section);
|
||||
return;
|
||||
mtx.owner_thread = 0;
|
||||
win32::releaseSRWLockExclusive(&mtx.srw_lock);
|
||||
}
|
||||
if (!win32::releaseMutex(mtx.handle)) return thread::UNLOCK_FAILED?;
|
||||
}
|
||||
|
||||
const int CONDITION_EVENT_ONE = 0;
|
||||
const int CONDITION_EVENT_ALL = 1;
|
||||
<*
|
||||
@require type.timed
|
||||
@require !mtx.initialized : "Mutex is already initialized"
|
||||
@ensure mtx.initialized
|
||||
*>
|
||||
fn void? NativeTimedMutex.init(&mtx, MutexType type)
|
||||
{
|
||||
*mtx = {
|
||||
.initialized = true,
|
||||
.recursive = type.recursive,
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
@require mtx.owner_thread != win32::getCurrentThreadId() : "Mutex was not unlocked before destroying it"
|
||||
@ensure !mtx.initialized
|
||||
*>
|
||||
fn void? NativeTimedMutex.destroy(&mtx)
|
||||
{
|
||||
*mtx = {};
|
||||
}
|
||||
|
||||
fn void? NativeTimedMutex.wait_cond_var(&mtx, uint ms) @local
|
||||
{
|
||||
if (!win32::sleepConditionVariableSRW(&mtx.cond_var, &mtx.srw_lock, ms, 0))
|
||||
{
|
||||
if (win32::getLastError() != win32::ERROR_TIMEOUT)
|
||||
{
|
||||
return thread::WAIT_FAILED?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn void? NativeTimedMutex.lock(&mtx)
|
||||
{
|
||||
Win32_DWORD current_thread = win32::getCurrentThreadId();
|
||||
if (mtx.owner_thread == current_thread)
|
||||
{
|
||||
if (!mtx.recursive) return thread::LOCK_FAILED?;
|
||||
mtx.locks++;
|
||||
return;
|
||||
}
|
||||
|
||||
win32::acquireSRWLockExclusive(&mtx.srw_lock);
|
||||
defer win32::releaseSRWLockExclusive(&mtx.srw_lock);
|
||||
|
||||
while (mtx.locks)
|
||||
{
|
||||
mtx.wait_cond_var(win32::INFINITE)!;
|
||||
}
|
||||
mtx.locks = 1;
|
||||
mtx.owner_thread = current_thread;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn void? NativeTimedMutex.lock_timeout(&mtx, ulong ms)
|
||||
{
|
||||
Win32_DWORD current_thread = win32::getCurrentThreadId();
|
||||
if (mtx.owner_thread == current_thread)
|
||||
{
|
||||
if (!mtx.recursive) return thread::LOCK_FAILED?;
|
||||
mtx.locks++;
|
||||
return;
|
||||
}
|
||||
|
||||
win32::acquireSRWLockExclusive(&mtx.srw_lock);
|
||||
defer win32::releaseSRWLockExclusive(&mtx.srw_lock);
|
||||
|
||||
if (!mtx.locks)
|
||||
{
|
||||
// Got the lock without needing to wait
|
||||
mtx.locks = 1;
|
||||
mtx.owner_thread = current_thread;
|
||||
return;
|
||||
}
|
||||
|
||||
NanoDuration duration = time::ms(ms).to_nano();
|
||||
Clock start = clock::now();
|
||||
for (NanoDuration remaining = duration; remaining > 0; remaining = duration - start.to_now())
|
||||
{
|
||||
ulong remaining_ms = remaining.to_ms();
|
||||
if (remaining_ms > uint.max) remaining_ms = uint.max;
|
||||
mtx.wait_cond_var((uint)remaining_ms)!;
|
||||
if (!mtx.locks)
|
||||
{
|
||||
// Got the lock
|
||||
mtx.locks = 1;
|
||||
mtx.owner_thread = current_thread;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return thread::WAIT_FAILED?;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn bool NativeTimedMutex.try_lock(&mtx)
|
||||
{
|
||||
Win32_DWORD current_thread = win32::getCurrentThreadId();
|
||||
if (mtx.owner_thread == current_thread)
|
||||
{
|
||||
if (!mtx.recursive) return false;
|
||||
mtx.locks++;
|
||||
return true;
|
||||
}
|
||||
|
||||
win32::acquireSRWLockExclusive(&mtx.srw_lock);
|
||||
defer win32::releaseSRWLockExclusive(&mtx.srw_lock);
|
||||
|
||||
if (mtx.locks) return false;
|
||||
mtx.locks = 1;
|
||||
mtx.owner_thread = current_thread;
|
||||
return true;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn void? NativeTimedMutex.unlock(&mtx)
|
||||
{
|
||||
win32::acquireSRWLockExclusive(&mtx.srw_lock);
|
||||
$if env::COMPILER_SAFE_MODE:
|
||||
if (mtx.owner_thread != win32::getCurrentThreadId())
|
||||
{
|
||||
win32::releaseSRWLockExclusive(&mtx.srw_lock);
|
||||
return thread::UNLOCK_FAILED?; // Mutex was not locked by the current thread
|
||||
}
|
||||
$endif
|
||||
|
||||
bool signal;
|
||||
if (--mtx.locks == 0)
|
||||
{
|
||||
mtx.owner_thread = 0;
|
||||
signal = true;
|
||||
}
|
||||
win32::releaseSRWLockExclusive(&mtx.srw_lock);
|
||||
if (signal) win32::wakeConditionVariable(&mtx.cond_var);
|
||||
}
|
||||
|
||||
fn void? NativeConditionVariable.init(&cond)
|
||||
{
|
||||
cond.waiters_count = 0;
|
||||
win32::initializeCriticalSection(&cond.waiters_count_lock);
|
||||
cond.event_one = win32::createEventA(null, 0, 0, null);
|
||||
if (!cond.event_one)
|
||||
{
|
||||
cond.event_all = (Win32_HANDLE)0;
|
||||
return thread::INIT_FAILED?;
|
||||
}
|
||||
cond.event_all = win32::createEventA(null, 1, 0, null);
|
||||
if (!cond.event_all)
|
||||
{
|
||||
win32::closeHandle(cond.event_one);
|
||||
cond.event_one = (Win32_HANDLE)0;
|
||||
return thread::INIT_FAILED?;
|
||||
}
|
||||
cond.cond_var = {};
|
||||
}
|
||||
|
||||
fn void? NativeConditionVariable.destroy(&cond) @maydiscard
|
||||
{
|
||||
if (cond.event_one) win32::closeHandle(cond.event_one);
|
||||
if (cond.event_all) win32::closeHandle(cond.event_all);
|
||||
win32::deleteCriticalSection(&cond.waiters_count_lock);
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
fn void? NativeConditionVariable.signal(&cond)
|
||||
{
|
||||
win32::enterCriticalSection(&cond.waiters_count_lock);
|
||||
bool have_waiters = cond.waiters_count > 0;
|
||||
win32::leaveCriticalSection(&cond.waiters_count_lock);
|
||||
if (have_waiters && !win32::setEvent(cond.event_one)) return thread::SIGNAL_FAILED?;
|
||||
win32::wakeConditionVariable(&cond.cond_var);
|
||||
}
|
||||
|
||||
fn void? NativeConditionVariable.broadcast(&cond)
|
||||
{
|
||||
win32::enterCriticalSection(&cond.waiters_count_lock);
|
||||
bool have_waiters = cond.waiters_count > 0;
|
||||
win32::leaveCriticalSection(&cond.waiters_count_lock);
|
||||
if (have_waiters && !win32::setEvent(cond.event_all)) return thread::SIGNAL_FAILED?;
|
||||
win32::wakeAllConditionVariable(&cond.cond_var);
|
||||
}
|
||||
|
||||
fn void? timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout) @private
|
||||
{
|
||||
win32::enterCriticalSection(&cond.waiters_count_lock);
|
||||
cond.waiters_count++;
|
||||
win32::leaveCriticalSection(&cond.waiters_count_lock);
|
||||
|
||||
mtx.unlock()!;
|
||||
|
||||
uint result = win32::waitForMultipleObjects(2, &cond.events, 0, timeout);
|
||||
switch (result)
|
||||
Win32_DWORD owner_thread = mtx.owner_thread;
|
||||
if (mtx.locks != 1 || owner_thread != win32::getCurrentThreadId()) return thread::WAIT_FAILED?;
|
||||
mtx.owner_thread = 0;
|
||||
mtx.locks = 0;
|
||||
defer
|
||||
{
|
||||
case win32::WAIT_TIMEOUT:
|
||||
mtx.lock()!;
|
||||
return thread::WAIT_TIMEOUT?;
|
||||
case win32::WAIT_FAILED:
|
||||
mtx.lock()!;
|
||||
return thread::WAIT_FAILED?;
|
||||
default:
|
||||
break;
|
||||
mtx.owner_thread = owner_thread;
|
||||
mtx.locks = 1;
|
||||
}
|
||||
|
||||
win32::enterCriticalSection(&cond.waiters_count_lock);
|
||||
cond.waiters_count--;
|
||||
// If event all && no waiters
|
||||
bool last_waiter = result == 1 && !cond.waiters_count;
|
||||
win32::leaveCriticalSection(&cond.waiters_count_lock);
|
||||
|
||||
if (last_waiter)
|
||||
if (!win32::sleepConditionVariableSRW(&cond.cond_var, &mtx.srw_lock, timeout, 0))
|
||||
{
|
||||
if (!win32::resetEvent(cond.event_all))
|
||||
if (win32::getLastError() == win32::ERROR_TIMEOUT)
|
||||
{
|
||||
mtx.lock()!;
|
||||
return thread::WAIT_FAILED?;
|
||||
return thread::WAIT_TIMEOUT?;
|
||||
}
|
||||
return thread::WAIT_FAILED?;
|
||||
}
|
||||
|
||||
mtx.lock()!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn void? NativeConditionVariable.wait(&cond, NativeMutex* mtx) @inline
|
||||
{
|
||||
return timedwait(cond, mtx, win32::INFINITE) @inline;
|
||||
}
|
||||
|
||||
<*
|
||||
@require mtx.initialized : "Mutex was not initialized"
|
||||
*>
|
||||
fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms) @inline
|
||||
{
|
||||
if (ms > uint.max) ms = uint.max;
|
||||
@@ -267,30 +341,12 @@ fn void native_thread_yield()
|
||||
|
||||
fn void NativeOnceFlag.call_once(&flag, OnceFn func)
|
||||
{
|
||||
while (@volatile_load(flag.status) < 3)
|
||||
var callback = fn Win32_BOOL(Win32_INIT_ONCE* init_once, void* parameter, void** context)
|
||||
{
|
||||
switch (@volatile_load(flag.status))
|
||||
{
|
||||
case 0:
|
||||
if (mem::compare_exchange_volatile(&flag.status, 1, 0, AtomicOrdering.SEQ_CONSISTENT, AtomicOrdering.SEQ_CONSISTENT) == 0)
|
||||
{
|
||||
win32::initializeCriticalSection(&flag.lock);
|
||||
win32::enterCriticalSection(&flag.lock);
|
||||
@volatile_store(flag.status, 2);
|
||||
func();
|
||||
@volatile_store(flag.status, 3);
|
||||
win32::leaveCriticalSection(&flag.lock);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
win32::enterCriticalSection(&flag.lock);
|
||||
win32::leaveCriticalSection(&flag.lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
((OnceFn)parameter)();
|
||||
return 1;
|
||||
};
|
||||
win32::initOnceExecuteOnce(&flag.init_once, callback, func, null);
|
||||
}
|
||||
|
||||
fn int? NativeThread.join(thread)
|
||||
|
||||
@@ -2,16 +2,16 @@ module std::thread;
|
||||
import std::thread::os;
|
||||
import std::time;
|
||||
|
||||
typedef MutexType = int;
|
||||
|
||||
const MutexType MUTEX_PLAIN = 0;
|
||||
const MutexType MUTEX_TIMED = 1;
|
||||
const MutexType MUTEX_RECURSIVE = 2;
|
||||
bitstruct MutexType : int
|
||||
{
|
||||
bool timed;
|
||||
bool recursive;
|
||||
}
|
||||
|
||||
typedef Mutex = NativeMutex;
|
||||
typedef TimedMutex = inline Mutex;
|
||||
typedef RecursiveMutex = inline Mutex;
|
||||
typedef TimedRecursiveMutex = inline Mutex;
|
||||
typedef TimedMutex = NativeTimedMutex;
|
||||
typedef TimedRecursiveMutex = inline TimedMutex;
|
||||
typedef ConditionVariable = NativeConditionVariable;
|
||||
typedef Thread = inline NativeThread;
|
||||
typedef OnceFlag = NativeOnceFlag;
|
||||
@@ -33,16 +33,21 @@ faultdef
|
||||
INTERRUPTED,
|
||||
CHANNEL_CLOSED;
|
||||
|
||||
macro void? Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_PLAIN);
|
||||
macro void? TimedMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_TIMED);
|
||||
macro void? RecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_RECURSIVE);
|
||||
macro void? TimedRecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_TIMED | MUTEX_RECURSIVE);
|
||||
macro void? Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {});
|
||||
macro void? RecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {.recursive});
|
||||
macro void? Mutex.destroy(&mutex) => NativeMutex.destroy((NativeMutex*)mutex);
|
||||
macro void? Mutex.lock(&mutex) => NativeMutex.lock((NativeMutex*)mutex);
|
||||
macro void? TimedMutex.lock_timeout(&mutex, ulong ms) => NativeMutex.lock_timeout((NativeMutex*)mutex, ms);
|
||||
macro void? TimedRecursiveMutex.lock_timeout(&mutex, ulong ms) => NativeMutex.lock_timeout((NativeMutex*)mutex, ms);
|
||||
macro bool Mutex.try_lock(&mutex) => NativeMutex.try_lock((NativeMutex*)mutex);
|
||||
macro bool Mutex.try_lock(&mutex) => NativeMutex.try_lock((NativeMutex*)mutex);
|
||||
macro void? Mutex.unlock(&mutex) => NativeMutex.unlock((NativeMutex*)mutex);
|
||||
|
||||
macro void? TimedMutex.init(&mutex) => NativeTimedMutex.init((NativeTimedMutex*)mutex, {.timed});
|
||||
macro void? TimedRecursiveMutex.init(&mutex) => NativeTimedMutex.init((NativeTimedMutex*)mutex, {.timed, .recursive});
|
||||
macro void? TimedMutex.destroy(&mutex) => NativeTimedMutex.destroy((NativeTimedMutex*)mutex);
|
||||
macro void? TimedMutex.lock(&mutex) => NativeTimedMutex.lock((NativeTimedMutex*)mutex);
|
||||
macro void? TimedMutex.lock_timeout(&mutex, ulong ms) => NativeTimedMutex.lock_timeout((NativeTimedMutex*)mutex, ms);
|
||||
macro bool TimedMutex.try_lock(&mutex) => NativeTimedMutex.try_lock((NativeTimedMutex*)mutex);
|
||||
macro void? TimedMutex.unlock(&mutex) => NativeTimedMutex.unlock((NativeTimedMutex*)mutex);
|
||||
|
||||
macro void Mutex.@in_lock(&mutex; @body)
|
||||
{
|
||||
(void)mutex.lock();
|
||||
|
||||
@@ -13,22 +13,37 @@ fn Clock now()
|
||||
fn NanoDuration Clock.mark(&self)
|
||||
{
|
||||
Clock mark = now();
|
||||
NanoDuration diff = (NanoDuration)(mark - *self);
|
||||
NanoDuration diff = mark - *self;
|
||||
*self = mark;
|
||||
return diff;
|
||||
}
|
||||
|
||||
fn Clock Clock.add_nano_duration(self, NanoDuration nano)
|
||||
fn Clock Clock.add_nano_duration(self, NanoDuration nano) @operator_s(+) @inline
|
||||
{
|
||||
return (Clock)((NanoDuration)self + nano);
|
||||
}
|
||||
|
||||
fn Clock Clock.add_duration(self, Duration duration)
|
||||
fn Clock Clock.sub_nano_duration(self, NanoDuration nano) @operator(-) @inline
|
||||
{
|
||||
return (Clock)((NanoDuration)self - nano);
|
||||
}
|
||||
|
||||
fn Clock Clock.add_duration(self, Duration duration) @operator_s(+) @inline
|
||||
{
|
||||
return self.add_nano_duration(duration.to_nano());
|
||||
}
|
||||
|
||||
fn NanoDuration Clock.to_now(self)
|
||||
fn Clock Clock.sub_duration(self, Duration duration) @operator(-) @inline
|
||||
{
|
||||
return (NanoDuration)(now() - self);
|
||||
return self.sub_nano_duration(duration.to_nano());
|
||||
}
|
||||
|
||||
fn NanoDuration Clock.nano_diff(self, Clock other) @operator(-) @inline
|
||||
{
|
||||
return (NanoDuration)self - (NanoDuration)other;
|
||||
}
|
||||
|
||||
fn NanoDuration Clock.to_now(self) @inline
|
||||
{
|
||||
return (NanoDuration)now() - (NanoDuration)self;
|
||||
}
|
||||
@@ -106,7 +106,7 @@ fn TzDateTime DateTime.to_gmt_offset(self, int gmt_offset)
|
||||
*>
|
||||
fn TzDateTime TzDateTime.to_gmt_offset(self, int gmt_offset) {
|
||||
if (self.gmt_offset == gmt_offset) return self;
|
||||
Time time = self.time + (Time)gmt_offset * (Time)time::SEC;
|
||||
Time time = self.time + gmt_offset * time::SEC;
|
||||
DateTime dt = from_time(time);
|
||||
dt.time = self.time;
|
||||
return { dt, gmt_offset };
|
||||
@@ -149,6 +149,8 @@ fn void DateTime.set_time(&self, Time time)
|
||||
self.time = time;
|
||||
}
|
||||
|
||||
fn DateTime DateTime.add_us(&self, Duration d) @operator_s(+) => from_time(self.time + d);
|
||||
fn DateTime DateTime.sub_us(&self, Duration d) @operator(-) => from_time(self.time - d);
|
||||
fn DateTime DateTime.add_seconds(&self, int seconds) => from_time(self.time.add_seconds(seconds));
|
||||
fn DateTime DateTime.add_minutes(&self, int minutes) => from_time(self.time.add_minutes(minutes));
|
||||
fn DateTime DateTime.add_hours(&self, int hours) => from_time(self.time.add_hours(hours));
|
||||
@@ -187,6 +189,8 @@ fn DateTime DateTime.add_months(&self, int months)
|
||||
}
|
||||
|
||||
|
||||
fn TzDateTime TzDateTime.add_us(&self, Duration d) @operator_s(+) => self.date_time.add_us(d).to_gmt_offset(self.gmt_offset);
|
||||
fn TzDateTime TzDateTime.sub_us(&self, Duration d) @operator(-) => self.date_time.sub_us(d).to_gmt_offset(self.gmt_offset);
|
||||
fn TzDateTime TzDateTime.add_seconds(&self, int seconds) => self.date_time.add_seconds(seconds).to_gmt_offset(self.gmt_offset);
|
||||
fn TzDateTime TzDateTime.add_minutes(&self, int minutes) => self.date_time.add_minutes(minutes).to_gmt_offset(self.gmt_offset);
|
||||
fn TzDateTime TzDateTime.add_hours(&self, int hours) => self.date_time.add_hours(hours).to_gmt_offset(self.gmt_offset);
|
||||
@@ -248,7 +252,8 @@ fn double DateTime.diff_sec(self, DateTime from)
|
||||
{
|
||||
return (double)self.time.diff_us(from.time) / (double)time::SEC;
|
||||
}
|
||||
fn Duration DateTime.diff_us(self, DateTime from)
|
||||
|
||||
fn Duration DateTime.diff_us(self, DateTime from) @operator(-)
|
||||
{
|
||||
return self.time.diff_us(from.time);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
module std::time::os @if(env::DARWIN);
|
||||
|
||||
struct Darwin_mach_timebase_info
|
||||
{
|
||||
uint numer;
|
||||
uint denom;
|
||||
}
|
||||
|
||||
alias Darwin_mach_timebase_info_t = Darwin_mach_timebase_info;
|
||||
alias Darwin_mach_timebase_info_data_t = Darwin_mach_timebase_info;
|
||||
|
||||
extern fn void mach_timebase_info(Darwin_mach_timebase_info_data_t* timebase);
|
||||
extern fn ulong mach_absolute_time();
|
||||
import std::os::darwin;
|
||||
|
||||
fn Clock native_clock()
|
||||
{
|
||||
static Darwin_mach_timebase_info_data_t timebase;
|
||||
if (!timebase.denom)
|
||||
{
|
||||
mach_timebase_info(&timebase);
|
||||
darwin::mach_timebase_info(&timebase);
|
||||
}
|
||||
return (Clock)(mach_absolute_time() * timebase.numer / timebase.denom);
|
||||
return (Clock)(darwin::mach_absolute_time() * timebase.numer / timebase.denom);
|
||||
}
|
||||
|
||||
@@ -1,82 +1,17 @@
|
||||
module std::time::os @if(env::POSIX);
|
||||
import libc;
|
||||
|
||||
extern fn CInt clock_gettime(int type, TimeSpec *time);
|
||||
import libc, std::os::posix;
|
||||
|
||||
fn Time native_timestamp()
|
||||
{
|
||||
TimeSpec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
return (Time)(ts.s * 1_000_000i64 + ts.ns / 1_000i64);
|
||||
posix::clock_gettime(posix::CLOCK_REALTIME, &ts);
|
||||
return (Time)(ts.s * 1_000_000L + ts.ns / 1_000L);
|
||||
}
|
||||
|
||||
fn Clock native_clock() @if(!env::DARWIN)
|
||||
{
|
||||
TimeSpec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (Clock)((ulong)ts.s * 1_000_000_000u64 + (ulong)ts.ns);
|
||||
posix::clock_gettime(posix::CLOCK_MONOTONIC, &ts);
|
||||
return (Clock)((ulong)ts.s * 1_000_000_000UL + (ulong)ts.ns);
|
||||
}
|
||||
|
||||
module std::time::os @if(env::OPENBSD);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 2;
|
||||
const CLOCK_MONOTONIC = 3;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 4;
|
||||
const CLOCK_UPTIME = 5;
|
||||
const CLOCK_BOOTTIME = 6;
|
||||
|
||||
module std::time::os @if(env::FREEBSD);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_VIRTUAL = 1;
|
||||
const CLOCK_PROF = 2;
|
||||
const CLOCK_MONOTONIC = 4;
|
||||
const CLOCK_UPTIME = 5;
|
||||
const CLOCK_UPTIME_PRECISE = 7;
|
||||
const CLOCK_UPTIME_FAST = 8;
|
||||
const CLOCK_REALTIME_PRECISE = 9;
|
||||
const CLOCK_REALTIME_FAST = 10;
|
||||
const CLOCK_MONOTONIC_PRECISE = 11;
|
||||
const CLOCK_MONOTONIC_FAST = 12;
|
||||
const CLOCK_SECOND = 13;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 14;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 15;
|
||||
const CLOCK_BOOTTIME = CLOCK_UPTIME;
|
||||
const CLOCK_REALTIME_COARSE = CLOCK_REALTIME_FAST;
|
||||
const CLOCK_MONOTONIC_COARSE = CLOCK_MONOTONIC_FAST;
|
||||
|
||||
module std::time::os @if(env::NETBSD);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_VIRTUAL = 1;
|
||||
const CLOCK_PROF = 2;
|
||||
const CLOCK_MONOTONIC = 3;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 0x20000000;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 0x40000000;
|
||||
|
||||
module std::time::os @if(env::WASI);
|
||||
// Not implemented
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 0;
|
||||
|
||||
module std::time::os @if(env::DARWIN);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 6;
|
||||
const CLOCK_MONOTONIC_RAW = 4;
|
||||
const CLOCK_MONOTONIC_RAW_APPROX = 5;
|
||||
const CLOCK_UPTIME_RAW = 8;
|
||||
const CLOCK_UPTIME_RAW_APPROX = 9;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 12;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 16;
|
||||
|
||||
module std::time::os @if(env::LINUX || env::ANDROID);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 1;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 2;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 3;
|
||||
const CLOCK_MONOTONIC_RAW = 4;
|
||||
const CLOCK_REALTIME_COARSE = 5;
|
||||
const CLOCK_MONOTONIC_COARSE = 6;
|
||||
const CLOCK_BOOTTIME = 7;
|
||||
const CLOCK_REALTIME_ALARM = 8;
|
||||
const CLOCK_BOOTTIME_ALARM = 9;
|
||||
const CLOCK_TAI = 11;
|
||||
|
||||
|
||||
@@ -2,13 +2,8 @@ module std::time::os @if(env::WIN32);
|
||||
import std::os::win32;
|
||||
import std::math;
|
||||
|
||||
extern fn void win32_GetSystemTimeAsFileTime(Win32_FILETIME* time) @extern("GetSystemTimeAsFileTime");
|
||||
extern fn Win32_BOOL win32_QueryPerformanceFrequency(Win32_LARGE_INTEGER* lpFrequency) @extern("QueryPerformanceFrequency");
|
||||
extern fn Win32_BOOL win32_QueryPerformanceCounter(Win32_LARGE_INTEGER* lpPerformanceCount) @extern("QueryPerformanceCounter");
|
||||
|
||||
const ulong WINDOWS_TICK_US @local = 10;
|
||||
const ulong WIN_TO_UNIX_EPOCH_US @local = 116444736000000000u64 / WINDOWS_TICK_US;
|
||||
|
||||
const ulong WIN_TO_UNIX_EPOCH_US @local = 116444736000000000UL / WINDOWS_TICK_US;
|
||||
|
||||
fn Clock native_clock()
|
||||
{
|
||||
@@ -17,17 +12,17 @@ fn Clock native_clock()
|
||||
ulong mult = 0;
|
||||
if (!freq.quadPart)
|
||||
{
|
||||
if (!win32_QueryPerformanceFrequency(&freq)) return 0;
|
||||
if (!win32::queryPerformanceFrequency(&freq)) return 0;
|
||||
}
|
||||
Win32_LARGE_INTEGER counter @noinit;
|
||||
if (!win32_QueryPerformanceCounter(&counter)) return 0;
|
||||
if (!win32::queryPerformanceCounter(&counter)) return 0;
|
||||
return (Clock)counter.quadPart.muldiv(1_000_000_000, freq.quadPart);
|
||||
}
|
||||
|
||||
fn Time native_timestamp()
|
||||
{
|
||||
Win32_FILETIME ft @noinit;
|
||||
win32_GetSystemTimeAsFileTime(&ft);
|
||||
win32::getSystemTimeAsFileTime(&ft);
|
||||
ulong result = (ulong)ft.dwHighDateTime << 32 | ft.dwLowDateTime;
|
||||
return (Time)(result / WINDOWS_TICK_US - WIN_TO_UNIX_EPOCH_US);
|
||||
}
|
||||
@@ -19,11 +19,11 @@ const Duration MONTH = 30 * DAY;
|
||||
const Duration YEAR = 36525 * DAY / 100;
|
||||
const Duration FOREVER = long.max;
|
||||
|
||||
fn Duration us(long l) @inline => (Duration)l * US;
|
||||
fn Duration ms(long l) @inline => (Duration)l * MS;
|
||||
fn Duration sec(long l) @inline => (Duration)l * SEC;
|
||||
fn Duration min(long l) @inline => (Duration)l * MIN;
|
||||
fn Duration hour(long l) @inline => (Duration)l * HOUR;
|
||||
fn Duration us(long l) @inline => l * US;
|
||||
fn Duration ms(long l) @inline => l * MS;
|
||||
fn Duration sec(long l) @inline =>l * SEC;
|
||||
fn Duration min(long l) @inline => l * MIN;
|
||||
fn Duration hour(long l) @inline => l * HOUR;
|
||||
fn Duration from_float(double s) @inline => (Duration)(s * (double)SEC);
|
||||
|
||||
struct DateTime
|
||||
@@ -82,21 +82,22 @@ fn Time now()
|
||||
$endif
|
||||
}
|
||||
|
||||
fn Time Time.add_seconds(time, long seconds) => time + (Time)(seconds * (long)SEC);
|
||||
fn Time Time.add_minutes(time, long minutes) => time + (Time)(minutes * (long)MIN);
|
||||
fn Time Time.add_hours(time, long hours) => time + (Time)(hours * (long)HOUR);
|
||||
fn Time Time.add_days(time, long days) => time + (Time)(days * (long)DAY);
|
||||
fn Time Time.add_weeks(time, long weeks) => time + (Time)(weeks * (long)WEEK);
|
||||
fn Time Time.add_duration(time, Duration duration) => time + (Time)duration;
|
||||
fn Time Time.add_seconds(time, long seconds) => time + seconds * SEC;
|
||||
fn Time Time.add_minutes(time, long minutes) => time + minutes * MIN;
|
||||
fn Time Time.add_hours(time, long hours) => time + hours * HOUR;
|
||||
fn Time Time.add_days(time, long days) => time + days * DAY;
|
||||
fn Time Time.add_weeks(time, long weeks) => time + weeks * WEEK;
|
||||
fn Time Time.add_duration(time, Duration duration) @operator_s(+) @inline => (Time)((long)time + (long)duration);
|
||||
fn Time Time.sub_duration(time, Duration duration) @operator(-) @inline => (Time)((long)time - (long)duration);
|
||||
|
||||
fn int Time.compare_to(time, Time other)
|
||||
{
|
||||
if (time == other) return 0;
|
||||
if ((long)time == (long)other) return 0;
|
||||
return time > other ? 1 : -1;
|
||||
}
|
||||
|
||||
fn double Time.to_seconds(time) => (long)time / (double)SEC;
|
||||
fn Duration Time.diff_us(time, Time other) => (Duration)(time - other);
|
||||
fn Duration Time.diff_us(time, Time other) @operator(-) => (Duration)((long)time - (long)other);
|
||||
fn double Time.diff_sec(time, Time other) => (long)time.diff_us(other) / (double)SEC;
|
||||
fn double Time.diff_min(time, Time other) => (long)time.diff_us(other) / (double)MIN;
|
||||
fn double Time.diff_hour(time, Time other) => (long)time.diff_us(other) / (double)HOUR;
|
||||
@@ -108,6 +109,7 @@ fn long NanoDuration.to_ms(nd) => (long)nd / 1_000_000;
|
||||
fn Duration NanoDuration.to_duration(nd) => (Duration)nd / 1_000;
|
||||
fn NanoDuration Duration.to_nano(td) => (NanoDuration)td * 1_000;
|
||||
fn long Duration.to_ms(td) => (long)td / 1_000;
|
||||
macro Duration Duration.mult(#td, long #val) @operator_s(*) @safemacro => (Duration)((long)#td * #val);
|
||||
|
||||
fn usz? NanoDuration.to_format(&self, Formatter* formatter) @dynamic
|
||||
{
|
||||
|
||||
125
releasenotes.md
125
releasenotes.md
@@ -1,5 +1,126 @@
|
||||
# C3C Release Notes
|
||||
|
||||
## 0.7.2 Change list
|
||||
|
||||
### Changes / improvements
|
||||
- Better default assert messages when no message is specified #2122
|
||||
- Add `--run-dir`, to specify directory for running executable using `compile-run` and `run` #2121.
|
||||
- Add `run-dir` to project.json.
|
||||
- Add `quiet` to project.json.
|
||||
- Deprecate uXX and iXX bit suffixes.
|
||||
- Add experimental LL / ULL suffixes for int128 and uint128 literals.
|
||||
- Allow the right hand side of `|||` and `&&&` be runtime values.
|
||||
- Added `@rnd()` compile time random function (using the `$$rnd()` builtin). #2078
|
||||
- Add `math::@ceil()` compile time ceil function. #2134
|
||||
- Improve error message when using keywords as functions/macros/variables #2133.
|
||||
- Deprecate `MyEnum.elements`.
|
||||
- Deprecate `SomeFn.params`.
|
||||
- Improve error message when encountering recursively defined structs. #2146
|
||||
- Limit vector max size, default is 4096 bits, but may be increased using --max-vector-size.
|
||||
- Allow the use of `has_tagof` on builtin types.
|
||||
- `@jump` now included in `--list-attributes` #2155.
|
||||
- Add `$$matrix_mul` and `$$matrix_transpose` builtins.
|
||||
- Add `d` as floating point suffix for `double` types.
|
||||
- Deprecate `f32`, `f64` and `f128` suffixes.
|
||||
- Allow recursive generic modules.
|
||||
- Add deprecation for `@param foo "abc"`.
|
||||
- Add `--header-output` and `header-output` options for controlling header output folder.
|
||||
- Generic faults is disallowed.
|
||||
|
||||
### Fixes
|
||||
- Assert triggered when casting from `int[2]` to `uint[2]` #2115
|
||||
- Assert when a macro with compile time value is discarded, e.g. `foo();` where `foo()` returns an untyped list. #2117
|
||||
- Fix stringify for compound initializers #2120.
|
||||
- Fix No index OOB check for `[:^n]` #2123.
|
||||
- Fix regression in Time diff due to operator overloading #2124.
|
||||
- attrdef with any invalid name causes compiler assert #2128.
|
||||
- Correctly error on `@attrdef Foo = ;`.
|
||||
- Contract on trying to use Object without initializing it.
|
||||
- Variable aliases of aliases would not resolve correctly. #2131
|
||||
- Variable aliases could not be assigned to.
|
||||
- Some folding was missing in binary op compile time resolution #2135.
|
||||
- Defining an enum like `ABC = { 1 2 }` was accidentally allowed.
|
||||
- Using a non-const as the end range for a bitstruct would trigger an assert.
|
||||
- Incorrect parsing of ad hoc generic types, like `Foo{int}****` #2140.
|
||||
- $define did not correctly handle generic types #2140.
|
||||
- Incorrect parsing of call attributes #2144.
|
||||
- Error when using named argument on trailing macro body expansion #2139.
|
||||
- Designated const initializers with `{}` would overwrite the parent field.
|
||||
- Empty default case in @jump switch does not fallthrough #2147.
|
||||
- `&&&` was accidentally available as a valid prefix operator.
|
||||
- Missing error on default values for body with default arguments #2148.
|
||||
- `--path` does not interact correctly with relative path arguments #2149.
|
||||
- Add missing `@noreturn` to `os::exit`.
|
||||
- Implicit casting from struct to interface failure for inheriting interfaces #2151.
|
||||
- Distinct types could not be used with tagof #2152.
|
||||
- `$$sat_mul` was missing.
|
||||
- `for` with incorrect `var` declaration caused crash #2154.
|
||||
- Check pointer/slice/etc on `[out]` and `&` params. #2156.
|
||||
- Compiler didn't check foreach over flexible array member, and folding a flexible array member was allowed #2164.
|
||||
- Too strict project view #2163.
|
||||
- Bug using `#foo` arguments with `$defined` #2173
|
||||
- Incorrect ensure on String.split.
|
||||
- Removed the naive check for compile time modification, which fixes #1997 but regresses in detection.
|
||||
|
||||
### Stdlib changes
|
||||
- Added `String.quick_ztr` and `String.is_zstr`
|
||||
- std::ascii moved into std::core::ascii. Old _m variants are deprecated, as is uint methods.
|
||||
- Add `String.tokenize_all` to replace the now deprecated `String.splitter`
|
||||
- Add `String.count` to count the number of instances of a string.
|
||||
- Add `String.replace` and `String.treplace` to replace substrings within a string.
|
||||
- Add `Duration * Int` and `Clock - Clock` overload.
|
||||
- Add `DateTime + Duration` overloads.
|
||||
- Add `Maybe.equals` and respective `==` operator when the inner type is equatable.
|
||||
- Add `inherit_stdio` option to `SubProcessOptions` to inherit parent's stdin, stdout, and stderr instead of creating pipes. #2012
|
||||
- Remove superfluous `cleanup` parameter in `os::exit` and `os::fastexit`.
|
||||
- Add `extern fn ioctl(CInt fd, ulong request, ...)` binding to libc;
|
||||
|
||||
## 0.7.1 Change list
|
||||
|
||||
### Changes / improvements
|
||||
- Better errors on some common casting mistakes (pointer->slice, String->ZString, deref pointer->array) #2064.
|
||||
- Better errors trying to convert an enum to an int and vice versa.
|
||||
- Function `@require` checks are added to the caller in safe mode. #186
|
||||
- Improved error message when narrowing isn't allowed.
|
||||
- Operator overloading for `+ - * / % & | ^ << >> ~ == != += -= *= /= %= &= |= ^= <<= >>=`
|
||||
- Add `@operator_r` and `@operator_s` attributes.
|
||||
- More stdlib tests: `sincos`, `ArenaAllocator`, `Slice2d`.
|
||||
- Make aliases able to use `@deprecated`.
|
||||
- Refactored stdlib file organization.
|
||||
- Allow `@if` on locals.
|
||||
- String str = "" is now guaranteed to be null terminated. #2083
|
||||
- Improved error messages on `Foo { 3, abc }` #2099.
|
||||
- `Foo[1..2] = { .baz = 123 }` inference now works. #2095
|
||||
- Deprecated old inference with slice copy. Copying must now ensure a slicing operator at the end of the right hand side: `foo[1..2] = bar[..]` rather than the old `foo[1..2] = bar`. The old behaviour can be mostly retained with `--use-old-slice-copy`).
|
||||
- Added `Enum.lookup` and `Enum.lookup_field`.
|
||||
- `c3c build` picks first target rather than the first executable #2105.
|
||||
- New Win32 Mutex, ConditionVariable and OnceFlag implementation
|
||||
|
||||
### Fixes
|
||||
- Trying to cast an enum to int and back caused the compiler to crash.
|
||||
- Incorrect rounding at compile time going from double to int.
|
||||
- Regression with invalid setup of the WASM temp allocator.
|
||||
- Correctly detect multiple overloads of the same type.
|
||||
- ABI bug on x64 Linux / MacOS when passing a union containing a struct of 3 floats. #2087
|
||||
- Bug with slice acces as inline struct member #2088.
|
||||
- `@if` now does implicit conversion to bool like `$if`. #2086
|
||||
- Fix broken enum inline -> bool conversions #2094.
|
||||
- `@if` was ignored on attrdef, regression 0.7 #2093.
|
||||
- `@ensure` was not included when the function doesn't return a value #2098.
|
||||
- Added missing `@clone_aligned` and add checks to `@tclone`
|
||||
- Comparing a distinct type with an enum with an inline distinct type failed unexpectedly.
|
||||
- The `%s` would not properly print function pointers.
|
||||
- Compiler crash when passing an untyped list as an argument to `assert` #2108.
|
||||
- `@ensure` should be allowed to read "out" variables. #2107
|
||||
- Error message for casting generic to incompatible type does not work properly with nested generics #1953
|
||||
- Fixed enum regression after 0.7.0 enum change.
|
||||
- ConditionVariable now properly works on Win32
|
||||
|
||||
### Stdlib changes
|
||||
- Hash functions for integer vectors and arrays.
|
||||
- Prefer `math::I` and `math::I_F` for `math::IMAGINARY` and `math::IMAGINARYF` the latter is deprecated.
|
||||
- Add `array::contains` to check for a value in an array or slice.
|
||||
|
||||
## 0.7.0 Change list
|
||||
|
||||
### Changes / improvements
|
||||
@@ -18,7 +139,7 @@
|
||||
- Removal of "any-switch".
|
||||
- Allow swizzling assign, eg. `abc.xz += { 5, 10 };`
|
||||
- Added `$$wstr16` and `$$wstr32` builtins.
|
||||
- `$foreach` "()" replaced by trailing ":" `$foreach ($x, $y : $foo)` -> `$foreach $x, $y : $foo:`
|
||||
- `$foreach` "()" replaced by trailing ":" `$foreach ($x, $y : $foo)` -> `$foreach $x, $y : $foo:`
|
||||
- `$for` "()" replaced by trailing ":" `$for (var $x = 0; $x < FOO; $x++)` -> `$for var $x = 0; $x < FOO; $x++:`
|
||||
- `$switch` "()" replaced by trailing ":" `$switch ($Type)` -> `$switch $Type:`
|
||||
- Empty `$switch` requires trailing ":" `$switch` -> `$switch:`
|
||||
@@ -71,7 +192,7 @@
|
||||
- `mem::temp_new` changed to `mem::tnew`.
|
||||
- `mem::temp_alloc` and related changed to `mem::talloc`.
|
||||
- `mem::temp_new_array` changed to `mem::temp_array`.
|
||||
- Add `ONHEAP` variants for List/HashMap for initializing global maps on the heap.
|
||||
- Add `ONHEAP` variants for List/HashMap for initializing global maps on the heap.
|
||||
- Remove Vec2 and other aliases from std::math. Replace `.length_sq()` with `sq_magnitude()`
|
||||
- Change all hash functions to have a common `hash` function.
|
||||
- `@wstring`, `@wstring32`, `@char32` and `@char16` compile time macros added.
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
module std::container::faults;
|
||||
|
||||
faultdef KEY_NOT_FOUND;
|
||||
|
||||
module std::container::map{Key, Type};
|
||||
import std::core::builtin;
|
||||
import std::io;
|
||||
|
||||
faultdef KEY_NOT_FOUND;
|
||||
|
||||
struct Entry
|
||||
{
|
||||
Key key;
|
||||
@@ -33,17 +35,17 @@ fn void Map.init(Map *map, uint capacity = 128)
|
||||
|
||||
fn Type? Map.valueForKey(Map *map, Key key)
|
||||
{
|
||||
if (!map.map) return KEY_NOT_FOUND?;
|
||||
if (!map.map) return faults::KEY_NOT_FOUND?;
|
||||
uint hash = key.hash();
|
||||
usz pos = hash & map.mod;
|
||||
Entry* entry = &map.map[pos];
|
||||
if (!entry) return KEY_NOT_FOUND?;
|
||||
if (!entry) return faults::KEY_NOT_FOUND?;
|
||||
while (entry)
|
||||
{
|
||||
if (entry.hash == hash && entry.key == key) return entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
return KEY_NOT_FOUND?;
|
||||
return faults::KEY_NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn Type? Map.set(Map *map, Key key, Type value) @maydiscard
|
||||
@@ -65,7 +67,7 @@ fn Type? Map.set(Map *map, Key key, Type value) @maydiscard
|
||||
entry.value = value;
|
||||
entry.hash = hash;
|
||||
entry.key = key;
|
||||
return KEY_NOT_FOUND?;
|
||||
return faults::KEY_NOT_FOUND?;
|
||||
}
|
||||
if (entry.hash == hash && entry.key == key)
|
||||
{
|
||||
@@ -85,7 +87,7 @@ fn Type? Map.set(Map *map, Key key, Type value) @maydiscard
|
||||
new.next = null;
|
||||
new.used = true;
|
||||
entry.next = new;
|
||||
return KEY_NOT_FOUND?;
|
||||
return faults::KEY_NOT_FOUND?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module test;
|
||||
import libc;
|
||||
|
||||
faultde NOPE;
|
||||
faultdef NOPE;
|
||||
|
||||
fn int? eventually_succeed()
|
||||
{
|
||||
|
||||
@@ -23,7 +23,7 @@ P [Pp][+-]?{D}+
|
||||
B64 [ \t\v\n\f]?[A-Za-z0-9+/][ \t\v\n\fA-Za-z0-9+/=]+
|
||||
HEX [ \t\v\n\f]?[A-Fa-f0-9][ \t\v\n\fA-Fa-f0-9]+
|
||||
INTTYPE ([ui](8|16|32|64|128)|[Uu][Ll]?|[Ll])
|
||||
REALTYPE ([f](8|16|32|64|128)?)
|
||||
REALTYPE ([fd])
|
||||
INT {D}(_?{D})*
|
||||
HINT {H}(_?{H})*
|
||||
OINT {O}(_?{O})*
|
||||
@@ -112,10 +112,11 @@ typedef struct {
|
||||
[^*]+ { }
|
||||
}
|
||||
\/\/.* { }
|
||||
"alias" { return(ALIAS); }
|
||||
"any" { return(ANY); }
|
||||
"anyfault" { return(ANYFAULT); }
|
||||
"asm" { return(ASM); }
|
||||
"assert" { return(ASSERT); }
|
||||
"attrdef" { return(ATTRDEF); }
|
||||
"bitstruct" { return(BITSTRUCT); }
|
||||
"bool" { return(BOOL); }
|
||||
"break" { return(BREAK); }
|
||||
@@ -124,10 +125,8 @@ typedef struct {
|
||||
"char" { return(CHAR); }
|
||||
"const" { return(CONST); }
|
||||
"continue" { return(CONTINUE); }
|
||||
"def" { return(DEF); }
|
||||
"default" { return(DEFAULT); }
|
||||
"defer" { return(DEFER); }
|
||||
"distinct" { return(DISTINCT); }
|
||||
"do" { return(DO); }
|
||||
"double" { return(DOUBLE); }
|
||||
"else" { return(ELSE); }
|
||||
@@ -135,6 +134,7 @@ typedef struct {
|
||||
"extern" { return(EXTERN); }
|
||||
"false" { return(FALSE); }
|
||||
"fault" { return(FAULT); }
|
||||
"faultdef" { return(FAULTDEF); }
|
||||
"float" { return(FLOAT); }
|
||||
"bfloat16" { return(BFLOAT16); }
|
||||
"float16" { return(FLOAT16); }
|
||||
@@ -165,6 +165,7 @@ typedef struct {
|
||||
"tlocal" { return(TLOCAL); }
|
||||
"true" { return(TRUE); }
|
||||
"try" { return(TRY); }
|
||||
"typedef" { return(TYPEDEF); }
|
||||
"typeid" { return(TYPEID); }
|
||||
"uint" { return(UINT); }
|
||||
"uint128" { return(UINT128); }
|
||||
|
||||
@@ -14,13 +14,13 @@ void yyerror(YYLTYPE * yylloc_param , yyscan_t yyscanner, const char *yymsgp);
|
||||
%token IDENT HASH_IDENT CT_IDENT CONST_IDENT
|
||||
%token TYPE_IDENT CT_TYPE_IDENT
|
||||
%token AT_TYPE_IDENT AT_IDENT CT_INCLUDE
|
||||
%token STRING_LITERAL INTEGER
|
||||
%token STRING_LITERAL INTEGER ALIAS
|
||||
%token CT_AND_OP CT_OR_OP CT_CONCAT_OP CT_EXEC
|
||||
%token INC_OP DEC_OP SHL_OP SHR_OP LE_OP GE_OP EQ_OP NE_OP
|
||||
%token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN
|
||||
%token SUB_ASSIGN SHL_ASSIGN SHR_ASSIGN AND_ASSIGN
|
||||
%token XOR_ASSIGN OR_ASSIGN VAR NUL ELVIS NEXTCASE ANYFAULT
|
||||
%token MODULE IMPORT DEF EXTERN
|
||||
%token XOR_ASSIGN OR_ASSIGN VAR NUL ELVIS NEXTCASE
|
||||
%token MODULE IMPORT TYPEDEF ATTRDEF FAULTDEF EXTERN
|
||||
%token CHAR SHORT INT LONG FLOAT DOUBLE CONST VOID USZ ISZ UPTR IPTR ANY
|
||||
%token ICHAR USHORT UINT ULONG BOOL INT128 UINT128 FLOAT16 FLOAT128 BFLOAT16
|
||||
%token TYPEID BITSTRUCT STATIC BANGBANG AT_CONST_IDENT HASH_TYPE_IDENT
|
||||
@@ -536,7 +536,7 @@ base_type_no_user_defined
|
||||
| UPTR
|
||||
| ISZ
|
||||
| USZ
|
||||
| ANYFAULT
|
||||
| FAULT
|
||||
| ANY
|
||||
| TYPEID
|
||||
| CT_TYPE_IDENT
|
||||
@@ -567,10 +567,8 @@ type_suffix
|
||||
| '[' constant_expr ']'
|
||||
| '[' ']'
|
||||
| '[' '*' ']'
|
||||
| '[' '?' ']'
|
||||
| LVEC constant_expr RVEC
|
||||
| LVEC '*' RVEC
|
||||
| LVEC '?' RVEC
|
||||
;
|
||||
type
|
||||
: base_type
|
||||
@@ -623,17 +621,17 @@ ct_switch_body
|
||||
;
|
||||
|
||||
ct_for_stmt
|
||||
: CT_FOR '(' for_cond ')' opt_stmt_list CT_ENDFOR
|
||||
: CT_FOR for_cond ':' opt_stmt_list CT_ENDFOR
|
||||
;
|
||||
|
||||
ct_foreach_stmt
|
||||
: CT_FOREACH '(' CT_IDENT ':' expr ')' opt_stmt_list CT_ENDFOREACH
|
||||
| CT_FOREACH '(' CT_IDENT ',' CT_IDENT ':' expr ')' opt_stmt_list CT_ENDFOREACH
|
||||
: CT_FOREACH CT_IDENT ':' expr ':' opt_stmt_list CT_ENDFOREACH
|
||||
| CT_FOREACH CT_IDENT ',' CT_IDENT ':' expr ':' opt_stmt_list CT_ENDFOREACH
|
||||
;
|
||||
ct_switch
|
||||
: CT_SWITCH '(' constant_expr ')'
|
||||
| CT_SWITCH '(' type ')'
|
||||
| CT_SWITCH
|
||||
: CT_SWITCH constant_expr ':'
|
||||
| CT_SWITCH type ':'
|
||||
| CT_SWITCH ':'
|
||||
;
|
||||
|
||||
ct_switch_stmt
|
||||
@@ -1076,13 +1074,12 @@ enum_declaration
|
||||
;
|
||||
|
||||
faults
|
||||
: CONST_IDENT
|
||||
| faults ',' CONST_IDENT
|
||||
: CONST_IDENT opt_attributes
|
||||
| faults ',' CONST_IDENT opt_attributes
|
||||
;
|
||||
|
||||
fault_declaration
|
||||
: FAULT TYPE_IDENT opt_interface_impl opt_attributes '{' faults '}'
|
||||
| FAULT TYPE_IDENT opt_interface_impl opt_attributes '{' faults ',' '}'
|
||||
: FAULTDEF faults ';'
|
||||
;
|
||||
|
||||
func_macro_name
|
||||
@@ -1188,9 +1185,24 @@ global_declaration
|
||||
| global_storage optional_type IDENT opt_attributes '=' expr ';'
|
||||
;
|
||||
|
||||
attribute_comma_list
|
||||
: attribute
|
||||
| attribute_comma_list ',' attribute
|
||||
;
|
||||
|
||||
opt_comma
|
||||
: ','
|
||||
| empty
|
||||
;
|
||||
|
||||
define_attribute_body
|
||||
: empty
|
||||
| '=' attribute_comma_list opt_comma
|
||||
;
|
||||
|
||||
define_attribute
|
||||
: AT_TYPE_IDENT '(' parameters ')' opt_attributes '=' '{' opt_attributes '}'
|
||||
| AT_TYPE_IDENT opt_attributes '=' '{' opt_attributes '}'
|
||||
: AT_TYPE_IDENT '(' parameters ')' opt_attributes define_attribute_body
|
||||
| AT_TYPE_IDENT opt_attributes define_attribute_body
|
||||
;
|
||||
|
||||
generic_expr
|
||||
@@ -1203,15 +1215,15 @@ opt_generic_parameters
|
||||
;
|
||||
|
||||
define_ident
|
||||
: IDENT '=' path_ident opt_generic_parameters
|
||||
| CONST_IDENT '=' path_const opt_generic_parameters
|
||||
| AT_IDENT '=' path_at_ident opt_generic_parameters
|
||||
: IDENT opt_attributes '=' path_ident opt_generic_parameters
|
||||
| CONST_IDENT opt_attributes '=' path_const opt_generic_parameters
|
||||
| AT_IDENT opt_attributes '=' path_at_ident opt_generic_parameters
|
||||
;
|
||||
|
||||
define_declaration
|
||||
: DEF define_ident opt_attributes ';'
|
||||
| DEF define_attribute opt_attributes ';'
|
||||
| DEF TYPE_IDENT opt_attributes '=' typedef_type opt_attributes ';'
|
||||
: ALIAS define_ident ';'
|
||||
| ATTRDEF define_attribute ';'
|
||||
| ALIAS TYPE_IDENT opt_attributes '=' typedef_type opt_attributes ';'
|
||||
;
|
||||
|
||||
interface_body
|
||||
@@ -1235,7 +1247,7 @@ interface_declaration_name
|
||||
;
|
||||
|
||||
distinct_declaration
|
||||
: DISTINCT TYPE_IDENT opt_interface_impl opt_attributes '=' opt_inline type ';'
|
||||
: TYPEDEF TYPE_IDENT opt_interface_impl opt_attributes '=' opt_inline type ';'
|
||||
;
|
||||
|
||||
module_param
|
||||
|
||||
246
resources/project_schema.json
Normal file
246
resources/project_schema.json
Normal file
@@ -0,0 +1,246 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"title": "Project Configuration Schema for C3 project.json",
|
||||
"description": "Schema for project.json configuration file based on C3 documentation",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"langrev": {
|
||||
"type": "string",
|
||||
"description": "Language version of C3.",
|
||||
"default": "1"
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"description": "Warnings used for all targets.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"dependency-search-paths": {
|
||||
"type": "array",
|
||||
"description": "Directories where C3 library files may be found.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"type": "array",
|
||||
"description": "Libraries to use for all targets.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"authors": {
|
||||
"type": "array",
|
||||
"description": "Authors, optionally with email.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Version using semantic versioning."
|
||||
},
|
||||
"sources": {
|
||||
"type": "array",
|
||||
"description": "Sources compiled for all targets.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"c-sources": {
|
||||
"type": "array",
|
||||
"description": "C sources if the project also compiles C sources relative to the project file.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"c-include-dirs": {
|
||||
"type": "array",
|
||||
"description": "Include directories for C sources relative to the project file.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"type": "string",
|
||||
"description": "Output location, relative to project file."
|
||||
},
|
||||
"targets": {
|
||||
"type": "object",
|
||||
"description": "Architecture and OS target specific configurations.",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Executable or library type for the target.",
|
||||
"enum": ["executable", "static-lib", "dynamic-lib"]
|
||||
},
|
||||
"dependencies": {
|
||||
"type": "array",
|
||||
"description": "Additional libraries for this target.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"sources": {
|
||||
"type": "array",
|
||||
"description": "Additional sources for this target.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"warnings": {
|
||||
"type": "array",
|
||||
"description": "Warnings specific to this target.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["type"]
|
||||
}
|
||||
},
|
||||
"cc": {
|
||||
"type": "string",
|
||||
"description": "C compiler to use for compiling C sources (if C sources are compiled together with C3 files).",
|
||||
"default": "cc"
|
||||
},
|
||||
"cpu": {
|
||||
"type": "string",
|
||||
"description": "CPU name, used for optimizations in the LLVM backend.",
|
||||
"default": "generic"
|
||||
},
|
||||
"debug-info": {
|
||||
"type": "string",
|
||||
"description": "Debug information level.",
|
||||
"enum": ["none", "full", "line-tables"],
|
||||
"default": "full"
|
||||
},
|
||||
"fp-math": {
|
||||
"type": "string",
|
||||
"description": "FP math behaviour.",
|
||||
"enum": ["strict", "relaxed", "fast"],
|
||||
"default": "strict"
|
||||
},
|
||||
"link-libc": {
|
||||
"type": "boolean",
|
||||
"description": "Link libc other default libraries.",
|
||||
"default": true
|
||||
},
|
||||
"memory-env": {
|
||||
"type": "string",
|
||||
"description": "Memory environment.",
|
||||
"enum": ["normal", "small", "tiny", "none"],
|
||||
"default": "normal"
|
||||
},
|
||||
"opt": {
|
||||
"type": "string",
|
||||
"description": "Optimization setting.",
|
||||
"enum": ["O0", "O1", "O2", "O3", "O4", "O5", "Os", "Oz"],
|
||||
"default": "O0"
|
||||
},
|
||||
"optlevel": {
|
||||
"type": "string",
|
||||
"description": "Code optimization level.",
|
||||
"enum": ["none", "less", "more", "max"],
|
||||
"default": "none"
|
||||
},
|
||||
"optsize": {
|
||||
"type": "string",
|
||||
"description": "Code size optimization.",
|
||||
"enum": ["none", "small", "tiny"],
|
||||
"default": "none"
|
||||
},
|
||||
"reloc": {
|
||||
"type": "string",
|
||||
"description": "Relocation model.",
|
||||
"enum": ["none", "pic", "PIC", "pie", "PIE"],
|
||||
"default": "none"
|
||||
},
|
||||
"trap-on-wrap": {
|
||||
"type": "boolean",
|
||||
"description": "Trap on signed and unsigned integer wrapping for testing.",
|
||||
"default": false
|
||||
},
|
||||
"safe": {
|
||||
"type": "boolean",
|
||||
"description": "Turn safety (contracts, runtime bounds checking, null pointer checks etc).",
|
||||
"default": true
|
||||
},
|
||||
"single-module": {
|
||||
"type": "boolean",
|
||||
"description": "Compile all modules together, enables more inlining.",
|
||||
"default": true
|
||||
},
|
||||
"soft-float": {
|
||||
"type": "boolean",
|
||||
"description": "Use / don't use soft float, value is otherwise target default.",
|
||||
"default": false
|
||||
},
|
||||
"strip-unused": {
|
||||
"type": "boolean",
|
||||
"description": "Strip unused code and globals from the output.",
|
||||
"default": true
|
||||
},
|
||||
"symtab": {
|
||||
"type": "integer",
|
||||
"description": "The size of the symtab, which limits the amount of symbols that can be used.",
|
||||
"default": 1048576
|
||||
},
|
||||
"linker": {
|
||||
"type": "string",
|
||||
"description": "Use the system linker.",
|
||||
"default": "cc"
|
||||
},
|
||||
"use-stdlib": {
|
||||
"type": "boolean",
|
||||
"description": "Include the standard library.",
|
||||
"default": true
|
||||
},
|
||||
"x86cpu": {
|
||||
"type": "string",
|
||||
"description": "Set general level of x64 cpu.",
|
||||
"enum": [
|
||||
"baseline",
|
||||
"ssse3",
|
||||
"sse4",
|
||||
"avx1",
|
||||
"avx2-v1",
|
||||
"avx2-v2",
|
||||
"avx512",
|
||||
"native"
|
||||
],
|
||||
"default": "native"
|
||||
},
|
||||
"x86vec": {
|
||||
"type": "string",
|
||||
"description": "Set max type of vector use.",
|
||||
"enum": ["none", "mmx", "sse", "avx", "avx512", "native"],
|
||||
"default": "sse"
|
||||
},
|
||||
"features": {
|
||||
"type": "array",
|
||||
"description": "List of upper-case constants that can be tested for in the source code using $feature(NAME_OF_FEATURE).",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"linker-search-paths": {
|
||||
"type": "array",
|
||||
"description": "This adds paths for the linker to search, when linking normal C libraries.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"linked-libraries": {
|
||||
"type": "array",
|
||||
"description": "This is a list of C libraries to link to.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
@@ -76,7 +76,7 @@ fn void main()
|
||||
testAllocator(tmem, 126);
|
||||
testAllocator(tmem, 12600);
|
||||
ArenaAllocator aa;
|
||||
aa.init(&&char[1024] {});
|
||||
aa.init(&&(char[1024]){});
|
||||
testAllocator(&aa, 126);
|
||||
io::printn("Test dynamic arena");
|
||||
DynamicArenaAllocator dynamic_arena;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user