mirror of
https://github.com/jart/cosmopolitan.git
synced 2026-11-07 17:22:16 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aefad27c09 | ||
|
|
b0cebefd8e | ||
|
|
34fa4edae8 | ||
|
|
c7c9916eb8 | ||
|
|
88848afdff | ||
|
|
e0c68ba56b | ||
|
|
bc6f307aa5 | ||
|
|
7bc687dbc4 | ||
|
|
1bc2a45f4a | ||
|
|
6ad44a10c2 | ||
|
|
9607bee4b2 | ||
|
|
2143d61dfa | ||
|
|
fdea2482dd | ||
|
|
c7d64e1b8b | ||
|
|
977d10172c | ||
|
|
3293fad0a9 | ||
|
|
036845632f | ||
|
|
4a47191f51 | ||
|
|
a831042eca | ||
|
|
b4b5af498f | ||
|
|
18e3a2e7ce | ||
|
|
bc37215177 | ||
|
|
7e35bc854f | ||
|
|
6eb8da23d4 | ||
|
|
0b80275299 | ||
|
|
ee883eabb3 | ||
|
|
7666e63ef9 | ||
|
|
60734c784a | ||
|
|
deb866efd5 | ||
|
|
ad16ba5460 | ||
|
|
db041429ed | ||
|
|
eedf7d2db6 | ||
|
|
9ba2b50805 | ||
|
|
7ecf16e306 | ||
|
|
db617bfd58 | ||
|
|
9b1285b915 | ||
|
|
b444b3a6e0 | ||
|
|
b0fdf97054 | ||
|
|
2f786453ee | ||
|
|
694ef1aa8b | ||
|
|
bc0fc702fe | ||
|
|
bea62f3631 | ||
|
|
dee25272f1 | ||
|
|
5ddb5c2ada | ||
|
|
f1dc5cff44 | ||
|
|
f1e83d5240 | ||
|
|
2fe8338f92 | ||
|
|
4ca513cba2 | ||
|
|
455910e8f2 | ||
|
|
9c68bc19b5 | ||
|
|
66d1050af6 | ||
|
|
fbc4fcbb71 | ||
|
|
afc986f741 | ||
|
|
5eb7cd6643 | ||
|
|
a8ed4fdd09 | ||
|
|
7b69652854 | ||
|
|
b235492e71 | ||
|
|
fc81fd8d16 | ||
|
|
38930de8e0 | ||
|
|
0e557d041d | ||
|
|
1d676b36e6 | ||
|
|
10a92cee94 | ||
|
|
42a9ed0131 | ||
|
|
12cb0669fb | ||
|
|
7f6a7d6fff | ||
|
|
9f6bf6ea71 | ||
|
|
102edf4ea2 | ||
|
|
21968acf99 | ||
|
|
98861b23fc | ||
|
|
dab6d7a345 | ||
|
|
90119c422c | ||
|
|
5907304049 | ||
|
|
035b0e2a62 | ||
|
|
7b67b20dae | ||
|
|
f0b0f926bf | ||
|
|
29eb7e67bb | ||
|
|
f71f61cd40 | ||
|
|
53c6edfd18 | ||
|
|
42a3bb729a | ||
|
|
c97a858470 | ||
|
|
4acd12a514 | ||
|
|
b734eec836 | ||
|
|
fe01642a20 | ||
|
|
e939659b70 | ||
|
|
ed6d133a27 | ||
|
|
97fc2aab41 | ||
|
|
662e7b217f | ||
|
|
27f2777cc6 | ||
|
|
538ce338f4 | ||
|
|
a15958edc6 | ||
|
|
8db646f6b2 | ||
|
|
fde03f8487 | ||
|
|
f24c854b28 | ||
|
|
0b3c81dd4e | ||
|
|
98c5847727 | ||
|
|
fd7da586b5 | ||
|
|
a51ccc8fb1 | ||
|
|
c7e3d9f7ff | ||
|
|
9ba5b227d9 | ||
|
|
aca4214ff6 | ||
|
|
379cd77078 | ||
|
|
36e5861b0c | ||
|
|
cc8a9eb93c | ||
|
|
0158579493 | ||
|
|
2de3845b25 | ||
|
|
93e22c581f | ||
|
|
ec2db4e40e | ||
|
|
55b7aa1632 | ||
|
|
4705705548 | ||
|
|
c8e10eef30 | ||
|
|
624573207e | ||
|
|
906bd06a5a | ||
|
|
c8c81af0c7 | ||
|
|
af7bd80430 | ||
|
|
26c051c297 | ||
|
|
9cc1bd04b2 | ||
|
|
69402f4d78 | ||
|
|
838b54f906 | ||
|
|
2d43d400c6 | ||
|
|
c22b413ac4 | ||
|
|
22094ae9ca | ||
|
|
bda2a4d55e | ||
|
|
b490e23d63 | ||
|
|
b40140e6c5 | ||
|
|
3142758675 | ||
|
|
cf9252f429 | ||
|
|
5fae582e82 | ||
|
|
ef00a7d0c2 | ||
|
|
746660066f | ||
|
|
fd15b2d7a3 | ||
|
|
e228aa3e14 | ||
|
|
9ddbfd921e | ||
|
|
729f7045e3 | ||
|
|
e47d67ba9b | ||
|
|
2477677c85 | ||
|
|
abdf6c9c26 | ||
|
|
5c3f854acb | ||
|
|
ad0a7c67c4 | ||
|
|
1312f60245 | ||
|
|
cafdb456ed | ||
|
|
4e9566cd33 | ||
|
|
5ce5fb6f2a | ||
|
|
d3279d3c0d | ||
|
|
e62ff3e19c | ||
|
|
913b573661 | ||
|
|
9add248c9b | ||
|
|
beb090b83f | ||
|
|
107d335c0d | ||
|
|
bd6630d62d | ||
|
|
a120bc7149 | ||
|
|
baad1df71d | ||
|
|
4e44517c9c | ||
|
|
26663dea9c | ||
|
|
23da0d75a5 | ||
|
|
4b2a00fd4a | ||
|
|
2f4e6e8d77 | ||
|
|
dd249ff5d4 | ||
|
|
4abcba8d8f | ||
|
|
dc1afc968b | ||
|
|
5edc0819c0 | ||
|
|
706cb66310 | ||
|
|
a8bc7ac119 | ||
|
|
d8fac40f55 | ||
|
|
000d6dbb0f | ||
|
|
17a85e4790 | ||
|
|
ad11fc32ad | ||
|
|
dcf9596620 | ||
|
|
85c58be942 | ||
|
|
e4d6eb382a | ||
|
|
fef24d622a | ||
|
|
12cc2de22e | ||
|
|
70603fa6ea | ||
|
|
9255113011 | ||
|
|
333c3d1f0a | ||
|
|
518eabadf5 | ||
|
|
556a294363 | ||
|
|
80804ccfff | ||
|
|
4a7dd31567 | ||
|
|
d730fc668c | ||
|
|
e975245102 | ||
|
|
126a44dc49 | ||
|
|
476926790a | ||
|
|
dd8c4dbd7d | ||
|
|
0e59afb403 | ||
|
|
f68fc1f815 | ||
|
|
6107eb38f9 | ||
|
|
d50f4c02f6 | ||
|
|
0d74673213 | ||
|
|
8527462b95 | ||
|
|
8313dca982 | ||
|
|
87a6669900 | ||
|
|
ce2fbf9325 | ||
|
|
1bfb348403 | ||
|
|
aaed879ec7 | ||
|
|
8201ef2b3d | ||
|
|
b1c9801897 | ||
|
|
f7754ab608 | ||
|
|
96abe91c29 | ||
|
|
bb7942e557 | ||
|
|
b14dddcc18 | ||
|
|
65e425fbca | ||
|
|
774c67fcd3 | ||
|
|
3c58ecd00c | ||
|
|
5aa970bc4e | ||
|
|
56ca00b022 | ||
|
|
ecbf453464 | ||
|
|
c3482af66d | ||
|
|
b73673e984 | ||
|
|
e260d90096 | ||
|
|
81bc8d0963 | ||
|
|
ef62730ae4 | ||
|
|
6397999fca | ||
|
|
b55e4d61a9 | ||
|
|
e65fe614b7 | ||
|
|
949c398327 | ||
|
|
baf70af780 | ||
|
|
c260144843 | ||
|
|
37e2660c7f | ||
|
|
675abfa029 | ||
|
|
e3d28de8a6 | ||
|
|
7f21547122 | ||
|
|
19563d37c1 | ||
|
|
ed1f992cb7 | ||
|
|
7d2c363963 | ||
|
|
462ba6909e | ||
|
|
b5fcb59a85 | ||
|
|
6b10f4d0b6 | ||
|
|
1260f9d0ed | ||
|
|
e142124730 | ||
|
|
5469202ea8 | ||
|
|
acd6c32184 | ||
|
|
6f868fe1de | ||
|
|
0f3457c172 | ||
|
|
a5c0189bf6 | ||
|
|
deb5e07b5a | ||
|
|
4d05060aac | ||
|
|
51c0f44d1c | ||
|
|
fbdf9d028c | ||
|
|
cceddd21b2 | ||
|
|
a0a404a431 | ||
|
|
2f48a02b44 | ||
|
|
58d252f3db | ||
|
|
95fee8614d | ||
|
|
d50d954a3c | ||
|
|
d99f066114 | ||
|
|
4754f200ee | ||
|
|
f882887178 | ||
|
|
dc579b79cd | ||
|
|
c66abd7260 | ||
|
|
d1157d471f | ||
|
|
5d3b91d8b9 | ||
|
|
07fde68d52 | ||
|
|
41fc76c2b8 | ||
|
|
1e9902af8b | ||
|
|
df04ab846a | ||
|
|
0d6ff04b87 | ||
|
|
03875beadb | ||
|
|
dd8544c3bd | ||
|
|
8f8145105c | ||
|
|
3c61a541bd | ||
|
|
79516bf08e | ||
|
|
90460ceb3c | ||
|
|
2ec413b5a9 | ||
|
|
39e7f24947 | ||
|
|
a089c07ddc | ||
|
|
ae57fa2c4e | ||
|
|
48b703b3f6 | ||
|
|
389d565d46 | ||
|
|
75e161b27b | ||
|
|
cca0edd62b | ||
|
|
7c83f4abc8 | ||
|
|
e1528a71e2 | ||
|
|
a6fe62cf13 | ||
|
|
c9152b6f14 | ||
|
|
5b9862907c | ||
|
|
c2420860e6 | ||
|
|
6baf6cdb10 | ||
|
|
06a1193b4d | ||
|
|
884d89235f | ||
|
|
610c951f71 | ||
|
|
12ecaf8650 | ||
|
|
185e957696 | ||
|
|
ebe1cbb1e3 | ||
|
|
e7b586e7f8 | ||
|
|
111ec9a989 | ||
|
|
f3ce684aef | ||
|
|
908b7a82ca | ||
|
|
bb06230f1e | ||
|
|
38cc4b3c68 | ||
|
|
37ca1badaf | ||
|
|
1a9f82bc9f | ||
|
|
df1aee7ce5 | ||
|
|
2d44142444 | ||
|
|
4bbc16e2cc | ||
|
|
863c704684 | ||
|
|
60e697f7b2 | ||
|
|
4389f4709a | ||
|
|
ca2c30c977 | ||
|
|
eb6e96f036 | ||
|
|
77be460290 | ||
|
|
8e14b27749 | ||
|
|
1d532ba3f8 | ||
|
|
b2a1811c01 | ||
|
|
2eda50929b | ||
|
|
098638cc6c | ||
|
|
732554ce3a | ||
|
|
914d521090 | ||
|
|
11d9fb521d | ||
|
|
de0cde8def | ||
|
|
5bd22aef12 | ||
|
|
1671283f1a | ||
|
|
0a79c6961f | ||
|
|
3fd275f59f | ||
|
|
2045e87b7c | ||
|
|
24666e121d | ||
|
|
ff1a0d1c40 | ||
|
|
7f0db3e3b9 | ||
|
|
31194165d2 | ||
|
|
c265c17d54 | ||
|
|
7499367060 | ||
|
|
3f26dfbb31 | ||
|
|
761c6ad615 | ||
|
|
a80ab3f8fe | ||
|
|
9ebacb7892 | ||
|
|
6ac3d3b804 | ||
|
|
f8cfc89eba | ||
|
|
4ed4a1095a | ||
|
|
8d8aecb6d9 | ||
|
|
1fba310e22 | ||
|
|
bb815eafaf | ||
|
|
d0360bf4bd | ||
|
|
8cdb3e136b | ||
|
|
3dab207351 | ||
|
|
1092d9b7e8 | ||
|
|
d40acc60b1 | ||
|
|
cf1559c448 | ||
|
|
01b09bc817 | ||
|
|
c1a0b017e9 | ||
|
|
77d3a07ff2 | ||
|
|
0d7c272d3f | ||
|
|
18964e5d76 | ||
|
|
e18fe1e112 | ||
|
|
8621034d42 | ||
|
|
32292aee84 | ||
|
|
81dd9639ed | ||
|
|
f147d3dde9 | ||
|
|
fb54604b31 | ||
|
|
cdfcee51ca | ||
|
|
18a620cc1a | ||
|
|
690d3df66e | ||
|
|
efb3a34608 | ||
|
|
642e9cb91a | ||
|
|
59692b0882 | ||
|
|
02e1cbcd00 | ||
|
|
0679cfeb41 | ||
|
|
edd5e2c8e3 | ||
|
|
c8e25d811c | ||
|
|
a31d5ea399 | ||
|
|
7d88343973 | ||
|
|
3915ca0f71 | ||
|
|
2c4b88753b | ||
|
|
0f486a13c8 | ||
|
|
1020dd41cc | ||
|
|
d3a13e8d70 | ||
|
|
7ba9a73840 | ||
|
|
4a1ae86124 | ||
|
|
5dd7ddb9ea | ||
|
|
f25fbbaaeb | ||
|
|
fbc4b03d4c | ||
|
|
e398f3887c | ||
|
|
2187d6d2dd | ||
|
|
0602ff6bab | ||
|
|
5660ec4741 | ||
|
|
62ace3623a | ||
|
|
6e809ee49b | ||
|
|
61c36c1dd6 | ||
|
|
0a9a6f86bb | ||
|
|
3de6632be6 | ||
|
|
c83ec5fdd9 | ||
|
|
23611cd854 | ||
|
|
62a97c919f | ||
|
|
16d244614e | ||
|
|
41cc053419 | ||
|
|
5d2d9e9640 |
No files matched your search
@@ -27,3 +27,5 @@ da8baf2aa5ce93b958aca90a0ae69f537806324b
|
||||
369f9740de4534c28d0e81ab2afc99decbb9a3e6
|
||||
# Get rid of .internal.h convention in LIBC_INTRIN
|
||||
86d884cce24d773e298a2714c1e3d91ecab9be45
|
||||
# Remove .internal from more header filenames
|
||||
31194165d2afca36c2315a6e7ca2f0797dde09e3
|
||||
+7
-1
@@ -1,4 +1,10 @@
|
||||
# -*- conf -*-
|
||||
*.gz binary
|
||||
/build/bootstrap/*.com binary
|
||||
*.so binary
|
||||
*.dll binary
|
||||
*.dylib binary
|
||||
/build/bootstrap/* binary
|
||||
/usr/share/terminfo/* binary
|
||||
/usr/share/terminfo/*/* binary
|
||||
/usr/share/zoneinfo/* binary
|
||||
/usr/share/zoneinfo/*/* binary
|
||||
@@ -1,5 +1,8 @@
|
||||
name: build
|
||||
|
||||
env:
|
||||
COSMOCC_VERSION: 3.9.2
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -17,15 +20,50 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
mode: ["", tiny, rel, tinylinux, optlinux]
|
||||
mode: ["", tiny, rel, tinylinux]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# Full checkout needed for git-restore-mtime-bare.
|
||||
fetch-depth: 0
|
||||
|
||||
# TODO(jart): fork this action.
|
||||
- uses: chetan/git-restore-mtime-action@v2
|
||||
|
||||
- uses: actions/cache/restore@v4
|
||||
id: cache
|
||||
with:
|
||||
path: |
|
||||
.cosmocc
|
||||
o
|
||||
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-
|
||||
${{ env.COSMOCC_VERSION }}-
|
||||
|
||||
- name: Restore mtimes
|
||||
if: steps.cache.outputs.cache-hit == 'true'
|
||||
run: |
|
||||
while read mtime file; do
|
||||
[ -f "$file" ] && touch -d "@$mtime" "$file"
|
||||
done < o/.mtimes
|
||||
|
||||
- name: support ape bins 1
|
||||
run: sudo cp build/bootstrap/ape.elf /usr/bin/ape
|
||||
run: sudo cp -a build/bootstrap/ape.elf /usr/bin/ape
|
||||
|
||||
- name: support ape bins 2
|
||||
run: sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
|
||||
|
||||
- name: make matrix
|
||||
run: V=0 make -j2 MODE=${{ matrix.mode }}
|
||||
|
||||
- name: Save mtimes
|
||||
run: |
|
||||
find o -type f -exec stat -c "%Y %n" {} \; > o/.mtimes
|
||||
|
||||
- uses: actions/cache/save@v4
|
||||
with:
|
||||
path: |
|
||||
.cosmocc
|
||||
o
|
||||
key: ${{ env.COSMOCC_VERSION }}-${{ matrix.mode }}-${{ github.sha }}
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Nightly cosmocc
|
||||
on:
|
||||
schedule:
|
||||
# https://crontab.guru/#37_4_*_*_*
|
||||
- cron: "37 4 * * *"
|
||||
workflow_dispatch:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
build-cosmocc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: |
|
||||
sudo cp build/bootstrap/ape.elf /usr/bin/ape
|
||||
sudo sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register"
|
||||
- run: tool/cosmocc/package.sh
|
||||
# https://github.com/actions/upload-artifact/issues/590
|
||||
- uses: actions/[email protected]
|
||||
with:
|
||||
name: cosmocc
|
||||
path: cosmocc
|
||||
compression-level: 9
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- conf -*-
|
||||
|
||||
/o
|
||||
/core
|
||||
/cosmocc
|
||||
/.cosmocc
|
||||
|
||||
@@ -15,3 +16,4 @@ __pycache__
|
||||
/tool/emacs/*.elc
|
||||
/perf.data
|
||||
/perf.data.old
|
||||
/qemu*core
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Cosmopolitan",
|
||||
"includePath": [
|
||||
"${workspaceFolder}",
|
||||
"${workspaceFolder}/libc/isystem"
|
||||
],
|
||||
"defines": [
|
||||
"_COSMO_SOURCE",
|
||||
"MODE=\"aarch64\""
|
||||
],
|
||||
"compilerPath": "${workspaceFolder}/.cosmocc/3.9.2/bin/aarch64-linux-cosmo-gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++17",
|
||||
"intelliSenseMode": "gcc-arm64",
|
||||
"compilerArgs": [
|
||||
"-nostdinc",
|
||||
"-iquote",
|
||||
"${workspaceFolder}",
|
||||
"-isystem",
|
||||
"${workspaceFolder}/libc/isystem",
|
||||
"-include",
|
||||
"libc/integral/normalize.inc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"[c]": {
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true
|
||||
},
|
||||
"[cpp]": {
|
||||
"editor.tabSize": 2,
|
||||
"editor.insertSpaces": true
|
||||
},
|
||||
"[makefile]": {
|
||||
"editor.tabSize": 8,
|
||||
"editor.insertSpaces": false
|
||||
},
|
||||
"[make]": {
|
||||
"editor.tabSize": 8,
|
||||
"editor.insertSpaces": false
|
||||
},
|
||||
"[assembly]": {
|
||||
"editor.tabSize": 8,
|
||||
"editor.insertSpaces": true
|
||||
},
|
||||
"files.associations": {
|
||||
"log.h": "c",
|
||||
"*.inc": "c"
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,8 @@ COMMA := ,
|
||||
PWD := $(shell pwd)
|
||||
|
||||
# detect wsl2 running cosmopolitan binaries on the host by checking whether:
|
||||
# - user ran build/bootstrap/make, in which case make's working directory is in wsl
|
||||
# - user ran .cosmocc/current/bin/make, in which case make's working directory
|
||||
# is in wsl
|
||||
# - user ran make, in which case cocmd's working directory is in wsl
|
||||
ifneq ($(findstring //wsl.localhost/,$(CURDIR) $(PWD)),)
|
||||
$(warning wsl2 interop is enabled)
|
||||
@@ -89,7 +90,7 @@ UNAME_S := $(shell uname -s)
|
||||
|
||||
# apple still distributes a 17 year old version of gnu make
|
||||
ifeq ($(MAKE_VERSION), 3.81)
|
||||
$(error please use build/bootstrap/make)
|
||||
$(error please use https://cosmo.zip/pub/cosmos/bin/make)
|
||||
endif
|
||||
|
||||
LC_ALL = C
|
||||
@@ -109,16 +110,15 @@ ECHO = $(BOOTSTRAP)/echo.ape
|
||||
CHMOD = $(BOOTSTRAP)/chmod.ape
|
||||
TOUCH = $(BOOTSTRAP)/touch.ape
|
||||
PKG = $(BOOTSTRAP)/package.ape
|
||||
MKDEPS = $(BOOTSTRAP)/mkdeps
|
||||
MKDEPS = build/bootstrap/mkdeps
|
||||
ZIPOBJ = $(BOOTSTRAP)/zipobj
|
||||
ZIPCOPY = $(BOOTSTRAP)/zipcopy
|
||||
ELF2PE = build/bootstrap/elf2pe
|
||||
PECHECK = $(BOOTSTRAP)/pecheck
|
||||
FIXUPOBJ = $(BOOTSTRAP)/fixupobj
|
||||
FIXUPOBJ = build/bootstrap/fixupobj
|
||||
OBJBINCOPY = $(BOOTSTRAP)/objbincopy
|
||||
MKDIR = build/bootstrap/mkdir -p
|
||||
COMPILE = build/bootstrap/compile -V9 -M2048m -P8192 $(QUOTA)
|
||||
|
||||
IGNORE := $(shell $(MKDIR) $(TMPDIR))
|
||||
MKDIR = $(BOOTSTRAP)/mkdir.ape -p
|
||||
COMPILE = $(BOOTSTRAP)/compile.ape -V9 -M2048m -P8192 $(QUOTA)
|
||||
|
||||
# the default build modes is empty string
|
||||
# on x86_64 hosts, MODE= is the same as MODE=x86_64
|
||||
@@ -134,14 +134,13 @@ endif
|
||||
|
||||
ifneq ($(findstring aarch64,$(MODE)),)
|
||||
ARCH = aarch64
|
||||
HOSTS ?= pi studio freebsdarm
|
||||
HOSTS ?= pi pi5 studio freebsdarm
|
||||
else
|
||||
ARCH = x86_64
|
||||
HOSTS ?= freebsd rhel7 xnu openbsd netbsd win10
|
||||
HOSTS ?= freebsd xnu win10 luna netbsd
|
||||
endif
|
||||
|
||||
ZIPOBJ_FLAGS += -a$(ARCH)
|
||||
IGNORE := $(shell $(MKDIR) $(TMPDIR))
|
||||
|
||||
export ADDR2LINE
|
||||
export LC_ALL
|
||||
@@ -150,14 +149,20 @@ export MODE
|
||||
export SOURCE_DATE_EPOCH
|
||||
export TMPDIR
|
||||
|
||||
COSMOCC = .cosmocc/3.5.7
|
||||
COSMOCC = .cosmocc/3.9.2
|
||||
BOOTSTRAP = $(COSMOCC)/bin
|
||||
TOOLCHAIN = $(COSMOCC)/bin/$(ARCH)-linux-cosmo-
|
||||
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.5.7 596876951b62ad2530c63afc40edd805d751fcb2416e544d249af04ad00bb4ed)
|
||||
DOWNLOAD := $(shell build/download-cosmocc.sh $(COSMOCC) 3.9.2 f4ff13af65fcd309f3f1cfd04275996fb7f72a4897726628a8c9cf732e850193)
|
||||
|
||||
IGNORE := $(shell $(MKDIR) $(TMPDIR))
|
||||
|
||||
ifeq ($(ARCH), x86_64)
|
||||
TLSCC = build/bootstrap/tlscc
|
||||
endif
|
||||
|
||||
AS = $(TOOLCHAIN)as
|
||||
CC = $(TOOLCHAIN)gcc
|
||||
CXX = $(TOOLCHAIN)g++
|
||||
CC = $(TLSCC) $(TOOLCHAIN)gcc
|
||||
CXX = $(TLSCC) $(TOOLCHAIN)g++
|
||||
CXXFILT = $(TOOLCHAIN)c++filt
|
||||
LD = $(TOOLCHAIN)ld.bfd
|
||||
NM = $(TOOLCHAIN)nm
|
||||
@@ -190,39 +195,42 @@ o/$(MODE): \
|
||||
o/$(MODE)/: o/$(MODE)
|
||||
o/$(MODE)/.: o/$(MODE)
|
||||
|
||||
# check if we're using o//third_party/make/make
|
||||
# we added sandboxing to guarantee cosmo's makefile is hermetic
|
||||
# it also shaves away 200ms of startup latency with native $(uniq)
|
||||
ifneq ($(LANDLOCKMAKE_VERSION),)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
ifeq ($(wildcard /usr/bin/ape),)
|
||||
$(warning please run ape/apeinstall.sh if you intend to use landlock make)
|
||||
$(shell sleep .5)
|
||||
endif
|
||||
endif
|
||||
ifneq ($(TOOLCHAIN),)
|
||||
.STRICT = 1
|
||||
endif
|
||||
endif
|
||||
|
||||
.PLEDGE += stdio rpath wpath cpath fattr proc
|
||||
.UNVEIL += \
|
||||
libc/integral \
|
||||
libc/stdbool.h \
|
||||
rwc:/dev/shm \
|
||||
# If you're using Cosmopolitan Make as your `make` command (you can
|
||||
# build it by saying `make o//third_party/make/make`) then these
|
||||
# variables will activate its pledge() and unveil() sandboxing
|
||||
# functionality when running on Linux. When cross-compiling, it's
|
||||
# important that your qemu binaries be statically compiled. See
|
||||
# third_party/qemu/ for our prebuilt qemu-aarch64 binary. Yes
|
||||
# binfmt_misc has to be configured to use it on foreign bins.
|
||||
.SANDBOXED = 1
|
||||
.PLEDGE := stdio rpath wpath cpath fattr proc
|
||||
.UNVEIL := \
|
||||
rwcx:o \
|
||||
rwcx:/tmp \
|
||||
rwcx:$(TMPDIR) \
|
||||
rx:/usr/bin/ape \
|
||||
rx:.cosmocc \
|
||||
rx:build/bootstrap \
|
||||
r:build/portcosmo.h \
|
||||
libc/integral \
|
||||
rwc:/dev/shm \
|
||||
/proc/stat \
|
||||
rw:/dev/null \
|
||||
rw:/dev/full \
|
||||
w:o/stack.log \
|
||||
/etc/hosts \
|
||||
/etc/ape.trust \
|
||||
/etc/ape.revoke \
|
||||
/etc/ssh/ssh_host_ed25519_key.pub \
|
||||
~/.runit.psk \
|
||||
~/.ssh/authorized_keys \
|
||||
~/.ssh/id_ed25519.pub \
|
||||
~/.ape.trust \
|
||||
~/.ape.revoke \
|
||||
/proc/self/status \
|
||||
rx:/usr/bin/qemu-aarch64 \
|
||||
rx:o/third_party/qemu/qemu-aarch64 \
|
||||
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
|
||||
rx:/usr/bin/qemu-x86_64 \
|
||||
rx:/usr/libexec/qemu-binfmt/aarch64-binfmt-P
|
||||
endif
|
||||
|
||||
PKGS =
|
||||
|
||||
@@ -257,8 +265,10 @@ include third_party/gdtoa/BUILD.mk # ├──DYNAMIC RUNTIME
|
||||
include third_party/nsync/mem/BUILD.mk # │ You can now use stdio
|
||||
include libc/proc/BUILD.mk # │ You can now use threads
|
||||
include libc/dlopen/BUILD.mk # │ You can now use processes
|
||||
include libc/thread/BUILD.mk # │ You can finally call malloc()
|
||||
include third_party/libunwind/BUILD.mk # | You can finally call malloc()
|
||||
include libc/thread/BUILD.mk # │
|
||||
include third_party/zlib/BUILD.mk # │
|
||||
include third_party/haclstar/BUILD.mk # │
|
||||
include libc/stdio/BUILD.mk # │
|
||||
include tool/hello/BUILD.mk # │
|
||||
include third_party/tz/BUILD.mk # │
|
||||
@@ -275,33 +285,39 @@ include libc/BUILD.mk #─┘
|
||||
include libc/sock/BUILD.mk #─┐
|
||||
include net/http/BUILD.mk # ├──ONLINE RUNTIME
|
||||
include third_party/musl/BUILD.mk # │ You can communicate with the network
|
||||
include third_party/regex/BUILD.mk # │
|
||||
include third_party/tr/BUILD.mk # │
|
||||
include third_party/sed/BUILD.mk # │
|
||||
include libc/system/BUILD.mk # │
|
||||
include libc/x/BUILD.mk # │
|
||||
include dsp/scale/BUILD.mk # │
|
||||
include dsp/mpeg/BUILD.mk # │
|
||||
include dsp/tty/BUILD.mk # │
|
||||
include dsp/audio/BUILD.mk # │
|
||||
include dsp/BUILD.mk # │
|
||||
include third_party/stb/BUILD.mk # │
|
||||
include third_party/mbedtls/BUILD.mk # │
|
||||
include third_party/ncurses/BUILD.mk # │
|
||||
include third_party/readline/BUILD.mk # │
|
||||
include third_party/libunwind/BUILD.mk # |
|
||||
include third_party/libcxxabi/BUILD.mk # |
|
||||
include third_party/miniaudio/BUILD.mk # |
|
||||
include third_party/double-conversion/BUILD.mk # │
|
||||
include dsp/prog/BUILD.mk # │
|
||||
include ctl/BUILD.mk # │
|
||||
include third_party/libcxx/BUILD.mk # │
|
||||
include third_party/openmp/BUILD.mk # │
|
||||
include third_party/pcre/BUILD.mk # │
|
||||
include third_party/less/BUILD.mk # │
|
||||
include net/https/BUILD.mk # │
|
||||
include third_party/regex/BUILD.mk # │
|
||||
include third_party/bash/BUILD.mk #─┘
|
||||
include net/https/BUILD.mk #─┘
|
||||
include third_party/tidy/BUILD.mk
|
||||
include third_party/BUILD.mk
|
||||
include third_party/nsync/testing/BUILD.mk
|
||||
include libc/testlib/BUILD.mk
|
||||
include tool/viz/lib/BUILD.mk
|
||||
include tool/args/BUILD.mk
|
||||
include tool/chat/BUILD.mk
|
||||
include test/math/BUILD.mk
|
||||
include test/c11/BUILD.mk
|
||||
include test/posix/BUILD.mk
|
||||
include test/ctl/BUILD.mk
|
||||
include test/libcxx/BUILD.mk
|
||||
@@ -310,11 +326,11 @@ include third_party/linenoise/BUILD.mk
|
||||
include third_party/maxmind/BUILD.mk
|
||||
include net/finger/BUILD.mk
|
||||
include third_party/double-conversion/test/BUILD.mk
|
||||
include third_party/dash/BUILD.mk
|
||||
include third_party/netcat/BUILD.mk
|
||||
include third_party/lua/BUILD.mk
|
||||
include third_party/tree/BUILD.mk
|
||||
include third_party/zstd/BUILD.mk
|
||||
include third_party/tr/BUILD.mk
|
||||
include third_party/sed/BUILD.mk
|
||||
include third_party/awk/BUILD.mk
|
||||
include third_party/hiredis/BUILD.mk
|
||||
include third_party/make/BUILD.mk
|
||||
@@ -343,7 +359,6 @@ include tool/decode/lib/BUILD.mk
|
||||
include tool/decode/BUILD.mk
|
||||
include tool/lambda/lib/BUILD.mk
|
||||
include tool/lambda/BUILD.mk
|
||||
include tool/plinko/lib/BUILD.mk
|
||||
include tool/plinko/BUILD.mk
|
||||
include test/tool/plinko/BUILD.mk
|
||||
include tool/net/BUILD.mk
|
||||
@@ -367,6 +382,7 @@ include test/libc/fmt/BUILD.mk
|
||||
include test/libc/time/BUILD.mk
|
||||
include test/libc/proc/BUILD.mk
|
||||
include test/libc/stdio/BUILD.mk
|
||||
include test/libc/system/BUILD.mk
|
||||
include test/libc/BUILD.mk
|
||||
include test/net/http/BUILD.mk
|
||||
include test/net/https/BUILD.mk
|
||||
@@ -411,87 +427,91 @@ o/$(MODE)/hdrs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(
|
||||
o/$(MODE)/incs.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(INCS) $(INCS),$(dir $(x)))) $(INCS) $(INCS)
|
||||
$(file >$@,$(INCS))
|
||||
o/$(MODE)/depend: o/$(MODE)/.x o/$(MODE)/srcs.txt o/$(MODE)/hdrs.txt o/$(MODE)/incs.txt $(SRCS) $(HDRS) $(INCS)
|
||||
$(COMPILE) -AMKDEPS -L320 $(MKDEPS) -o $@ -s -r o/$(MODE)/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt
|
||||
$(COMPILE) -AMKDEPS -L320 $(MKDEPS) -o $@ -s -r o/$(MODE)/ -S c++:third_party/libcxx/ -S libc/isystem/ @o/$(MODE)/srcs.txt @o/$(MODE)/hdrs.txt @o/$(MODE)/incs.txt
|
||||
|
||||
o/$(MODE)/srcs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(SRCS),$(dir $(x))))
|
||||
$(file >$@) $(foreach x,$(SRCS),$(file >>$@,$(x)))
|
||||
o/$(MODE)/hdrs-old.txt: o/$(MODE)/.x $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x))))
|
||||
$(file >$@) $(foreach x,$(HDRS) $(INCS),$(file >>$@,$(x)))
|
||||
|
||||
TAGS: private .UNSANDBOXED = 1
|
||||
TAGS: private .SANDBOXED = 0
|
||||
TAGS: o/$(MODE)/srcs-old.txt $(SRCS) #o/$(MODE)/third_party/ctags/ctags
|
||||
@$(RM) $@
|
||||
@o/$(MODE)/third_party/ctags/ctags $(TAGSFLAGS) -L $< -o $@
|
||||
|
||||
HTAGS: private .UNSANDBOXED = 1
|
||||
HTAGS: private .SANDBOXED = 0
|
||||
HTAGS: o/$(MODE)/hdrs-old.txt $(filter-out third_party/libcxx/%,$(HDRS)) #o/$(MODE)/third_party/ctags/ctags
|
||||
@$(RM) $@
|
||||
@build/htags o/$(MODE)/third_party/ctags/ctags -L $< -o $@
|
||||
|
||||
loc: private .UNSANDBOXED = 1
|
||||
loc: private .SANDBOXED = 0
|
||||
loc: o/$(MODE)/tool/build/summy
|
||||
find -name \*.h -or -name \*.c -or -name \*.S | \
|
||||
find -name \*.h -or -name \*.hpp -or -name \*.c -or -name \*.cc -or -name \*.cpp -or -name \*.S -or -name \*.mk | \
|
||||
$(XARGS) wc -l | grep total | awk '{print $$1}' | $<
|
||||
|
||||
# PLEASE: MAINTAIN TOPOLOGICAL ORDER
|
||||
# FROM HIGHEST LEVEL TO LOWEST LEVEL
|
||||
COSMOPOLITAN_OBJECTS = \
|
||||
COSMOPOLITAN = \
|
||||
CTL \
|
||||
THIRD_PARTY_DOUBLECONVERSION \
|
||||
THIRD_PARTY_OPENMP \
|
||||
TOOL_ARGS \
|
||||
NET_HTTP \
|
||||
LIBC_SOCK \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_NT_IPHLPAPI \
|
||||
LIBC_X \
|
||||
THIRD_PARTY_GETOPT \
|
||||
DSP_AUDIO \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_ELF \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_IRQ \
|
||||
LIBC_LOG \
|
||||
THIRD_PARTY_TZ \
|
||||
THIRD_PARTY_MUSL \
|
||||
THIRD_PARTY_ZLIB_GZ \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_NT_ADVAPI32 \
|
||||
LIBC_NT_BCRYPTPRIMITIVES \
|
||||
LIBC_NT_COMDLG32 \
|
||||
LIBC_NT_GDI32 \
|
||||
LIBC_NT_IPHLPAPI \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_NT_NTDLL \
|
||||
LIBC_NT_PDH \
|
||||
LIBC_NT_POWRPROF \
|
||||
LIBC_NT_PSAPI \
|
||||
LIBC_NT_REALTIME \
|
||||
LIBC_NT_SHELL32 \
|
||||
LIBC_NT_SYNCHRONIZATION \
|
||||
LIBC_NT_USER32 \
|
||||
LIBC_NT_WINMM \
|
||||
LIBC_NT_WS2_32 \
|
||||
LIBC_PROC \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_SOCK \
|
||||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_SYSTEM \
|
||||
LIBC_SYSV \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_THREAD \
|
||||
LIBC_TINYMATH \
|
||||
LIBC_VGA \
|
||||
LIBC_X \
|
||||
NET_HTTP \
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
THIRD_PARTY_DOUBLECONVERSION \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_LIBCXXABI \
|
||||
THIRD_PARTY_LIBUNWIND \
|
||||
LIBC_STDIO \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_REGEX \
|
||||
LIBC_THREAD \
|
||||
LIBC_PROC \
|
||||
THIRD_PARTY_NSYNC_MEM \
|
||||
LIBC_MEM \
|
||||
THIRD_PARTY_DLMALLOC \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_RUNTIME \
|
||||
THIRD_PARTY_MUSL \
|
||||
THIRD_PARTY_NSYNC \
|
||||
LIBC_ELF \
|
||||
LIBC_IRQ \
|
||||
LIBC_CALLS \
|
||||
LIBC_SYSV_CALLS \
|
||||
LIBC_VGA \
|
||||
LIBC_NT_PSAPI \
|
||||
LIBC_NT_POWRPROF \
|
||||
LIBC_NT_PDH \
|
||||
LIBC_NT_GDI32 \
|
||||
LIBC_NT_COMDLG32 \
|
||||
LIBC_NT_USER32 \
|
||||
LIBC_NT_NTDLL \
|
||||
LIBC_NT_ADVAPI32 \
|
||||
LIBC_NT_SYNCHRONIZATION \
|
||||
LIBC_FMT \
|
||||
THIRD_PARTY_ZLIB \
|
||||
THIRD_PARTY_NSYNC_MEM \
|
||||
THIRD_PARTY_OPENMP \
|
||||
THIRD_PARTY_PUFF \
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
LIBC_TINYMATH \
|
||||
THIRD_PARTY_REGEX \
|
||||
THIRD_PARTY_TZ \
|
||||
THIRD_PARTY_XED \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_INTRIN \
|
||||
LIBC_NT_BCRYPTPRIMITIVES \
|
||||
LIBC_NT_KERNEL32 \
|
||||
LIBC_NEXGEN32E
|
||||
THIRD_PARTY_ZLIB \
|
||||
THIRD_PARTY_ZLIB_GZ \
|
||||
TOOL_ARGS \
|
||||
|
||||
COSMOPOLITAN_H_PKGS = \
|
||||
APE \
|
||||
DSP_AUDIO \
|
||||
LIBC \
|
||||
LIBC_CALLS \
|
||||
LIBC_ELF \
|
||||
@@ -535,21 +555,21 @@ COSMOCC_PKGS = \
|
||||
THIRD_PARTY_INTEL
|
||||
|
||||
o/$(MODE)/cosmopolitan.a: \
|
||||
$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_A_OBJS))
|
||||
$(call reverse,$(call uniq,$(foreach x,$(COSMOPOLITAN),$($(x)))))
|
||||
|
||||
COSMOCC_HDRS = \
|
||||
$(wildcard libc/integral/*) \
|
||||
$(foreach x,$(COSMOCC_PKGS),$($(x)_HDRS)) \
|
||||
$(foreach x,$(COSMOCC_PKGS),$($(x)_INCS))
|
||||
|
||||
o/cosmocc.h.txt: Makefile
|
||||
o/cosmocc.h.txt: Makefile libc $(MAKEFILES) $(call uniq,$(foreach x,$(HDRS) $(INCS),$(dir $(x)))) $(HDRS) $(INCS)
|
||||
$(file >$@, $(call uniq,$(COSMOCC_HDRS)))
|
||||
|
||||
COSMOPOLITAN_H_ROOT_HDRS = \
|
||||
libc/integral/normalize.inc \
|
||||
$(foreach x,$(COSMOPOLITAN_H_PKGS),$($(x)_HDRS))
|
||||
|
||||
o/cosmopolitan.html: private .UNSANDBOXED = 1
|
||||
o/cosmopolitan.html: private .SANDBOXED = 0
|
||||
o/cosmopolitan.html: \
|
||||
o/$(MODE)/third_party/chibicc/chibicc.dbg \
|
||||
$(filter-out %.s,$(foreach x,$(COSMOPOLITAN_OBJECTS),$($(x)_SRCS))) \
|
||||
@@ -572,6 +592,7 @@ ifeq ($(ARCH), x86_64)
|
||||
TOOLCHAIN_ARTIFACTS = \
|
||||
o/$(MODE)/ape/ape.lds \
|
||||
o/$(MODE)/libc/crt/crt.o \
|
||||
o/$(MODE)/libc/crt/crtfastmath.o \
|
||||
o/$(MODE)/ape/ape.elf \
|
||||
o/$(MODE)/ape/ape.o \
|
||||
o/$(MODE)/ape/ape-copy-self.o \
|
||||
@@ -590,6 +611,7 @@ TOOLCHAIN_ARTIFACTS = \
|
||||
o/$(MODE)/ape/ape.elf \
|
||||
o/$(MODE)/ape/aarch64.lds \
|
||||
o/$(MODE)/libc/crt/crt.o \
|
||||
o/$(MODE)/libc/crt/crtfastmath.o \
|
||||
o/$(MODE)/cosmopolitan.a \
|
||||
o/$(MODE)/third_party/libcxx/libcxx.a \
|
||||
o/$(MODE)/tool/build/march-native \
|
||||
@@ -605,7 +627,7 @@ clean_toolchain:
|
||||
$(RM) $(TOOLCHAIN_ARTIFACTS)
|
||||
|
||||
aarch64: private .INTERNET = true
|
||||
aarch64: private .UNSANDBOXED = true
|
||||
aarch64: private .SANDBOXED = 0
|
||||
aarch64:
|
||||
$(MAKE) m=aarch64
|
||||
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
[](https://github.com/jart/cosmopolitan/actions/workflows/build.yml)
|
||||
# Cosmopolitan
|
||||
|
||||
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C
|
||||
[Cosmopolitan Libc](https://justine.lol/cosmopolitan/index.html) makes C/C++
|
||||
a build-once run-anywhere language, like Java, except it doesn't need an
|
||||
interpreter or virtual machine. Instead, it reconfigures stock GCC and
|
||||
Clang to output a POSIX-approved polyglot format that runs natively on
|
||||
Linux + Mac + Windows + FreeBSD + OpenBSD + NetBSD + BIOS with the best
|
||||
possible performance and the tiniest footprint imaginable.
|
||||
Linux + Mac + Windows + FreeBSD + OpenBSD 7.3 + NetBSD + BIOS with the
|
||||
best possible performance and the tiniest footprint imaginable.
|
||||
|
||||
## Background
|
||||
|
||||
@@ -87,15 +87,22 @@ ape/apeinstall.sh
|
||||
```
|
||||
|
||||
You can now build the mono repo with any modern version of GNU Make. To
|
||||
make life easier, we've included one in the cosmocc toolchain, which is
|
||||
guaranteed to be compatible and furthermore includes our extensions for
|
||||
doing build system sandboxing.
|
||||
bootstrap your build, you can install Cosmopolitan Make from this site:
|
||||
|
||||
https://cosmo.zip/pub/cosmos/bin/make
|
||||
|
||||
E.g.:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make -j8
|
||||
curl -LO https://cosmo.zip/pub/cosmos/bin/make
|
||||
./make -j8
|
||||
o//examples/hello
|
||||
```
|
||||
|
||||
After you've built the repo once, you can also use the make from your
|
||||
cosmocc at `.cosmocc/current/bin/make`. You might even prefer to alias
|
||||
make to `$COSMO/.cosmocc/current/bin/make`.
|
||||
|
||||
Since the Cosmopolitan repository is very large, you might only want to
|
||||
build one particular thing. Here's an example of a target that can be
|
||||
compiled relatively quickly, which is a simple POSIX test that only
|
||||
@@ -103,7 +110,7 @@ depends on core LIBC packages.
|
||||
|
||||
```sh
|
||||
rm -rf o//libc o//test
|
||||
build/bootstrap/make o//test/posix/signal_test
|
||||
.cosmocc/current/bin/make o//test/posix/signal_test
|
||||
o//test/posix/signal_test
|
||||
```
|
||||
|
||||
@@ -112,21 +119,21 @@ list out each individual one. For example if you wanted to build and run
|
||||
all the unit tests in the `TEST_POSIX` package, you could say:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make o//test/posix
|
||||
.cosmocc/current/bin/make o//test/posix
|
||||
```
|
||||
|
||||
Cosmopolitan provides a variety of build modes. For example, if you want
|
||||
really tiny binaries (as small as 12kb in size) then you'd say:
|
||||
|
||||
```sh
|
||||
build/bootstrap/make m=tiny
|
||||
.cosmocc/current/bin/make m=tiny
|
||||
```
|
||||
|
||||
You can furthermore cut out the bloat of other operating systems, and
|
||||
have Cosmopolitan become much more similar to Musl Libc.
|
||||
|
||||
```sh
|
||||
build/bootstrap/make m=tinylinux
|
||||
.cosmocc/current/bin/make m=tinylinux
|
||||
```
|
||||
|
||||
For further details, see [//build/config.mk](build/config.mk).
|
||||
@@ -242,16 +249,16 @@ server. You're welcome to join us! <https://discord.gg/FwAVVu7eJ4>
|
||||
|
||||
## Support Vector
|
||||
|
||||
| Platform | Min Version | Circa |
|
||||
| :--- | ---: | ---: |
|
||||
| AMD | K8 Venus | 2005 |
|
||||
| Intel | Core | 2006 |
|
||||
| Linux | 2.6.18 | 2007 |
|
||||
| Windows | 8 [1] | 2012 |
|
||||
| Darwin (macOS) | 23.1.0+ | 2023 |
|
||||
| OpenBSD | 7 | 2021 |
|
||||
| FreeBSD | 13 | 2020 |
|
||||
| NetBSD | 9.2 | 2021 |
|
||||
| Platform | Min Version | Circa |
|
||||
| :--- | ---: | ---: |
|
||||
| AMD | K8 | 2003 |
|
||||
| Intel | Core | 2006 |
|
||||
| Linux | 2.6.18 | 2007 |
|
||||
| Windows | 8 [1] | 2012 |
|
||||
| Darwin (macOS) | 23.1.0+ | 2023 |
|
||||
| OpenBSD | 7.3 or earlier | 2023 |
|
||||
| FreeBSD | 13 | 2020 |
|
||||
| NetBSD | 9.2 | 2021 |
|
||||
|
||||
[1] See our [vista branch](https://github.com/jart/cosmopolitan/tree/vista)
|
||||
for a community supported version of Cosmopolitan that works on Windows
|
||||
|
||||
+12
-12
@@ -45,10 +45,12 @@ o/$(MODE)/ape: $(APE)
|
||||
|
||||
o/$(MODE)/ape/aarch64.lds: \
|
||||
ape/aarch64.lds \
|
||||
libc/zip.internal.h \
|
||||
libc/zip.h \
|
||||
libc/thread/tls.h \
|
||||
libc/calls/struct/timespec.h \
|
||||
libc/macros.internal.h \
|
||||
libc/macros.h \
|
||||
libc/sysv/consts/prot.h \
|
||||
libc/dce.h \
|
||||
libc/str/str.h
|
||||
|
||||
APE_LOADER_LDFLAGS = \
|
||||
@@ -162,8 +164,8 @@ o/$(MODE)/ape/ape-no-modify-self.o: \
|
||||
libc/dce.h \
|
||||
libc/elf/def.h \
|
||||
libc/thread/tls.h \
|
||||
libc/macho.internal.h \
|
||||
libc/macros.internal.h \
|
||||
libc/macho.h \
|
||||
libc/macros.h \
|
||||
libc/nexgen32e/uart.internal.h \
|
||||
libc/calls/metalfile.internal.h \
|
||||
libc/nt/pedef.internal.h \
|
||||
@@ -188,8 +190,8 @@ o/$(MODE)/ape/ape-copy-self.o: \
|
||||
libc/dce.h \
|
||||
libc/elf/def.h \
|
||||
libc/thread/tls.h \
|
||||
libc/macho.internal.h \
|
||||
libc/macros.internal.h \
|
||||
libc/macho.h \
|
||||
libc/macros.h \
|
||||
libc/nexgen32e/uart.internal.h \
|
||||
libc/calls/metalfile.internal.h \
|
||||
libc/nt/pedef.internal.h \
|
||||
@@ -246,8 +248,6 @@ o/$(MODE)/ape: $(APE_CHECKS) \
|
||||
o/$(MODE)/ape/ape.lds \
|
||||
o/$(MODE)/ape/ape.elf \
|
||||
o/$(MODE)/ape/ape.macho \
|
||||
o/$(MODE)/ape/ape-copy-self.o \
|
||||
o/$(MODE)/ape/ape-no-modify-self.o
|
||||
|
||||
endif
|
||||
|
||||
@@ -261,8 +261,8 @@ o/$(MODE)/ape/ape.o: \
|
||||
libc/thread/tls.h \
|
||||
ape/ape.internal.h \
|
||||
ape/macros.internal.h \
|
||||
libc/macho.internal.h \
|
||||
libc/macros.internal.h \
|
||||
libc/macho.h \
|
||||
libc/macros.h \
|
||||
libc/sysv/consts/prot.h \
|
||||
libc/nt/pedef.internal.h \
|
||||
libc/runtime/pc.internal.h \
|
||||
@@ -283,7 +283,7 @@ o/$(MODE)/ape/ape.lds: \
|
||||
libc/dce.h \
|
||||
libc/elf/def.h \
|
||||
libc/elf/pf2prot.internal.h \
|
||||
libc/macros.internal.h \
|
||||
libc/macros.h \
|
||||
libc/nt/pedef.internal.h \
|
||||
libc/str/str.h \
|
||||
libc/zip.internal.h
|
||||
libc/zip.h
|
||||
+21
-7
@@ -4,6 +4,8 @@
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#define CACHELINE 64
|
||||
|
||||
ENTRY(_start)
|
||||
OUTPUT_ARCH(aarch64)
|
||||
OUTPUT_FORMAT("elf64-littleaarch64",
|
||||
@@ -73,13 +75,16 @@ SECTIONS {
|
||||
KEEP(*(SORT_NONE(.fini)))
|
||||
} =0x1f2003d5
|
||||
|
||||
. += CONSTANT(MAXPAGESIZE);
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
__privileged_start = .;
|
||||
|
||||
.privileged : {
|
||||
__privileged_start = ABSOLUTE(.) & -CONSTANT(MAXPAGESIZE);
|
||||
*(.privileged*)
|
||||
} =0x1f2003d6
|
||||
|
||||
. = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
_rodata = .;
|
||||
|
||||
.rodata : {
|
||||
KEEP(*(.rodata.pytab.0));
|
||||
KEEP(*(.rodata.pytab.1));
|
||||
@@ -103,10 +108,8 @@ SECTIONS {
|
||||
*(.eh_frame_entry .eh_frame_entry.*)
|
||||
}
|
||||
|
||||
.eh_frame : ONLY_IF_RO {
|
||||
KEEP(*(.eh_frame))
|
||||
*(.eh_frame.*)
|
||||
}
|
||||
__eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0;
|
||||
__eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0;
|
||||
|
||||
.gcc_except_table : ONLY_IF_RO {
|
||||
*(.gcc_except_table .gcc_except_table.*)
|
||||
@@ -127,9 +130,11 @@ SECTIONS {
|
||||
. += CONSTANT(MAXPAGESIZE);
|
||||
. = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(COMMONPAGESIZE));
|
||||
|
||||
.eh_frame : ONLY_IF_RW {
|
||||
.eh_frame : {
|
||||
__eh_frame_start = .;
|
||||
KEEP(*(.eh_frame))
|
||||
*(.eh_frame.*)
|
||||
__eh_frame_end = .;
|
||||
}
|
||||
|
||||
.gnu_extab : ONLY_IF_RW {
|
||||
@@ -200,6 +205,9 @@ SECTIONS {
|
||||
.data : {
|
||||
__data_start = .;
|
||||
KEEP(*(SORT_BY_NAME(.piro.data.sort.*)))
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.data.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.data .data.* .gnu.linkonce.d.*)
|
||||
KEEP(*(SORT_BY_NAME(.sort.data.*)))
|
||||
SORT(CONSTRUCTORS)
|
||||
@@ -214,6 +222,9 @@ SECTIONS {
|
||||
|
||||
.bss : {
|
||||
*(.dynbss)
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.bss.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||
KEEP(*(SORT_BY_NAME(.piro.bss.sort.*)))
|
||||
*(COMMON)
|
||||
@@ -259,6 +270,9 @@ SECTIONS {
|
||||
.debug_ranges 0 : { *(.debug_ranges) }
|
||||
.debug_macro 0 : { *(.debug_macro) }
|
||||
.debug_addr 0 : { *(.debug_addr) }
|
||||
.debug_names 0 : { *(.debug_names) }
|
||||
.debug_loclists 0 : { *(.debug_loclists) }
|
||||
.debug_str_offsets 0 : { *(.debug_str_offsets) }
|
||||
.ARM.attributes 0 : { KEEP(*(.ARM.attributes)) KEEP(*(.gnu.attributes)) }
|
||||
.note.gnu.arm.ident 0 : { KEEP(*(.note.gnu.arm.ident)) }
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#ifndef __APPLE__
|
||||
#error "ape/ape-m1.c is for apple silicon. chances you want ape/loader.c"
|
||||
#endif
|
||||
#ifndef __aarch64__
|
||||
#error "ape/ape-m1.c is for apple silicon; you want: make o//ape/ape.macho"
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include "libc/calls/metalfile.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/elf/def.h"
|
||||
#include "libc/macho.internal.h"
|
||||
#include "libc/macho.h"
|
||||
#include "libc/nexgen32e/uart.internal.h"
|
||||
#include "libc/nt/pedef.internal.h"
|
||||
#include "libc/runtime/pc.internal.h"
|
||||
@@ -789,6 +789,33 @@ ape.ident:
|
||||
.type ape.ident,@object
|
||||
.previous
|
||||
|
||||
#if SupportsFreebsd()
|
||||
//
|
||||
// This PT_NOTE enables the following kernel behaviors:
|
||||
//
|
||||
// - P_OSREL_SIGSEGV ensures SIGEGV signals aren't misdelivered as
|
||||
// as SIGBUS with invalid siginfo fields.
|
||||
//
|
||||
// - P_OSREL_SHUTDOWN_ENOTCONN is probably a good thing
|
||||
//
|
||||
// - P_OSREL_SIGWAIT prevents sigwait() from raising EINTR
|
||||
//
|
||||
// - P_OSREL_MAP_FSTRICT (basically MAP_SHARED_VALIDATE?)
|
||||
//
|
||||
.section .note.freebsd.ident,"a",@progbits
|
||||
.balign 4
|
||||
freebsd.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1 // NT_FREEBSD_ABI_TAG
|
||||
1: .asciz "FreeBSD"
|
||||
2: .balign 4
|
||||
3: .long 1300000 // v13.0+ c. 2020
|
||||
4: .size freebsd.ident,.-freebsd.ident
|
||||
.type freebsd.ident,@object
|
||||
.previous
|
||||
#endif /* SupportsFreebsd() */
|
||||
|
||||
#if SupportsOpenbsd()
|
||||
.section .note.openbsd.ident,"a",@progbits
|
||||
.balign 4
|
||||
@@ -1086,6 +1113,19 @@ ape_pe: .ascin "PE",4
|
||||
.long PERDAT // Flags
|
||||
.previous
|
||||
|
||||
.section .pe.sections,"a",@progbits
|
||||
.ascin ".idata",8 // Section Name
|
||||
.long ape_idt_memsz // Virtual Size or Physical Address
|
||||
.long ape_idt_rva // Relative Virtual Address
|
||||
.long ape_idt_filesz // Physical Size
|
||||
.long ape_idt_offset // Physical Offset
|
||||
.long 0 // Relocation Table Offset
|
||||
.long 0 // Line Number Table Offset
|
||||
.short 0 // Relocation Count
|
||||
.short 0 // Line Number Count
|
||||
.long PEIMPS // Flags
|
||||
.previous
|
||||
|
||||
.section .pe.sections,"a",@progbits
|
||||
.ascin ".data",8 // Section Name
|
||||
.long ape_ram_memsz // Virtual Size or Physical Address
|
||||
@@ -1711,6 +1751,7 @@ kernel: movabs $ape_stack_vaddr,%rsp
|
||||
mov $0x79000,%esi
|
||||
mov $0x7f000,%edx
|
||||
call __reclaim_boot_pages
|
||||
movl $1,__pib(%rip)
|
||||
push $_HOSTMETAL // sets __hostos in crt.S
|
||||
pop %rcx
|
||||
pushq .Lenv0(%rip) // envp[0][0]
|
||||
@@ -1828,7 +1869,7 @@ __bss_end:
|
||||
.section .notice,"aR",@progbits
|
||||
.asciz "\n\n\
|
||||
Cosmopolitan\n\
|
||||
Copyright 2024 Justine Alexandra Roberts Tunney\n\
|
||||
Copyright 2025 Justine Alexandra Roberts Tunney\n\
|
||||
\n\
|
||||
Permission to use, copy, modify, and/or distribute this software for\n\
|
||||
any purpose with or without fee is hereby granted, provided that the\n\
|
||||
@@ -1844,4 +1885,4 @@ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n\
|
||||
PERFORMANCE OF THIS SOFTWARE."
|
||||
|
||||
.end
|
||||
|
||||
|
||||
+63
-22
@@ -186,6 +186,8 @@
|
||||
/* uncomment if .com.dbg won't execute on your kernel (will break .com file) */
|
||||
/* #define APE_FIX_COM_DBG */
|
||||
|
||||
#define CACHELINE 64
|
||||
|
||||
#ifdef __x86__
|
||||
#define CODE_GRANULE 1
|
||||
#else
|
||||
@@ -199,7 +201,7 @@
|
||||
#endif
|
||||
|
||||
#ifndef IMAGE_BASE_VIRTUAL
|
||||
#define IMAGE_BASE_VIRTUAL 0x400000
|
||||
#define IMAGE_BASE_VIRTUAL 0x4200000
|
||||
#endif
|
||||
|
||||
#if IMAGE_BASE_VIRTUAL > 0xffffffff
|
||||
@@ -237,11 +239,10 @@ SECTIONS {
|
||||
KEEP(*(.elf.phdrs))
|
||||
ape_phdrs_end = .;
|
||||
|
||||
/* OpenBSD */
|
||||
/* ELF PT_NOTE data structures */
|
||||
. = ALIGN(. != 0 ? __SIZEOF_POINTER__ : 0);
|
||||
ape_note = .;
|
||||
KEEP(*(.note.openbsd.ident))
|
||||
KEEP(*(.note.netbsd.ident))
|
||||
KEEP(*(.note.*))
|
||||
ape_note_end = .;
|
||||
|
||||
/* Portable Executable */
|
||||
@@ -310,7 +311,7 @@ SECTIONS {
|
||||
. = ALIGN(__privileged_end > __privileged_start ? CONSTANT(COMMONPAGESIZE) : 0);
|
||||
/*END: morphable code */
|
||||
__privileged_start = .;
|
||||
*(.privileged)
|
||||
*(.privileged .privileged.*)
|
||||
__privileged_end = .;
|
||||
|
||||
KEEP(*(.ape.pad.text))
|
||||
@@ -321,6 +322,7 @@ SECTIONS {
|
||||
/*BEGIN: Read Only Data */
|
||||
|
||||
.rodata ALIGN(CONSTANT(COMMONPAGESIZE)) : {
|
||||
_rodata = .;
|
||||
KEEP(*(.rodata.pytab.0));
|
||||
KEEP(*(.rodata.pytab.1));
|
||||
KEEP(*(.rodata.pytab.2));
|
||||
@@ -329,6 +331,10 @@ SECTIONS {
|
||||
*(.ubsan.types)
|
||||
*(.ubsan.data)
|
||||
|
||||
__eh_frame_hdr_start_actual = .;
|
||||
*(.eh_frame_hdr)
|
||||
__eh_frame_hdr_end_actual = .;
|
||||
|
||||
/* Legal Notices */
|
||||
__notices = .;
|
||||
KEEP(*(.notice))
|
||||
@@ -338,12 +344,6 @@ SECTIONS {
|
||||
|
||||
/*BEGIN: read-only data that's only needed for initialization */
|
||||
|
||||
#if SupportsWindows()
|
||||
/* Windows DLL Import Directory */
|
||||
KEEP(*(.idata.ro));
|
||||
KEEP(*(SORT_BY_NAME(.idata.ro.*)))
|
||||
#endif
|
||||
|
||||
/* Encoded Data Structures w/ Linear Initialization Order */
|
||||
KEEP(*(.initroprologue))
|
||||
KEEP(*(SORT_BY_NAME(.initro.*)))
|
||||
@@ -382,13 +382,34 @@ SECTIONS {
|
||||
_tbss_end = .;
|
||||
} :Tls
|
||||
|
||||
.idata : {
|
||||
#if SupportsWindows()
|
||||
KEEP(*(.dllstr));
|
||||
KEEP(*(.idata.ro));
|
||||
KEEP(*(SORT_BY_NAME(.idata.ro.*)));
|
||||
KEEP(*(SORT_BY_NAME(.piro.data.sort.iat.*)));
|
||||
KEEP(*(.data.nt.*));
|
||||
#endif
|
||||
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0);
|
||||
} :Ram
|
||||
|
||||
.eh_frame : {
|
||||
__eh_frame_start = .;
|
||||
KEEP(*(.eh_frame))
|
||||
*(.eh_frame.*)
|
||||
__eh_frame_end = .;
|
||||
} :Ram
|
||||
|
||||
.data . : {
|
||||
/*BEGIN: Read/Write Data */
|
||||
#if SupportsWindows()
|
||||
KEEP(*(SORT_BY_NAME(.piro.data.sort.iat.*)))
|
||||
#endif
|
||||
/*BEGIN: NT FORK COPYING */
|
||||
KEEP(*(.dataprologue))
|
||||
|
||||
/* Precious Runtime Constants */
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.data.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
|
||||
*(.data .data.*)
|
||||
*(.gnu_extab)
|
||||
*(.gcc_except_table .gcc_except_table.*)
|
||||
@@ -430,7 +451,6 @@ SECTIONS {
|
||||
KEEP(*(.piro.pad.data))
|
||||
*(.igot.plt)
|
||||
KEEP(*(.dataepilogue))
|
||||
|
||||
. = ALIGN(. != 0 ? CONSTANT(COMMONPAGESIZE) : 0);
|
||||
/*END: NT FORK COPYING */
|
||||
_edata = .;
|
||||
@@ -454,6 +474,11 @@ SECTIONS {
|
||||
. += . > 0 ? CODE_GRANULE : 0;
|
||||
/*END: Post-Initialization Read-Only */
|
||||
|
||||
/* Precious Runtime Constants */
|
||||
. = ALIGN(CACHELINE);
|
||||
*(.bss.rarechange)
|
||||
. = ALIGN(CACHELINE);
|
||||
|
||||
/* Statically Allocated Empty Space */
|
||||
*(SORT_BY_ALIGNMENT(.bss))
|
||||
*(SORT_BY_ALIGNMENT(.bss.*))
|
||||
@@ -510,6 +535,9 @@ SECTIONS {
|
||||
.debug_rnglists 0 : { *(.debug_rnglists) }
|
||||
.debug_macro 0 : { *(.debug_macro) }
|
||||
.debug_addr 0 : { *(.debug_addr) }
|
||||
.debug_names 0 : { *(.debug_names) }
|
||||
.debug_loclists 0 : { *(.debug_loclists) }
|
||||
.debug_str_offsets 0 : { *(.debug_str_offsets) }
|
||||
.gnu.attributes 0 : { KEEP(*(.gnu.attributes)) }
|
||||
.GCC.command.line 0 : { *(.GCC.command.line) }
|
||||
|
||||
@@ -536,7 +564,7 @@ SECTIONS {
|
||||
}
|
||||
}
|
||||
|
||||
ape_pe_filealignment = 512;
|
||||
ape_pe_filealignment = 4096;
|
||||
ape_pe_sectionalignment = 4096;
|
||||
ape_pe_sizeofheaders = SIZEOF(.head);
|
||||
ape_pe_sizeofimage = ROUNDUP(_end - __executable_start, ape_pe_sectionalignment);
|
||||
@@ -573,11 +601,19 @@ ape_rom_memsz = ape_rom_filesz;
|
||||
ape_rom_align = CONSTANT(COMMONPAGESIZE);
|
||||
ape_rom_rva = RVA(ape_rom_vaddr);
|
||||
|
||||
ape_ram_vaddr = ADDR(.data);
|
||||
ape_idt_vaddr = ADDR(.idata);
|
||||
ape_idt_offset = ape_idt_vaddr - __executable_start;
|
||||
ape_idt_paddr = LOADADDR(.idata);
|
||||
ape_idt_filesz = ADDR(.eh_frame) - ADDR(.idata);
|
||||
ape_idt_memsz = ape_idt_filesz;
|
||||
ape_idt_align = CONSTANT(COMMONPAGESIZE);
|
||||
ape_idt_rva = RVA(ape_idt_vaddr);
|
||||
|
||||
ape_ram_vaddr = ADDR(.eh_frame);
|
||||
ape_ram_offset = ape_ram_vaddr - __executable_start;
|
||||
ape_ram_paddr = LOADADDR(.data);
|
||||
ape_ram_filesz = ADDR(.bss) - ADDR(.data);
|
||||
ape_ram_memsz = _end - ADDR(.data);
|
||||
ape_ram_paddr = LOADADDR(.eh_frame);
|
||||
ape_ram_filesz = ADDR(.bss) - ADDR(.eh_frame);
|
||||
ape_ram_memsz = _end - ADDR(.eh_frame);
|
||||
ape_ram_align = CONSTANT(COMMONPAGESIZE);
|
||||
ape_ram_rva = RVA(ape_ram_vaddr);
|
||||
|
||||
@@ -587,7 +623,7 @@ ape_stack_offset = 0;
|
||||
ape_stack_vaddr = DEFINED(ape_stack_vaddr) ? ape_stack_vaddr : 0x700000000000;
|
||||
ape_stack_paddr = ape_ram_paddr + ape_ram_filesz;
|
||||
ape_stack_filesz = 0;
|
||||
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024;
|
||||
ape_stack_memsz = DEFINED(ape_stack_memsz) ? ape_stack_memsz : 8 * 1024 * 1024; /* for gnu make */
|
||||
|
||||
ape_note_offset = ape_cod_offset + (ape_note - ape_cod_vaddr);
|
||||
ape_note_filesz = ape_note_end - ape_note;
|
||||
@@ -601,6 +637,9 @@ ape_text_memsz = ape_text_filesz;
|
||||
ape_text_align = CONSTANT(COMMONPAGESIZE);
|
||||
ape_text_rva = RVA(ape_text_vaddr);
|
||||
|
||||
__eh_frame_hdr_start = __eh_frame_hdr_end_actual > __eh_frame_hdr_start_actual ? __eh_frame_hdr_start_actual : 0;
|
||||
__eh_frame_hdr_end = __eh_frame_hdr_end_actual > __eh_frame_hdr_start_actual ? __eh_frame_hdr_end_actual : 0;
|
||||
|
||||
/* we roundup here because xnu wants the file load segments page-aligned */
|
||||
/* but we don't want to add the nop padding to the ape program, so we'll */
|
||||
/* let ape.S dd read past the end of the file into the wrapping binaries */
|
||||
@@ -698,8 +737,10 @@ CHURN(ape_pe_sections);
|
||||
CHURN(ape_pe_sections_end);
|
||||
CHURN(ape_pe_shnum);
|
||||
CHURN(ape_phdrs_end);
|
||||
CHURN(WinMain);
|
||||
#endif /* SupportsWindows() */
|
||||
#if SupportsWindows()
|
||||
CHURN(WinMain);
|
||||
#endif
|
||||
#endif /* SupportsXnu() */
|
||||
|
||||
#if SupportsWindows() || SupportsMetal()
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ if [ ! -f ape/loader.c ]; then
|
||||
cd "$COSMO" || exit
|
||||
fi
|
||||
|
||||
if [ -x build/bootstrap/make ]; then
|
||||
MAKE=build/bootstrap/make
|
||||
if [ -x .cosmocc/current/bin/make ]; then
|
||||
MAKE=.cosmocc/current/bin/make
|
||||
else
|
||||
MAKE=make
|
||||
fi
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
*/.section ".piro.data.sort.iat.2.\name\().3","awG",@progbits,"\name",comdat
|
||||
.quad 0
|
||||
.previous
|
||||
.section .rodata.str1.1,"aSM",@progbits,1
|
||||
.section .dllstr,"aSM",@progbits,1
|
||||
".Lidata.str.\name":
|
||||
.asciz "\name\().dll"
|
||||
.previous
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
// Calls _start() function of loaded program.
|
||||
//
|
||||
|
||||
+2
-2
@@ -16,10 +16,10 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macho.internal.h"
|
||||
#include "libc/macho.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
// Apple Mach-O Executable Headers
|
||||
// Fixups are applied by objbincopy
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#ifndef APE_MACROS_H_
|
||||
#define APE_MACROS_H_
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
#ifdef __ASSEMBLER__
|
||||
/* clang-format off */
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
#define IMAGE_BASE_REAL 0x2000
|
||||
|
||||
#ifndef IMAGE_BASE_VIRTUAL
|
||||
#define IMAGE_BASE_VIRTUAL 0x400000
|
||||
#define IMAGE_BASE_VIRTUAL 0x4200000
|
||||
#endif
|
||||
|
||||
#ifndef IMAGE_BASE_PHYSICAL
|
||||
|
||||
@@ -6,6 +6,7 @@ extern const char __notices[] __attribute__((__weak__));
|
||||
extern unsigned char __executable_start[] __attribute__((__weak__));
|
||||
extern unsigned char __privileged_start[] __attribute__((__weak__));
|
||||
extern unsigned char _ehead[] __attribute__((__weak__));
|
||||
extern unsigned char _rodata[] __attribute__((__weak__));
|
||||
extern unsigned char _etext[] __attribute__((__weak__));
|
||||
extern unsigned char _edata[] __attribute__((__weak__));
|
||||
extern unsigned char _ezip[] __attribute__((__weak__));
|
||||
@@ -28,5 +29,8 @@ extern unsigned long __got_start[] __attribute__((__weak__));
|
||||
extern unsigned long __got_end[] __attribute__((__weak__));
|
||||
extern unsigned char ape_phdrs[] __attribute__((__weak__));
|
||||
|
||||
#define __rarechange _Section(".bss.rarechange")
|
||||
#define __data_rarechange _Section(".data.rarechange")
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_APE_SECTIONS_INTERNAL_H_ */
|
||||
+418
-10
@@ -36,8 +36,9 @@ considered an APE program.
|
||||
|
||||
This is the canonical magic used by almost all APE programs. It enables
|
||||
maximum portability between OSes. When interpreted as a shell script, it
|
||||
is assiging a single quoted string to an unused variable. The shell will
|
||||
then ignore subsequent binary content that's placed inside the string.
|
||||
is assigning a single quoted string to an unused variable. The shell
|
||||
will then ignore subsequent binary content that's placed inside the
|
||||
string.
|
||||
|
||||
It is strongly recommended that this magic value be immediately followed
|
||||
by a newline (\n or hex 0a) character. Some shells, e.g. FreeBSD SH and
|
||||
@@ -167,12 +168,12 @@ printf '\177ELF\2\1\1\011\0\0\0\0\0\0\0\0\2\0\076\0\1\0\0\0\166\105\100\000\000\
|
||||
|
||||
This `printf` statement MUST appear in the first 8192 bytes of the APE
|
||||
executable, so as to limit how much of the initial portion of a file an
|
||||
intepreter must load.
|
||||
interpreter must load.
|
||||
|
||||
Multiple such `printf` statements MAY appear in hte first 8192 bytes, in
|
||||
Multiple such `printf` statements MAY appear in the first 8192 bytes, in
|
||||
order to specify multiple architectures. For example, fat binaries built
|
||||
by the `apelink` program (provided by Cosmo Libc) will have two encoded
|
||||
ELF headers, for amd64 and arm64, each of which point into the proper
|
||||
ELF headers, for AMD64 and ARM64, each of which point into the proper
|
||||
file offsets for their respective native code. Therefore, kernels and
|
||||
interpreters which load the APE format directly MUST check the
|
||||
`e_machine` field of the `Elf64_Ehdr` that's decoded from the octal
|
||||
@@ -238,11 +239,11 @@ iterations over APE's history before we eventually got it right:
|
||||
- `arg=$(( 9293))` b/c busybox sh disliked quoted space
|
||||
- `arg=9293 ` is generated by modern apelink program
|
||||
|
||||
Software that parses the APE file format, which needs to extract to be
|
||||
able extract the Macho-O x86-64 header SHOULD support the old binaries
|
||||
that use the previous encodings. To make backwards compatibility simple
|
||||
the following regular expression may be used, which generalizes to all
|
||||
defined formats:
|
||||
Software that parses the APE file format, which needs to extract the
|
||||
Macho-O x86-64 header SHOULD support the old binaries that use the
|
||||
previous encodings. To make backwards compatibility simple the following
|
||||
regular expression may be used, which generalizes to all defined
|
||||
formats:
|
||||
|
||||
```c
|
||||
regcomp(&rx,
|
||||
@@ -269,3 +270,410 @@ regcomp(&rx,
|
||||
|
||||
For further details, see the canonical implementation in
|
||||
`cosmopolitan/tool/build/assimilate.c`.
|
||||
|
||||
## Static Linking
|
||||
|
||||
Actually Portable Executables are always statically linked. This
|
||||
revision of the specification does not define any facility for storing
|
||||
code in dynamic shared objects.
|
||||
|
||||
Cosmopolitan Libc provides a solution that enables APE binaries have
|
||||
limited access to dlopen(). By manually loading a platform-specific
|
||||
executable and asking the OS-specific libc's dlopen() to load
|
||||
OS-specific libraries, it becomes possible to use GPUs and GUIs. This
|
||||
has worked great for AI projects like llamafile.
|
||||
|
||||
There is no way for an Actually Portable Executable to interact with
|
||||
OS-specific dynamic shared object extension modules to programming
|
||||
languages. For example, a Lua interpreter compiled as an Actually
|
||||
Portable Executable would have no way of linking extension libraries
|
||||
downloaded from the Lua Rocks package manager. This is primarily because
|
||||
different OSes define incompatible ABIs.
|
||||
|
||||
While it was possible to polyglot PE+ELF+MachO to create multi-OS
|
||||
executables, it simply isn't possible to do that same thing for
|
||||
DLL+DYLIB+SO. Therefore, in order to have DSOs, APE would need to either
|
||||
choose one of the existing formats or invent one of its own, and then
|
||||
develop its own parallel ecosystem of extension software. In the future,
|
||||
the APE specification may expand to encompass this. However the focus to
|
||||
date has been exclusively on executables with limited dlopen() support.
|
||||
|
||||
## Application Binary Interface (ABI)
|
||||
|
||||
APE binaries use the System V ABI, as defined by:
|
||||
|
||||
- [System V ABI - AMD64 Architecture Processor Supplement](https://gitlab.com/x86-psABIs/x86-64-ABI)
|
||||
- AARCH64 has a uniform consensus defined by ARM Limited
|
||||
|
||||
There are however a few changes we've had to make.
|
||||
|
||||
### No Red Zone
|
||||
|
||||
Actually Portable Executables that have Windows and/or bare metal in
|
||||
their support vector MUST be compiled using `-mno-red-zone`. This is
|
||||
because, on Windows, DLLs and other software lurking in the va-space
|
||||
might use tricks like SetThreadContext() to take control of a thread
|
||||
whereas on bare metal, it's also generally accepted that kernel-mode
|
||||
code cannot assume a red zone either due to hardware interrupts that
|
||||
pull the exact same kinds of stunts.
|
||||
|
||||
APE software that only has truly System V ABI conformant OSes (e.g.
|
||||
Linux) in their support vector MAY use the red zone optimization.
|
||||
|
||||
### Thread Local Storage
|
||||
|
||||
#### aarch64
|
||||
|
||||
Here's the TLS memory layout on aarch64:
|
||||
|
||||
```
|
||||
x28
|
||||
%tpidr_el0
|
||||
│
|
||||
│ _Thread_local
|
||||
┌───┼───┬──────────┬──────────┐
|
||||
│tib│dtv│ .tdata │ .tbss │
|
||||
├───┴───┴──────────┴──────────┘
|
||||
│
|
||||
__get_tls()
|
||||
```
|
||||
|
||||
The ARM64 code in actually portable executables use the `x28` register
|
||||
to store the address of the thread information block. All aarch64 code
|
||||
linked into these executables SHOULD be compiled with `-ffixed-x28`
|
||||
which is supported by both Clang and GCC.
|
||||
|
||||
The runtime library for an actually portable executables MAY choose to
|
||||
use `tpidr_el0` instead, if OSes like MacOS aren't being targeted. For
|
||||
example, if the goal is to create a Linux-only fat binary linker program
|
||||
for Musl Libc, then choosing to use the existing `tpidr_el0` convention
|
||||
would be friction-free alternative.
|
||||
|
||||
It's not possible for an APE runtime that targets the full range of OSes
|
||||
to use the `tpidr_el0` register for TLS because Apple won't allow it. On
|
||||
MacOS ARM64 systems, this register can only be used by a runtime to
|
||||
implement the `sched_getcpu()` system call. It's reserved by MacOS.
|
||||
|
||||
#### x86-64
|
||||
|
||||
Here's the TLS memory layout on x86_64:
|
||||
|
||||
```
|
||||
__get_tls()
|
||||
│
|
||||
%fs Linux/FreeBSD/NetBSD/OpenBSD
|
||||
_Thread_local │
|
||||
┌───┬──────────┬──────────┼───┐
|
||||
│pad│ .tdata │ .tbss │tib│
|
||||
└───┴──────────┴──────────┼───┘
|
||||
│
|
||||
Windows/Mac %gs
|
||||
```
|
||||
|
||||
Quite possibly the greatest challenge in Actually Portable Executable
|
||||
working, has been overcoming the incompatibilities between OSes in how
|
||||
thread-local storage works on x86-64. The AMD64 architecture defines two
|
||||
special segment registers. Every OS uses one of these segment registers
|
||||
to implement TLS support. However not all OSes agree on which register
|
||||
to use. Some OSes grant userspace the power to define either of these
|
||||
registers to hold any value that is desired. Some OSes only effectively
|
||||
allow a single one of them to be changed. Lastly, some OSes, e.g.
|
||||
Windows, claim ownership of the memory layout these registers point
|
||||
towards too.
|
||||
|
||||
Here's a breakdown on how much power is granted to userspace runtimes by
|
||||
each OS when it comes to changing amd64 segment registers.
|
||||
|
||||
| | %fs | %gs |
|
||||
|---------|--------------|--------------|
|
||||
| Linux | unrestricted | unrestricted |
|
||||
| MacOS | inaccessible | unrestricted |
|
||||
| Windows | inaccessible | restricted |
|
||||
| FreeBSD | unrestricted | unrestricted |
|
||||
| NetBSD | unrestricted | broken |
|
||||
| OpenBSD | unrestricted | inaccessible |
|
||||
|
||||
Therefore, regardless of which register one we choose, some OSes are
|
||||
going to be incompatible.
|
||||
|
||||
APE binaries are always built with a Linux compiler. So another issue
|
||||
arises in the fact that our Linux-flavored GCC and Clang toolchains
|
||||
(which are used to produce cross-OS binaries) are also only capable of
|
||||
producing TLS instructions that use the %fs convention.
|
||||
|
||||
To solve these challenges, the `cosmocc` compiler rewrites GCC generated
|
||||
assembly so that the `%fs` referencing instructions created by GCC when
|
||||
using the `-mno-tls-direct-seg-refs` flag are replaced by calls to the
|
||||
following functions:
|
||||
|
||||
- `__get_tls_rax`
|
||||
- `__get_tls_rbx`
|
||||
- `__get_tls_rcx`
|
||||
- `__get_tls_rdx`
|
||||
- `__get_tls_rdi`
|
||||
- `__get_tls_rsi`
|
||||
- `__get_tls_rbp`
|
||||
- `__get_tls_r8`
|
||||
- `__get_tls_r9`
|
||||
- `__get_tls_r10`
|
||||
- `__get_tls_r11`
|
||||
- `__get_tls_r12`
|
||||
- `__get_tls_r13`
|
||||
- `__get_tls_r14`
|
||||
- `__get_tls_r15`
|
||||
- `__add_tls_rax`
|
||||
- `__add_tls_rbx`
|
||||
- `__add_tls_rcx`
|
||||
- `__add_tls_rdx`
|
||||
- `__add_tls_rdi`
|
||||
- `__add_tls_rsi`
|
||||
- `__add_tls_rbp`
|
||||
- `__add_tls_r8`
|
||||
- `__add_tls_r9`
|
||||
- `__add_tls_r10`
|
||||
- `__add_tls_r11`
|
||||
- `__add_tls_r12`
|
||||
- `__add_tls_r13`
|
||||
- `__add_tls_r14`
|
||||
- `__add_tls_r15`
|
||||
|
||||
The `cosmocc` script internally uses a tool named `tlscc` which wraps a
|
||||
gcc or clang compilation so it generates intermediate assembly code that
|
||||
it can modify to call the above functions. In the process, it also adds
|
||||
the `-mno-tls-direct-seg-refs` and `-mno-red-zone` flags to the compile
|
||||
command to ensure the compiler only generates TLS assembly instructions
|
||||
that are understood by the tool.
|
||||
|
||||
The above functions do not clobber any registers. The function name
|
||||
specifies the register to which the result is stored or added. The only
|
||||
above function that can be called by C code is `__get_tls_rax`.
|
||||
|
||||
When Windows and MacOS aren't in the support vector, then the above
|
||||
assembly rewriting pass can be avoided entirely, and the binary can
|
||||
simply use the TLS opcodes that GCC and Clang generate for Linux.
|
||||
|
||||
### Thread Information Block (TIB)
|
||||
|
||||
The Actually Portable Executable Thread Information Block (TIB) is
|
||||
defined by this version of the specification as follows:
|
||||
|
||||
- The 64-bit TIB self-pointer is stored at offset 0x00.
|
||||
- The 64-bit TIB self-pointer is also stored at offset 0x30.
|
||||
- The 32-bit `errno` value is stored at offset 0x3c.
|
||||
|
||||
All other parts of the thread information block should be considered
|
||||
unspecified and therefore reserved for future specifications.
|
||||
|
||||
The APE thread information block is aligned on a 64-byte boundary.
|
||||
|
||||
Cosmopolitan Libc v3.5.8 (c. 2024-07-21) currently implements a thread
|
||||
information block that's 512 bytes in size.
|
||||
|
||||
### Foreign Function Calls
|
||||
|
||||
Even though APE programs always use the System V ABI, there arises the
|
||||
occasional need to interface with foreign functions, e.g. WIN32. The
|
||||
`__attribute__((__ms_abi__))` annotation introduced by GCC v6 is used
|
||||
for this purpose.
|
||||
|
||||
The ability to change a function's ABI on a case-by-case basis is
|
||||
surprisingly enough supported by GCC, Clang, NVCC, and even the AMD HIP
|
||||
compilers for both UNIX systems and Windows. All of these compilers
|
||||
support both the System V ABI and the Microsoft x64 ABI.
|
||||
|
||||
APE binaries will actually favor the Microsoft ABI even when running on
|
||||
UNIX OSes for certain dlopen() use-cases. For example, if we control the
|
||||
code to a CUDA module, which we compile on each OS separately from our
|
||||
main APE binary, then any function that's inside the APE binary whose
|
||||
pointer may be passed into a foreign module SHOULD be compiled to use
|
||||
the Microsoft ABI. This is because in practice the OS-specific module
|
||||
may need to be compiled by MSVC, where MS ABI is the *only* ABI, which
|
||||
forces our UNIX programs to partially conform. Thankfully, all UNIX
|
||||
compilers support doing it on a case-by-case basis.
|
||||
|
||||
### Char Signedness
|
||||
|
||||
Actually Portable Executable defines `char` as signed.
|
||||
|
||||
Therefore conformant APE software MUST use `-fsigned-char` when building
|
||||
code for aarch64, as well as any other architecture that (unlike x86-64)
|
||||
would otherwise define `char` as being `unsigned char` by default.
|
||||
|
||||
This decision was one of the cases where it made sense to offer a more
|
||||
consistent runtime experience for fat multi-arch binaries. However you
|
||||
SHOULD still write code to assume `char` can go either way. But if all
|
||||
you care about is using APE, then you CAN assume `char` is signed.
|
||||
|
||||
### Long Double
|
||||
|
||||
On AMD64 platforms, APE binaries define `long double` as 80-bit.
|
||||
|
||||
On ARM64 platforms, APE binaries define `long double` as 128-bit.
|
||||
|
||||
We accept inconsistency in this case, because hardware acceleration is
|
||||
far more valuable than stylistic consistency in the case of mathematics.
|
||||
|
||||
One challenge arises on AMD64 for supporting `long double` across OSes.
|
||||
Unlike UNIX systems, the Windows Executive on x86-64 initializes the x87
|
||||
FPU to have double (64-bit) precision rather than 80-bit. That's because
|
||||
code compiled by MSVC treats `long double` as though it were `double` to
|
||||
prefer always using the more modern SSE instructions. However System V
|
||||
requires genuine 80-bit `long double` support on AMD64.
|
||||
|
||||
Therefore, if an APE program detects that it's been started on a Windows
|
||||
x86-64 system, then it SHOULD use the following assembly to initialize
|
||||
the x87 FPU in System V ABI mode.
|
||||
|
||||
```asm
|
||||
fldcw 1f(%rip)
|
||||
.rodata
|
||||
.balign 2
|
||||
// 8087 FPU Control Word
|
||||
// IM: Invalid Operation ───────────────┐
|
||||
// DM: Denormal Operand ───────────────┐│
|
||||
// ZM: Zero Divide ───────────────────┐││
|
||||
// OM: Overflow ─────────────────────┐│││
|
||||
// UM: Underflow ───────────────────┐││││
|
||||
// PM: Precision ──────────────────┐│││││
|
||||
// PC: Precision Control ───────┐ ││││││
|
||||
// {float,∅,double,long double}│ ││││││
|
||||
// RC: Rounding Control ──────┐ │ ││││││
|
||||
// {even, →-∞, →+∞, →0} │┌┤ ││││││
|
||||
// ┌┤││ ││││││
|
||||
// d││││rr││││││
|
||||
1: .short 0b00000000000000000001101111111
|
||||
.previous
|
||||
```
|
||||
|
||||
## Executable File Alignment
|
||||
|
||||
Actually Portable Executable is a statically-linked flat executable file
|
||||
format that is, as a thing in itself, agnostic to file alignments. For
|
||||
example, the shell script payload at the beginning of the file and its
|
||||
statements have no such requirements. Alignment requirements are however
|
||||
imposed by the executable formats that APE wraps.
|
||||
|
||||
1. ELF requires that file offsets be congruent with virtual addresses
|
||||
modulo the CPU page size. So when we add a shell script to the start
|
||||
of an executable, we need to round up to the page size in order to
|
||||
maintain ELF's invariant. Although no such roundup is required on the
|
||||
program segments once the invariant is restored. ELF loaders will
|
||||
happily map program headers from arbitrary file intervals (which may
|
||||
overlap) onto arbitrarily virtual intervals (which don't need to be
|
||||
contiguous). In order to do that, the loaders will generally use
|
||||
UNIX's mmap() function which is more restrictive and only accepts
|
||||
addresses and offsets that are page aligned. To make it possible to
|
||||
map an unaligned ELF program header that could potentially start and
|
||||
stop at any byte, ELF loaders round-out the intervals, which means
|
||||
adjacent unrelated data might also get mapped, which may need to be
|
||||
explicitly zero'd. Thanks to the cleverness of ELF, it's possible to
|
||||
have an executable file be very tiny, without needing any alignment
|
||||
bytes, and it'll be loaded into a properly aligned virtual space
|
||||
where segments can be as sparse as we want them to be.
|
||||
|
||||
2. PE doesn't care about congruence and instead defines two separate
|
||||
kinds of alignment. First, PE requires that the layout of segment
|
||||
memory inside the file be aligned on at minimum the classic 512 byte
|
||||
MS-DOS page size. This means that, unlike ELF, some alignment padding
|
||||
may need to be encoded into the file, making it slightly larger. Next
|
||||
PE imposes an alignment restriction on segments once they've been
|
||||
mapped into the virtual address space, which must be rounded to the
|
||||
system page size. Like ELF, PE segments need to be properly ordered
|
||||
but they're allowed to drift apart once mapped in a non-contiguous
|
||||
sparsely mapped way. When inserting shell script content at the start
|
||||
of a PE file, the most problematic thing is the need to round up to
|
||||
the 64kb system granularity, which results in a lot of needless bytes
|
||||
of padding being inserted by a naive second-pass linker.
|
||||
|
||||
3. Apple's Mach-O format is the strictest of them all. While both ELF
|
||||
and PE are defined in such a way that invites great creativity, XNU
|
||||
will simply refuse to an executable that does anything creative with
|
||||
alignment. All loaded segments need to both start and end on a page
|
||||
aligned address. XNU also wants segments to be contiguous similar to
|
||||
portable executable, except it applies to both the file and virtual
|
||||
spaces, which must follow the same structure.
|
||||
|
||||
Actually Portable Executables must conform to the strictest requirements
|
||||
demanded by the support vector. Therefore an APE binary that has headers
|
||||
for all three of the above executable formats MUST conform to the Apple
|
||||
way of doing things. GNU ld linker scripts aren't very good at producing
|
||||
ELF binaries that rigidly conform to this simple naive layout. There are
|
||||
so many ways things can go wrong, where third party code might slip its
|
||||
own custom section name in-between the linker script sections that are
|
||||
explicitly defined, thereby causing ELF's powerful features to manifest
|
||||
and the resulting content overlapping. The best `ld` flag that helps is
|
||||
`--orphan-handling=error` which can help with explaining such mysteries.
|
||||
|
||||
While Cosmopolitan was originally defined to just use stock GNU tools,
|
||||
this proved intractable over time, and the project has been evolving in
|
||||
the direction of building its own. Inventing the `apelink` program was
|
||||
what enabled the project to achieve multi-architecture binaries whereas
|
||||
previously it was only possible to do multi-OS binaries. In the future,
|
||||
our hope is that a fast power linker like Mold can be adapted to produce
|
||||
fat APE binaries directly from object files in one pass.
|
||||
|
||||
## Position Independent Code
|
||||
|
||||
APE doesn't currently support position independent executable formats.
|
||||
This is because APE was originally written for the GNU linker, where PIC
|
||||
and PIE were after-thoughts and never fully incorporated with the older
|
||||
more powerful linker script techniques upon which APE relies. Future
|
||||
iterations of this specification are intended to converge on modern
|
||||
standards, as our tooling becomes developed enough to support it.
|
||||
|
||||
However this only applies to the wrapped executable formats themselves.
|
||||
While our convention to date has been to always load ELF programs at the
|
||||
4mb mark, this is not guaranteed across OSes and architectures. Programs
|
||||
should have no expectations that a program will be loaded to any given
|
||||
address. For example, Cosmo currently implements APE on AARCH64 as
|
||||
loading executables to a starting address of 0x000800000000. This
|
||||
address occupies a sweet spot of requirements.
|
||||
|
||||
## Address Space
|
||||
|
||||
In order to create a single binary that supports as many platforms as
|
||||
possible without needing to be recompiled, there's a very narrow range
|
||||
of addresses that can be used. That range is somewhere between 32 bits
|
||||
and 39 bits.
|
||||
|
||||
- Embedded devices that claim to be 64-bit will oftentimes only support
|
||||
a virtual address space that's 39 bits in size.
|
||||
|
||||
- We can't load executable images on AARCH64 beneath 0x100000000 (4gb)
|
||||
because Apple forbids doing that, possibly in an effort to enforce a
|
||||
best practice for spotting 32-bit to 64-bit transition bugs. Please
|
||||
note that this restriction only applies to Apple ARM64 systems. The
|
||||
x86-64 version of XNU will happily load APE binaries to 0x00400000.
|
||||
|
||||
- The AMD64 architecture on desktops and servers can usually be counted
|
||||
upon to provide a 47-bit address space. The Linux Kernel for instance
|
||||
grants each userspace program full dominion over addresses 0x00200000
|
||||
through 0x00007fffffffffff provided the hardware supports this. On
|
||||
modern workstations supporting Intel and AMD's new PML5T feature which
|
||||
virtualizes memory using a radix trie that's five layers deep, Linux
|
||||
is able to offer userspace its choice of fixed addresses from
|
||||
0x00200000 through 0x00ffffffffffffff. The only exception to this rule
|
||||
we've encountered so far is that Windows 7 and Windows Vista behaved
|
||||
similar to embedded devices in reducing the number of va bits.
|
||||
|
||||
## Page Size
|
||||
|
||||
APE software MUST be page size agnostic. For many years the industry had
|
||||
converged on a strong consensus of having a page size that's 4096 bytes.
|
||||
However this convention was never guaranteed. New computers have become
|
||||
extremely popular, such as Apple Silicon, that use a 16kb page size.
|
||||
|
||||
By convention, Cosmopolitan Libc currently generates ELF headers for
|
||||
x86-64 that are strictly aligned on a 4096-byte page size. On ARM64
|
||||
Cosmopolitan is currently implemented to always generate ELF headers
|
||||
aligned on a 16kb page size.
|
||||
|
||||
In addition to being page size agnostic, APE software that cares about
|
||||
working correctly on Windows needs to be aware of the concept of
|
||||
allocation granularity. While the page size on Windows is generally 4kb
|
||||
in size, memory mappings can only be created on addresses that aligned
|
||||
to the system allocation granularity, which is generally 64kb. If you
|
||||
use a function like mmap() with Cosmopolitan Libc, then the `addr` and
|
||||
`offset` parameters need to be aligned to `sysconf(_SC_GRANSIZE)` or
|
||||
else your software won't work on Windows. Windows has other limitations
|
||||
too, such as lacking the ability to carve or punch holes in mappings.
|
||||
+11
-1
@@ -18,7 +18,7 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "ape/ape.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
#ifdef __aarch64__
|
||||
|
||||
@@ -61,6 +61,16 @@ netbsd.ident:
|
||||
4: .size netbsd.ident,.-netbsd.ident
|
||||
.type netbsd.ident,@object
|
||||
.balign 4
|
||||
freebsd.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
.long 1 // NT_FREEBSD_ABI_TAG
|
||||
1: .asciz "FreeBSD"
|
||||
2: .balign 4
|
||||
3: .long 1300000 // v13.0+ c. 2020
|
||||
4: .size freebsd.ident,.-freebsd.ident
|
||||
.type freebsd.ident,@object
|
||||
.balign 4
|
||||
ape.ident:
|
||||
.long 2f-1f
|
||||
.long 4f-3f
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
// Invokes system call.
|
||||
//
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -5,6 +5,7 @@
|
||||
-Wa,-msse2avx
|
||||
-Werror=maybe-uninitialized
|
||||
-Wno-literal-suffix
|
||||
-Wno-prio-ctor-dtor
|
||||
-Wno-unused-but-set-variable
|
||||
-Wunsafe-loop-optimizations
|
||||
-fbranch-target-load-optimize
|
||||
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
+85
-44
@@ -14,7 +14,6 @@ ENABLE_FTRACE = 1
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CCFLAGS += -O2 $(BACKTRACES)
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG
|
||||
TARGET_ARCH ?= -msse3
|
||||
endif
|
||||
ifeq ($(MODE), x86_64)
|
||||
ENABLE_FTRACE = 1
|
||||
@@ -64,7 +63,6 @@ ENABLE_FTRACE = 1
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DDWARFLESS
|
||||
CONFIG_LDFLAGS += -S
|
||||
TARGET_ARCH ?= -msse3
|
||||
endif
|
||||
|
||||
# Optimized Mode
|
||||
@@ -84,7 +82,7 @@ ENABLE_FTRACE = 1
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O3 -fmerge-all-constants
|
||||
TARGET_ARCH ?= -march=native
|
||||
CONFIG_TARGET_ARCH ?= -march=native
|
||||
endif
|
||||
|
||||
# Optimized Linux Mode
|
||||
@@ -98,11 +96,22 @@ endif
|
||||
# - Turns off support for other operating systems
|
||||
#
|
||||
ifeq ($(MODE), optlinux)
|
||||
TLSCC =
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
|
||||
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
|
||||
CONFIG_COPTS += -mred-zone
|
||||
TARGET_ARCH ?= -march=native
|
||||
CONFIG_TARGET_ARCH ?= -mavx
|
||||
endif
|
||||
ifeq ($(MODE), x86_64-optlinux)
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
|
||||
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
|
||||
CONFIG_TARGET_ARCH ?= -mavx
|
||||
endif
|
||||
ifeq ($(MODE), aarch64-optlinux)
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DSYSDEBUG -DSUPPORT_VECTOR=1
|
||||
CONFIG_CCFLAGS += -O3 -fmerge-all-constants
|
||||
endif
|
||||
|
||||
# Release Mode
|
||||
@@ -123,7 +132,6 @@ endif
|
||||
ifeq ($(MODE), rel)
|
||||
CONFIG_CPPFLAGS += -DNDEBUG -DDWARFLESS
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
TARGET_ARCH ?= -msse3
|
||||
PYFLAGS += -O1
|
||||
endif
|
||||
|
||||
@@ -139,19 +147,33 @@ endif
|
||||
ifeq ($(MODE), dbg)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O0 -fno-inline
|
||||
CONFIG_COPTS += -fsanitize=undefined
|
||||
TARGET_ARCH ?= -msse3
|
||||
OVERRIDE_CFLAGS += -O0
|
||||
OVERRIDE_CXXFLAGS += -O0
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
|
||||
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
|
||||
OVERRIDE_CCFLAGS += -fno-pie
|
||||
QUOTA ?= -C64 -L300
|
||||
endif
|
||||
ifeq ($(MODE), x86_64-dbg)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
OVERRIDE_CFLAGS += -O0
|
||||
OVERRIDE_CXXFLAGS += -O0
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
|
||||
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
|
||||
OVERRIDE_CCFLAGS += -fno-pie
|
||||
QUOTA ?= -C64 -L300
|
||||
endif
|
||||
ifeq ($(MODE), aarch64-dbg)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O0 -fno-inline -fdce
|
||||
CONFIG_COPTS += -fsanitize=undefined
|
||||
OVERRIDE_CFLAGS += -O0 -fdce
|
||||
OVERRIDE_CXXFLAGS += -O0 -fdce
|
||||
CONFIG_CPPFLAGS += -DMODE_DBG -D__SANITIZE_UNDEFINED__ -Wno-unused-variable -Wno-unused-but-set-variable
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG
|
||||
CONFIG_COPTS += -fsanitize=undefined -fno-data-sections
|
||||
QUOTA ?= -C64 -L300
|
||||
endif
|
||||
|
||||
@@ -167,10 +189,18 @@ endif
|
||||
#
|
||||
ifeq ($(MODE), sysv)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_OFLAGS ?= -g -ggdb
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
|
||||
TARGET_ARCH ?= -msse3
|
||||
endif
|
||||
ifeq ($(MODE), x86_64-sysv)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
|
||||
endif
|
||||
ifeq ($(MODE), aarch64-sysv)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121
|
||||
endif
|
||||
|
||||
# Tiny Mode
|
||||
@@ -188,8 +218,7 @@ endif
|
||||
ifeq ($(MODE), tiny)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY
|
||||
-DNDEBUG
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
@@ -200,8 +229,6 @@ CONFIG_CCFLAGS += \
|
||||
-momit-leaf-frame-pointer \
|
||||
-foptimize-sibling-calls \
|
||||
-DDWARFLESS
|
||||
TARGET_ARCH ?= \
|
||||
-msse3
|
||||
PYFLAGS += \
|
||||
-O2 \
|
||||
-B
|
||||
@@ -209,8 +236,7 @@ endif
|
||||
ifeq ($(MODE), x86_64-tiny)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY
|
||||
-DNDEBUG
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
@@ -221,8 +247,6 @@ CONFIG_CCFLAGS += \
|
||||
-momit-leaf-frame-pointer \
|
||||
-foptimize-sibling-calls \
|
||||
-DDWARFLESS
|
||||
TARGET_ARCH ?= \
|
||||
-msse3
|
||||
PYFLAGS += \
|
||||
-O2 \
|
||||
-B
|
||||
@@ -231,8 +255,7 @@ ifeq ($(MODE), aarch64-tiny)
|
||||
# TODO(jart): -mcmodel=tiny
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY
|
||||
-DNDEBUG
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
@@ -262,10 +285,37 @@ endif
|
||||
# - YOLO
|
||||
#
|
||||
ifeq ($(MODE), tinylinux)
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DSUPPORT_VECTOR=1 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
-fno-align-jumps \
|
||||
-fno-align-labels \
|
||||
-fno-align-loops
|
||||
endif
|
||||
ifeq ($(MODE), x86_64-tinylinux)
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DSUPPORT_VECTOR=1 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
-Os \
|
||||
-fno-align-functions \
|
||||
-fno-align-jumps \
|
||||
-fno-align-labels \
|
||||
-fno-align-loops
|
||||
endif
|
||||
ifeq ($(MODE), aarch64-tinylinux)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=1 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -274,8 +324,6 @@ CONFIG_CCFLAGS += \
|
||||
-fno-align-jumps \
|
||||
-fno-align-labels \
|
||||
-fno-align-loops
|
||||
TARGET_ARCH ?= \
|
||||
-msse3
|
||||
endif
|
||||
|
||||
# Linux+BSD Tiny Mode
|
||||
@@ -293,10 +341,10 @@ endif
|
||||
# - YOLO
|
||||
#
|
||||
ifeq ($(MODE), tinylinuxbsd)
|
||||
TLSCC =
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=113 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -305,8 +353,6 @@ CONFIG_CCFLAGS += \
|
||||
-fno-align-jumps \
|
||||
-fno-align-labels \
|
||||
-fno-align-loops
|
||||
TARGET_ARCH ?= \
|
||||
-msse3
|
||||
endif
|
||||
|
||||
# Unix Tiny Mode
|
||||
@@ -326,7 +372,6 @@ ifeq ($(MODE), tinysysv)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=121 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -335,8 +380,6 @@ CONFIG_CCFLAGS += \
|
||||
-fno-align-jumps \
|
||||
-fno-align-labels \
|
||||
-fno-align-loops
|
||||
TARGET_ARCH ?= \
|
||||
-msse3
|
||||
endif
|
||||
|
||||
# Tiny Metallic Unix Mode
|
||||
@@ -356,7 +399,6 @@ ifeq ($(MODE), tinynowin)
|
||||
CONFIG_CPPFLAGS += \
|
||||
-DTINY \
|
||||
-DNDEBUG \
|
||||
-DTRUSTWORTHY \
|
||||
-DSUPPORT_VECTOR=251 \
|
||||
-DDWARFLESS
|
||||
CONFIG_CCFLAGS += \
|
||||
@@ -365,8 +407,6 @@ CONFIG_CCFLAGS += \
|
||||
-fno-align-jumps \
|
||||
-fno-align-labels \
|
||||
-fno-align-loops
|
||||
TARGET_ARCH ?= \
|
||||
-msse3
|
||||
endif
|
||||
|
||||
# no x87 instructions mode
|
||||
@@ -388,7 +428,6 @@ ENABLE_FTRACE = 1
|
||||
CONFIG_COPTS += -mlong-double-64
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -O2
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG -DNOX87
|
||||
TARGET_ARCH ?= -msse3
|
||||
endif
|
||||
|
||||
# LLVM Mode
|
||||
@@ -400,8 +439,7 @@ endif
|
||||
# The rest of the monorepo may not work with llvm.
|
||||
#
|
||||
ifeq ($(MODE), llvm)
|
||||
.STRICT = 0
|
||||
TARGET_ARCH ?= -msse3
|
||||
.SANDBOXED = 0
|
||||
CONFIG_CCFLAGS += $(BACKTRACES) -DSYSDEBUG -O2
|
||||
AS = clang
|
||||
CC = clang
|
||||
@@ -441,10 +479,9 @@ endif
|
||||
# like objconv to convert them to COFF or MachO. Then use ANSI mode to
|
||||
# rollup one header file that'll enable linkage with minimal issues.
|
||||
ifeq ($(MODE), ansi)
|
||||
CONFIG_CFLAGS += -std=c11
|
||||
CONFIG_CFLAGS += -std=c23
|
||||
#CONFIG_CPPFLAGS += -ansi
|
||||
CONFIG_CXXFLAGS += -std=c++11
|
||||
TARGET_ARCH ?= -msse3
|
||||
CONFIG_CXXFLAGS += -std=c++23
|
||||
endif
|
||||
|
||||
ifneq ($(ENABLE_FTRACE),)
|
||||
@@ -482,7 +519,7 @@ endif
|
||||
ifeq ($(ARCH), aarch64)
|
||||
# this flag causes gcc to generate functions like this
|
||||
#
|
||||
# nop nop nop nop nop nop
|
||||
# nop nop nop nop nop nop nop nop
|
||||
# func:
|
||||
# nop
|
||||
# ...
|
||||
@@ -499,6 +536,10 @@ ifeq ($(ARCH), aarch64)
|
||||
# b -5
|
||||
# ...
|
||||
#
|
||||
CONFIG_CCFLAGS += -fpatchable-function-entry=7,6
|
||||
# we only need -fpatchable-function-entry=7,6 but we round up to 32
|
||||
# bytes so we don't break function alignment.
|
||||
CONFIG_CCFLAGS += -fpatchable-function-entry=9,8
|
||||
endif
|
||||
endif
|
||||
|
||||
TARGET_ARCH ?= $(CONFIG_TARGET_ARCH)
|
||||
+6
-17
@@ -53,11 +53,7 @@
|
||||
# OVERRIDE_FOO set ~/.cosmo.mk and target-specific (use rarely)
|
||||
#
|
||||
|
||||
ifeq ($(LANDLOCKMAKE_VERSION),)
|
||||
TMPSAFE = $(join $(TMPDIR)/,$(subst /,_,$@)).tmp
|
||||
else
|
||||
TMPSAFE = $(TMPDIR)/
|
||||
endif
|
||||
|
||||
BACKTRACES = \
|
||||
-fno-schedule-insns2 \
|
||||
@@ -92,11 +88,9 @@ DEFAULT_COPTS ?= \
|
||||
-fno-gnu-unique \
|
||||
-fstrict-aliasing \
|
||||
-fstrict-overflow \
|
||||
-fno-semantic-interposition \
|
||||
-fno-dwarf2-cfi-asm \
|
||||
-fno-unwind-tables \
|
||||
-fno-asynchronous-unwind-tables
|
||||
-fno-semantic-interposition
|
||||
|
||||
ifneq ($(TLSCC),)
|
||||
ifeq ($(ARCH), x86_64)
|
||||
# Microsoft says "[a]ny memory below the stack beyond the red zone
|
||||
# [note: Windows defines the x64 red zone size as 0] is considered
|
||||
@@ -106,6 +100,7 @@ DEFAULT_COPTS += \
|
||||
-mno-red-zone \
|
||||
-mno-tls-direct-seg-refs
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), aarch64)
|
||||
#
|
||||
@@ -115,14 +110,10 @@ ifeq ($(ARCH), aarch64)
|
||||
# - Cosmopolitan Libc uses x28 for thread-local storage because Apple
|
||||
# forbids us from using tpidr_el0 too.
|
||||
#
|
||||
# - Cosmopolitan currently lacks an implementation of the runtime
|
||||
# libraries needed by the -moutline-atomics flag
|
||||
#
|
||||
DEFAULT_COPTS += \
|
||||
-ffixed-x18 \
|
||||
-ffixed-x28 \
|
||||
-fsigned-char \
|
||||
-mno-outline-atomics
|
||||
-fsigned-char
|
||||
endif
|
||||
|
||||
MATHEMATICAL = \
|
||||
@@ -139,12 +130,10 @@ DEFAULT_CPPFLAGS += \
|
||||
-isystem libc/isystem
|
||||
|
||||
DEFAULT_CFLAGS = \
|
||||
-std=gnu2x
|
||||
-std=gnu23
|
||||
|
||||
DEFAULT_CXXFLAGS = \
|
||||
-std=gnu++20 \
|
||||
-fno-rtti \
|
||||
-fno-exceptions \
|
||||
-std=gnu++23 \
|
||||
-fuse-cxa-atexit \
|
||||
-Wno-int-in-bool-context \
|
||||
-Wno-narrowing \
|
||||
|
||||
@@ -99,3 +99,8 @@ rm -f cosmocc.zip cosmocc.zip.sha256sum
|
||||
# commit output directory
|
||||
cd "${OLDPWD}" || die
|
||||
mv "${OUTPUT_TMP}" "${OUTPUT_DIR}" || die
|
||||
|
||||
# update current symlink
|
||||
BASE=$(basename "${OUTPUT_DIR}")
|
||||
DIR=$(dirname "${OUTPUT_DIR}")
|
||||
ln -sfn "$BASE" "$DIR/current"
|
||||
+8
-8
@@ -6,14 +6,14 @@ if [ -n "$OBJDUMP" ]; then
|
||||
fi
|
||||
|
||||
find_objdump() {
|
||||
if [ -x .cosmocc/3.3.5/bin/$1-linux-cosmo-objdump ]; then
|
||||
OBJDUMP=.cosmocc/3.3.5/bin/$1-linux-cosmo-objdump
|
||||
elif [ -x .cosmocc/3.3.5/bin/$1-linux-musl-objdump ]; then
|
||||
OBJDUMP=.cosmocc/3.3.5/bin/$1-linux-musl-objdump
|
||||
elif [ -x "$COSMO/.cosmocc/3.3.5/bin/$1-linux-cosmo-objdump" ]; then
|
||||
OBJDUMP="$COSMO/.cosmocc/3.3.5/bin/$1-linux-cosmo-objdump"
|
||||
elif [ -x "$COSMO/.cosmocc/3.3.5/bin/$1-linux-musl-objdump" ]; then
|
||||
OBJDUMP="$COSMO/.cosmocc/3.3.5/bin/$1-linux-musl-objdump"
|
||||
if [ -x .cosmocc/3.9.2/bin/$1-linux-cosmo-objdump ]; then
|
||||
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump
|
||||
elif [ -x .cosmocc/3.9.2/bin/$1-linux-musl-objdump ]; then
|
||||
OBJDUMP=.cosmocc/3.9.2/bin/$1-linux-musl-objdump
|
||||
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump" ]; then
|
||||
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-cosmo-objdump"
|
||||
elif [ -x "$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump" ]; then
|
||||
OBJDUMP="$COSMO/.cosmocc/3.9.2/bin/$1-linux-musl-objdump"
|
||||
else
|
||||
echo "error: toolchain not found (try running 'cosmocc --update' or 'make' in the cosmo monorepo)" >&2
|
||||
exit 1
|
||||
|
||||
+1
-2
@@ -23,8 +23,7 @@
|
||||
# - tool/build/runitd.c
|
||||
|
||||
.PRECIOUS: o/$(MODE)/%.ok
|
||||
o/$(MODE)/%.ok: private .PLEDGE = stdio rpath wpath cpath proc fattr inet dns
|
||||
o/$(MODE)/%.ok: private .UNVEIL += r:/etc/resolv.conf
|
||||
o/$(MODE)/%.ok: private .PLEDGE = fattr inet dns
|
||||
o/$(MODE)/%.ok: \
|
||||
o/$(MODE)/tool/build/runit \
|
||||
o/$(MODE)/tool/build/runitd \
|
||||
|
||||
+10
-19
@@ -145,16 +145,7 @@ o/%.zip.o: o/%
|
||||
o/$(MODE)/%.zip.o: %
|
||||
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
|
||||
|
||||
o/$(MODE)/%.zip.o: %
|
||||
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
|
||||
|
||||
# an issue with sandboxing arises when creating directory entries in the
|
||||
# zip file. we need the trailing slash (e.g. o//foo/.zip.o) but Landlock
|
||||
# Make avoids sandboxing directory names that have a trailing slash (so
|
||||
# they can be used to watch for deleted files, without creating overly
|
||||
# broad unveiling). such rules need to be written more explicitly.
|
||||
o/$(MODE)%/.zip.o: %
|
||||
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
|
||||
o/$(MODE)/%.zip.o: private .SANDBOXED = 0
|
||||
|
||||
################################################################################
|
||||
# STRICT HEADER CHECKING
|
||||
@@ -163,43 +154,43 @@ o/$(MODE)%/.zip.o: %
|
||||
# and it would be too costly in terms of make latency to have every
|
||||
# header file depend on $(HDRS) and $(INCS).
|
||||
|
||||
o/%.h.ok: private .UNSANDBOXED = 1
|
||||
o/%.h.ok: private .SANDBOXED = 0
|
||||
o/%.h.ok: %.h
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $<
|
||||
|
||||
o/$(MODE)/%.h.ok: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%.h.ok: private .SANDBOXED = 0
|
||||
o/$(MODE)/%.h.ok: %.h
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.c) -xc -g0 -o $@ $<
|
||||
|
||||
o/$(MODE)/%.hh.ok: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%.hh.ok: private .SANDBOXED = 0
|
||||
o/$(MODE)/%.hh.ok: %.hh
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
|
||||
|
||||
o/%.okk: .UNSANDBOXED = 1
|
||||
o/%.okk: .SANDBOXED = 0
|
||||
o/%.okk: %
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
|
||||
|
||||
o/$(MODE)/%.okk: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%.okk: private .SANDBOXED = 0
|
||||
o/$(MODE)/%.okk: %
|
||||
@$(COMPILE) -ACHECK.h $(COMPILE.cxx) -xc++ -g0 -o $@ $<
|
||||
|
||||
################################################################################
|
||||
# EMACS ASSEMBLY GENERATION
|
||||
|
||||
o/$(MODE)/%-gcc.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-gcc.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-gcc.asm: %.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
|
||||
|
||||
o/$(MODE)/%-gcc.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-gcc.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-gcc.asm: %.cc
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
|
||||
|
||||
o/$(MODE)/%-clang.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-clang.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-clang.asm: %.c
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
|
||||
|
||||
# TODO(jart): Make intrinsics support Clang.
|
||||
o/$(MODE)/%-clang.asm: CC = $(CLANG)
|
||||
o/$(MODE)/%-clang.asm: private .UNSANDBOXED = 1
|
||||
o/$(MODE)/%-clang.asm: private .SANDBOXED = 0
|
||||
o/$(MODE)/%-clang.asm: %.cc
|
||||
@$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
|
||||
@@ -4,5 +4,5 @@ UNAMES=$(uname -s)
|
||||
if [ x"$UNAMES" = x"Darwin" ] && [ x"$UNAMEM" = x"arm64" ]; then
|
||||
exec ape "$@"
|
||||
else
|
||||
exec "$@"
|
||||
exec rusage "$@"
|
||||
fi
|
||||
@@ -0,0 +1,10 @@
|
||||
-nostdinc
|
||||
-iquote
|
||||
.
|
||||
-isystem
|
||||
libc/isystem
|
||||
-include
|
||||
libc/integral/normalize.inc
|
||||
-D_COSMO_SOURCE
|
||||
-DMODE="aarch64"
|
||||
-Wno-unknown-pragmas
|
||||
@@ -17,6 +17,9 @@ struct conditional<false, T, F>
|
||||
typedef F type;
|
||||
};
|
||||
|
||||
template<bool B, typename T, typename F>
|
||||
using conditional_t = typename conditional<B, T, F>::type;
|
||||
|
||||
} // namespace ctl
|
||||
|
||||
#endif // CTL_CONDITIONAL_H_
|
||||
@@ -19,6 +19,9 @@ template<typename _Tp>
|
||||
struct is_void : public is_void_<typename ctl::remove_cv<_Tp>::type>::type
|
||||
{};
|
||||
|
||||
template<typename T>
|
||||
inline constexpr bool is_void_v = is_void<T>::value;
|
||||
|
||||
} // namespace ctl
|
||||
|
||||
#endif // CTL_IS_VOID_H_
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
namespace ctl {
|
||||
|
||||
struct string;
|
||||
class string;
|
||||
|
||||
class istream : public ios
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#define COSMOPOLITAN_CTL_NEW_H_
|
||||
|
||||
#ifndef TINY
|
||||
__static_yoink("__demangle");
|
||||
__static_yoink("cosmo_demangle");
|
||||
#endif
|
||||
|
||||
namespace ctl {
|
||||
|
||||
@@ -241,8 +241,9 @@ class set
|
||||
private:
|
||||
friend class set;
|
||||
node_type* node_;
|
||||
node_type* root_;
|
||||
|
||||
explicit reverse_iterator(node_type* node) : node_(node)
|
||||
explicit reverse_iterator(node_type* node, node_type* root) : node_(node), root_(root)
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -347,17 +348,17 @@ class set
|
||||
|
||||
reverse_iterator rbegin()
|
||||
{
|
||||
return reverse_iterator(rightmost(root_));
|
||||
return reverse_iterator(rightmost(root_), root_);
|
||||
}
|
||||
|
||||
const_reverse_iterator rbegin() const
|
||||
{
|
||||
return const_reverse_iterator(rightmost(root_));
|
||||
return const_reverse_iterator(rightmost(root_), root_);
|
||||
}
|
||||
|
||||
const_reverse_iterator crbegin() const
|
||||
{
|
||||
return const_reverse_iterator(rightmost(root_));
|
||||
return const_reverse_iterator(rightmost(root_), root_);
|
||||
}
|
||||
|
||||
iterator end() noexcept
|
||||
@@ -377,17 +378,17 @@ class set
|
||||
|
||||
reverse_iterator rend()
|
||||
{
|
||||
return reverse_iterator(nullptr);
|
||||
return reverse_iterator(nullptr, root_);
|
||||
}
|
||||
|
||||
const_reverse_iterator rend() const
|
||||
{
|
||||
return const_reverse_iterator(nullptr);
|
||||
return const_reverse_iterator(nullptr, root_);
|
||||
}
|
||||
|
||||
const_reverse_iterator crend() const
|
||||
{
|
||||
return const_reverse_iterator(nullptr);
|
||||
return const_reverse_iterator(nullptr, root_);
|
||||
}
|
||||
|
||||
void clear() noexcept
|
||||
|
||||
@@ -0,0 +1,618 @@
|
||||
// -*-mode:c++;indent-tabs-mode:nil;c-basic-offset:4;tab-width:8;coding:utf-8-*-
|
||||
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
|
||||
#ifndef CTL_SHARED_PTR_H_
|
||||
#define CTL_SHARED_PTR_H_
|
||||
|
||||
#include "exception.h"
|
||||
#include "is_base_of.h"
|
||||
#include "is_constructible.h"
|
||||
#include "is_convertible.h"
|
||||
#include "remove_extent.h"
|
||||
#include "unique_ptr.h"
|
||||
|
||||
// XXX currently needed to use placement-new syntax (move to cxx.inc?)
|
||||
void*
|
||||
operator new(size_t, void*) noexcept;
|
||||
|
||||
namespace ctl {
|
||||
|
||||
class bad_weak_ptr : public exception
|
||||
{
|
||||
public:
|
||||
const char* what() const noexcept override
|
||||
{
|
||||
return "ctl::bad_weak_ptr";
|
||||
}
|
||||
};
|
||||
|
||||
namespace __ {
|
||||
|
||||
template<typename T>
|
||||
struct ptr_ref
|
||||
{
|
||||
using type = T&;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ptr_ref<void>
|
||||
{
|
||||
using type = void;
|
||||
};
|
||||
|
||||
static inline __attribute__((always_inline)) void
|
||||
incref(size_t* r) noexcept
|
||||
{
|
||||
#ifdef NDEBUG
|
||||
__atomic_fetch_add(r, 1, __ATOMIC_RELAXED);
|
||||
#else
|
||||
ssize_t refs = __atomic_fetch_add(r, 1, __ATOMIC_RELAXED);
|
||||
if (refs < 0)
|
||||
__builtin_trap();
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline)) bool
|
||||
decref(size_t* r) noexcept
|
||||
{
|
||||
if (!__atomic_fetch_sub(r, 1, __ATOMIC_RELEASE)) {
|
||||
__atomic_thread_fence(__ATOMIC_ACQUIRE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class shared_ref
|
||||
{
|
||||
public:
|
||||
constexpr shared_ref() noexcept = default;
|
||||
shared_ref(const shared_ref&) = delete;
|
||||
shared_ref& operator=(const shared_ref&) = delete;
|
||||
|
||||
virtual ~shared_ref() = default;
|
||||
|
||||
void keep_shared() noexcept
|
||||
{
|
||||
incref(&shared);
|
||||
}
|
||||
|
||||
void drop_shared() noexcept
|
||||
{
|
||||
if (decref(&shared)) {
|
||||
dispose();
|
||||
drop_weak();
|
||||
}
|
||||
}
|
||||
|
||||
void keep_weak() noexcept
|
||||
{
|
||||
incref(&weak);
|
||||
}
|
||||
|
||||
void drop_weak() noexcept
|
||||
{
|
||||
if (decref(&weak)) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
size_t use_count() const noexcept
|
||||
{
|
||||
return __atomic_load_n(&shared, __ATOMIC_RELAXED) + 1;
|
||||
}
|
||||
|
||||
size_t weak_count() const noexcept
|
||||
{
|
||||
return __atomic_load_n(&weak, __ATOMIC_RELAXED);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void dispose() noexcept = 0;
|
||||
|
||||
size_t shared = 0;
|
||||
size_t weak = 0;
|
||||
};
|
||||
|
||||
template<typename T, typename D>
|
||||
class shared_pointer : public shared_ref
|
||||
{
|
||||
public:
|
||||
static shared_pointer* make(T* const p, D d)
|
||||
{
|
||||
return make(unique_ptr<T, D>(p, move(d)));
|
||||
}
|
||||
|
||||
static shared_pointer* make(unique_ptr<T, D> p)
|
||||
{
|
||||
return new shared_pointer(p.release(), move(p.get_deleter()));
|
||||
}
|
||||
|
||||
private:
|
||||
shared_pointer(T* const p, D d) noexcept : p(p), d(move(d))
|
||||
{
|
||||
}
|
||||
|
||||
void dispose() noexcept override
|
||||
{
|
||||
move(d)(p);
|
||||
}
|
||||
|
||||
T* const p;
|
||||
[[no_unique_address]] D d;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class shared_emplace : public shared_ref
|
||||
{
|
||||
public:
|
||||
union
|
||||
{
|
||||
T t;
|
||||
};
|
||||
|
||||
~shared_emplace() override
|
||||
{
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void construct(Args&&... args)
|
||||
{
|
||||
::new (&t) T(forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static unique_ptr<shared_emplace> make()
|
||||
{
|
||||
return unique_ptr(new shared_emplace());
|
||||
}
|
||||
|
||||
private:
|
||||
explicit constexpr shared_emplace() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
void dispose() noexcept override
|
||||
{
|
||||
t.~T();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
concept shared_ptr_compatible = is_convertible_v<U*, T*>;
|
||||
|
||||
} // namespace __
|
||||
|
||||
template<typename T>
|
||||
class weak_ptr;
|
||||
|
||||
template<typename T>
|
||||
class shared_ptr
|
||||
{
|
||||
public:
|
||||
using element_type = remove_extent_t<T>;
|
||||
using weak_type = weak_ptr<T>;
|
||||
|
||||
constexpr shared_ptr() noexcept = default;
|
||||
constexpr shared_ptr(nullptr_t) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
explicit shared_ptr(U* const p) : shared_ptr(p, default_delete<U>())
|
||||
{
|
||||
}
|
||||
|
||||
template<typename U, typename D>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
shared_ptr(U*, D);
|
||||
|
||||
template<typename U>
|
||||
shared_ptr(const shared_ptr<U>& r, element_type* p) noexcept
|
||||
: p(p), rc(r.rc)
|
||||
{
|
||||
if (rc)
|
||||
rc->keep_shared();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
shared_ptr(shared_ptr<U>&& r, element_type* p) noexcept : p(p), rc(r.rc)
|
||||
{
|
||||
r.p = nullptr;
|
||||
r.rc = nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
shared_ptr(const shared_ptr<U>& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
if (rc)
|
||||
rc->keep_shared();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
shared_ptr(shared_ptr<U>&& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
r.p = nullptr;
|
||||
r.rc = nullptr;
|
||||
}
|
||||
|
||||
shared_ptr(const shared_ptr& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
if (rc)
|
||||
rc->keep_shared();
|
||||
}
|
||||
|
||||
shared_ptr(shared_ptr&& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
r.p = nullptr;
|
||||
r.rc = nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
explicit shared_ptr(const weak_ptr<U>& r) : p(r.p), rc(r.rc)
|
||||
{
|
||||
if (r.expired()) {
|
||||
throw bad_weak_ptr();
|
||||
}
|
||||
rc->keep_shared();
|
||||
}
|
||||
|
||||
template<typename U, typename D>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
shared_ptr(unique_ptr<U, D>&& r)
|
||||
: p(r.p), rc(__::shared_pointer<U, D>::make(move(r)))
|
||||
{
|
||||
}
|
||||
|
||||
~shared_ptr()
|
||||
{
|
||||
if (rc)
|
||||
rc->drop_shared();
|
||||
}
|
||||
|
||||
shared_ptr& operator=(shared_ptr r) noexcept
|
||||
{
|
||||
swap(r);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
shared_ptr& operator=(shared_ptr<U> r) noexcept
|
||||
{
|
||||
shared_ptr<T>(move(r)).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset() noexcept
|
||||
{
|
||||
shared_ptr().swap(*this);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
void reset(U* const p2)
|
||||
{
|
||||
shared_ptr<T>(p2).swap(*this);
|
||||
}
|
||||
|
||||
template<typename U, typename D>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
void reset(U* const p2, D d)
|
||||
{
|
||||
shared_ptr<T>(p2, d).swap(*this);
|
||||
}
|
||||
|
||||
void swap(shared_ptr& r) noexcept
|
||||
{
|
||||
using ctl::swap;
|
||||
swap(p, r.p);
|
||||
swap(rc, r.rc);
|
||||
}
|
||||
|
||||
element_type* get() const noexcept
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
typename __::ptr_ref<T>::type operator*() const noexcept
|
||||
{
|
||||
if (!p)
|
||||
__builtin_trap();
|
||||
return *p;
|
||||
}
|
||||
|
||||
T* operator->() const noexcept
|
||||
{
|
||||
if (!p)
|
||||
__builtin_trap();
|
||||
return p;
|
||||
}
|
||||
|
||||
long use_count() const noexcept
|
||||
{
|
||||
return rc ? rc->use_count() : 0;
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
bool owner_before(const shared_ptr<U>& r) const noexcept
|
||||
{
|
||||
return rc < r.rc;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
bool owner_before(const weak_ptr<U>& r) const noexcept
|
||||
{
|
||||
return rc < r.rc;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename U>
|
||||
friend class weak_ptr;
|
||||
|
||||
template<typename U>
|
||||
friend class shared_ptr;
|
||||
|
||||
template<typename U, typename... Args>
|
||||
friend shared_ptr<U> make_shared(Args&&... args);
|
||||
|
||||
element_type* p = nullptr;
|
||||
__::shared_ref* rc = nullptr;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class weak_ptr
|
||||
{
|
||||
public:
|
||||
using element_type = remove_extent_t<T>;
|
||||
|
||||
constexpr weak_ptr() noexcept = default;
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
weak_ptr(const shared_ptr<U>& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
if (rc)
|
||||
rc->keep_weak();
|
||||
}
|
||||
|
||||
weak_ptr(const weak_ptr& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
if (rc)
|
||||
rc->keep_weak();
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
weak_ptr(const weak_ptr<U>& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
if (rc)
|
||||
rc->keep_weak();
|
||||
}
|
||||
|
||||
weak_ptr(weak_ptr&& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
r.p = nullptr;
|
||||
r.rc = nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
weak_ptr(weak_ptr<U>&& r) noexcept : p(r.p), rc(r.rc)
|
||||
{
|
||||
r.p = nullptr;
|
||||
r.rc = nullptr;
|
||||
}
|
||||
|
||||
~weak_ptr()
|
||||
{
|
||||
if (rc)
|
||||
rc->drop_weak();
|
||||
}
|
||||
|
||||
long use_count() const noexcept
|
||||
{
|
||||
return rc ? rc->use_count() : 0;
|
||||
}
|
||||
|
||||
bool expired() const noexcept
|
||||
{
|
||||
return !use_count();
|
||||
}
|
||||
|
||||
void reset() noexcept
|
||||
{
|
||||
weak_ptr().swap(*this);
|
||||
}
|
||||
|
||||
void swap(weak_ptr& r) noexcept
|
||||
{
|
||||
using ctl::swap;
|
||||
swap(p, r.p);
|
||||
swap(rc, r.rc);
|
||||
}
|
||||
|
||||
weak_ptr& operator=(weak_ptr r) noexcept
|
||||
{
|
||||
swap(r);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
weak_ptr& operator=(weak_ptr<U> r) noexcept
|
||||
{
|
||||
weak_ptr<T>(move(r)).swap(*this);
|
||||
}
|
||||
|
||||
shared_ptr<T> lock() const noexcept
|
||||
{
|
||||
if (expired())
|
||||
return nullptr;
|
||||
shared_ptr<T> r;
|
||||
r.p = p;
|
||||
r.rc = rc;
|
||||
if (rc)
|
||||
rc->keep_shared();
|
||||
return r;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
bool owner_before(const weak_ptr<U>& r) const noexcept
|
||||
{
|
||||
return rc < r.rc;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
bool owner_before(const shared_ptr<U>& r) const noexcept
|
||||
{
|
||||
return rc < r.rc;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename U>
|
||||
friend class shared_ptr;
|
||||
|
||||
template<typename U, typename... Args>
|
||||
friend shared_ptr<U> make_shared(Args&&...);
|
||||
|
||||
element_type* p = nullptr;
|
||||
__::shared_ref* rc = nullptr;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class enable_shared_from_this
|
||||
{
|
||||
public:
|
||||
shared_ptr<T> shared_from_this()
|
||||
{
|
||||
return shared_ptr<T>(weak_this);
|
||||
}
|
||||
shared_ptr<T const> shared_from_this() const
|
||||
{
|
||||
return shared_ptr<T>(weak_this);
|
||||
}
|
||||
|
||||
weak_ptr<T> weak_from_this()
|
||||
{
|
||||
return weak_this;
|
||||
}
|
||||
weak_ptr<T const> weak_from_this() const
|
||||
{
|
||||
return weak_this;
|
||||
}
|
||||
|
||||
protected:
|
||||
constexpr enable_shared_from_this() noexcept = default;
|
||||
enable_shared_from_this(const enable_shared_from_this& r) noexcept
|
||||
{
|
||||
}
|
||||
~enable_shared_from_this() = default;
|
||||
|
||||
enable_shared_from_this& operator=(
|
||||
const enable_shared_from_this& r) noexcept
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename U, typename... Args>
|
||||
friend shared_ptr<U> make_shared(Args&&...);
|
||||
|
||||
template<typename U>
|
||||
friend class shared_ptr;
|
||||
|
||||
weak_ptr<T> weak_this;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
template<typename U, typename D>
|
||||
requires __::shared_ptr_compatible<T, U>
|
||||
shared_ptr<T>::shared_ptr(U* const p, D d)
|
||||
: p(p), rc(__::shared_pointer<U, D>::make(p, move(d)))
|
||||
{
|
||||
if constexpr (is_base_of_v<enable_shared_from_this<U>, U>) {
|
||||
p->weak_this = *this;
|
||||
}
|
||||
}
|
||||
|
||||
// Our make_shared supports passing a weak self reference as the first parameter
|
||||
// to your constructor, e.g.:
|
||||
//
|
||||
// struct Tree : ctl::weak_self_base
|
||||
// {
|
||||
// ctl::shared_ptr<Tree> l, r;
|
||||
// ctl::weak_ptr<Tree> parent;
|
||||
// Tree(weak_ptr<Tree> const& self, auto&& l2, auto&& r2)
|
||||
// : l(ctl::forward<decltype(l2)>(l2)),
|
||||
// r(ctl::forward<decltype(r2)>(r2))
|
||||
// {
|
||||
// if (l) l->parent = self;
|
||||
// if (r) r->parent = self;
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// int main() {
|
||||
// auto t = ctl::make_shared<Tree>(
|
||||
// ctl::make_shared<Tree>(nullptr, nullptr), nullptr);
|
||||
// return t->l->parent.lock().get() == t.get() ? 0 : 1;
|
||||
// }
|
||||
//
|
||||
// As shown, passing the parameter at object construction time lets you complete
|
||||
// object construction without needing a separate Init method. But because we go
|
||||
// off spec as far as the STL is concerned, there is a potential ambiguity where
|
||||
// you might have a constructor with a weak_ptr first parameter that is intended
|
||||
// to be something other than a self-reference. So this feature is opt-in by way
|
||||
// of inheriting from the following struct.
|
||||
struct weak_self_base
|
||||
{};
|
||||
|
||||
template<typename T, typename... Args>
|
||||
shared_ptr<T>
|
||||
make_shared(Args&&... args)
|
||||
{
|
||||
unique_ptr rc = __::shared_emplace<T>::make();
|
||||
if constexpr (is_base_of_v<weak_self_base, T> &&
|
||||
is_constructible_v<T, const weak_ptr<T>&, Args...>) {
|
||||
// A __::shared_ref has a virtual weak reference that is owned by all of
|
||||
// the shared references. We can avoid some unnecessary refcount changes
|
||||
// by "borrowing" that reference and passing it to the constructor, then
|
||||
// promoting it to a shared reference by swapping it with the shared_ptr
|
||||
// that we return.
|
||||
weak_ptr<T> w;
|
||||
w.p = &rc->t;
|
||||
w.rc = rc.get();
|
||||
try {
|
||||
rc->construct(const_cast<const weak_ptr<T>&>(w),
|
||||
forward<Args>(args)...);
|
||||
} catch (...) {
|
||||
w.p = nullptr;
|
||||
w.rc = nullptr;
|
||||
throw;
|
||||
}
|
||||
rc.release();
|
||||
shared_ptr<T> r;
|
||||
swap(r.p, w.p);
|
||||
swap(r.rc, w.rc);
|
||||
return r;
|
||||
} else {
|
||||
rc->construct(forward<Args>(args)...);
|
||||
shared_ptr<T> r;
|
||||
r.p = &rc->t;
|
||||
r.rc = rc.release();
|
||||
if constexpr (is_base_of_v<enable_shared_from_this<T>, T>) {
|
||||
r->weak_this = r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ctl
|
||||
|
||||
#endif // CTL_SHARED_PTR_H_
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// -*- mode:c++; indent-tabs-mode:nil; c-basic-offset:4; coding:utf-8 -*-
|
||||
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
|
||||
//
|
||||
// Copyright 2026 Justin Alexander Roberts Tunney
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for
|
||||
// any purpose with or without fee is hereby granted, provided that the
|
||||
// above copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include "sort.h"
|
||||
#include "libc/mem/alg.h"
|
||||
|
||||
namespace ctl {
|
||||
namespace detail {
|
||||
|
||||
// This lives out of line so that programs calling ctl::sort() only need
|
||||
// to depend on ctl, rather than on whichever library qsort_r() is in.
|
||||
void
|
||||
sort_r(void* a,
|
||||
size_t n,
|
||||
size_t es,
|
||||
int (*cmp)(const void*, const void*, void*),
|
||||
void* arg)
|
||||
{
|
||||
qsort_r(a, n, es, cmp, arg);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace ctl
|
||||
+71
-9
@@ -2,23 +2,54 @@
|
||||
// vi: set et ft=cpp ts=4 sts=4 sw=4 fenc=utf-8 :vi
|
||||
#ifndef CTL_SORT_H_
|
||||
#define CTL_SORT_H_
|
||||
#include "is_same.h"
|
||||
#include "iterator_traits.h"
|
||||
#include "less.h"
|
||||
#include "partition.h"
|
||||
#include "new.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace ctl {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename RandomIt, typename Compare>
|
||||
void
|
||||
quicksort(RandomIt first, RandomIt last, Compare comp)
|
||||
sort_r(void*, size_t, size_t, int (*)(const void*, const void*, void*), void*);
|
||||
|
||||
template<typename T, typename Compare>
|
||||
int
|
||||
sort_compare(const T& x, const T& y, Compare& comp)
|
||||
{
|
||||
if (first < last) {
|
||||
auto pivot = ctl::partition(first, last, comp);
|
||||
quicksort(first, pivot, comp);
|
||||
quicksort(pivot + 1, last, comp);
|
||||
}
|
||||
if (comp(x, y))
|
||||
return -1;
|
||||
if (comp(y, x))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T, typename Compare>
|
||||
int
|
||||
sort_direct(const void* a, const void* b, void* arg)
|
||||
{
|
||||
return sort_compare(*static_cast<const T*>(a),
|
||||
*static_cast<const T*>(b),
|
||||
*static_cast<Compare*>(arg));
|
||||
}
|
||||
|
||||
template<typename RandomIt, typename Compare>
|
||||
struct sort_context
|
||||
{
|
||||
RandomIt first;
|
||||
Compare& comp;
|
||||
};
|
||||
|
||||
template<typename RandomIt, typename Compare>
|
||||
int
|
||||
sort_indirect(const void* a, const void* b, void* arg)
|
||||
{
|
||||
auto* ctx = static_cast<sort_context<RandomIt, Compare>*>(arg);
|
||||
return sort_compare(ctx->first[*static_cast<const size_t*>(a)],
|
||||
ctx->first[*static_cast<const size_t*>(b)],
|
||||
ctx->comp);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
@@ -27,7 +58,38 @@ template<typename RandomIt, typename Compare>
|
||||
void
|
||||
sort(RandomIt first, RandomIt last, Compare comp)
|
||||
{
|
||||
detail::quicksort(first, last, comp);
|
||||
using T = typename ctl::iterator_traits<RandomIt>::value_type;
|
||||
size_t n = last - first;
|
||||
if (n < 2)
|
||||
return;
|
||||
if constexpr (ctl::is_same_v<RandomIt, T*> && __is_trivially_copyable(T)) {
|
||||
detail::sort_r(
|
||||
first, n, sizeof(T), detail::sort_direct<T, Compare>, &comp);
|
||||
return;
|
||||
}
|
||||
size_t* idx = new size_t[n];
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
idx[i] = i;
|
||||
detail::sort_context<RandomIt, Compare> ctx{ first, comp };
|
||||
detail::sort_r(
|
||||
idx, n, sizeof(size_t), detail::sort_indirect<RandomIt, Compare>, &ctx);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (idx[i] == i)
|
||||
continue;
|
||||
T tmp(ctl::move(first[i]));
|
||||
size_t j = i;
|
||||
for (;;) {
|
||||
size_t k = idx[j];
|
||||
idx[j] = j;
|
||||
if (k == i) {
|
||||
first[j] = ctl::move(tmp);
|
||||
break;
|
||||
}
|
||||
first[j] = ctl::move(first[k]);
|
||||
j = k;
|
||||
}
|
||||
}
|
||||
delete[] idx;
|
||||
}
|
||||
|
||||
template<typename RandomIt>
|
||||
|
||||
+72
-1
@@ -21,6 +21,9 @@
|
||||
#include <__atomic/fence.h>
|
||||
#include <stdckdint.h>
|
||||
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
namespace ctl {
|
||||
|
||||
void
|
||||
@@ -274,7 +277,7 @@ size_t
|
||||
string::find(const char ch, const size_t pos) const noexcept
|
||||
{
|
||||
char* q;
|
||||
if ((q = (char*)memchr(data(), ch, size())))
|
||||
if (pos < size() && (q = (char*)memchr(data() + pos, ch, size() - pos)))
|
||||
return q - data();
|
||||
return npos;
|
||||
}
|
||||
@@ -380,4 +383,72 @@ string::erase(const size_t pos, size_t count) noexcept
|
||||
return *this;
|
||||
}
|
||||
|
||||
void
|
||||
string::append(const ctl::string_view& s, size_t pos, size_t count) noexcept
|
||||
{
|
||||
append(s.substr(pos, count));
|
||||
}
|
||||
|
||||
size_t
|
||||
string::find_last_of(char c, size_t pos) const noexcept
|
||||
{
|
||||
const char* b = data();
|
||||
size_t n = size();
|
||||
if (pos > n)
|
||||
pos = n;
|
||||
const char* p = (const char*)memrchr(b, c, pos);
|
||||
return p ? p - b : npos;
|
||||
}
|
||||
|
||||
size_t
|
||||
string::find_last_of(ctl::string_view set, size_t pos) const noexcept
|
||||
{
|
||||
if (empty() || set.empty())
|
||||
return npos;
|
||||
bool lut[256] = {};
|
||||
for (char c : set)
|
||||
lut[c & 255] = true;
|
||||
const char* b = data();
|
||||
size_t last = size() - 1;
|
||||
if (pos > last)
|
||||
pos = last;
|
||||
for (;;) {
|
||||
if (lut[b[pos] & 255])
|
||||
return pos;
|
||||
if (!pos)
|
||||
return npos;
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
string::find_first_of(char c, size_t pos) const noexcept
|
||||
{
|
||||
size_t n = size();
|
||||
if (pos >= n)
|
||||
return npos;
|
||||
const char* b = data();
|
||||
const char* p = (const char*)memchr(b + pos, c, n - pos);
|
||||
return p ? p - b : npos;
|
||||
}
|
||||
|
||||
size_t
|
||||
string::find_first_of(ctl::string_view set, size_t pos) const noexcept
|
||||
{
|
||||
if (set.empty())
|
||||
return npos;
|
||||
bool lut[256] = {};
|
||||
for (char c : set)
|
||||
lut[c & 255] = true;
|
||||
const char* b = data();
|
||||
size_t n = size();
|
||||
for (;;) {
|
||||
if (pos >= n)
|
||||
return npos;
|
||||
if (lut[b[pos] & 255])
|
||||
return pos;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ctl
|
||||
+6
-1
@@ -125,6 +125,7 @@ class string
|
||||
void append(char, size_t) noexcept;
|
||||
void append(unsigned long) noexcept;
|
||||
void append(const void*, size_t) noexcept;
|
||||
void append(const ctl::string_view&, size_t, size_t = npos) noexcept;
|
||||
string& insert(size_t, ctl::string_view) noexcept;
|
||||
string& erase(size_t = 0, size_t = npos) noexcept;
|
||||
string substr(size_t = 0, size_t = npos) const noexcept;
|
||||
@@ -136,6 +137,10 @@ class string
|
||||
bool starts_with(ctl::string_view) const noexcept;
|
||||
size_t find(char, size_t = 0) const noexcept;
|
||||
size_t find(ctl::string_view, size_t = 0) const noexcept;
|
||||
size_t find_first_of(char, size_t = 0) const noexcept;
|
||||
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
|
||||
size_t find_last_of(char, size_t = npos) const noexcept;
|
||||
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
|
||||
|
||||
void swap(string& s) noexcept
|
||||
{
|
||||
@@ -302,7 +307,7 @@ class string
|
||||
append(ch);
|
||||
}
|
||||
|
||||
void append(const ctl::string_view s) noexcept
|
||||
void append(const ctl::string_view& s) noexcept
|
||||
{
|
||||
append(s.p, s.n);
|
||||
}
|
||||
|
||||
+63
-1
@@ -29,7 +29,7 @@ size_t
|
||||
string_view::find(const char ch, const size_t pos) const noexcept
|
||||
{
|
||||
char* q;
|
||||
if (n && (q = (char*)memchr(p, ch, n)))
|
||||
if (pos < n && (q = (char*)memchr(p + pos, ch, n - pos)))
|
||||
return q - p;
|
||||
return npos;
|
||||
}
|
||||
@@ -108,4 +108,66 @@ string_view::starts_with(const string_view s) const noexcept
|
||||
return !memcmp(p, s.p, s.n);
|
||||
}
|
||||
|
||||
size_t
|
||||
string_view::find_last_of(char c, size_t pos) const noexcept
|
||||
{
|
||||
const char* b = data();
|
||||
size_t n = size();
|
||||
if (pos > n)
|
||||
pos = n;
|
||||
const char* p = (const char*)memrchr(b, c, pos);
|
||||
return p ? p - b : npos;
|
||||
}
|
||||
|
||||
size_t
|
||||
string_view::find_last_of(ctl::string_view set, size_t pos) const noexcept
|
||||
{
|
||||
if (empty() || set.empty())
|
||||
return npos;
|
||||
bool lut[256] = {};
|
||||
for (char c : set)
|
||||
lut[c & 255] = true;
|
||||
const char* b = data();
|
||||
size_t last = size() - 1;
|
||||
if (pos > last)
|
||||
pos = last;
|
||||
for (;;) {
|
||||
if (lut[b[pos] & 255])
|
||||
return pos;
|
||||
if (!pos)
|
||||
return npos;
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
string_view::find_first_of(char c, size_t pos) const noexcept
|
||||
{
|
||||
size_t n = size();
|
||||
if (pos >= n)
|
||||
return npos;
|
||||
const char* b = data();
|
||||
const char* p = (const char*)memchr(b + pos, c, n - pos);
|
||||
return p ? p - b : npos;
|
||||
}
|
||||
|
||||
size_t
|
||||
string_view::find_first_of(ctl::string_view set, size_t pos) const noexcept
|
||||
{
|
||||
if (set.empty())
|
||||
return npos;
|
||||
bool lut[256] = {};
|
||||
for (char c : set)
|
||||
lut[c & 255] = true;
|
||||
const char* b = data();
|
||||
size_t n = size();
|
||||
for (;;) {
|
||||
if (pos >= n)
|
||||
return npos;
|
||||
if (lut[b[pos] & 255])
|
||||
return pos;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ctl
|
||||
+6
-2
@@ -45,6 +45,10 @@ struct string_view
|
||||
string_view substr(size_t = 0, size_t = npos) const noexcept;
|
||||
size_t find(char, size_t = 0) const noexcept;
|
||||
size_t find(string_view, size_t = 0) const noexcept;
|
||||
size_t find_first_of(char, size_t = 0) const noexcept;
|
||||
size_t find_first_of(ctl::string_view, size_t = 0) const noexcept;
|
||||
size_t find_last_of(char, size_t = npos) const noexcept;
|
||||
size_t find_last_of(ctl::string_view, size_t = npos) const noexcept;
|
||||
|
||||
constexpr string_view& operator=(const string_view s) noexcept
|
||||
{
|
||||
@@ -109,12 +113,12 @@ struct string_view
|
||||
return p[n - 1];
|
||||
}
|
||||
|
||||
constexpr const_iterator begin() noexcept
|
||||
constexpr const_iterator begin() const noexcept
|
||||
{
|
||||
return p;
|
||||
}
|
||||
|
||||
constexpr const_iterator end() noexcept
|
||||
constexpr const_iterator end() const noexcept
|
||||
{
|
||||
return p + n;
|
||||
}
|
||||
|
||||
+3
-1
@@ -2,7 +2,9 @@
|
||||
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
|
||||
|
||||
.PHONY: o/$(MODE)/dsp
|
||||
o/$(MODE)/dsp: o/$(MODE)/dsp/core \
|
||||
o/$(MODE)/dsp: o/$(MODE)/dsp/audio \
|
||||
o/$(MODE)/dsp/core \
|
||||
o/$(MODE)/dsp/mpeg \
|
||||
o/$(MODE)/dsp/scale \
|
||||
o/$(MODE)/dsp/prog \
|
||||
o/$(MODE)/dsp/tty
|
||||
@@ -0,0 +1,56 @@
|
||||
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
|
||||
#── vi: set noet ft=make ts=8 sw=8 fenc=utf-8 :vi ────────────────────┘
|
||||
|
||||
PKGS += DSP_AUDIO
|
||||
|
||||
DSP_AUDIO_ARTIFACTS += DSP_AUDIO_A
|
||||
DSP_AUDIO = $(DSP_AUDIO_A_DEPS) $(DSP_AUDIO_A)
|
||||
DSP_AUDIO_A = o/$(MODE)/dsp/audio/audio.a
|
||||
DSP_AUDIO_A_FILES := $(wildcard dsp/audio/*)
|
||||
DSP_AUDIO_A_HDRS = $(filter %.h,$(DSP_AUDIO_A_FILES)) dsp/audio/cosmoaudio/cosmoaudio.h
|
||||
DSP_AUDIO_A_SRCS = $(filter %.c,$(DSP_AUDIO_A_FILES))
|
||||
|
||||
DSP_AUDIO_A_DATA = \
|
||||
dsp/audio/cosmoaudio/miniaudio.h \
|
||||
dsp/audio/cosmoaudio/cosmoaudio.c \
|
||||
dsp/audio/cosmoaudio/cosmoaudio.h \
|
||||
dsp/audio/cosmoaudio/cosmoaudio.dll \
|
||||
|
||||
DSP_AUDIO_A_OBJS = \
|
||||
$(DSP_AUDIO_A_SRCS:%.c=o/$(MODE)/%.o) \
|
||||
$(DSP_AUDIO_A_DATA:%=o/$(MODE)/%.zip.o) \
|
||||
|
||||
DSP_AUDIO_A_CHECKS = \
|
||||
$(DSP_AUDIO_A).pkg \
|
||||
$(DSP_AUDIO_A_HDRS:%=o/$(MODE)/%.ok)
|
||||
|
||||
DSP_AUDIO_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_DLOPEN \
|
||||
LIBC_INTRIN \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_PROC \
|
||||
LIBC_THREAD \
|
||||
|
||||
DSP_AUDIO_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(DSP_AUDIO_A_DIRECTDEPS),$($(x))))
|
||||
|
||||
$(DSP_AUDIO_A): dsp/audio/ \
|
||||
$(DSP_AUDIO_A).pkg \
|
||||
$(DSP_AUDIO_A_OBJS)
|
||||
|
||||
$(DSP_AUDIO_A).pkg: \
|
||||
$(DSP_AUDIO_A_OBJS) \
|
||||
$(foreach x,$(DSP_AUDIO_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
DSP_AUDIO_LIBS = $(foreach x,$(DSP_AUDIO_ARTIFACTS),$($(x)))
|
||||
DSP_AUDIO_SRCS = $(foreach x,$(DSP_AUDIO_ARTIFACTS),$($(x)_SRCS))
|
||||
DSP_AUDIO_HDRS = $(foreach x,$(DSP_AUDIO_ARTIFACTS),$($(x)_HDRS))
|
||||
DSP_AUDIO_CHECKS = $(foreach x,$(DSP_AUDIO_ARTIFACTS),$($(x)_CHECKS))
|
||||
DSP_AUDIO_OBJS = $(foreach x,$(DSP_AUDIO_ARTIFACTS),$($(x)_OBJS))
|
||||
$(DSP_AUDIO_OBJS): $(BUILD_FILES) dsp/audio/BUILD.mk
|
||||
|
||||
.PHONY: o/$(MODE)/dsp/audio
|
||||
o/$(MODE)/dsp/audio: $(DSP_AUDIO_CHECKS)
|
||||
@@ -0,0 +1,358 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2024 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/audio/cosmoaudio/cosmoaudio.h"
|
||||
#include "dsp/audio/describe.h"
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/dlopen/dlfcn.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/strace.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/proc/posix_spawn.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/temp.h"
|
||||
#include "libc/thread/thread.h"
|
||||
|
||||
#define COSMOAUDIO_MINIMUM_VERISON 1
|
||||
|
||||
#define COSMOAUDIO_DSO_NAME "cosmoaudio." STRINGIFY(COSMOAUDIO_MINIMUM_VERISON)
|
||||
|
||||
__static_yoink("dsp/audio/cosmoaudio/miniaudio.h");
|
||||
__static_yoink("dsp/audio/cosmoaudio/cosmoaudio.h");
|
||||
__static_yoink("dsp/audio/cosmoaudio/cosmoaudio.c");
|
||||
__static_yoink("dsp/audio/cosmoaudio/cosmoaudio.dll");
|
||||
|
||||
static const struct Source {
|
||||
const char *zip;
|
||||
const char *name;
|
||||
} srcs[] = {
|
||||
{"/zip/dsp/audio/cosmoaudio/miniaudio.h", "miniaudio.h"},
|
||||
{"/zip/dsp/audio/cosmoaudio/cosmoaudio.h", "cosmoaudio.h"},
|
||||
{"/zip/dsp/audio/cosmoaudio/cosmoaudio.c", "cosmoaudio.c"}, // must last
|
||||
};
|
||||
|
||||
static struct {
|
||||
pthread_once_t once;
|
||||
typeof(cosmoaudio_open) *open;
|
||||
typeof(cosmoaudio_close) *close;
|
||||
typeof(cosmoaudio_write) *write;
|
||||
typeof(cosmoaudio_flush) *flush;
|
||||
typeof(cosmoaudio_read) *read;
|
||||
typeof(cosmoaudio_poll) *poll;
|
||||
} g_audio;
|
||||
|
||||
static const char *cosmoaudio_tmp_dir(void) {
|
||||
const char *tmpdir;
|
||||
if (!(tmpdir = getenv("TMPDIR")) || !*tmpdir)
|
||||
if (!(tmpdir = getenv("HOME")) || !*tmpdir)
|
||||
tmpdir = ".";
|
||||
return tmpdir;
|
||||
}
|
||||
|
||||
static bool cosmoaudio_app_dir(char *path, size_t size) {
|
||||
strlcpy(path, cosmoaudio_tmp_dir(), size);
|
||||
strlcat(path, "/.cosmo/", size);
|
||||
if (makedirs(path, 0755))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cosmoaudio_dso_path(char *path, size_t size) {
|
||||
if (!cosmoaudio_app_dir(path, size))
|
||||
return false;
|
||||
strlcat(path, COSMOAUDIO_DSO_NAME, size);
|
||||
if (IsWindows()) {
|
||||
strlcat(path, ".dll", size);
|
||||
} else if (IsXnu()) {
|
||||
strlcat(path, ".dylib", size);
|
||||
} else {
|
||||
strlcat(path, ".so", size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cosmoaudio_extract(const char *zip, const char *to) {
|
||||
int fdin, fdout;
|
||||
char stage[PATH_MAX];
|
||||
strlcpy(stage, to, sizeof(stage));
|
||||
if (strlcat(stage, ".XXXXXX", sizeof(stage)) >= sizeof(stage)) {
|
||||
errno = ENAMETOOLONG;
|
||||
return false;
|
||||
}
|
||||
if ((fdout = mkostemp(stage, O_CLOEXEC)) == -1)
|
||||
return false;
|
||||
if ((fdin = open(zip, O_RDONLY | O_CLOEXEC)) == -1) {
|
||||
close(fdout);
|
||||
unlink(stage);
|
||||
return false;
|
||||
}
|
||||
if (copyfd(fdin, fdout, -1) == -1) {
|
||||
close(fdin);
|
||||
close(fdout);
|
||||
unlink(stage);
|
||||
return false;
|
||||
}
|
||||
if (close(fdout)) {
|
||||
close(fdin);
|
||||
unlink(stage);
|
||||
return false;
|
||||
}
|
||||
if (close(fdin)) {
|
||||
unlink(stage);
|
||||
return false;
|
||||
}
|
||||
if (rename(stage, to)) {
|
||||
unlink(stage);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool cosmoaudio_build(const char *dso) {
|
||||
|
||||
// extract sauce
|
||||
char src[PATH_MAX];
|
||||
for (int i = 0; i < sizeof(srcs) / sizeof(*srcs); ++i) {
|
||||
if (!cosmoaudio_app_dir(src, PATH_MAX))
|
||||
return false;
|
||||
strlcat(src, srcs[i].name, sizeof(src));
|
||||
if (!cosmoaudio_extract(srcs[i].zip, src))
|
||||
return false;
|
||||
}
|
||||
|
||||
// create temporary name for compiled dso
|
||||
// it'll ensure build operation is atomic
|
||||
int fd;
|
||||
char tmpdso[PATH_MAX];
|
||||
strlcpy(tmpdso, dso, sizeof(tmpdso));
|
||||
strlcat(tmpdso, ".XXXXXX", sizeof(tmpdso));
|
||||
if ((fd = mkostemp(tmpdso, O_CLOEXEC)) != -1) {
|
||||
close(fd);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// build cosmoaudio with host c compiler
|
||||
char *args[] = {
|
||||
"cc", //
|
||||
"-w", //
|
||||
"-I.", //
|
||||
"-O2", //
|
||||
"-fPIC", //
|
||||
"-shared", //
|
||||
"-pthread", //
|
||||
"-DNDEBUG", //
|
||||
IsAarch64() ? "-ffixed-x28" : "-DIGNORE1", //
|
||||
src, //
|
||||
"-o", //
|
||||
tmpdso, //
|
||||
"-lm", //
|
||||
IsNetbsd() ? 0 : "-ldl", //
|
||||
NULL,
|
||||
};
|
||||
int pid, ws;
|
||||
errno_t err = posix_spawnp(&pid, args[0], NULL, NULL, args, environ);
|
||||
if (err)
|
||||
return false;
|
||||
while (waitpid(pid, &ws, 0) == -1)
|
||||
if (errno != EINTR)
|
||||
return false;
|
||||
if (ws)
|
||||
return false;
|
||||
|
||||
// move dso to its final destination
|
||||
if (rename(tmpdso, dso))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *cosmoaudio_dlopen(const char *name) {
|
||||
void *handle;
|
||||
if ((handle = cosmo_dlopen(name, RTLD_NOW))) {
|
||||
typeof(cosmoaudio_version) *version;
|
||||
if ((version = cosmo_dlsym(handle, "cosmoaudio_version")))
|
||||
if (version() >= COSMOAUDIO_MINIMUM_VERISON)
|
||||
return handle;
|
||||
cosmo_dlclose(handle);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cosmoaudio_setup_impl(void) {
|
||||
void *handle;
|
||||
if (IsOpenbsd())
|
||||
return; // no dlopen support yet
|
||||
if (IsXnu() && !IsXnuSilicon())
|
||||
return; // no dlopen support yet
|
||||
if (!(handle = cosmoaudio_dlopen(COSMOAUDIO_DSO_NAME ".so")) &&
|
||||
!(handle = cosmoaudio_dlopen("lib" COSMOAUDIO_DSO_NAME ".so")) &&
|
||||
!(handle = cosmoaudio_dlopen("cosmoaudio.so")) &&
|
||||
!(handle = cosmoaudio_dlopen("libcosmoaudio.so"))) {
|
||||
char dso[PATH_MAX];
|
||||
if (!cosmoaudio_dso_path(dso, sizeof(dso)))
|
||||
return;
|
||||
if ((handle = cosmoaudio_dlopen(dso)))
|
||||
goto WeAreGood;
|
||||
if (IsWindows()) {
|
||||
if (cosmoaudio_extract("/zip/dsp/audio/cosmoaudio/cosmoaudio.dll", dso)) {
|
||||
if ((handle = cosmoaudio_dlopen(dso))) {
|
||||
goto WeAreGood;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!cosmoaudio_build(dso))
|
||||
return;
|
||||
if (!(handle = cosmoaudio_dlopen(dso)))
|
||||
return;
|
||||
}
|
||||
WeAreGood:
|
||||
g_audio.open = cosmo_dlsym(handle, "cosmoaudio_open");
|
||||
g_audio.close = cosmo_dlsym(handle, "cosmoaudio_close");
|
||||
g_audio.write = cosmo_dlsym(handle, "cosmoaudio_write");
|
||||
g_audio.flush = cosmo_dlsym(handle, "cosmoaudio_flush");
|
||||
g_audio.read = cosmo_dlsym(handle, "cosmoaudio_read");
|
||||
g_audio.poll = cosmo_dlsym(handle, "cosmoaudio_poll");
|
||||
}
|
||||
|
||||
static void cosmoaudio_setup(void) {
|
||||
BLOCK_CANCELATION;
|
||||
cosmoaudio_setup_impl();
|
||||
ALLOW_CANCELATION;
|
||||
}
|
||||
|
||||
static void cosmoaudio_init(void) {
|
||||
pthread_once(&g_audio.once, cosmoaudio_setup);
|
||||
}
|
||||
|
||||
COSMOAUDIO_ABI int cosmoaudio_open(
|
||||
struct CosmoAudio **out_ca, const struct CosmoAudioOpenOptions *options) {
|
||||
int status;
|
||||
char sbuf[32];
|
||||
char dbuf[256];
|
||||
cosmoaudio_init();
|
||||
if (g_audio.open) {
|
||||
BLOCK_SIGNALS;
|
||||
status = g_audio.open(out_ca, options);
|
||||
ALLOW_SIGNALS;
|
||||
} else {
|
||||
status = COSMOAUDIO_ELINK;
|
||||
}
|
||||
STRACE("cosmoaudio_open([%p], %s) → %s",
|
||||
out_ca ? *out_ca : (struct CosmoAudio *)-1,
|
||||
cosmoaudio_describe_open_options(dbuf, sizeof(dbuf), options),
|
||||
cosmoaudio_describe_status(sbuf, sizeof(sbuf), status));
|
||||
return status;
|
||||
}
|
||||
|
||||
COSMOAUDIO_ABI int cosmoaudio_close(struct CosmoAudio *ca) {
|
||||
int status;
|
||||
char sbuf[32];
|
||||
if (g_audio.close) {
|
||||
BLOCK_SIGNALS;
|
||||
status = g_audio.close(ca);
|
||||
ALLOW_SIGNALS;
|
||||
} else {
|
||||
status = COSMOAUDIO_ELINK;
|
||||
}
|
||||
STRACE("cosmoaudio_close(%p) → %s", ca,
|
||||
cosmoaudio_describe_status(sbuf, sizeof(sbuf), status));
|
||||
return status;
|
||||
}
|
||||
|
||||
COSMOAUDIO_ABI int cosmoaudio_write(struct CosmoAudio *ca, const float *data,
|
||||
int frames) {
|
||||
int status;
|
||||
char sbuf[32];
|
||||
if (g_audio.write) {
|
||||
BLOCK_SIGNALS;
|
||||
status = g_audio.write(ca, data, frames);
|
||||
ALLOW_SIGNALS;
|
||||
} else {
|
||||
status = COSMOAUDIO_ELINK;
|
||||
}
|
||||
if (frames <= 0 || frames >= 160)
|
||||
DATATRACE("cosmoaudio_write(%p, %p, %d) → %s", ca, data, frames,
|
||||
cosmoaudio_describe_status(sbuf, sizeof(sbuf), status));
|
||||
return status;
|
||||
}
|
||||
|
||||
COSMOAUDIO_ABI int cosmoaudio_read(struct CosmoAudio *ca, float *data,
|
||||
int frames) {
|
||||
int status;
|
||||
char sbuf[32];
|
||||
if (g_audio.read) {
|
||||
BLOCK_SIGNALS;
|
||||
status = g_audio.read(ca, data, frames);
|
||||
ALLOW_SIGNALS;
|
||||
} else {
|
||||
status = COSMOAUDIO_ELINK;
|
||||
}
|
||||
if (frames <= 0 || frames >= 160)
|
||||
DATATRACE("cosmoaudio_read(%p, %p, %d) → %s", ca, data, frames,
|
||||
cosmoaudio_describe_status(sbuf, sizeof(sbuf), status));
|
||||
return status;
|
||||
}
|
||||
|
||||
COSMOAUDIO_ABI int cosmoaudio_flush(struct CosmoAudio *ca) {
|
||||
int status;
|
||||
char sbuf[32];
|
||||
if (g_audio.flush) {
|
||||
BLOCK_SIGNALS;
|
||||
status = g_audio.flush(ca);
|
||||
ALLOW_SIGNALS;
|
||||
} else {
|
||||
status = COSMOAUDIO_ELINK;
|
||||
}
|
||||
DATATRACE("cosmoaudio_flush(%p) → %s", ca,
|
||||
cosmoaudio_describe_status(sbuf, sizeof(sbuf), status));
|
||||
return status;
|
||||
}
|
||||
|
||||
COSMOAUDIO_ABI int cosmoaudio_poll(struct CosmoAudio *ca,
|
||||
int *in_out_readFrames,
|
||||
int *in_out_writeFrames) {
|
||||
int status;
|
||||
char sbuf[32];
|
||||
char fbuf[2][20];
|
||||
if (g_audio.poll) {
|
||||
BLOCK_SIGNALS;
|
||||
status = g_audio.poll(ca, in_out_readFrames, in_out_writeFrames);
|
||||
ALLOW_SIGNALS;
|
||||
} else {
|
||||
status = COSMOAUDIO_ELINK;
|
||||
}
|
||||
DATATRACE("cosmoaudio_poll(%p, %s, %s) → %s", ca,
|
||||
cosmoaudio_describe_poll_frames(fbuf[0], sizeof(fbuf[0]),
|
||||
in_out_readFrames),
|
||||
cosmoaudio_describe_poll_frames(fbuf[1], sizeof(fbuf[1]),
|
||||
in_out_writeFrames),
|
||||
cosmoaudio_describe_status(sbuf, sizeof(sbuf), status));
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
*.o
|
||||
/Debug
|
||||
/Release
|
||||
@@ -0,0 +1,87 @@
|
||||
# Makefile for MSVC x64 Command Line Developer Tools
|
||||
#
|
||||
# nmake /f Makefile.msvc check
|
||||
# nmake /f Makefile.msvc MODE=debug check
|
||||
#
|
||||
# Note: MSVC 2019 makes the DLL 64kb smaller than MSVC 2022.
|
||||
|
||||
# Compiler and linker
|
||||
CC=cl
|
||||
LINK=link
|
||||
|
||||
# Build mode (can be overridden from command line)
|
||||
!IFNDEF MODE
|
||||
MODE=release
|
||||
!ENDIF
|
||||
|
||||
# Library dependencies.
|
||||
TEST_LIBS=OneCore.lib
|
||||
|
||||
# Compiler flags
|
||||
CFLAGS_COMMON=/nologo /W4 /Gy /EHsc
|
||||
CFLAGS_DEBUG=/Od /Zi /MDd /D_DEBUG
|
||||
CFLAGS_RELEASE=/O2 /MT /DNDEBUG
|
||||
|
||||
!IF "$(MODE)"=="debug"
|
||||
CFLAGS=$(CFLAGS_COMMON) $(CFLAGS_DEBUG)
|
||||
LDFLAGS=/DEBUG
|
||||
OUT_DIR=Debug
|
||||
!ELSE
|
||||
CFLAGS=$(CFLAGS_COMMON) $(CFLAGS_RELEASE) /GL
|
||||
LDFLAGS=/RELEASE /OPT:REF /OPT:ICF /LTCG /INCREMENTAL:NO
|
||||
OUT_DIR=Release
|
||||
!ENDIF
|
||||
|
||||
# Additional flags for DLL
|
||||
DLL_CFLAGS=$(CFLAGS) /D_USRDLL /D_WINDLL
|
||||
|
||||
# Linker flags
|
||||
LDFLAGS=/NOLOGO /SUBSYSTEM:CONSOLE $(LDFLAGS)
|
||||
|
||||
# Output file names
|
||||
DLL_TARGET=$(OUT_DIR)\cosmoaudio.dll
|
||||
TEST_TARGET=$(OUT_DIR)\test.exe
|
||||
|
||||
# Source files
|
||||
DLL_SOURCES=cosmoaudio.c
|
||||
TEST_SOURCES=test.c
|
||||
|
||||
# Object files
|
||||
DLL_OBJECTS=$(OUT_DIR)\cosmoaudio.obj
|
||||
TEST_OBJECTS=$(OUT_DIR)\test.obj
|
||||
|
||||
# Default target
|
||||
all: $(OUT_DIR) $(DLL_TARGET) $(TEST_TARGET)
|
||||
|
||||
# Create output directory
|
||||
$(OUT_DIR):
|
||||
if not exist $(OUT_DIR) mkdir $(OUT_DIR)
|
||||
|
||||
# Rule to build the DLL
|
||||
$(DLL_TARGET): $(OUT_DIR) $(DLL_OBJECTS)
|
||||
$(LINK) /DLL $(LDFLAGS) /OUT:$(DLL_TARGET) $(DLL_OBJECTS)
|
||||
|
||||
# Rule to build the test program
|
||||
$(TEST_TARGET): $(OUT_DIR) $(TEST_OBJECTS) $(DLL_TARGET)
|
||||
$(LINK) $(LDFLAGS) /OUT:$(TEST_TARGET) $(TEST_OBJECTS) $(DLL_TARGET:.dll=.lib) $(TEST_LIBS)
|
||||
|
||||
# Rules to compile .c files to .obj files with header dependencies
|
||||
{.}.c{$(OUT_DIR)}.obj:
|
||||
$(CC) $(DLL_CFLAGS) /c /Fo$(OUT_DIR)\ $<
|
||||
|
||||
$(OUT_DIR)\test.obj: $(OUT_DIR) test.c cosmoaudio.h
|
||||
$(CC) $(CFLAGS) /c /Fo$(OUT_DIR)\ test.c
|
||||
|
||||
$(OUT_DIR)\cosmoaudio.obj: $(OUT_DIR) cosmoaudio.c miniaudio.h cosmoaudio.h
|
||||
$(CC) $(DLL_CFLAGS) /c /Fo$(OUT_DIR)\ cosmoaudio.c
|
||||
|
||||
# Clean target
|
||||
clean:
|
||||
if exist $(OUT_DIR) rmdir /s /q $(OUT_DIR)
|
||||
|
||||
# Run tests (now called 'check')
|
||||
check: $(TEST_TARGET)
|
||||
$(TEST_TARGET)
|
||||
|
||||
# Phony targets
|
||||
.PHONY: all clean check
|
||||
@@ -0,0 +1,519 @@
|
||||
// Copyright 2024 Justine Alexandra Roberts Tunney
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for
|
||||
// any purpose with or without fee is hereby granted, provided that the
|
||||
// above copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#define COSMOAUDIO_BUILD
|
||||
#include "cosmoaudio.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MA_DEBUG_OUTPUT
|
||||
#define MA_DR_MP3_NO_STDIO
|
||||
#define MA_NO_DECODING
|
||||
#define MA_NO_ENCODING
|
||||
#define MA_NO_ENGINE
|
||||
#define MA_NO_GENERATION
|
||||
#define MA_NO_NODE_GRAPH
|
||||
#define MA_NO_RESOURCE_MANAGER
|
||||
#define MA_STATIC
|
||||
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
|
||||
struct CosmoAudio {
|
||||
enum CosmoAudioDeviceType deviceType;
|
||||
ma_uint32 outputBufferFrames;
|
||||
ma_uint32 inputBufferFrames;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int isLeft;
|
||||
ma_context context;
|
||||
ma_device device;
|
||||
ma_pcm_rb output;
|
||||
ma_pcm_rb input;
|
||||
ma_event event;
|
||||
ma_log log;
|
||||
};
|
||||
|
||||
static int read_ring_buffer(ma_log* log, ma_pcm_rb* rb, float* pOutput,
|
||||
ma_uint32 frameCount, ma_uint32 channels) {
|
||||
ma_result result;
|
||||
ma_uint32 framesRead;
|
||||
ma_uint32 framesToRead;
|
||||
for (framesRead = 0; framesRead < frameCount; framesRead += framesToRead) {
|
||||
framesToRead = frameCount - framesRead;
|
||||
void* pMappedBuffer;
|
||||
result = ma_pcm_rb_acquire_read(rb, &framesToRead, &pMappedBuffer);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_log_postf(log, MA_LOG_LEVEL_WARNING,
|
||||
"ma_pcm_rb_acquire_read failed: %s\n",
|
||||
ma_result_description(result));
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
if (!framesToRead)
|
||||
break;
|
||||
memcpy(pOutput + framesRead * channels, pMappedBuffer,
|
||||
framesToRead * channels * sizeof(float));
|
||||
result = ma_pcm_rb_commit_read(rb, framesToRead);
|
||||
if (result != MA_SUCCESS) {
|
||||
if (result == MA_AT_END) {
|
||||
framesRead += framesToRead;
|
||||
break;
|
||||
}
|
||||
ma_log_postf(log, MA_LOG_LEVEL_WARNING,
|
||||
"ma_pcm_rb_commit_read failed: %s\n",
|
||||
ma_result_description(result));
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
}
|
||||
return framesRead;
|
||||
}
|
||||
|
||||
static int write_ring_buffer(ma_log* log, ma_pcm_rb* rb, const float* pInput,
|
||||
ma_uint32 frameCount, ma_uint32 channels) {
|
||||
ma_result result;
|
||||
ma_uint32 framesWritten;
|
||||
ma_uint32 framesToWrite;
|
||||
for (framesWritten = 0; framesWritten < frameCount;
|
||||
framesWritten += framesToWrite) {
|
||||
framesToWrite = frameCount - framesWritten;
|
||||
void* pMappedBuffer;
|
||||
result = ma_pcm_rb_acquire_write(rb, &framesToWrite, &pMappedBuffer);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_log_postf(log, MA_LOG_LEVEL_WARNING,
|
||||
"ma_pcm_rb_acquire_write failed: %s\n",
|
||||
ma_result_description(result));
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
if (!framesToWrite)
|
||||
break;
|
||||
memcpy(pMappedBuffer, pInput + framesWritten * channels,
|
||||
framesToWrite * channels * sizeof(float));
|
||||
result = ma_pcm_rb_commit_write(rb, framesToWrite);
|
||||
if (result != MA_SUCCESS) {
|
||||
if (result == MA_AT_END) {
|
||||
framesWritten += framesToWrite;
|
||||
break;
|
||||
}
|
||||
ma_log_postf(log, MA_LOG_LEVEL_WARNING,
|
||||
"ma_pcm_rb_commit_write failed: %s\n",
|
||||
ma_result_description(result));
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
}
|
||||
return framesWritten;
|
||||
}
|
||||
|
||||
static void data_callback_f32(ma_device* pDevice, float* pOutput,
|
||||
const float* pInput, ma_uint32 frameCount) {
|
||||
struct CosmoAudio* ca = (struct CosmoAudio*)pDevice->pUserData;
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypePlayback) {
|
||||
//
|
||||
// "By default, miniaudio will pre-silence the data callback's
|
||||
// output buffer. If you know that you will always write valid data
|
||||
// to the output buffer you can disable pre-silencing by setting
|
||||
// the noPreSilence config option in the device config to true."
|
||||
//
|
||||
// —Quoth miniaudio documentation § 16.1. Low Level API
|
||||
//
|
||||
if (ca->isLeft) {
|
||||
int framesCopied = read_ring_buffer(&ca->log, &ca->output, pOutput,
|
||||
frameCount, ca->channels);
|
||||
if (framesCopied < (int)frameCount)
|
||||
ca->isLeft = 0;
|
||||
} else {
|
||||
// TODO(jart): Maybe we should stretch the audio too short?
|
||||
int frameOffset;
|
||||
int availableFrames = ma_pcm_rb_available_read(&ca->output);
|
||||
if (availableFrames >= (int)frameCount) {
|
||||
frameOffset = 0;
|
||||
} else {
|
||||
frameOffset = frameCount - availableFrames;
|
||||
frameCount = availableFrames;
|
||||
}
|
||||
read_ring_buffer(&ca->log, &ca->output,
|
||||
pOutput + frameOffset * ca->channels, frameCount,
|
||||
ca->channels);
|
||||
ca->isLeft = 1;
|
||||
}
|
||||
}
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypeCapture)
|
||||
write_ring_buffer(&ca->log, &ca->input, pInput, frameCount, ca->channels);
|
||||
ma_event_signal(&ca->event);
|
||||
}
|
||||
|
||||
static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
|
||||
ma_uint32 frameCount) {
|
||||
data_callback_f32(pDevice, (float*)pOutput, (const float*)pInput, frameCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current version of cosmo audio library.
|
||||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_version(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens access to speaker and microphone.
|
||||
*
|
||||
* @param out_ca will receive pointer to allocated CosmoAudio object,
|
||||
* which must be freed by caller with cosmoaudio_close(); if this
|
||||
* function fails, then this will receive a NULL pointer value so
|
||||
* that cosmoaudio_close(), cosmoaudio_write() etc. can be called
|
||||
* without crashing if no error checking is performed
|
||||
* @return 0 on success, or negative error code on failure
|
||||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_open( //
|
||||
struct CosmoAudio** out_ca, //
|
||||
const struct CosmoAudioOpenOptions* options) {
|
||||
|
||||
// Validate arguments.
|
||||
if (!out_ca)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
*out_ca = NULL;
|
||||
if (!options)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (options->sizeofThis < (int)sizeof(struct CosmoAudioOpenOptions))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (options->bufferFrames < 0)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (options->sampleRate < 8000)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (options->channels < 1)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!options->deviceType)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (options->deviceType &
|
||||
~(kCosmoAudioDeviceTypePlayback | kCosmoAudioDeviceTypeCapture))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
|
||||
// Allocate cosmo audio object.
|
||||
struct CosmoAudio* ca;
|
||||
ca = (struct CosmoAudio*)calloc(1, sizeof(struct CosmoAudio));
|
||||
if (!ca)
|
||||
return COSMOAUDIO_ERROR;
|
||||
ca->channels = options->channels;
|
||||
ca->sampleRate = options->sampleRate;
|
||||
ca->deviceType = options->deviceType;
|
||||
|
||||
// Create win32-style condition variable.
|
||||
if (ma_event_init(&ca->event) != MA_SUCCESS) {
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
|
||||
// Create audio log.
|
||||
if (ma_log_init(NULL, &ca->log) != MA_SUCCESS) {
|
||||
ma_event_uninit(&ca->event);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
if (!options->debugLog)
|
||||
ca->log.callbackCount = 0;
|
||||
|
||||
// Create audio context.
|
||||
ma_context_config contextConfig = ma_context_config_init();
|
||||
contextConfig.pLog = &ca->log;
|
||||
if (ma_context_init(NULL, 0, &contextConfig, &ca->context) != MA_SUCCESS) {
|
||||
ma_event_uninit(&ca->event);
|
||||
ma_log_uninit(&ca->log);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
|
||||
// Initialize device.
|
||||
ma_result result;
|
||||
ma_device_config deviceConfig;
|
||||
deviceConfig = ma_device_config_init(ca->deviceType);
|
||||
deviceConfig.sampleRate = ca->sampleRate;
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypeCapture) {
|
||||
deviceConfig.capture.channels = ca->channels;
|
||||
deviceConfig.capture.format = ma_format_f32;
|
||||
deviceConfig.capture.shareMode = ma_share_mode_shared;
|
||||
}
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypePlayback) {
|
||||
deviceConfig.playback.channels = ca->channels;
|
||||
deviceConfig.playback.format = ma_format_f32;
|
||||
}
|
||||
deviceConfig.dataCallback = data_callback;
|
||||
deviceConfig.pUserData = ca;
|
||||
result = ma_device_init(&ca->context, &deviceConfig, &ca->device);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_context_uninit(&ca->context);
|
||||
ma_event_uninit(&ca->event);
|
||||
ma_log_uninit(&ca->log);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
|
||||
// Initialize the speaker ring buffer.
|
||||
int period = ca->device.playback.internalPeriodSizeInFrames;
|
||||
if (!options->bufferFrames) {
|
||||
ca->outputBufferFrames = period * 10;
|
||||
} else if (options->bufferFrames < period * 2) {
|
||||
ca->outputBufferFrames = period * 2;
|
||||
} else {
|
||||
ca->outputBufferFrames = options->bufferFrames;
|
||||
}
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypePlayback) {
|
||||
result = ma_pcm_rb_init(ma_format_f32, ca->channels, ca->outputBufferFrames,
|
||||
NULL, NULL, &ca->output);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_device_uninit(&ca->device);
|
||||
ma_context_uninit(&ca->context);
|
||||
ma_event_uninit(&ca->event);
|
||||
ma_log_uninit(&ca->log);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
ma_pcm_rb_set_sample_rate(&ca->output, ca->sampleRate);
|
||||
}
|
||||
|
||||
// Initialize the microphone ring buffer.
|
||||
period = ca->device.capture.internalPeriodSizeInFrames;
|
||||
if (!options->bufferFrames) {
|
||||
ca->inputBufferFrames = period * 10;
|
||||
} else if (options->bufferFrames < period * 2) {
|
||||
ca->inputBufferFrames = period * 2;
|
||||
} else {
|
||||
ca->inputBufferFrames = options->bufferFrames;
|
||||
}
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypeCapture) {
|
||||
result = ma_pcm_rb_init(ma_format_f32, ca->channels, ca->inputBufferFrames,
|
||||
NULL, NULL, &ca->input);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_device_uninit(&ca->device);
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypePlayback)
|
||||
ma_pcm_rb_uninit(&ca->output);
|
||||
ma_context_uninit(&ca->context);
|
||||
ma_event_uninit(&ca->event);
|
||||
ma_log_uninit(&ca->log);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
ma_pcm_rb_set_sample_rate(&ca->output, ca->sampleRate);
|
||||
}
|
||||
|
||||
// Start audio playback.
|
||||
if (ma_device_start(&ca->device) != MA_SUCCESS) {
|
||||
ma_device_uninit(&ca->device);
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypePlayback)
|
||||
ma_pcm_rb_uninit(&ca->output);
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypeCapture)
|
||||
ma_pcm_rb_uninit(&ca->input);
|
||||
ma_context_uninit(&ca->context);
|
||||
ma_event_uninit(&ca->event);
|
||||
ma_log_uninit(&ca->log);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
|
||||
*out_ca = ca;
|
||||
return COSMOAUDIO_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes audio device and frees all associated resources.
|
||||
*
|
||||
* This function is non-blocking and will drop buffered audio. In
|
||||
* playback mode, you need to call cosmoaudio_flush() to ensure data
|
||||
* supplied by cosmoaudio_write() gets played on your speaker.
|
||||
*
|
||||
* Calling this function twice on the same object will result in
|
||||
* undefined behavior. Even if this function fails, the `ca` will be
|
||||
* freed to the greatest extent possible.
|
||||
*
|
||||
* @param ca is CosmoAudio object returned earlier by cosmoaudio_open()
|
||||
* @return 0 on success, or negative error code on failure
|
||||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_close(struct CosmoAudio* ca) {
|
||||
if (!ca)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
ma_device_uninit(&ca->device); // do this first
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypePlayback)
|
||||
ma_pcm_rb_uninit(&ca->output);
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypeCapture)
|
||||
ma_pcm_rb_uninit(&ca->input);
|
||||
ma_context_uninit(&ca->context);
|
||||
ma_event_uninit(&ca->event);
|
||||
ma_log_uninit(&ca->log);
|
||||
free(ca);
|
||||
return COSMOAUDIO_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes raw audio data to speaker.
|
||||
*
|
||||
* The data is written to a ring buffer in real-time, which is then
|
||||
* played back very soon on the audio device. This has tolerence for
|
||||
* a certain amount of buffering, but expects that this function is
|
||||
* repeatedly called at a regular time interval. The caller should
|
||||
* have its own sleep loop for this purpose.
|
||||
*
|
||||
* This function never blocks. Programs that don't have their own timer
|
||||
* can use cosmoaudio_poll() to wait until audio may be written.
|
||||
*
|
||||
* For any given CosmoAudio object, it's assumed that only a single
|
||||
* thread will call this function.
|
||||
*
|
||||
* @param ca is CosmoAudio object returned earlier by cosmoaudio_open()
|
||||
* @param data is pointer to raw audio samples, expected to be in the range
|
||||
* -1.0 to 1.0, where channels are interleaved
|
||||
* @param frames is the number of frames (i.e. number of samples divided by
|
||||
* number of channels) from `data` to write to audio device
|
||||
* @return number of frames written, or negative error code on failure
|
||||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_write(struct CosmoAudio* ca, const float* data,
|
||||
int frames) {
|
||||
if (!ca)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (frames < 0)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!(ca->deviceType & kCosmoAudioDeviceTypePlayback))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (1u + frames > ca->outputBufferFrames)
|
||||
return COSMOAUDIO_ENOBUF;
|
||||
if (!frames)
|
||||
return 0;
|
||||
if (!data)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
return write_ring_buffer(&ca->log, &ca->output, data, frames, ca->channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads raw audio data from microphone.
|
||||
*
|
||||
* The data is read from a ring buffer in real-time, which is then
|
||||
* played back on the audio device. This has tolerence for a certain
|
||||
* amount of buffering (based on the `bufferFrames` parameter passed to
|
||||
* cosmoaudio_open(), which by default assumes this function will be
|
||||
* called at at a regular time interval.
|
||||
*
|
||||
* This function never blocks. Programs that don't have their own timer
|
||||
* can use cosmoaudio_poll() to wait until audio may be read.
|
||||
*
|
||||
* For any given CosmoAudio object, it's assumed that only a single
|
||||
* thread will call this function.
|
||||
*
|
||||
* @param ca is CosmoAudio object returned earlier by cosmoaudio_open()
|
||||
* @param data is pointer to raw audio samples, expected to be in the range
|
||||
* -1.0 to 1.0, where channels are interleaved
|
||||
* @param frames is the number of frames (i.e. number of samples divided by
|
||||
* number of channels) from `data` to read from microphone
|
||||
* @return number of frames read, or negative error code on failure
|
||||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_read(struct CosmoAudio* ca, float* data,
|
||||
int frames) {
|
||||
if (!ca)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (frames < 0)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!(ca->deviceType & kCosmoAudioDeviceTypeCapture))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!frames)
|
||||
return 0;
|
||||
if (!data)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
return read_ring_buffer(&ca->log, &ca->input, data, frames, ca->channels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until it's possible to read/write audio.
|
||||
*
|
||||
* This function is uninterruptible. All signals are masked throughout
|
||||
* the duration of time this function may block, including cancelation
|
||||
* signals, because this is not a cancelation point. Cosmopolitan Libc
|
||||
* applies this masking in its dlopen wrapper.
|
||||
*
|
||||
* @param ca is CosmoAudio object returned earlier by cosmoaudio_open()
|
||||
* @param in_out_readFrames if non-NULL specifies how many frames of
|
||||
* capture data be immediately readable by cosmoaudio_read() before
|
||||
* this can return; it must not exceed the buffer size; on return
|
||||
* this will be set to the actual number of frames in the buffer;
|
||||
* if the caller supplies a zero then this call is a non-blocking
|
||||
* way to query buffer sizes
|
||||
* @param in_out_writeFrames if non-NULL specifies how many frames of
|
||||
* capture data be immediately writable by cosmoaudio_write() before
|
||||
* this can return; it must not exceed the buffer size; on return
|
||||
* this will be set to the actual number of frames in the buffer;
|
||||
* if the caller supplies a zero then this call is a non-blocking
|
||||
* way to query buffer sizes
|
||||
* @return 0 on success, or negative error code on error
|
||||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_poll(struct CosmoAudio* ca,
|
||||
int* in_out_readFrames,
|
||||
int* in_out_writeFrames) {
|
||||
if (!ca)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!in_out_readFrames && !in_out_writeFrames)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (in_out_readFrames && !(ca->deviceType & kCosmoAudioDeviceTypeCapture))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (in_out_writeFrames && !(ca->deviceType & kCosmoAudioDeviceTypePlayback))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (in_out_readFrames && 1u + *in_out_readFrames > ca->inputBufferFrames)
|
||||
return COSMOAUDIO_ENOBUF;
|
||||
if (in_out_writeFrames && 1u + *in_out_writeFrames > ca->outputBufferFrames)
|
||||
return COSMOAUDIO_ENOBUF;
|
||||
for (;;) {
|
||||
int done = 1;
|
||||
ma_uint32 readable = 0;
|
||||
ma_uint32 writable = 0;
|
||||
if (in_out_readFrames) {
|
||||
readable = ma_pcm_rb_available_read(&ca->input);
|
||||
done &= readable >= (ma_uint32)*in_out_readFrames;
|
||||
}
|
||||
if (in_out_writeFrames) {
|
||||
writable = ma_pcm_rb_available_write(&ca->output);
|
||||
done &= writable >= (ma_uint32)*in_out_writeFrames;
|
||||
}
|
||||
if (done) {
|
||||
if (in_out_readFrames)
|
||||
*in_out_readFrames = readable;
|
||||
if (in_out_writeFrames)
|
||||
*in_out_writeFrames = writable;
|
||||
return COSMOAUDIO_SUCCESS;
|
||||
}
|
||||
if (ma_event_wait(&ca->event) != MA_SUCCESS)
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for written samples to be sent to device.
|
||||
*
|
||||
* This function is only valid to call in playback or duplex mode.
|
||||
*
|
||||
* This function is uninterruptible. All signals are masked throughout
|
||||
* the duration of time this function may block, including cancelation
|
||||
* signals, because this is not a cancelation point. Cosmopolitan Libc
|
||||
* applies this masking in its dlopen wrapper.
|
||||
*
|
||||
* @param ca is CosmoAudio object returned earlier by cosmoaudio_open()
|
||||
* @return 0 on success, or negative error code on failure
|
||||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_flush(struct CosmoAudio* ca) {
|
||||
if (!ca)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!(ca->deviceType & kCosmoAudioDeviceTypePlayback))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
for (;;) {
|
||||
if (!ma_pcm_rb_available_read(&ca->output))
|
||||
return COSMOAUDIO_SUCCESS;
|
||||
if (ma_event_wait(&ca->event) != MA_SUCCESS)
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,104 @@
|
||||
#ifndef COSMOAUDIO_H_
|
||||
#define COSMOAUDIO_H_
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define COSMOAUDIO_ABI
|
||||
#ifdef COSMOAUDIO_BUILD
|
||||
#define COSMOAUDIO_API __declspec(dllexport)
|
||||
#else
|
||||
#define COSMOAUDIO_API __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#define COSMOAUDIO_API
|
||||
#ifdef __x86_64__
|
||||
#define COSMOAUDIO_ABI __attribute__((__ms_abi__, __visibility__("default")))
|
||||
#else
|
||||
#define COSMOAUDIO_ABI __attribute__((__visibility__("default")))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define COSMOAUDIO_SUCCESS -0 // no error or nothing written
|
||||
#define COSMOAUDIO_ERROR -1 // unspecified error
|
||||
#define COSMOAUDIO_EINVAL -2 // invalid parameters passed to api
|
||||
#define COSMOAUDIO_ELINK -3 // loading cosmoaudio dso failed
|
||||
#define COSMOAUDIO_ENOBUF -4 // invalid buffering parameters
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct CosmoAudio;
|
||||
|
||||
enum CosmoAudioDeviceType {
|
||||
kCosmoAudioDeviceTypePlayback = 1,
|
||||
kCosmoAudioDeviceTypeCapture = 2,
|
||||
kCosmoAudioDeviceTypeDuplex =
|
||||
kCosmoAudioDeviceTypePlayback | kCosmoAudioDeviceTypeCapture,
|
||||
};
|
||||
|
||||
struct CosmoAudioOpenOptions {
|
||||
|
||||
// This field must be set to sizeof(struct CosmoAudioOpenOptions) or
|
||||
// cosmoaudio_open() will return COSMOAUDIO_EINVAL.
|
||||
int sizeofThis;
|
||||
|
||||
// Whether you want this object to open the speaker or microphone.
|
||||
// Please note that asking for microphone access may cause some OSes
|
||||
// like MacOS to show a popup asking the user for permission.
|
||||
enum CosmoAudioDeviceType deviceType;
|
||||
|
||||
// The sample rate can be 44100 for CD quality, 8000 for telephone
|
||||
// quality, etc. Values below 8000 are currently not supported.
|
||||
int sampleRate;
|
||||
|
||||
// The number of audio channels in each interleaved frame. Should be 1
|
||||
// for mono or 2 for stereo.
|
||||
int channels;
|
||||
|
||||
// Number of frames in each ring buffer. A frame consists of a PCM
|
||||
// sample for each channel. Set to 0 for default. If this is less than
|
||||
// the device period size times two, it'll be increased to that value.
|
||||
int bufferFrames;
|
||||
|
||||
// Enables debug logging if non-zero.
|
||||
int debugLog;
|
||||
};
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_version(void) COSMOAUDIO_ABI;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_open( //
|
||||
struct CosmoAudio **out_ca, //
|
||||
const struct CosmoAudioOpenOptions *options //
|
||||
) COSMOAUDIO_ABI;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_close( //
|
||||
struct CosmoAudio *ca //
|
||||
) COSMOAUDIO_ABI;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_write( //
|
||||
struct CosmoAudio *ca, //
|
||||
const float *samples, //
|
||||
int frameCount //
|
||||
) COSMOAUDIO_ABI;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_flush( //
|
||||
struct CosmoAudio *ca //
|
||||
) COSMOAUDIO_ABI;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_read( //
|
||||
struct CosmoAudio *ca, //
|
||||
float *out_samples, //
|
||||
int frameCount //
|
||||
) COSMOAUDIO_ABI;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_poll( //
|
||||
struct CosmoAudio *ca, //
|
||||
int *in_out_readFrames, //
|
||||
int *in_out_writeFrames //
|
||||
) COSMOAUDIO_ABI;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* COSMOAUDIO_H_ */
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../../third_party/miniaudio/miniaudio.h
|
||||
@@ -0,0 +1,76 @@
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include "cosmoaudio.h"
|
||||
|
||||
#define SAMPLING_RATE 44100
|
||||
#define WAVE_INTERVAL 440
|
||||
#define CHANNELS 2
|
||||
|
||||
#ifndef M_PIf
|
||||
#define M_PIf 3.14159265358979323846f
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
|
||||
struct CosmoAudioOpenOptions cao = {0};
|
||||
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
|
||||
cao.deviceType = kCosmoAudioDeviceTypePlayback;
|
||||
cao.sampleRate = SAMPLING_RATE;
|
||||
cao.channels = CHANNELS;
|
||||
|
||||
int status;
|
||||
struct CosmoAudio *ca;
|
||||
status = cosmoaudio_open(&ca, &cao);
|
||||
if (status != COSMOAUDIO_SUCCESS) {
|
||||
fprintf(stderr, "failed to open audio: %d\n", status);
|
||||
return 1;
|
||||
}
|
||||
|
||||
float buf[256 * CHANNELS];
|
||||
for (int g = 0; g < SAMPLING_RATE;) {
|
||||
int frames = 1;
|
||||
status = cosmoaudio_poll(ca, NULL, &frames);
|
||||
if (status != COSMOAUDIO_SUCCESS) {
|
||||
fprintf(stderr, "failed to poll output: %d\n", status);
|
||||
return 2;
|
||||
}
|
||||
if (frames > 256)
|
||||
frames = 256;
|
||||
if (frames > SAMPLING_RATE - g)
|
||||
frames = SAMPLING_RATE - g;
|
||||
for (int f = 0; f < frames; ++f) {
|
||||
float t = (float)g++ / SAMPLING_RATE;
|
||||
float s = sinf(2 * M_PIf * WAVE_INTERVAL * t);
|
||||
for (int c = 0; c < CHANNELS; c++)
|
||||
buf[f * CHANNELS + c] = s * .3f;
|
||||
}
|
||||
status = cosmoaudio_write(ca, buf, frames);
|
||||
if (status != frames) {
|
||||
fprintf(stderr, "failed to write output: %d\n", status);
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
status = cosmoaudio_flush(ca);
|
||||
if (status != COSMOAUDIO_SUCCESS) {
|
||||
fprintf(stderr, "failed to flush output: %d\n", status);
|
||||
return 4;
|
||||
}
|
||||
|
||||
status = cosmoaudio_close(ca);
|
||||
if (status != COSMOAUDIO_SUCCESS) {
|
||||
fprintf(stderr, "failed to close audio: %d\n", status);
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2024 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/audio/describe.h"
|
||||
#include "dsp/audio/cosmoaudio/cosmoaudio.h"
|
||||
#include "libc/intrin/describeflags.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
#define append(...) o += ksnprintf(buf + o, n - o, __VA_ARGS__)
|
||||
|
||||
const char *cosmoaudio_describe_status(char *buf, int n, int status) {
|
||||
switch (status) {
|
||||
case COSMOAUDIO_SUCCESS:
|
||||
return "COSMOAUDIO_SUCCESS";
|
||||
case COSMOAUDIO_ERROR:
|
||||
return "COSMOAUDIO_ERROR";
|
||||
case COSMOAUDIO_EINVAL:
|
||||
return "COSMOAUDIO_EINVAL";
|
||||
case COSMOAUDIO_ELINK:
|
||||
return "COSMOAUDIO_ELINK";
|
||||
case COSMOAUDIO_ENOBUF:
|
||||
return "COSMOAUDIO_ENOBUF";
|
||||
default:
|
||||
ksnprintf(buf, n, "%d", status);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
const char *cosmoaudio_describe_open_options(
|
||||
char *buf, int n, const struct CosmoAudioOpenOptions *options) {
|
||||
int o = 0;
|
||||
char b128[128];
|
||||
bool gotsome = false;
|
||||
if (!options)
|
||||
return "NULL";
|
||||
if (kisdangerous(options)) {
|
||||
ksnprintf(buf, n, "%p", options);
|
||||
return buf;
|
||||
}
|
||||
append("{");
|
||||
|
||||
if (options->sampleRate) {
|
||||
if (gotsome)
|
||||
append(", ");
|
||||
append(".sampleRate=%d", options->sampleRate);
|
||||
gotsome = true;
|
||||
}
|
||||
|
||||
if (options->channels) {
|
||||
if (gotsome)
|
||||
append(", ");
|
||||
append(".channels=%d", options->channels);
|
||||
gotsome = true;
|
||||
}
|
||||
|
||||
if (options->deviceType) {
|
||||
if (gotsome)
|
||||
append(", ");
|
||||
static struct DescribeFlags kDeviceType[] = {
|
||||
{kCosmoAudioDeviceTypeDuplex, "Duplex"}, //
|
||||
{kCosmoAudioDeviceTypeCapture, "Capture"}, //
|
||||
{kCosmoAudioDeviceTypePlayback, "Playback"}, //
|
||||
};
|
||||
append(".deviceType=%s",
|
||||
_DescribeFlags(b128, 128, kDeviceType, ARRAYLEN(kDeviceType),
|
||||
"kCosmoAudioDeviceType", options->deviceType));
|
||||
gotsome = true;
|
||||
}
|
||||
|
||||
if (options->bufferFrames) {
|
||||
if (gotsome)
|
||||
append(", ");
|
||||
append(".bufferFrames=%d", options->bufferFrames);
|
||||
gotsome = true;
|
||||
}
|
||||
|
||||
if (options->debugLog) {
|
||||
if (gotsome)
|
||||
append(", ");
|
||||
append(".debugLog=%d", options->debugLog);
|
||||
gotsome = true;
|
||||
}
|
||||
|
||||
if (options->sizeofThis) {
|
||||
if (gotsome)
|
||||
append(", ");
|
||||
append(".sizeofThis=%d", options->sizeofThis);
|
||||
gotsome = true;
|
||||
}
|
||||
|
||||
append("}");
|
||||
return buf;
|
||||
}
|
||||
|
||||
const char *cosmoaudio_describe_poll_frames(char *buf, int n,
|
||||
int *in_out_frames) {
|
||||
if (!in_out_frames)
|
||||
return "NULL";
|
||||
if (kisdangerous(in_out_frames)) {
|
||||
ksnprintf(buf, n, "%p", in_out_frames);
|
||||
return buf;
|
||||
}
|
||||
ksnprintf(buf, n, "[%d]", *in_out_frames);
|
||||
return buf;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef COSMOPOLITAN_DSP_AUDIO_DESCRIBE_H_
|
||||
#define COSMOPOLITAN_DSP_AUDIO_DESCRIBE_H_
|
||||
#include "dsp/audio/cosmoaudio/cosmoaudio.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
const char *cosmoaudio_describe_status(char *, int, int);
|
||||
const char *cosmoaudio_describe_open_options(
|
||||
char *, int, const struct CosmoAudioOpenOptions *);
|
||||
const char *cosmoaudio_describe_poll_frames(char *, int, int *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_DSP_AUDIO_DESCRIBE_H_ */
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#ifndef COSMOPOLITAN_DSP_CORE_C161_H_
|
||||
#define COSMOPOLITAN_DSP_CORE_C161_H_
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
#define EXTRA_SHARP 2
|
||||
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#ifndef COSMOPOLITAN_DSP_CORE_C161S_H_
|
||||
#define COSMOPOLITAN_DSP_CORE_C161S_H_
|
||||
#include "dsp/core/c161.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
__funline signed char C161S(signed char al, signed char bl, signed char cl) {
|
||||
short ax, bx, cx;
|
||||
|
||||
+6
-7
@@ -9,14 +9,13 @@ int mulaw(int);
|
||||
int unmulaw(int);
|
||||
void *double2byte(long, const void *, double, double) vallocesque;
|
||||
void *byte2double(long, const void *, double, double) vallocesque;
|
||||
void *dct(float[restrict hasatleast 8][8], unsigned, float, float, float, float,
|
||||
float);
|
||||
void *dctjpeg(float[restrict hasatleast 8][8], unsigned);
|
||||
void *dct(float[hasatleast 8][8], unsigned, float, float, float, float, float);
|
||||
void *dctjpeg(float[hasatleast 8][8], unsigned);
|
||||
double det3(const double[3][3]) nosideeffect;
|
||||
void *inv3(double[restrict 3][3], const double[restrict 3][3], double);
|
||||
void *matmul3(double[restrict 3][3], const double[3][3], const double[3][3]);
|
||||
void *vmatmul3(double[restrict 3], const double[3], const double[3][3]);
|
||||
void *matvmul3(double[restrict 3], const double[3][3], const double[3]);
|
||||
void *inv3(double[3][3], const double[3][3], double);
|
||||
void *matmul3(double[3][3], const double[3][3], const double[3][3]);
|
||||
void *vmatmul3(double[3], const double[3], const double[3][3]);
|
||||
void *matvmul3(double[3], const double[3][3], const double[3]);
|
||||
double rgb2stdtv(double) pureconst;
|
||||
double rgb2lintv(double) pureconst;
|
||||
double rgb2stdpc(double, double) pureconst;
|
||||
|
||||
+3
-3
@@ -65,8 +65,8 @@
|
||||
*
|
||||
* @cost ~100ns
|
||||
*/
|
||||
void *dct(float M[restrict hasatleast 8][8], unsigned stride, float c0,
|
||||
float c1, float c2, float c3, float c4) {
|
||||
void *dct(float M[hasatleast 8][8], unsigned stride, float c0, float c1,
|
||||
float c2, float c3, float c4) {
|
||||
unsigned y, x;
|
||||
for (y = 0; y < stride * 8; y += stride) {
|
||||
DCT(M[y][0], M[y][1], M[y][2], M[y][3], M[y][4], M[y][5], M[y][6], M[y][7],
|
||||
@@ -79,7 +79,7 @@ void *dct(float M[restrict hasatleast 8][8], unsigned stride, float c0,
|
||||
return M;
|
||||
}
|
||||
|
||||
void *dctjpeg(float M[restrict hasatleast 8][8], unsigned stride) {
|
||||
void *dctjpeg(float M[hasatleast 8][8], unsigned stride) {
|
||||
return dct(M, stride, .707106781f, .382683433f, .541196100f, 1.306562965f,
|
||||
.707106781f);
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/core/core.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include "libc/assert.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "dsp/core/core.h"
|
||||
#include "dsp/core/q.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#ifndef COSMOPOLITAN_DSP_CORE_HALF_H_
|
||||
#define COSMOPOLITAN_DSP_CORE_HALF_H_
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
/**
|
||||
* Divides integer in half w/ rounding.
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@
|
||||
* @define 𝐀⁻¹=𝐁 such that 𝐀×𝐁=𝐁×𝐀=𝐈ₙ
|
||||
* @see det3()
|
||||
*/
|
||||
void *inv3(double B[restrict 3][3], const double A[restrict 3][3], double d) {
|
||||
void *inv3(double B[3][3], const double A[3][3], double d) {
|
||||
d = d ? 1 / d : NAN;
|
||||
B[0][0] = (A[1][1] * A[2][2] - A[2][1] * A[1][2]) * d;
|
||||
B[0][1] = (A[2][1] * A[0][2] - A[0][1] * A[2][2]) * d;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#ifndef COSMOPOLITAN_DSP_CORE_KS8_H_
|
||||
#define COSMOPOLITAN_DSP_CORE_KS8_H_
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
/**
|
||||
* Performs 16-bit scaled rounded madd w/ eight coefficients or fewer.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#ifndef COSMOPOLITAN_DSP_CORE_KSS8_H_
|
||||
#define COSMOPOLITAN_DSP_CORE_KSS8_H_
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
|
||||
/**
|
||||
* Performs 16-bit scaled rounded saturated madd w/ eight coefficients or fewer.
|
||||
|
||||
+1
-2
@@ -22,8 +22,7 @@
|
||||
/**
|
||||
* Multiplies 3×3 matrices.
|
||||
*/
|
||||
void *matmul3(double R[restrict 3][3], const double A[3][3],
|
||||
const double B[3][3]) {
|
||||
void *matmul3(double R[3][3], const double A[3][3], const double B[3][3]) {
|
||||
int i, j, k;
|
||||
memset(R, 0, sizeof(double) * 3 * 3);
|
||||
for (i = 0; i < 3; ++i) {
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
*
|
||||
* @see vmatmul3() for noncommutative corollary
|
||||
*/
|
||||
void *matvmul3(double R[restrict 3], const double M[3][3], const double V[3]) {
|
||||
void *matvmul3(double R[3], const double M[3][3], const double V[3]) {
|
||||
R[0] = V[0] * M[0][0] + V[1] * M[0][1] + V[2] * M[0][2];
|
||||
R[1] = V[0] * M[1][0] + V[1] * M[1][1] + V[2] * M[1][2];
|
||||
R[2] = V[0] * M[2][0] + V[1] * M[2][1] + V[2] * M[2][2];
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#ifndef COSMOPOLITAN_DSP_CORE_Q_H_
|
||||
#define COSMOPOLITAN_DSP_CORE_Q_H_
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/math.h"
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/core/core.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/macros.h"
|
||||
#include "third_party/aarch64/arm_neon.internal.h"
|
||||
#include "third_party/intel/emmintrin.internal.h"
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
*
|
||||
* @see matvmul3() for noncommutative corollary
|
||||
*/
|
||||
void *vmatmul3(double R[restrict 3], const double V[3], const double M[3][3]) {
|
||||
void *vmatmul3(double R[3], const double V[3], const double M[3][3]) {
|
||||
R[0] = V[0] * M[0][0] + V[1] * M[1][0] + V[2] * M[2][0];
|
||||
R[1] = V[0] * M[0][1] + V[1] * M[1][1] + V[2] * M[2][1];
|
||||
R[2] = V[0] * M[0][2] + V[1] * M[1][2] + V[2] * M[2][2];
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
DisableFormat: true
|
||||
SortIncludes: Never
|
||||
+4
-8
@@ -25,18 +25,13 @@ DSP_MPEG_A_CHECKS = \
|
||||
|
||||
DSP_MPEG_A_DIRECTDEPS = \
|
||||
LIBC_CALLS \
|
||||
LIBC_FMT \
|
||||
LIBC_INTRIN \
|
||||
LIBC_LOG \
|
||||
LIBC_LOG \
|
||||
LIBC_MEM \
|
||||
LIBC_NEXGEN32E \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_STDIO \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_TINYMATH \
|
||||
THIRD_PARTY_COMPILER_RT
|
||||
THIRD_PARTY_COMPILER_RT \
|
||||
|
||||
DSP_MPEG_A_DEPS := \
|
||||
$(call uniq,$(foreach x,$(DSP_MPEG_A_DIRECTDEPS),$($(x))))
|
||||
@@ -49,9 +44,10 @@ $(DSP_MPEG_A).pkg: \
|
||||
$(DSP_MPEG_A_OBJS) \
|
||||
$(foreach x,$(DSP_MPEG_A_DIRECTDEPS),$($(x)_A).pkg)
|
||||
|
||||
o/$(MODE)/dsp/mpeg/clamp4int256-k8.o: private \
|
||||
o/$(MODE)/dsp/mpeg/pl_mpeg.o: private \
|
||||
CFLAGS += \
|
||||
-Os
|
||||
-ffunction-sections \
|
||||
-fdata-sections
|
||||
|
||||
DSP_MPEG_LIBS = $(foreach x,$(DSP_MPEG_ARTIFACTS),$($(x)))
|
||||
DSP_MPEG_SRCS = $(foreach x,$(DSP_MPEG_ARTIFACTS),$($(x)_SRCS))
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
DESCRIPTION
|
||||
|
||||
pl_mpeg lets you decode .mpg files
|
||||
|
||||
ORIGIN
|
||||
|
||||
https://github.com/phoboslab/pl_mpeg/
|
||||
9e40dd6536269d788728e32c39bfacf2ab7a0866
|
||||
|
||||
LICENSE
|
||||
|
||||
MIT
|
||||
|
||||
LOCAL CHANGES
|
||||
|
||||
- Added API for extracting pixel aspect ratio
|
||||
https://github.com/phoboslab/pl_mpeg/pull/42
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
# PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
|
||||
|
||||
Single-file MIT licensed library for C/C++
|
||||
|
||||
See [pl_mpeg.h](https://github.com/phoboslab/pl_mpeg/blob/master/pl_mpeg.h) for
|
||||
the documentation.
|
||||
|
||||
|
||||
## Why?
|
||||
|
||||
This is meant as a simple way to get video playback into your app or game. Other
|
||||
solutions, such as ffmpeg require huge libraries and a lot of glue code.
|
||||
|
||||
MPEG1 is an old and inefficient codec, but it's still good enough for many use
|
||||
cases. All patents related to MPEG1 and MP2 have expired, so it's completely
|
||||
free now.
|
||||
|
||||
This library does not make use of any SIMD instructions, but because of
|
||||
the relative simplicity of the codec it still manages to decode 4k60fps video
|
||||
on a single CPU core (on my i7-6700k at least).
|
||||
|
||||
## Compilation on Linux
|
||||
|
||||
Use a GCC invocation like the following to build the example `pl_mpeg_player`
|
||||
program:
|
||||
|
||||
```shell
|
||||
gcc -o pl_mpeg_player pl_mpeg_player.c $(pkg-config --cflags --libs sdl2 glew)
|
||||
```
|
||||
|
||||
## Example Usage
|
||||
|
||||
- [pl_mpeg_extract_frames.c](https://github.com/phoboslab/pl_mpeg/blob/master/pl_mpeg_extract_frames.c)
|
||||
extracts all frames from a video and saves them as PNG.
|
||||
- [pl_mpeg_player.c](https://github.com/phoboslab/pl_mpeg/blob/master/pl_mpeg_player.c)
|
||||
implements a video player using SDL2 and OpenGL for rendering.
|
||||
|
||||
|
||||
|
||||
## Encoding for PL_MPEG
|
||||
|
||||
Most [MPEG-PS](https://en.wikipedia.org/wiki/MPEG_program_stream) (`.mpg`) files
|
||||
containing MPEG1 Video ("mpeg1") and MPEG1 Audio Layer II ("mp2") streams should
|
||||
work with PL_MPEG. Note that `.mpg` files can also contain MPEG2 Video, which is
|
||||
not supported by this library.
|
||||
|
||||
You can encode video in a suitable format using ffmpeg:
|
||||
|
||||
```
|
||||
ffmpeg -i input.mp4 -c:v mpeg1video -q:v 0 -c:a mp2 -format mpeg output.mpg
|
||||
```
|
||||
|
||||
`-q:v` sets a fixed video quality with a variable bitrate, where `0` is the
|
||||
highest. You may use `-b:v` to set a fixed bitrate instead; e.g.
|
||||
`-b:v 2000k` for 2000 kbit/s. Please refer to the
|
||||
[ffmpeg documentation](http://ffmpeg.org/ffmpeg.html#Options) for more details.
|
||||
|
||||
If you just want to quickly test the library, try this file:
|
||||
|
||||
https://phoboslab.org/files/bjork-all-is-full-of-love.mpg
|
||||
|
||||
|
||||
## Limitations
|
||||
|
||||
- no error reporting. PL_MPEG will silently ignore any invalid data.
|
||||
- the pts (presentation time stamp) for packets in the MPEG-PS container is
|
||||
ignored. This may cause sync issues with some files.
|
||||
- bugs, probably.
|
||||
@@ -1,92 +0,0 @@
|
||||
PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer
|
||||
Dominic Szablewski - https://phoboslab.org
|
||||
|
||||
-- Synopsis
|
||||
|
||||
// This function gets called for each decoded video frame
|
||||
void my_video_callback(plm_t *plm, plm_frame_t *frame, void *user) {
|
||||
// Do something with frame->y.data, frame->cr.data, frame->cb.data
|
||||
}
|
||||
|
||||
// This function gets called for each decoded audio frame
|
||||
void my_audio_callback(plm_t *plm, plm_samples_t *frame, void *user) {
|
||||
// Do something with samples->interleaved
|
||||
}
|
||||
|
||||
// Load a .mpg (MPEG Program Stream) file
|
||||
plm_t *plm = plm_create_with_filename("some-file.mpg");
|
||||
|
||||
// Install the video & audio decode callbacks
|
||||
plm_set_video_decode_callback(plm, my_video_callback, my_data);
|
||||
plm_set_audio_decode_callback(plm, my_audio_callback, my_data);
|
||||
|
||||
|
||||
// Decode
|
||||
do {
|
||||
plm_decode(plm, time_since_last_call);
|
||||
} while (!plm_has_ended(plm));
|
||||
|
||||
// All done
|
||||
plm_destroy(plm);
|
||||
|
||||
|
||||
|
||||
-- Documentation
|
||||
|
||||
This library provides several interfaces to load, demux and decode MPEG video
|
||||
and audio data. A high-level API combines the demuxer, video & audio decoders
|
||||
in an easy to use wrapper.
|
||||
|
||||
Lower-level APIs for accessing the demuxer, video decoder and audio decoder,
|
||||
as well as providing different data sources are also available.
|
||||
|
||||
Interfaces are written in an object orientet style, meaning you create object
|
||||
instances via various different constructor functions (plm_*create()),
|
||||
do some work on them and later dispose them via plm_*destroy().
|
||||
|
||||
plm_* -- the high-level interface, combining demuxer and decoders
|
||||
plm_buffer_* -- the data source used by all interfaces
|
||||
plm_demux_* -- the MPEG-PS demuxer
|
||||
plm_video_* -- the MPEG1 Video ("mpeg1") decoder
|
||||
plm_audio_* -- the MPEG1 Audio Layer II ("mp2") decoder
|
||||
|
||||
|
||||
This library uses malloc(), realloc() and free() to manage memory. Typically
|
||||
all allocation happens up-front when creating the interface. However, the
|
||||
default buffer size may be too small for certain inputs. In these cases plmpeg
|
||||
will realloc() the buffer with a larger size whenever needed. You can configure
|
||||
the default buffer size by defining PLM_BUFFER_DEFAULT_SIZE *before*
|
||||
including this library.
|
||||
|
||||
With the high-level interface you have two options to decode video & audio:
|
||||
|
||||
1) Use plm_decode() and just hand over the delta time since the last call.
|
||||
It will decode everything needed and call your callbacks (specified through
|
||||
plm_set_{video|audio}_decode_callback()) any number of times.
|
||||
|
||||
2) Use plm_decode_video() and plm_decode_audio() to decode exactly one
|
||||
frame of video or audio data at a time. How you handle the synchronization of
|
||||
both streams is up to you.
|
||||
|
||||
If you only want to decode video *or* audio through these functions, you should
|
||||
disable the other stream (plm_set_{video|audio}_enabled(false))
|
||||
|
||||
|
||||
Video data is decoded into a struct with all 3 planes (Y, Cr, Cb) stored in
|
||||
separate buffers. You can either convert this to RGB on the CPU (slow) via the
|
||||
plm_frame_to_rgb() function or do it on the GPU with the following matrix:
|
||||
|
||||
mat4 rec601 = mat4(
|
||||
1.16438, 0.00000, 1.59603, -0.87079,
|
||||
1.16438, -0.39176, -0.81297, 0.52959,
|
||||
1.16438, 2.01723, 0.00000, -1.08139,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
gl_FragColor = vec4(y, cb, cr, 1.0) * rec601;
|
||||
|
||||
Audio data is decoded into a struct with either one single float array with the
|
||||
samples for the left and right channel interleaved, or if the
|
||||
PLM_AUDIO_SEPARATE_CHANNELS is defined *before* including this library, into
|
||||
two separate float arrays - one for each channel.
|
||||
|
||||
See below for detailed the API documentation.
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef COSMOPOLITAN_DSP_MPEG_BLOCKSET_H_
|
||||
#define COSMOPOLITAN_DSP_MPEG_BLOCKSET_H_
|
||||
|
||||
#define PLM_BLOCK_SET(DEST, DEST_INDEX, DEST_WIDTH, SOURCE_INDEX, \
|
||||
SOURCE_WIDTH, BLOCK_SIZE, OP) \
|
||||
do { \
|
||||
int dest_scan = DEST_WIDTH - BLOCK_SIZE; \
|
||||
int source_scan = SOURCE_WIDTH - BLOCK_SIZE; \
|
||||
for (int y = 0; y < BLOCK_SIZE; y++) { \
|
||||
for (int x = 0; x < BLOCK_SIZE; x++) { \
|
||||
DEST[DEST_INDEX] = OP; \
|
||||
SOURCE_INDEX++; \
|
||||
DEST_INDEX++; \
|
||||
} \
|
||||
SOURCE_INDEX += source_scan; \
|
||||
DEST_INDEX += dest_scan; \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#endif /* COSMOPOLITAN_DSP_MPEG_BLOCKSET_H_ */
|
||||
@@ -1,153 +0,0 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
||||
│ vi: set noet ft=c ts=4 sw=4 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer │
|
||||
│ Dominic Szablewski - https://phoboslab.org │
|
||||
│ │
|
||||
│ The MIT License(MIT) │
|
||||
│ Copyright(c) 2019 Dominic Szablewski │
|
||||
│ │
|
||||
│ 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. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/mpeg/buffer.h"
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/madv.h"
|
||||
__static_yoink("pl_mpeg_notice");
|
||||
|
||||
/* clang-format off */
|
||||
// -----------------------------------------------------------------------------
|
||||
// plm_buffer implementation
|
||||
|
||||
plm_buffer_t *plm_buffer_create_with_filename(const char *filename) {
|
||||
FILE *fh = fopen(filename, "rb");
|
||||
if (!fh) {
|
||||
return NULL;
|
||||
}
|
||||
fadvise(fileno(fh), 0, 0, MADV_SEQUENTIAL);
|
||||
return plm_buffer_create_with_file(fh, true);
|
||||
}
|
||||
|
||||
plm_buffer_t *plm_buffer_create_with_file(FILE *fh, int close_when_done) {
|
||||
plm_buffer_t *b;
|
||||
b = plm_buffer_create_with_capacity(PLM_BUFFER_DEFAULT_SIZE);
|
||||
b->fh = fh;
|
||||
b->close_when_done = close_when_done;
|
||||
b->mode = PLM_BUFFER_MODE_FILE;
|
||||
plm_buffer_set_load_callback(b, plm_buffer_load_file_callback, NULL);
|
||||
return b;
|
||||
}
|
||||
|
||||
plm_buffer_t *plm_buffer_create_with_memory(unsigned char *bytes, size_t length, int free_when_done) {
|
||||
plm_buffer_t *b;
|
||||
b = memalign(_Alignof(plm_buffer_t), sizeof(plm_buffer_t));
|
||||
memset(b, 0, sizeof(plm_buffer_t));
|
||||
b->capacity = length;
|
||||
b->length = length;
|
||||
b->free_when_done = free_when_done;
|
||||
b->bytes = bytes;
|
||||
b->mode = PLM_BUFFER_MODE_FIXED_MEM;
|
||||
return b;
|
||||
}
|
||||
|
||||
plm_buffer_t * plm_buffer_create_with_capacity(size_t capacity) {
|
||||
plm_buffer_t *b;
|
||||
b = memalign(_Alignof(plm_buffer_t), sizeof(plm_buffer_t));
|
||||
memset(b, 0, sizeof(plm_buffer_t));
|
||||
b->capacity = capacity;
|
||||
b->free_when_done = true;
|
||||
b->bytes = (unsigned char *)malloc(capacity);
|
||||
b->mode = PLM_BUFFER_MODE_DYNAMIC_MEM;
|
||||
return b;
|
||||
}
|
||||
|
||||
void plm_buffer_destroy(plm_buffer_t *self) {
|
||||
if (self->fh && self->close_when_done) {
|
||||
fclose(self->fh);
|
||||
}
|
||||
if (self->free_when_done) {
|
||||
free(self->bytes);
|
||||
}
|
||||
free(self);
|
||||
}
|
||||
|
||||
size_t plm_buffer_write(plm_buffer_t *self, unsigned char *bytes, size_t length) {
|
||||
if (self->mode == PLM_BUFFER_MODE_FIXED_MEM) {
|
||||
return 0;
|
||||
}
|
||||
// This should be a ring buffer, but instead it just shifts all unread data
|
||||
// to the beginning of the buffer and appends new data at the end. Seems
|
||||
// to be good enough.
|
||||
plm_buffer_discard_read_bytes(self);
|
||||
// Do we have to resize to fit the new data?
|
||||
size_t bytes_available = self->capacity - self->length;
|
||||
if (bytes_available < length) {
|
||||
size_t new_size = self->capacity;
|
||||
do {
|
||||
new_size *= 2;
|
||||
} while (new_size - self->length < length);
|
||||
self->bytes = (unsigned char *)realloc(self->bytes, new_size);
|
||||
self->capacity = new_size;
|
||||
}
|
||||
memcpy(self->bytes + self->length, bytes, length);
|
||||
self->length += length;
|
||||
return length;
|
||||
}
|
||||
|
||||
void plm_buffer_set_load_callback(plm_buffer_t *self, plm_buffer_load_callback fp, void *user) {
|
||||
self->load_callback = fp;
|
||||
self->load_callback_user_data = user;
|
||||
}
|
||||
|
||||
void plm_buffer_rewind(plm_buffer_t *self) {
|
||||
if (self->fh) {
|
||||
fseek(self->fh, 0, SEEK_SET);
|
||||
self->length = 0;
|
||||
}
|
||||
if (self->mode != PLM_BUFFER_MODE_FIXED_MEM) {
|
||||
self->length = 0;
|
||||
}
|
||||
self->bit_index = 0;
|
||||
}
|
||||
|
||||
void plm_buffer_discard_read_bytes(plm_buffer_t *self) {
|
||||
size_t byte_pos = self->bit_index >> 3;
|
||||
if (byte_pos == self->length) {
|
||||
self->bit_index = 0;
|
||||
self->length = 0;
|
||||
}
|
||||
else if (byte_pos > 0) {
|
||||
memmove(self->bytes, self->bytes + byte_pos, self->length - byte_pos);
|
||||
self->bit_index -= byte_pos << 3;
|
||||
self->length -= byte_pos;
|
||||
}
|
||||
}
|
||||
|
||||
void plm_buffer_load_file_callback(plm_buffer_t *self, void *user) {
|
||||
plm_buffer_discard_read_bytes(self);
|
||||
unsigned bytes_available = self->capacity - self->length;
|
||||
unsigned bytes_read = fread(self->bytes + self->length, 1, bytes_available, self->fh);
|
||||
self->length += bytes_read;
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
#ifndef COSMOPOLITAN_DSP_MPEG_BUFFER_H_
|
||||
#define COSMOPOLITAN_DSP_MPEG_BUFFER_H_
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
enum plm_buffer_mode {
|
||||
PLM_BUFFER_MODE_FILE,
|
||||
PLM_BUFFER_MODE_FIXED_MEM,
|
||||
PLM_BUFFER_MODE_DYNAMIC_MEM
|
||||
};
|
||||
|
||||
typedef struct plm_buffer_t {
|
||||
unsigned bit_index;
|
||||
unsigned capacity;
|
||||
unsigned length;
|
||||
int free_when_done;
|
||||
int close_when_done;
|
||||
FILE *fh;
|
||||
plm_buffer_load_callback load_callback;
|
||||
void *load_callback_user_data;
|
||||
unsigned char *bytes;
|
||||
enum plm_buffer_mode mode;
|
||||
} plm_buffer_t;
|
||||
|
||||
typedef struct {
|
||||
int16_t index;
|
||||
int16_t value;
|
||||
} plm_vlc_t;
|
||||
|
||||
typedef struct {
|
||||
int16_t index;
|
||||
uint16_t value;
|
||||
} plm_vlc_uint_t;
|
||||
|
||||
/* bool plm_buffer_has(plm_buffer_t *, size_t); */
|
||||
/* int plm_buffer_read(plm_buffer_t *, int); */
|
||||
/* void plm_buffer_align(plm_buffer_t *); */
|
||||
/* void plm_buffer_skip(plm_buffer_t *, size_t); */
|
||||
/* int plm_buffer_skip_bytes(plm_buffer_t *, unsigned char); */
|
||||
/* int plm_buffer_next_start_code(plm_buffer_t *); */
|
||||
/* int plm_buffer_find_start_code(plm_buffer_t *, int); */
|
||||
/* int plm_buffer_no_start_code(plm_buffer_t *); */
|
||||
/* int16_t plm_buffer_read_vlc(plm_buffer_t *, const plm_vlc_t *); */
|
||||
/* uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *, const plm_vlc_uint_t *); */
|
||||
|
||||
void plm_buffer_discard_read_bytes(plm_buffer_t *);
|
||||
relegated void plm_buffer_load_file_callback(plm_buffer_t *, void *);
|
||||
|
||||
forceinline bool plm_buffer_has(plm_buffer_t *b, size_t bits) {
|
||||
unsigned have;
|
||||
have = b->length;
|
||||
have <<= 3;
|
||||
have -= b->bit_index;
|
||||
if (bits <= have) {
|
||||
return true;
|
||||
} else {
|
||||
if (b->load_callback) {
|
||||
b->load_callback(b, b->load_callback_user_data);
|
||||
return ((b->length << 3) - b->bit_index) >= bits;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
forceinline int plm_buffer_read(plm_buffer_t *self, int count) {
|
||||
if (!plm_buffer_has(self, count))
|
||||
return 0;
|
||||
int value = 0;
|
||||
while (count) {
|
||||
int current_byte = self->bytes[self->bit_index >> 3];
|
||||
int remaining = 8 - (self->bit_index & 7); // Remaining bits in byte
|
||||
int read = remaining < count ? remaining : count; // Bits in self run
|
||||
int shift = remaining - read;
|
||||
int mask = (0xff >> (8 - read));
|
||||
value = (value << read) | ((current_byte & (mask << shift)) >> shift);
|
||||
self->bit_index += read;
|
||||
count -= read;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
forceinline void plm_buffer_align(plm_buffer_t *self) {
|
||||
self->bit_index = ((self->bit_index + 7) >> 3) << 3;
|
||||
}
|
||||
|
||||
forceinline void plm_buffer_skip(plm_buffer_t *self, size_t count) {
|
||||
if (plm_buffer_has(self, count)) {
|
||||
self->bit_index += count;
|
||||
}
|
||||
}
|
||||
|
||||
forceinline int plm_buffer_skip_bytes(plm_buffer_t *self, unsigned char v) {
|
||||
unsigned skipped;
|
||||
plm_buffer_align(self);
|
||||
skipped = 0;
|
||||
while (plm_buffer_has(self, 8)) {
|
||||
if (v == self->bytes[self->bit_index >> 3]) {
|
||||
self->bit_index += 8;
|
||||
++skipped;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return skipped;
|
||||
}
|
||||
|
||||
forceinline int plm_buffer_next_start_code(plm_buffer_t *self) {
|
||||
plm_buffer_align(self);
|
||||
while (plm_buffer_has(self, (5 << 3))) {
|
||||
size_t byte_index = (self->bit_index) >> 3;
|
||||
if (self->bytes[byte_index] == 0x00 &&
|
||||
self->bytes[byte_index + 1] == 0x00 &&
|
||||
self->bytes[byte_index + 2] == 0x01) {
|
||||
self->bit_index = (byte_index + 4) << 3;
|
||||
return self->bytes[byte_index + 3];
|
||||
}
|
||||
self->bit_index += 8;
|
||||
}
|
||||
self->bit_index = (self->length << 3);
|
||||
return -1;
|
||||
}
|
||||
|
||||
forceinline int plm_buffer_find_start_code(plm_buffer_t *self, int code) {
|
||||
int current = 0;
|
||||
while (true) {
|
||||
current = plm_buffer_next_start_code(self);
|
||||
if (current == code || current == -1) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
forceinline int plm_buffer_no_start_code(plm_buffer_t *self) {
|
||||
if (!plm_buffer_has(self, (5 << 3))) {
|
||||
return false;
|
||||
}
|
||||
size_t byte_index = ((self->bit_index + 7) >> 3);
|
||||
return !(self->bytes[byte_index] == 0x00 &&
|
||||
self->bytes[byte_index + 1] == 0x00 &&
|
||||
self->bytes[byte_index + 2] == 0x01);
|
||||
}
|
||||
|
||||
forceinline int16_t plm_buffer_read_vlc(plm_buffer_t *self,
|
||||
const plm_vlc_t *table) {
|
||||
plm_vlc_t state = {0, 0};
|
||||
do {
|
||||
state = table[state.index + plm_buffer_read(self, 1)];
|
||||
} while (state.index > 0);
|
||||
return state.value;
|
||||
}
|
||||
|
||||
forceinline uint16_t plm_buffer_read_vlc_uint(plm_buffer_t *self,
|
||||
const plm_vlc_uint_t *table) {
|
||||
return (uint16_t)plm_buffer_read_vlc(self, (plm_vlc_t *)table);
|
||||
}
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_DSP_MPEG_BUFFER_H_ */
|
||||
@@ -1,30 +0,0 @@
|
||||
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
|
||||
│ vi: set noet ft=asm ts=8 sw=8 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/macros.internal.h"
|
||||
|
||||
clamp4int256$core:
|
||||
.leafprologue
|
||||
pxor %xmm1,%xmm1
|
||||
pmaxsd %xmm1,%xmm0
|
||||
pminsd 0f(%rip),%xmm0
|
||||
.leafepilogue
|
||||
.endfn clamp4int256$core,globl
|
||||
|
||||
.rodata.cst16
|
||||
0: .long 255,255,255,255
|
||||
@@ -1,203 +0,0 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
||||
│ vi: set noet ft=c ts=4 sw=4 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer │
|
||||
│ Dominic Szablewski - https://phoboslab.org │
|
||||
│ │
|
||||
│ The MIT License(MIT) │
|
||||
│ Copyright(c) 2019 Dominic Szablewski │
|
||||
│ │
|
||||
│ 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. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/mpeg/demux.h"
|
||||
#include "dsp/mpeg/buffer.h"
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
__static_yoink("pl_mpeg_notice");
|
||||
|
||||
/* clang-format off */
|
||||
// ----------------------------------------------------------------------------
|
||||
// plm_demux implementation
|
||||
|
||||
plm_demux_t *plm_demux_create(plm_buffer_t *buffer, int destroy_when_done) {
|
||||
plm_demux_t *self = (plm_demux_t *)malloc(sizeof(plm_demux_t));
|
||||
memset(self, 0, sizeof(plm_demux_t));
|
||||
|
||||
self->buffer = buffer;
|
||||
self->destroy_buffer_when_done = destroy_when_done;
|
||||
|
||||
if (plm_buffer_find_start_code(self->buffer, START_PACK) != -1) {
|
||||
plm_demux_decode_pack_header(self);
|
||||
}
|
||||
if (plm_buffer_find_start_code(self->buffer, START_SYSTEM) != -1) {
|
||||
plm_demux_decode_system_header(self);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
void plm_demux_destroy(plm_demux_t *self) {
|
||||
if (self->destroy_buffer_when_done) {
|
||||
plm_buffer_destroy(self->buffer);
|
||||
}
|
||||
free(self);
|
||||
}
|
||||
|
||||
int plm_demux_get_num_video_streams(plm_demux_t *self) {
|
||||
return self->num_video_streams;
|
||||
}
|
||||
|
||||
int plm_demux_get_num_audio_streams(plm_demux_t *self) {
|
||||
return self->num_audio_streams;
|
||||
}
|
||||
|
||||
void plm_demux_rewind(plm_demux_t *self) {
|
||||
plm_buffer_rewind(self->buffer);
|
||||
}
|
||||
|
||||
plm_packet_t *plm_demux_decode(plm_demux_t *self) {
|
||||
if (self->current_packet.length) {
|
||||
size_t bits_till_next_packet = self->current_packet.length << 3;
|
||||
if (!plm_buffer_has(self->buffer, bits_till_next_packet)) {
|
||||
return NULL;
|
||||
}
|
||||
plm_buffer_skip(self->buffer, bits_till_next_packet);
|
||||
self->current_packet.length = 0;
|
||||
}
|
||||
|
||||
if (!self->has_pack_header) {
|
||||
if (plm_buffer_find_start_code(self->buffer, START_PACK) != -1) {
|
||||
plm_demux_decode_pack_header(self);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!self->has_system_header) {
|
||||
if (plm_buffer_find_start_code(self->buffer, START_SYSTEM) != -1) {
|
||||
plm_demux_decode_system_header(self);
|
||||
}
|
||||
else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// pending packet just waiting for data?
|
||||
if (self->next_packet.length) {
|
||||
return plm_demux_get_packet(self);
|
||||
}
|
||||
|
||||
int code;
|
||||
do {
|
||||
code = plm_buffer_next_start_code(self->buffer);
|
||||
if (
|
||||
code == PLM_DEMUX_PACKET_VIDEO_1 ||
|
||||
code == PLM_DEMUX_PACKET_PRIVATE ||
|
||||
(code >= PLM_DEMUX_PACKET_AUDIO_1 && code <= PLM_DEMUX_PACKET_AUDIO_4)
|
||||
) {
|
||||
return plm_demux_decode_packet(self, code);
|
||||
}
|
||||
} while (code != -1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double plm_demux_read_time(plm_demux_t *self) {
|
||||
int64_t clock = plm_buffer_read(self->buffer, 3) << 30;
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
clock |= plm_buffer_read(self->buffer, 15) << 15;
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
clock |= plm_buffer_read(self->buffer, 15);
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
return (double)clock / 90000.0;
|
||||
}
|
||||
|
||||
void plm_demux_decode_pack_header(plm_demux_t *self) {
|
||||
if (plm_buffer_read(self->buffer, 4) != 0x02) {
|
||||
return; // invalid
|
||||
}
|
||||
self->system_clock_ref = plm_demux_read_time(self);
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
plm_buffer_skip(self->buffer, 22); // mux_rate * 50
|
||||
plm_buffer_skip(self->buffer, 1);
|
||||
|
||||
self->has_pack_header = true;
|
||||
}
|
||||
|
||||
void plm_demux_decode_system_header(plm_demux_t *self) {
|
||||
plm_buffer_skip(self->buffer, 16); // header_length
|
||||
plm_buffer_skip(self->buffer, 24); // rate bound
|
||||
self->num_audio_streams = plm_buffer_read(self->buffer, 6);
|
||||
plm_buffer_skip(self->buffer, 5); // misc flags
|
||||
self->num_video_streams = plm_buffer_read(self->buffer, 5);
|
||||
|
||||
self->has_system_header = true;
|
||||
}
|
||||
|
||||
plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int start_code) {
|
||||
if (!plm_buffer_has(self->buffer, 8 << 3)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->next_packet.type = start_code;
|
||||
self->next_packet.length = plm_buffer_read(self->buffer, 16);
|
||||
self->next_packet.length -= plm_buffer_skip_bytes(self->buffer, 0xff); // stuffing
|
||||
|
||||
// skip P-STD
|
||||
if (plm_buffer_read(self->buffer, 2) == 0x01) {
|
||||
plm_buffer_skip(self->buffer, 16);
|
||||
self->next_packet.length -= 2;
|
||||
}
|
||||
|
||||
int pts_dts_marker = plm_buffer_read(self->buffer, 2);
|
||||
if (pts_dts_marker == 0x03) {
|
||||
self->next_packet.pts = plm_demux_read_time(self);
|
||||
plm_buffer_skip(self->buffer, 40); // skip dts
|
||||
self->next_packet.length -= 10;
|
||||
}
|
||||
else if (pts_dts_marker == 0x02) {
|
||||
self->next_packet.pts = plm_demux_read_time(self);
|
||||
self->next_packet.length -= 5;
|
||||
}
|
||||
else if (pts_dts_marker == 0x00) {
|
||||
self->next_packet.pts = 0;
|
||||
plm_buffer_skip(self->buffer, 4);
|
||||
self->next_packet.length -= 1;
|
||||
}
|
||||
else {
|
||||
return NULL; // invalid
|
||||
}
|
||||
|
||||
return plm_demux_get_packet(self);
|
||||
}
|
||||
|
||||
plm_packet_t *plm_demux_get_packet(plm_demux_t *self) {
|
||||
if (!plm_buffer_has(self->buffer, self->next_packet.length << 3)) {
|
||||
return NULL;
|
||||
}
|
||||
self->current_packet.data = self->buffer->bytes + (self->buffer->bit_index >> 3);
|
||||
self->current_packet.length = self->next_packet.length;
|
||||
self->current_packet.type = self->next_packet.type;
|
||||
self->current_packet.pts = self->next_packet.pts;
|
||||
self->next_packet.length = 0;
|
||||
return &self->current_packet;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef COSMOPOLITAN_DSP_MPEG_DEMUX_H_
|
||||
#define COSMOPOLITAN_DSP_MPEG_DEMUX_H_
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define START_PACK 0xBA
|
||||
#define START_END 0xB9
|
||||
#define START_SYSTEM 0xBB
|
||||
|
||||
typedef struct plm_demux_t {
|
||||
plm_buffer_t *buffer;
|
||||
int destroy_buffer_when_done;
|
||||
double system_clock_ref;
|
||||
int has_pack_header;
|
||||
int has_system_header;
|
||||
int num_audio_streams;
|
||||
int num_video_streams;
|
||||
plm_packet_t current_packet;
|
||||
plm_packet_t next_packet;
|
||||
} plm_demux_t;
|
||||
|
||||
double plm_demux_read_time(plm_demux_t *self);
|
||||
void plm_demux_decode_pack_header(plm_demux_t *self);
|
||||
void plm_demux_decode_system_header(plm_demux_t *self);
|
||||
plm_packet_t *plm_demux_decode_packet(plm_demux_t *self, int start_code);
|
||||
plm_packet_t *plm_demux_get_packet(plm_demux_t *self);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_DSP_MPEG_DEMUX_H_ */
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=4 sw=4 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer │
|
||||
│ Dominic Szablewski - https://phoboslab.org │
|
||||
│ │
|
||||
│ The MIT License(MIT) │
|
||||
│ Copyright(c) 2019 Dominic Szablewski │
|
||||
│ │
|
||||
│ 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. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/core/half.h"
|
||||
__static_yoink("pl_mpeg_notice");
|
||||
|
||||
/**
|
||||
* Computes Fixed-Point 8x8 Inverse Discrete Cosine Transform.
|
||||
*
|
||||
* @note discovered by Nasir Ahmed
|
||||
*/
|
||||
void plm_video_idct(int block[8][8]) {
|
||||
int i, t1, t2, m0;
|
||||
int b1, b3, b4, b6, b7;
|
||||
int y3, y4, y5, y6, y7;
|
||||
int x0, x1, x2, x3, x4;
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
b1 = block[4][i];
|
||||
b3 = block[2][i] + block[6][i];
|
||||
b4 = block[5][i] - block[3][i];
|
||||
t1 = block[1][i] + block[7][i];
|
||||
t2 = block[3][i] + block[5][i];
|
||||
b6 = block[1][i] - block[7][i];
|
||||
b7 = t1 + t2;
|
||||
m0 = block[0][i];
|
||||
x4 = ((b6 * 473 - b4 * 196 + 128) >> 8) - b7;
|
||||
x0 = x4 - (((t1 - t2) * 362 + 128) >> 8);
|
||||
x1 = m0 - b1;
|
||||
x2 = (((block[2][i] - block[6][i]) * 362 + 128) >> 8) - b3;
|
||||
x3 = m0 + b1;
|
||||
y3 = x1 + x2;
|
||||
y4 = x3 + b3;
|
||||
y5 = x1 - x2;
|
||||
y6 = x3 - b3;
|
||||
y7 = -x0 - ((b4 * 473 + b6 * 196 + 128) >> 8);
|
||||
block[0][i] = b7 + y4;
|
||||
block[1][i] = x4 + y3;
|
||||
block[2][i] = y5 - x0;
|
||||
block[3][i] = y6 - y7;
|
||||
block[4][i] = y6 + y7;
|
||||
block[5][i] = x0 + y5;
|
||||
block[6][i] = y3 - x4;
|
||||
block[7][i] = y4 - b7;
|
||||
}
|
||||
|
||||
for (i = 0; i < 8; ++i) {
|
||||
b1 = block[i][4];
|
||||
b3 = block[i][2] + block[i][6];
|
||||
b4 = block[i][5] - block[i][3];
|
||||
t1 = block[i][1] + block[i][7];
|
||||
t2 = block[i][3] + block[i][5];
|
||||
b6 = block[i][1] - block[i][7];
|
||||
b7 = t1 + t2;
|
||||
m0 = block[i][0];
|
||||
x4 = ((b6 * 473 - b4 * 196 + 128) >> 8) - b7;
|
||||
x0 = x4 - (((t1 - t2) * 362 + 128) >> 8);
|
||||
x1 = m0 - b1;
|
||||
x2 = (((block[i][2] - block[i][6]) * 362 + 128) >> 8) - b3;
|
||||
x3 = m0 + b1;
|
||||
y3 = x1 + x2;
|
||||
y4 = x3 + b3;
|
||||
y5 = x1 - x2;
|
||||
y6 = x3 - b3;
|
||||
y7 = -x0 - ((b4 * 473 + b6 * 196 + 128) >> 8);
|
||||
block[i][0] = (b7 + y4 + 128) >> 8;
|
||||
block[i][1] = (x4 + y3 + 128) >> 8;
|
||||
block[i][2] = (y5 - x0 + 128) >> 8;
|
||||
block[i][3] = (y6 - y7 + 128) >> 8;
|
||||
block[i][4] = (y6 + y7 + 128) >> 8;
|
||||
block[i][5] = (x0 + y5 + 128) >> 8;
|
||||
block[i][6] = (y3 - x4 + 128) >> 8;
|
||||
block[i][7] = (y4 - b7 + 128) >> 8;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#ifndef COSMOPOLITAN_DSP_MPEG_IDCT_H_
|
||||
#define COSMOPOLITAN_DSP_MPEG_IDCT_H_
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
void plm_video_idct(int *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* COSMOPOLITAN_DSP_MPEG_IDCT_H_ */
|
||||
@@ -1,172 +0,0 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
||||
│ vi: set et ft=c ts=4 sw=4 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer │
|
||||
│ Dominic Szablewski - https://phoboslab.org │
|
||||
│ │
|
||||
│ The MIT License(MIT) │
|
||||
│ Copyright(c) 2019 Dominic Szablewski │
|
||||
│ │
|
||||
│ 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. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
#include "dsp/mpeg/video.h"
|
||||
#include "libc/log/check.h"
|
||||
|
||||
forceinline void plm_video_process_macroblock(plm_video_t *self,
|
||||
uint8_t *restrict d,
|
||||
uint8_t *restrict s, int motion_h,
|
||||
int motion_v, bool interpolate,
|
||||
unsigned BW) {
|
||||
unsigned si, di, max_address;
|
||||
int y, x, dest_scan, source_scan, dw, hp, vp, odd_h, odd_v;
|
||||
dw = self->mb_width * BW;
|
||||
hp = motion_h >> 1;
|
||||
vp = motion_v >> 1;
|
||||
odd_h = (motion_h & 1) == 1;
|
||||
odd_v = (motion_v & 1) == 1;
|
||||
si = ((self->mb_row * BW) + vp) * dw + (self->mb_col * BW) + hp;
|
||||
di = (self->mb_row * dw + self->mb_col) * BW;
|
||||
max_address = (dw * (self->mb_height * BW - BW + 1) - BW);
|
||||
if (si > max_address || di > max_address)
|
||||
return;
|
||||
d += di;
|
||||
s += si;
|
||||
switch (((interpolate << 2) | (odd_h << 1) | (odd_v)) & 7) {
|
||||
case 0:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
*d++ = *s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
*d++ = (s[0] + s[dw] + 1) >> 1;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
*d++ = (s[0] + s[1] + 1) >> 1;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
*d++ = (s[0] + s[1] + s[dw] + s[dw + 1] + 2) >> 2;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
d[0] = (d[0] + (s[0]) + 1) >> 1;
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
d[0] = (d[0] + ((s[0] + s[dw] + 1) >> 1) + 1) >> 1;
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
d[0] = (d[0] + ((s[0] + s[1] + 1) >> 1) + 1) >> 1;
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
dest_scan = dw - BW;
|
||||
source_scan = dw - BW;
|
||||
for (y = 0; y < BW; y++) {
|
||||
for (x = 0; x < BW; x++) {
|
||||
d[0] = (d[0] + ((s[0] + s[1] + s[dw] + s[dw + 1] + 2) >> 2) + 1) >> 1;
|
||||
d++;
|
||||
s++;
|
||||
}
|
||||
s += source_scan;
|
||||
d += dest_scan;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void plm_video_process_macroblock_8(plm_video_t *self, uint8_t *restrict d,
|
||||
uint8_t *restrict s, int motion_h,
|
||||
int motion_v, bool interpolate) {
|
||||
DCHECK_ALIGNED(8, d);
|
||||
DCHECK_ALIGNED(8, s);
|
||||
plm_video_process_macroblock(self, d, s, motion_h, motion_v, interpolate, 8);
|
||||
}
|
||||
|
||||
void plm_video_process_macroblock_16(plm_video_t *self, uint8_t *restrict d,
|
||||
uint8_t *restrict s, int motion_h,
|
||||
int motion_v, bool interpolate) {
|
||||
DCHECK_ALIGNED(16, d);
|
||||
DCHECK_ALIGNED(16, s);
|
||||
plm_video_process_macroblock(self, d, s, motion_h, motion_v, interpolate, 16);
|
||||
}
|
||||
-769
@@ -1,769 +0,0 @@
|
||||
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
||||
│ vi: set noet ft=c ts=4 sw=4 fenc=utf-8 :vi │
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ PL_MPEG - MPEG1 Video decoder, MP2 Audio decoder, MPEG-PS demuxer │
|
||||
│ Dominic Szablewski - https://phoboslab.org │
|
||||
│ │
|
||||
│ The MIT License(MIT) │
|
||||
│ Copyright(c) 2019 Dominic Szablewski │
|
||||
│ │
|
||||
│ 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. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "dsp/mpeg/buffer.h"
|
||||
#include "dsp/mpeg/mpeg.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
|
||||
/* clang-format off */
|
||||
// -----------------------------------------------------------------------------
|
||||
// plm_audio implementation
|
||||
|
||||
// Based on kjmp2 by Martin J. Fiedler
|
||||
// http://keyj.emphy.de/kjmp2/
|
||||
|
||||
#define PLM_AUDIO_FRAME_SYNC 0x7ff
|
||||
|
||||
#define PLM_AUDIO_MPEG_2_5 0x0
|
||||
#define PLM_AUDIO_MPEG_2 0x2
|
||||
#define PLM_AUDIO_MPEG_1 0x3
|
||||
|
||||
#define PLM_AUDIO_LAYER_III 0x1
|
||||
#define PLM_AUDIO_LAYER_II 0x2
|
||||
#define PLM_AUDIO_LAYER_I 0x3
|
||||
|
||||
#define PLM_AUDIO_MODE_STEREO 0x0
|
||||
#define PLM_AUDIO_MODE_JOINT_STEREO 0x1
|
||||
#define PLM_AUDIO_MODE_DUAL_CHANNEL 0x2
|
||||
#define PLM_AUDIO_MODE_MONO 0x3
|
||||
|
||||
static const unsigned short PLM_AUDIO_SAMPLE_RATE[] = {
|
||||
44100, 48000, 32000, 0, // MPEG-1
|
||||
22050, 24000, 16000, 0 // MPEG-2
|
||||
};
|
||||
|
||||
static const short PLM_AUDIO_BIT_RATE[] = {
|
||||
32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, // MPEG-1
|
||||
8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 // MPEG-2
|
||||
};
|
||||
|
||||
static const int PLM_AUDIO_SCALEFACTOR_BASE[] = {
|
||||
0x02000000, 0x01965FEA, 0x01428A30
|
||||
};
|
||||
|
||||
static const float PLM_AUDIO_SYNTHESIS_WINDOW[] = {
|
||||
0.0, -0.5, -0.5, -0.5, -0.5, -0.5,
|
||||
-0.5, -1.0, -1.0, -1.0, -1.0, -1.5,
|
||||
-1.5, -2.0, -2.0, -2.5, -2.5, -3.0,
|
||||
-3.5, -3.5, -4.0, -4.5, -5.0, -5.5,
|
||||
-6.5, -7.0, -8.0, -8.5, -9.5, -10.5,
|
||||
-12.0, -13.0, -14.5, -15.5, -17.5, -19.0,
|
||||
-20.5, -22.5, -24.5, -26.5, -29.0, -31.5,
|
||||
-34.0, -36.5, -39.5, -42.5, -45.5, -48.5,
|
||||
-52.0, -55.5, -58.5, -62.5, -66.0, -69.5,
|
||||
-73.5, -77.0, -80.5, -84.5, -88.0, -91.5,
|
||||
-95.0, -98.0, -101.0, -104.0, 106.5, 109.0,
|
||||
111.0, 112.5, 113.5, 114.0, 114.0, 113.5,
|
||||
112.0, 110.5, 107.5, 104.0, 100.0, 94.5,
|
||||
88.5, 81.5, 73.0, 63.5, 53.0, 41.5,
|
||||
28.5, 14.5, -1.0, -18.0, -36.0, -55.5,
|
||||
-76.5, -98.5, -122.0, -147.0, -173.5, -200.5,
|
||||
-229.5, -259.5, -290.5, -322.5, -355.5, -389.5,
|
||||
-424.0, -459.5, -495.5, -532.0, -568.5, -605.0,
|
||||
-641.5, -678.0, -714.0, -749.0, -783.5, -817.0,
|
||||
-849.0, -879.5, -908.5, -935.0, -959.5, -981.0,
|
||||
-1000.5, -1016.0, -1028.5, -1037.5, -1042.5, -1043.5,
|
||||
-1040.0, -1031.5, 1018.5, 1000.0, 976.0, 946.5,
|
||||
911.0, 869.5, 822.0, 767.5, 707.0, 640.0,
|
||||
565.5, 485.0, 397.0, 302.5, 201.0, 92.5,
|
||||
-22.5, -144.0, -272.5, -407.0, -547.5, -694.0,
|
||||
-846.0, -1003.0, -1165.0, -1331.5, -1502.0, -1675.5,
|
||||
-1852.5, -2031.5, -2212.5, -2394.0, -2576.5, -2758.5,
|
||||
-2939.5, -3118.5, -3294.5, -3467.5, -3635.5, -3798.5,
|
||||
-3955.0, -4104.5, -4245.5, -4377.5, -4499.0, -4609.5,
|
||||
-4708.0, -4792.5, -4863.5, -4919.0, -4958.0, -4979.5,
|
||||
-4983.0, -4967.5, -4931.5, -4875.0, -4796.0, -4694.5,
|
||||
-4569.5, -4420.0, -4246.0, -4046.0, -3820.0, -3567.0,
|
||||
3287.0, 2979.5, 2644.0, 2280.5, 1888.0, 1467.5,
|
||||
1018.5, 541.0, 35.0, -499.0, -1061.0, -1650.0,
|
||||
-2266.5, -2909.0, -3577.0, -4270.0, -4987.5, -5727.5,
|
||||
-6490.0, -7274.0, -8077.5, -8899.5, -9739.0, -10594.5,
|
||||
-11464.5, -12347.0, -13241.0, -14144.5, -15056.0, -15973.5,
|
||||
-16895.5, -17820.0, -18744.5, -19668.0, -20588.0, -21503.0,
|
||||
-22410.5, -23308.5, -24195.0, -25068.5, -25926.5, -26767.0,
|
||||
-27589.0, -28389.0, -29166.5, -29919.0, -30644.5, -31342.0,
|
||||
-32009.5, -32645.0, -33247.0, -33814.5, -34346.0, -34839.5,
|
||||
-35295.0, -35710.0, -36084.5, -36417.5, -36707.5, -36954.0,
|
||||
-37156.5, -37315.0, -37428.0, -37496.0, 37519.0, 37496.0,
|
||||
37428.0, 37315.0, 37156.5, 36954.0, 36707.5, 36417.5,
|
||||
36084.5, 35710.0, 35295.0, 34839.5, 34346.0, 33814.5,
|
||||
33247.0, 32645.0, 32009.5, 31342.0, 30644.5, 29919.0,
|
||||
29166.5, 28389.0, 27589.0, 26767.0, 25926.5, 25068.5,
|
||||
24195.0, 23308.5, 22410.5, 21503.0, 20588.0, 19668.0,
|
||||
18744.5, 17820.0, 16895.5, 15973.5, 15056.0, 14144.5,
|
||||
13241.0, 12347.0, 11464.5, 10594.5, 9739.0, 8899.5,
|
||||
8077.5, 7274.0, 6490.0, 5727.5, 4987.5, 4270.0,
|
||||
3577.0, 2909.0, 2266.5, 1650.0, 1061.0, 499.0,
|
||||
-35.0, -541.0, -1018.5, -1467.5, -1888.0, -2280.5,
|
||||
-2644.0, -2979.5, 3287.0, 3567.0, 3820.0, 4046.0,
|
||||
4246.0, 4420.0, 4569.5, 4694.5, 4796.0, 4875.0,
|
||||
4931.5, 4967.5, 4983.0, 4979.5, 4958.0, 4919.0,
|
||||
4863.5, 4792.5, 4708.0, 4609.5, 4499.0, 4377.5,
|
||||
4245.5, 4104.5, 3955.0, 3798.5, 3635.5, 3467.5,
|
||||
3294.5, 3118.5, 2939.5, 2758.5, 2576.5, 2394.0,
|
||||
2212.5, 2031.5, 1852.5, 1675.5, 1502.0, 1331.5,
|
||||
1165.0, 1003.0, 846.0, 694.0, 547.5, 407.0,
|
||||
272.5, 144.0, 22.5, -92.5, -201.0, -302.5,
|
||||
-397.0, -485.0, -565.5, -640.0, -707.0, -767.5,
|
||||
-822.0, -869.5, -911.0, -946.5, -976.0, -1000.0,
|
||||
1018.5, 1031.5, 1040.0, 1043.5, 1042.5, 1037.5,
|
||||
1028.5, 1016.0, 1000.5, 981.0, 959.5, 935.0,
|
||||
908.5, 879.5, 849.0, 817.0, 783.5, 749.0,
|
||||
714.0, 678.0, 641.5, 605.0, 568.5, 532.0,
|
||||
495.5, 459.5, 424.0, 389.5, 355.5, 322.5,
|
||||
290.5, 259.5, 229.5, 200.5, 173.5, 147.0,
|
||||
122.0, 98.5, 76.5, 55.5, 36.0, 18.0,
|
||||
1.0, -14.5, -28.5, -41.5, -53.0, -63.5,
|
||||
-73.0, -81.5, -88.5, -94.5, -100.0, -104.0,
|
||||
-107.5, -110.5, -112.0, -113.5, -114.0, -114.0,
|
||||
-113.5, -112.5, -111.0, -109.0, 106.5, 104.0,
|
||||
101.0, 98.0, 95.0, 91.5, 88.0, 84.5,
|
||||
80.5, 77.0, 73.5, 69.5, 66.0, 62.5,
|
||||
58.5, 55.5, 52.0, 48.5, 45.5, 42.5,
|
||||
39.5, 36.5, 34.0, 31.5, 29.0, 26.5,
|
||||
24.5, 22.5, 20.5, 19.0, 17.5, 15.5,
|
||||
14.5, 13.0, 12.0, 10.5, 9.5, 8.5,
|
||||
8.0, 7.0, 6.5, 5.5, 5.0, 4.5,
|
||||
4.0, 3.5, 3.5, 3.0, 2.5, 2.5,
|
||||
2.0, 2.0, 1.5, 1.5, 1.0, 1.0,
|
||||
1.0, 1.0, 0.5, 0.5, 0.5, 0.5,
|
||||
0.5, 0.5
|
||||
};
|
||||
|
||||
// Quantizer lookup, step 1: bitrate classes
|
||||
static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_1[2][16] = {
|
||||
// 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384 <- bitrate
|
||||
{ 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, // mono
|
||||
// 16, 24, 28, 32, 40, 48, 56, 64, 80, 96,112,128,160,192 <- bitrate / chan
|
||||
{ 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2 } // stereo
|
||||
};
|
||||
|
||||
// Quantizer lookup, step 2: bitrate class, sample rate -> B2 table idx, sblimit
|
||||
static const uint8_t PLM_AUDIO_QUANT_TAB_A = (27 | 64); // Table 3-B.2a: high-rate, sblimit = 27
|
||||
static const uint8_t PLM_AUDIO_QUANT_TAB_B = (30 | 64); // Table 3-B.2b: high-rate, sblimit = 30
|
||||
static const uint8_t PLM_AUDIO_QUANT_TAB_C = 8; // Table 3-B.2c: low-rate, sblimit = 8
|
||||
static const uint8_t PLM_AUDIO_QUANT_TAB_D = 12; // Table 3-B.2d: low-rate, sblimit = 12
|
||||
|
||||
static const uint8_t QUANT_LUT_STEP_2[3][3] = {
|
||||
// 44.1 kHz, 48 kHz, 32 kHz
|
||||
{ PLM_AUDIO_QUANT_TAB_C, PLM_AUDIO_QUANT_TAB_C, PLM_AUDIO_QUANT_TAB_D }, // 32 - 48 kbit/sec/ch
|
||||
{ PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_A }, // 56 - 80 kbit/sec/ch
|
||||
{ PLM_AUDIO_QUANT_TAB_B, PLM_AUDIO_QUANT_TAB_A, PLM_AUDIO_QUANT_TAB_B } // 96+ kbit/sec/ch
|
||||
};
|
||||
|
||||
// Quantizer lookup, step 3: B2 table, subband -> nbal, row index
|
||||
// (upper 4 bits: nbal, lower 4 bits: row index)
|
||||
static const uint8_t PLM_AUDIO_QUANT_LUT_STEP_3[3][32] = {
|
||||
// Low-rate table (3-B.2c and 3-B.2d)
|
||||
{
|
||||
0x44,0x44,
|
||||
0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34,0x34
|
||||
},
|
||||
// High-rate table (3-B.2a and 3-B.2b)
|
||||
{
|
||||
0x43,0x43,0x43,
|
||||
0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42,
|
||||
0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,0x31,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x20
|
||||
},
|
||||
// MPEG-2 LSR table (B.2 in ISO 13818-3)
|
||||
{
|
||||
0x45,0x45,0x45,0x45,
|
||||
0x34,0x34,0x34,0x34,0x34,0x34,0x34,
|
||||
0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,
|
||||
0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24,0x24
|
||||
}
|
||||
};
|
||||
|
||||
// Quantizer lookup, step 4: table row, allocation[] value -> quant table index
|
||||
static const uint8_t PLM_AUDIO_QUANT_LUT_STEP4[6][16] = {
|
||||
{ 0, 1, 2, 17 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 17 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17 },
|
||||
{ 0, 1, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 },
|
||||
{ 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 17 },
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }
|
||||
};
|
||||
|
||||
typedef struct plm_quantizer_spec_t {
|
||||
unsigned short levels;
|
||||
unsigned char group;
|
||||
unsigned char bits;
|
||||
} plm_quantizer_spec_t;
|
||||
|
||||
static const plm_quantizer_spec_t PLM_AUDIO_QUANT_TAB[] = {
|
||||
{ 3, 1, 5 }, // 1
|
||||
{ 5, 1, 7 }, // 2
|
||||
{ 7, 0, 3 }, // 3
|
||||
{ 9, 1, 10 }, // 4
|
||||
{ 15, 0, 4 }, // 5
|
||||
{ 31, 0, 5 }, // 6
|
||||
{ 63, 0, 6 }, // 7
|
||||
{ 127, 0, 7 }, // 8
|
||||
{ 255, 0, 8 }, // 9
|
||||
{ 511, 0, 9 }, // 10
|
||||
{ 1023, 0, 10 }, // 11
|
||||
{ 2047, 0, 11 }, // 12
|
||||
{ 4095, 0, 12 }, // 13
|
||||
{ 8191, 0, 13 }, // 14
|
||||
{ 16383, 0, 14 }, // 15
|
||||
{ 32767, 0, 15 }, // 16
|
||||
{ 65535, 0, 16 } // 17
|
||||
};
|
||||
|
||||
struct plm_audio_t {
|
||||
double time;
|
||||
int samples_decoded;
|
||||
int samplerate_index;
|
||||
int bitrate_index;
|
||||
int version;
|
||||
int layer;
|
||||
int mode;
|
||||
int bound;
|
||||
int v_pos;
|
||||
int next_frame_data_size;
|
||||
plm_buffer_t *buffer;
|
||||
int destroy_buffer_when_done;
|
||||
const plm_quantizer_spec_t *allocation[2][32];
|
||||
uint8_t scale_factor_info[2][32];
|
||||
int scale_factor[2][32][3];
|
||||
int sample[2][32][3];
|
||||
plm_samples_t samples;
|
||||
float D[1024];
|
||||
float V[1024];
|
||||
float U[32];
|
||||
} forcealign(64);
|
||||
|
||||
typedef plm_audio_t plm_audio_t;
|
||||
|
||||
int plm_audio_decode_header(plm_audio_t *self);
|
||||
void plm_audio_decode_frame(plm_audio_t *self);
|
||||
const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3);
|
||||
void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part);
|
||||
void plm_audio_matrix_transform(int s[32][3], int ss, float *d, int dp);
|
||||
|
||||
plm_audio_t *plm_audio_create_with_buffer(plm_buffer_t *buffer, int destroy_when_done) {
|
||||
plm_audio_t *self = (plm_audio_t *)memalign(_Alignof(plm_audio_t), sizeof(plm_audio_t));
|
||||
memset(self, 0, sizeof(plm_audio_t));
|
||||
|
||||
self->samples.count = PLM_AUDIO_SAMPLES_PER_FRAME;
|
||||
self->buffer = buffer;
|
||||
self->destroy_buffer_when_done = destroy_when_done;
|
||||
self->samplerate_index = 3; // indicates 0 samplerate
|
||||
|
||||
memcpy(self->D, PLM_AUDIO_SYNTHESIS_WINDOW, 512 * sizeof(float));
|
||||
memcpy(self->D + 512, PLM_AUDIO_SYNTHESIS_WINDOW, 512 * sizeof(float));
|
||||
|
||||
// Decode first header
|
||||
if (plm_buffer_has(self->buffer, 48)) {
|
||||
self->next_frame_data_size = plm_audio_decode_header(self);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void plm_audio_destroy(plm_audio_t *self) {
|
||||
if (self->destroy_buffer_when_done) {
|
||||
plm_buffer_destroy(self->buffer);
|
||||
}
|
||||
free(self);
|
||||
}
|
||||
|
||||
int plm_audio_get_samplerate(plm_audio_t *self) {
|
||||
return PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
|
||||
}
|
||||
|
||||
double plm_audio_get_time(plm_audio_t *self) {
|
||||
return self->time;
|
||||
}
|
||||
|
||||
void plm_audio_rewind(plm_audio_t *self) {
|
||||
plm_buffer_rewind(self->buffer);
|
||||
self->time = 0;
|
||||
self->samples_decoded = 0;
|
||||
self->next_frame_data_size = 0;
|
||||
|
||||
// TODO: needed?
|
||||
memset(self->V, 0, sizeof(self->V));
|
||||
memset(self->U, 0, sizeof(self->U));
|
||||
}
|
||||
|
||||
plm_samples_t *plm_audio_decode(plm_audio_t *self) {
|
||||
DEBUGF("%s", "plm_audio_decode");
|
||||
// Do we have at least enough information to decode the frame header?
|
||||
if (!self->next_frame_data_size) {
|
||||
if (!plm_buffer_has(self->buffer, 48)) {
|
||||
return NULL;
|
||||
}
|
||||
self->next_frame_data_size = plm_audio_decode_header(self);
|
||||
}
|
||||
|
||||
if (
|
||||
self->next_frame_data_size == 0 ||
|
||||
!plm_buffer_has(self->buffer, self->next_frame_data_size << 3)
|
||||
) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
plm_audio_decode_frame(self);
|
||||
self->next_frame_data_size = 0;
|
||||
|
||||
self->samples.time = self->time;
|
||||
|
||||
self->samples_decoded += PLM_AUDIO_SAMPLES_PER_FRAME;
|
||||
self->time = (double)self->samples_decoded /
|
||||
(double)PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
|
||||
|
||||
return &self->samples;
|
||||
}
|
||||
|
||||
int plm_audio_decode_header(plm_audio_t *self) {
|
||||
// Check for valid header: syncword OK, MPEG-Audio Layer 2
|
||||
plm_buffer_skip_bytes(self->buffer, 0x00);
|
||||
|
||||
int sync = plm_buffer_read(self->buffer, 11);
|
||||
self->version = plm_buffer_read(self->buffer, 2);
|
||||
self->layer = plm_buffer_read(self->buffer, 2);
|
||||
int hasCRC = !plm_buffer_read(self->buffer, 1);
|
||||
|
||||
if (
|
||||
sync != PLM_AUDIO_FRAME_SYNC ||
|
||||
self->version != PLM_AUDIO_MPEG_1 ||
|
||||
self->layer != PLM_AUDIO_LAYER_II
|
||||
) {
|
||||
return false; // Invalid header or unsupported version
|
||||
}
|
||||
|
||||
self->bitrate_index = plm_buffer_read(self->buffer, 4) - 1;
|
||||
if (self->bitrate_index > 13) {
|
||||
return false; // Invalid bit rate or 'free format'
|
||||
}
|
||||
|
||||
self->samplerate_index = plm_buffer_read(self->buffer, 2);
|
||||
if (self->samplerate_index == 3) {
|
||||
return false; // Invalid sample rate
|
||||
}
|
||||
|
||||
if (self->version == PLM_AUDIO_MPEG_2) {
|
||||
self->samplerate_index += 4;
|
||||
self->bitrate_index += 14;
|
||||
}
|
||||
int padding = plm_buffer_read(self->buffer, 1);
|
||||
plm_buffer_skip(self->buffer, 1); // f_private
|
||||
self->mode = plm_buffer_read(self->buffer, 2);
|
||||
|
||||
// Parse the mode_extension, set up the stereo bound
|
||||
self->bound = 0;
|
||||
if (self->mode == PLM_AUDIO_MODE_JOINT_STEREO) {
|
||||
self->bound = (plm_buffer_read(self->buffer, 2) + 1) << 2;
|
||||
}
|
||||
else {
|
||||
plm_buffer_skip(self->buffer, 2);
|
||||
self->bound = (self->mode == PLM_AUDIO_MODE_MONO) ? 0 : 32;
|
||||
}
|
||||
|
||||
// Discard the last 4 bits of the header and the CRC value, if present
|
||||
plm_buffer_skip(self->buffer, 4);
|
||||
if (hasCRC) {
|
||||
plm_buffer_skip(self->buffer, 16);
|
||||
}
|
||||
|
||||
// Compute frame size, check if we have enough data to decode the whole
|
||||
// frame.
|
||||
int bitrate = PLM_AUDIO_BIT_RATE[self->bitrate_index];
|
||||
int samplerate = PLM_AUDIO_SAMPLE_RATE[self->samplerate_index];
|
||||
int frame_size = (144000 * bitrate / samplerate) + padding;
|
||||
return frame_size - (hasCRC ? 6 : 4);
|
||||
}
|
||||
|
||||
void plm_audio_decode_frame(plm_audio_t *self) {
|
||||
// Prepare the quantizer table lookups
|
||||
int tab3 = 0;
|
||||
int sblimit = 0;
|
||||
if (self->version == PLM_AUDIO_MPEG_2) {
|
||||
// MPEG-2 (LSR)
|
||||
tab3 = 2;
|
||||
sblimit = 30;
|
||||
}
|
||||
else {
|
||||
// MPEG-1
|
||||
int tab1 = (self->mode == PLM_AUDIO_MODE_MONO) ? 0 : 1;
|
||||
int tab2 = PLM_AUDIO_QUANT_LUT_STEP_1[tab1][self->bitrate_index];
|
||||
tab3 = QUANT_LUT_STEP_2[tab2][self->samplerate_index];
|
||||
sblimit = tab3 & 63;
|
||||
tab3 >>= 6;
|
||||
}
|
||||
|
||||
if (self->bound > sblimit) {
|
||||
self->bound = sblimit;
|
||||
}
|
||||
|
||||
// Read the allocation information
|
||||
for (int sb = 0; sb < self->bound; sb++) {
|
||||
self->allocation[0][sb] = plm_audio_read_allocation(self, sb, tab3);
|
||||
self->allocation[1][sb] = plm_audio_read_allocation(self, sb, tab3);
|
||||
}
|
||||
|
||||
for (int sb = self->bound; sb < sblimit; sb++) {
|
||||
self->allocation[0][sb] =
|
||||
self->allocation[1][sb] =
|
||||
plm_audio_read_allocation(self, sb, tab3);
|
||||
}
|
||||
|
||||
// Read scale factor selector information
|
||||
int channels = (self->mode == PLM_AUDIO_MODE_MONO) ? 1 : 2;
|
||||
for (int sb = 0; sb < sblimit; sb++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
if (self->allocation[ch][sb]) {
|
||||
self->scale_factor_info[ch][sb] = plm_buffer_read(self->buffer, 2);
|
||||
}
|
||||
}
|
||||
if (self->mode == PLM_AUDIO_MODE_MONO) {
|
||||
self->scale_factor_info[1][sb] = self->scale_factor_info[0][sb];
|
||||
}
|
||||
}
|
||||
|
||||
// Read scale factors
|
||||
for (int sb = 0; sb < sblimit; sb++) {
|
||||
for (int ch = 0; ch < channels; ch++) {
|
||||
if (self->allocation[ch][sb]) {
|
||||
int *sf = self->scale_factor[ch][sb];
|
||||
switch (self->scale_factor_info[ch][sb]) {
|
||||
case 0:
|
||||
sf[0] = plm_buffer_read(self->buffer, 6);
|
||||
sf[1] = plm_buffer_read(self->buffer, 6);
|
||||
sf[2] = plm_buffer_read(self->buffer, 6);
|
||||
break;
|
||||
case 1:
|
||||
sf[0] =
|
||||
sf[1] = plm_buffer_read(self->buffer, 6);
|
||||
sf[2] = plm_buffer_read(self->buffer, 6);
|
||||
break;
|
||||
case 2:
|
||||
sf[0] =
|
||||
sf[1] =
|
||||
sf[2] = plm_buffer_read(self->buffer, 6);
|
||||
break;
|
||||
case 3:
|
||||
sf[0] = plm_buffer_read(self->buffer, 6);
|
||||
sf[1] =
|
||||
sf[2] = plm_buffer_read(self->buffer, 6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (self->mode == PLM_AUDIO_MODE_MONO) {
|
||||
self->scale_factor[1][sb][0] = self->scale_factor[0][sb][0];
|
||||
self->scale_factor[1][sb][1] = self->scale_factor[0][sb][1];
|
||||
self->scale_factor[1][sb][2] = self->scale_factor[0][sb][2];
|
||||
}
|
||||
}
|
||||
|
||||
// Coefficient input and reconstruction
|
||||
int out_pos = 0;
|
||||
for (int part = 0; part < 3; part++) {
|
||||
for (int granule = 0; granule < 4; granule++) {
|
||||
|
||||
// Read the samples
|
||||
for (int sb = 0; sb < self->bound; sb++) {
|
||||
plm_audio_read_samples(self, 0, sb, part);
|
||||
plm_audio_read_samples(self, 1, sb, part);
|
||||
}
|
||||
for (int sb = self->bound; sb < sblimit; sb++) {
|
||||
plm_audio_read_samples(self, 0, sb, part);
|
||||
self->sample[1][sb][0] = self->sample[0][sb][0];
|
||||
self->sample[1][sb][1] = self->sample[0][sb][1];
|
||||
self->sample[1][sb][2] = self->sample[0][sb][2];
|
||||
}
|
||||
for (int sb = sblimit; sb < 32; sb++) {
|
||||
self->sample[0][sb][0] = 0;
|
||||
self->sample[0][sb][1] = 0;
|
||||
self->sample[0][sb][2] = 0;
|
||||
self->sample[1][sb][0] = 0;
|
||||
self->sample[1][sb][1] = 0;
|
||||
self->sample[1][sb][2] = 0;
|
||||
}
|
||||
|
||||
// Synthesis loop
|
||||
for (int p = 0; p < 3; p++) {
|
||||
// Shifting step
|
||||
self->v_pos = (self->v_pos - 64) & 1023;
|
||||
|
||||
for (int ch = 0; ch < 2; ch++) {
|
||||
plm_audio_matrix_transform(self->sample[ch], p, self->V, self->v_pos);
|
||||
|
||||
// Build U, windowing, calculate output
|
||||
memset(self->U, 0, sizeof(self->U));
|
||||
|
||||
int d_index = 512 - (self->v_pos >> 1);
|
||||
int v_index = (self->v_pos % 128) >> 1;
|
||||
while (v_index < 1024) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
self->U[i] += self->D[d_index++] * self->V[v_index++];
|
||||
}
|
||||
|
||||
v_index += 128 - 32;
|
||||
d_index += 64 - 32;
|
||||
}
|
||||
|
||||
d_index -= (512 - 32);
|
||||
v_index = (128 - 32 + 1024) - v_index;
|
||||
while (v_index < 1024) {
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
self->U[i] += self->D[d_index++] * self->V[v_index++];
|
||||
}
|
||||
|
||||
v_index += 128 - 32;
|
||||
d_index += 64 - 32;
|
||||
}
|
||||
|
||||
// Output samples
|
||||
#ifdef PLM_AUDIO_SEPARATE_CHANNELS
|
||||
float *out_channel = ch == 0
|
||||
? self->samples.left
|
||||
: self->samples.right;
|
||||
for (int j = 0; j < 32; j++) {
|
||||
out_channel[out_pos + j] = self->U[j] / 2147418112.0f;
|
||||
}
|
||||
#else
|
||||
for (int j = 0; j < 32; j++) {
|
||||
self->samples.interleaved[((out_pos + j) << 1) + ch] =
|
||||
self->U[j] / 2147418112.0f;
|
||||
}
|
||||
#endif
|
||||
} // End of synthesis channel loop
|
||||
out_pos += 32;
|
||||
} // End of synthesis sub-block loop
|
||||
|
||||
} // Decoding of the granule finished
|
||||
}
|
||||
|
||||
plm_buffer_align(self->buffer);
|
||||
}
|
||||
|
||||
const plm_quantizer_spec_t *plm_audio_read_allocation(plm_audio_t *self, int sb, int tab3) {
|
||||
int tab4 = PLM_AUDIO_QUANT_LUT_STEP_3[tab3][sb];
|
||||
int qtab = PLM_AUDIO_QUANT_LUT_STEP4[tab4 & 15][plm_buffer_read(self->buffer, tab4 >> 4)];
|
||||
return qtab ? (&PLM_AUDIO_QUANT_TAB[qtab - 1]) : 0;
|
||||
}
|
||||
|
||||
void plm_audio_read_samples(plm_audio_t *self, int ch, int sb, int part) {
|
||||
const plm_quantizer_spec_t *q = self->allocation[ch][sb];
|
||||
int sf = self->scale_factor[ch][sb][part];
|
||||
int *sample = self->sample[ch][sb];
|
||||
int val = 0;
|
||||
|
||||
if (!q) {
|
||||
// No bits allocated for this subband
|
||||
sample[0] = sample[1] = sample[2] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve scalefactor
|
||||
if (sf == 63) {
|
||||
sf = 0;
|
||||
}
|
||||
else {
|
||||
int shift = (sf / 3) | 0;
|
||||
sf = (PLM_AUDIO_SCALEFACTOR_BASE[sf % 3] + ((1u << shift) >> 1)) >> shift;
|
||||
}
|
||||
|
||||
// Decode samples
|
||||
int adj = q->levels;
|
||||
if (q->group) {
|
||||
// Decode grouped samples
|
||||
val = plm_buffer_read(self->buffer, q->bits);
|
||||
sample[0] = val % adj;
|
||||
val /= adj;
|
||||
sample[1] = val % adj;
|
||||
sample[2] = val / adj;
|
||||
}
|
||||
else {
|
||||
// Decode direct samples
|
||||
sample[0] = plm_buffer_read(self->buffer, q->bits);
|
||||
sample[1] = plm_buffer_read(self->buffer, q->bits);
|
||||
sample[2] = plm_buffer_read(self->buffer, q->bits);
|
||||
}
|
||||
|
||||
// Postmultiply samples
|
||||
int scale = 65536 / (adj + 1);
|
||||
adj = ((adj + 1) >> 1) - 1;
|
||||
|
||||
val = (adj - sample[0]) * scale;
|
||||
sample[0] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
|
||||
|
||||
val = (adj - sample[1]) * scale;
|
||||
sample[1] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
|
||||
|
||||
val = (adj - sample[2]) * scale;
|
||||
sample[2] = (val * (sf >> 12) + ((val * (sf & 4095) + 2048) >> 12)) >> 12;
|
||||
}
|
||||
|
||||
void plm_audio_matrix_transform(int s[32][3], int ss, float *d, int dp) {
|
||||
float t01, t02, t03, t04, t05, t06, t07, t08, t09, t10, t11, t12,
|
||||
t13, t14, t15, t16, t17, t18, t19, t20, t21, t22, t23, t24,
|
||||
t25, t26, t27, t28, t29, t30, t31, t32, t33;
|
||||
|
||||
t01 = (float)(s[0][ss] + s[31][ss]); t02 = (float)(s[0][ss] - s[31][ss]) * 0.500602998235f;
|
||||
t03 = (float)(s[1][ss] + s[30][ss]); t04 = (float)(s[1][ss] - s[30][ss]) * 0.505470959898f;
|
||||
t05 = (float)(s[2][ss] + s[29][ss]); t06 = (float)(s[2][ss] - s[29][ss]) * 0.515447309923f;
|
||||
t07 = (float)(s[3][ss] + s[28][ss]); t08 = (float)(s[3][ss] - s[28][ss]) * 0.53104259109f;
|
||||
t09 = (float)(s[4][ss] + s[27][ss]); t10 = (float)(s[4][ss] - s[27][ss]) * 0.553103896034f;
|
||||
t11 = (float)(s[5][ss] + s[26][ss]); t12 = (float)(s[5][ss] - s[26][ss]) * 0.582934968206f;
|
||||
t13 = (float)(s[6][ss] + s[25][ss]); t14 = (float)(s[6][ss] - s[25][ss]) * 0.622504123036f;
|
||||
t15 = (float)(s[7][ss] + s[24][ss]); t16 = (float)(s[7][ss] - s[24][ss]) * 0.674808341455f;
|
||||
t17 = (float)(s[8][ss] + s[23][ss]); t18 = (float)(s[8][ss] - s[23][ss]) * 0.744536271002f;
|
||||
t19 = (float)(s[9][ss] + s[22][ss]); t20 = (float)(s[9][ss] - s[22][ss]) * 0.839349645416f;
|
||||
t21 = (float)(s[10][ss] + s[21][ss]); t22 = (float)(s[10][ss] - s[21][ss]) * 0.972568237862f;
|
||||
t23 = (float)(s[11][ss] + s[20][ss]); t24 = (float)(s[11][ss] - s[20][ss]) * 1.16943993343f;
|
||||
t25 = (float)(s[12][ss] + s[19][ss]); t26 = (float)(s[12][ss] - s[19][ss]) * 1.48416461631f;
|
||||
t27 = (float)(s[13][ss] + s[18][ss]); t28 = (float)(s[13][ss] - s[18][ss]) * 2.05778100995f;
|
||||
t29 = (float)(s[14][ss] + s[17][ss]); t30 = (float)(s[14][ss] - s[17][ss]) * 3.40760841847f;
|
||||
t31 = (float)(s[15][ss] + s[16][ss]); t32 = (float)(s[15][ss] - s[16][ss]) * 10.1900081235f;
|
||||
|
||||
t33 = t01 + t31; t31 = (t01 - t31) * 0.502419286188f;
|
||||
t01 = t03 + t29; t29 = (t03 - t29) * 0.52249861494f;
|
||||
t03 = t05 + t27; t27 = (t05 - t27) * 0.566944034816f;
|
||||
t05 = t07 + t25; t25 = (t07 - t25) * 0.64682178336f;
|
||||
t07 = t09 + t23; t23 = (t09 - t23) * 0.788154623451f;
|
||||
t09 = t11 + t21; t21 = (t11 - t21) * 1.06067768599f;
|
||||
t11 = t13 + t19; t19 = (t13 - t19) * 1.72244709824f;
|
||||
t13 = t15 + t17; t17 = (t15 - t17) * 5.10114861869f;
|
||||
t15 = t33 + t13; t13 = (t33 - t13) * 0.509795579104f;
|
||||
t33 = t01 + t11; t01 = (t01 - t11) * 0.601344886935f;
|
||||
t11 = t03 + t09; t09 = (t03 - t09) * 0.899976223136f;
|
||||
t03 = t05 + t07; t07 = (t05 - t07) * 2.56291544774f;
|
||||
t05 = t15 + t03; t15 = (t15 - t03) * 0.541196100146f;
|
||||
t03 = t33 + t11; t11 = (t33 - t11) * 1.30656296488f;
|
||||
t33 = t05 + t03; t05 = (t05 - t03) * 0.707106781187f;
|
||||
t03 = t15 + t11; t15 = (t15 - t11) * 0.707106781187f;
|
||||
t03 += t15;
|
||||
t11 = t13 + t07; t13 = (t13 - t07) * 0.541196100146f;
|
||||
t07 = t01 + t09; t09 = (t01 - t09) * 1.30656296488f;
|
||||
t01 = t11 + t07; t07 = (t11 - t07) * 0.707106781187f;
|
||||
t11 = t13 + t09; t13 = (t13 - t09) * 0.707106781187f;
|
||||
t11 += t13; t01 += t11;
|
||||
t11 += t07; t07 += t13;
|
||||
t09 = t31 + t17; t31 = (t31 - t17) * 0.509795579104f;
|
||||
t17 = t29 + t19; t29 = (t29 - t19) * 0.601344886935f;
|
||||
t19 = t27 + t21; t21 = (t27 - t21) * 0.899976223136f;
|
||||
t27 = t25 + t23; t23 = (t25 - t23) * 2.56291544774f;
|
||||
t25 = t09 + t27; t09 = (t09 - t27) * 0.541196100146f;
|
||||
t27 = t17 + t19; t19 = (t17 - t19) * 1.30656296488f;
|
||||
t17 = t25 + t27; t27 = (t25 - t27) * 0.707106781187f;
|
||||
t25 = t09 + t19; t19 = (t09 - t19) * 0.707106781187f;
|
||||
t25 += t19;
|
||||
t09 = t31 + t23; t31 = (t31 - t23) * 0.541196100146f;
|
||||
t23 = t29 + t21; t21 = (t29 - t21) * 1.30656296488f;
|
||||
t29 = t09 + t23; t23 = (t09 - t23) * 0.707106781187f;
|
||||
t09 = t31 + t21; t31 = (t31 - t21) * 0.707106781187f;
|
||||
t09 += t31; t29 += t09; t09 += t23; t23 += t31;
|
||||
t17 += t29; t29 += t25; t25 += t09; t09 += t27;
|
||||
t27 += t23; t23 += t19; t19 += t31;
|
||||
t21 = t02 + t32; t02 = (t02 - t32) * 0.502419286188f;
|
||||
t32 = t04 + t30; t04 = (t04 - t30) * 0.52249861494f;
|
||||
t30 = t06 + t28; t28 = (t06 - t28) * 0.566944034816f;
|
||||
t06 = t08 + t26; t08 = (t08 - t26) * 0.64682178336f;
|
||||
t26 = t10 + t24; t10 = (t10 - t24) * 0.788154623451f;
|
||||
t24 = t12 + t22; t22 = (t12 - t22) * 1.06067768599f;
|
||||
t12 = t14 + t20; t20 = (t14 - t20) * 1.72244709824f;
|
||||
t14 = t16 + t18; t16 = (t16 - t18) * 5.10114861869f;
|
||||
t18 = t21 + t14; t14 = (t21 - t14) * 0.509795579104f;
|
||||
t21 = t32 + t12; t32 = (t32 - t12) * 0.601344886935f;
|
||||
t12 = t30 + t24; t24 = (t30 - t24) * 0.899976223136f;
|
||||
t30 = t06 + t26; t26 = (t06 - t26) * 2.56291544774f;
|
||||
t06 = t18 + t30; t18 = (t18 - t30) * 0.541196100146f;
|
||||
t30 = t21 + t12; t12 = (t21 - t12) * 1.30656296488f;
|
||||
t21 = t06 + t30; t30 = (t06 - t30) * 0.707106781187f;
|
||||
t06 = t18 + t12; t12 = (t18 - t12) * 0.707106781187f;
|
||||
t06 += t12;
|
||||
t18 = t14 + t26; t26 = (t14 - t26) * 0.541196100146f;
|
||||
t14 = t32 + t24; t24 = (t32 - t24) * 1.30656296488f;
|
||||
t32 = t18 + t14; t14 = (t18 - t14) * 0.707106781187f;
|
||||
t18 = t26 + t24; t24 = (t26 - t24) * 0.707106781187f;
|
||||
t18 += t24; t32 += t18;
|
||||
t18 += t14; t26 = t14 + t24;
|
||||
t14 = t02 + t16; t02 = (t02 - t16) * 0.509795579104f;
|
||||
t16 = t04 + t20; t04 = (t04 - t20) * 0.601344886935f;
|
||||
t20 = t28 + t22; t22 = (t28 - t22) * 0.899976223136f;
|
||||
t28 = t08 + t10; t10 = (t08 - t10) * 2.56291544774f;
|
||||
t08 = t14 + t28; t14 = (t14 - t28) * 0.541196100146f;
|
||||
t28 = t16 + t20; t20 = (t16 - t20) * 1.30656296488f;
|
||||
t16 = t08 + t28; t28 = (t08 - t28) * 0.707106781187f;
|
||||
t08 = t14 + t20; t20 = (t14 - t20) * 0.707106781187f;
|
||||
t08 += t20;
|
||||
t14 = t02 + t10; t02 = (t02 - t10) * 0.541196100146f;
|
||||
t10 = t04 + t22; t22 = (t04 - t22) * 1.30656296488f;
|
||||
t04 = t14 + t10; t10 = (t14 - t10) * 0.707106781187f;
|
||||
t14 = t02 + t22; t02 = (t02 - t22) * 0.707106781187f;
|
||||
t14 += t02; t04 += t14; t14 += t10; t10 += t02;
|
||||
t16 += t04; t04 += t08; t08 += t14; t14 += t28;
|
||||
t28 += t10; t10 += t20; t20 += t02; t21 += t16;
|
||||
t16 += t32; t32 += t04; t04 += t06; t06 += t08;
|
||||
t08 += t18; t18 += t14; t14 += t30; t30 += t28;
|
||||
t28 += t26; t26 += t10; t10 += t12; t12 += t20;
|
||||
t20 += t24; t24 += t02;
|
||||
|
||||
d[dp + 48] = -t33;
|
||||
d[dp + 49] = d[dp + 47] = -t21;
|
||||
d[dp + 50] = d[dp + 46] = -t17;
|
||||
d[dp + 51] = d[dp + 45] = -t16;
|
||||
d[dp + 52] = d[dp + 44] = -t01;
|
||||
d[dp + 53] = d[dp + 43] = -t32;
|
||||
d[dp + 54] = d[dp + 42] = -t29;
|
||||
d[dp + 55] = d[dp + 41] = -t04;
|
||||
d[dp + 56] = d[dp + 40] = -t03;
|
||||
d[dp + 57] = d[dp + 39] = -t06;
|
||||
d[dp + 58] = d[dp + 38] = -t25;
|
||||
d[dp + 59] = d[dp + 37] = -t08;
|
||||
d[dp + 60] = d[dp + 36] = -t11;
|
||||
d[dp + 61] = d[dp + 35] = -t18;
|
||||
d[dp + 62] = d[dp + 34] = -t09;
|
||||
d[dp + 63] = d[dp + 33] = -t14;
|
||||
d[dp + 32] = -t05;
|
||||
d[dp + 0] = t05; d[dp + 31] = -t30;
|
||||
d[dp + 1] = t30; d[dp + 30] = -t27;
|
||||
d[dp + 2] = t27; d[dp + 29] = -t28;
|
||||
d[dp + 3] = t28; d[dp + 28] = -t07;
|
||||
d[dp + 4] = t07; d[dp + 27] = -t26;
|
||||
d[dp + 5] = t26; d[dp + 26] = -t23;
|
||||
d[dp + 6] = t23; d[dp + 25] = -t10;
|
||||
d[dp + 7] = t10; d[dp + 24] = -t15;
|
||||
d[dp + 8] = t15; d[dp + 23] = -t12;
|
||||
d[dp + 9] = t12; d[dp + 22] = -t19;
|
||||
d[dp + 10] = t19; d[dp + 21] = -t20;
|
||||
d[dp + 11] = t20; d[dp + 20] = -t13;
|
||||
d[dp + 12] = t13; d[dp + 19] = -t24;
|
||||
d[dp + 13] = t24; d[dp + 18] = -t31;
|
||||
d[dp + 14] = t31; d[dp + 17] = -t02;
|
||||
d[dp + 15] = t02; d[dp + 16] = 0.0;
|
||||
};
|
||||
|
||||
Loaded 100 of 6736 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user