Compare commits
456
Commits
be64571b0e
..
v3.3.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
741566fafd | ||
|
|
9bec020f04 | ||
|
|
fe5f13d8ce | ||
|
|
df03b48319 | ||
|
|
cd93b2e479 | ||
|
|
66acbaad9e | ||
|
|
180292289d | ||
|
|
68640dc5e2 | ||
|
|
20e890964f | ||
|
|
c215486442 | ||
|
|
9881c0ac9a | ||
|
|
17be4841e6 | ||
|
|
421728c137 | ||
|
|
b014ffa3b1 | ||
|
|
8da18774f0 | ||
|
|
ac5baef3b8 | ||
|
|
e90229db7a | ||
|
|
4a92f55f17 | ||
|
|
3c81feedbb | ||
|
|
9029e21a4f | ||
|
|
605c1ddfae | ||
|
|
f73a870a34 | ||
|
|
8b3b1816b3 | ||
|
|
f46c3ab38a | ||
|
|
9226891bb9 | ||
|
|
6fdda743fb | ||
|
|
575e0a52b1 | ||
|
|
5df1a105c7 | ||
|
|
34a1b2c1db | ||
|
|
f3a8045ecc | ||
|
|
6a3326e9a5 | ||
|
|
222eb006e6 | ||
|
|
5e67c596b9 | ||
|
|
9055c5c825 | ||
|
|
c83f9333de | ||
|
|
07e0034783 | ||
|
|
e015dfceda | ||
|
|
60467d56b9 | ||
|
|
2e943affb7 | ||
|
|
aa48942d91 | ||
|
|
5ae4d7d6d1 | ||
|
|
900c16415b | ||
|
|
bca2e720ad | ||
|
|
850f709a10 | ||
|
|
55ff77c4ea | ||
|
|
d5578f009a | ||
|
|
b3830683f6 | ||
|
|
0060c51c12 | ||
|
|
23613d16b7 | ||
|
|
8cd1d56b4a | ||
|
|
6b8fe820aa | ||
|
|
2554810306 | ||
|
|
08029141d4 | ||
|
|
a1123b361d | ||
|
|
f4d2e551b5 | ||
|
|
1bbe124e3e | ||
|
|
c1b2e29c07 | ||
|
|
a51686c8f0 | ||
|
|
f599b5f425 | ||
|
|
0063bb8641 | ||
|
|
2a20362b44 | ||
|
|
02372cb644 | ||
|
|
5804c5d345 | ||
|
|
ffdeb5fdc2 | ||
|
|
a998f6b981 | ||
|
|
ea689e7357 | ||
|
|
a97b8f742a | ||
|
|
99902cfa99 | ||
|
|
7a314a0038 | ||
|
|
f073e5a21f | ||
|
|
536a1a3b01 | ||
|
|
a6b697dd0e | ||
|
|
c67a448d00 | ||
|
|
2e5163ba33 | ||
|
|
515493b963 | ||
|
|
8061692801 | ||
|
|
70997f091f | ||
|
|
f61cb3cae0 | ||
|
|
946bcc9b59 | ||
|
|
7ffebe7dce | ||
|
|
9c727950d8 | ||
|
|
c7c4f88866 | ||
|
|
6346765e32 | ||
|
|
270dbfe5ca | ||
|
|
7fd808fe7b | ||
|
|
95deda1f59 | ||
|
|
3f3d1e1e5d | ||
|
|
877b8e9a04 | ||
|
|
4ce59f537c | ||
|
|
7d13bde13b | ||
|
|
3aa792a620 | ||
|
|
c0b667847c | ||
|
|
ce2956f072 | ||
|
|
a1182016af | ||
|
|
59423e294a | ||
|
|
ef5c9376a9 | ||
|
|
eeed101228 | ||
|
|
6b88d7e3fe | ||
|
|
58dab46902 | ||
|
|
269ee5d9ab | ||
|
|
610df6869f | ||
|
|
1cdb08a81a | ||
|
|
8d9ef89329 | ||
|
|
3d3452f523 | ||
|
|
8e41b627bc | ||
|
|
7a54c28c0f | ||
|
|
bd659e64fc | ||
|
|
21ac756fad | ||
|
|
243f04c0ca | ||
|
|
4cc8cb3d25 | ||
|
|
a9f5b9c3c6 | ||
|
|
9af9cedd51 | ||
|
|
1e536cc3ba | ||
|
|
e6f31e4f7b | ||
|
|
6cd3c6f596 | ||
|
|
5a504c6791 | ||
|
|
a4492cf8e8 | ||
|
|
ce76ac1e7c | ||
|
|
cff418b9e9 | ||
|
|
b88cb71aa8 | ||
|
|
8efcf14462 | ||
|
|
f383fbb43e | ||
|
|
24069529c3 | ||
|
|
d2b4ec2052 | ||
|
|
0a761eef05 | ||
|
|
821406315e | ||
|
|
8adbc1f841 | ||
|
|
1f3df20b7d | ||
|
|
458046a5d0 | ||
|
|
6d4a72aff9 | ||
|
|
326265722e | ||
|
|
83b879a180 | ||
|
|
b372447a46 | ||
|
|
fac9cb57eb | ||
|
|
abad2fa02a | ||
|
|
576699996f | ||
|
|
c2ca99cac4 | ||
|
|
a07f05d1a3 | ||
|
|
ebf16c6ba3 | ||
|
|
91138f9239 | ||
|
|
ae8cb2e473 | ||
|
|
f5c2ed89a4 | ||
|
|
989a5f60c4 | ||
|
|
659a3e457c | ||
|
|
c2a156e256 | ||
|
|
f3ee1ee468 | ||
|
|
7be1b42107 | ||
|
|
41cac2ef7f | ||
|
|
24716505be | ||
|
|
7e21961943 | ||
|
|
6f27d5ea91 | ||
|
|
54691a8ede | ||
|
|
ab91bb2c9e | ||
|
|
52303c81ae | ||
|
|
2372ae6675 | ||
|
|
ad2de98093 | ||
|
|
aa6ef1ec2c | ||
|
|
5e939a7158 | ||
|
|
1cbb148a1f | ||
|
|
a065dfb573 | ||
|
|
3904b3a1ee | ||
|
|
d15022603f | ||
|
|
debf6ed8eb | ||
|
|
6c5352e9de | ||
|
|
4ea30f0762 | ||
|
|
4b95a681d4 | ||
|
|
c0d901d7e9 | ||
|
|
31727dd6e7 | ||
|
|
af56a5be3f | ||
|
|
e16abc2459 | ||
|
|
05b7cfbbea | ||
|
|
a5928b03c9 | ||
|
|
a9c6b8ba72 | ||
|
|
c6560ab7f5 | ||
|
|
04858b76c0 | ||
|
|
e3c1283017 | ||
|
|
b55a3ff5d8 | ||
|
|
50b6a0b0f7 | ||
|
|
6b7421c25d | ||
|
|
ec0ab2170c | ||
|
|
efadbf5fc2 | ||
|
|
056dbbf3d7 | ||
|
|
b11ee05df4 | ||
|
|
de9ce5791a | ||
|
|
331f7e5e4e | ||
|
|
ae7a8d4bab | ||
|
|
9c5b95c229 | ||
|
|
432e4a48d5 | ||
|
|
1bdc1ad955 | ||
|
|
0a00721c10 | ||
|
|
725955cb2e | ||
|
|
0354c7b485 | ||
|
|
9f32891bbc | ||
|
|
b3ebff3635 | ||
|
|
14bd6ce5fd | ||
|
|
09e96f8112 | ||
|
|
9cbd0b29ef | ||
|
|
26fd329caa | ||
|
|
1e224702a3 | ||
|
|
74cca183e0 | ||
|
|
e26bf613a5 | ||
|
|
81e3ab2c67 | ||
|
|
8ec0757b34 | ||
|
|
eb98de7c2b | ||
|
|
d3fccd61b5 | ||
|
|
011083ab83 | ||
|
|
b8c548ee4b | ||
|
|
75be2316e0 | ||
|
|
d452225d02 | ||
|
|
74164717d0 | ||
|
|
6c8434a530 | ||
|
|
cac66b9dbb | ||
|
|
175f627f93 | ||
|
|
be30189d15 | ||
|
|
3689c17d09 | ||
|
|
8e952d9bc8 | ||
|
|
def7576101 | ||
|
|
3fed536a74 | ||
|
|
aae39fe656 | ||
|
|
4e3945d1d0 | ||
|
|
98a802e3eb | ||
|
|
0d4aa7a9c2 | ||
|
|
a229f9be8c | ||
|
|
2156d2ff3a | ||
|
|
12f083edb0 | ||
|
|
f444911452 | ||
|
|
163283f77f | ||
|
|
6064ed346c | ||
|
|
77f3c40ebd | ||
|
|
d3f1686d58 | ||
|
|
a359331df7 | ||
|
|
14115f7e7e | ||
|
|
12bd3eb489 | ||
|
|
266d43e262 | ||
|
|
a568b30163 | ||
|
|
b1773746d8 | ||
|
|
a479a43f4c | ||
|
|
b476ceaeef | ||
|
|
4d69b97149 | ||
|
|
181c4b8614 | ||
|
|
0d541f6ee8 | ||
|
|
9e34b37d8d | ||
|
|
b884d0e6a6 | ||
|
|
494bbeb398 | ||
|
|
7605eb4e57 | ||
|
|
037b0fe698 | ||
|
|
b13274cd81 | ||
|
|
4f9f632362 | ||
|
|
6b039aeac0 | ||
|
|
2ab3bfc2e1 | ||
|
|
bf99bef291 | ||
|
|
50e23d8a13 | ||
|
|
3a49dba366 | ||
|
|
1cfc52b35e | ||
|
|
25395553d7 | ||
|
|
9a651665ba | ||
|
|
8dbdfa2094 | ||
|
|
add2ba541d | ||
|
|
63ed2633ea | ||
|
|
3c32b247c0 | ||
|
|
aa04fab6ea | ||
|
|
2017f6de79 | ||
|
|
63acdd1336 | ||
|
|
cfa8094260 | ||
|
|
31373eba03 | ||
|
|
778a4da092 | ||
|
|
6fb078a868 | ||
|
|
0942643b4e | ||
|
|
0e5942af34 | ||
|
|
c83c652b37 | ||
|
|
74428f1caf | ||
|
|
d661312cfa | ||
|
|
70e075d2f6 | ||
|
|
96db885344 | ||
|
|
fb512e4a15 | ||
|
|
9361f0fe37 | ||
|
|
e7d2553400 | ||
|
|
509724cc31 | ||
|
|
82c23eed5e | ||
|
|
9403193f09 | ||
|
|
fa1d9eec0d | ||
|
|
4c14588d92 | ||
|
|
9e66bd60bd | ||
|
|
ce4957d0a0 | ||
|
|
b9ea290322 | ||
|
|
1479c13417 | ||
|
|
4520f2269d | ||
|
|
6119cf5c5f | ||
|
|
358f61e556 | ||
|
|
7ac6100a5a | ||
|
|
19efb08a3a | ||
|
|
491c742189 | ||
|
|
4f5dd3900f | ||
|
|
dbedcb3100 | ||
|
|
d9314fb41e | ||
|
|
fe2bb65b06 | ||
|
|
ed4ec54c7a | ||
|
|
ba5e902a1d | ||
|
|
01f066d20c | ||
|
|
80bd68f313 | ||
|
|
cda7e413e2 | ||
|
|
54da75f89f | ||
|
|
7df64a3168 | ||
|
|
f9f8f092b5 | ||
|
|
f89a4bf343 | ||
|
|
0d795cc0d9 | ||
|
|
28ac9ed8c8 | ||
|
|
99f7dcd794 | ||
|
|
c5ca39939d | ||
|
|
a308359942 | ||
|
|
a3d9bcf1a1 | ||
|
|
a534d44db5 | ||
|
|
11949e69be | ||
|
|
2c9e4c91a0 | ||
|
|
76b078fbc0 | ||
|
|
0569fca193 | ||
|
|
6078e54087 | ||
|
|
180425707b | ||
|
|
62dcfdaa93 | ||
|
|
19134d0e15 | ||
|
|
ca2b1cbf23 | ||
|
|
f60442bfcf | ||
|
|
a4a1e82c40 | ||
|
|
d50b41acac | ||
|
|
71b6242f16 | ||
|
|
241edfc7e4 | ||
|
|
d635e03cd8 | ||
|
|
68fe421ab0 | ||
|
|
622a4391a0 | ||
|
|
ff4d3c9e99 | ||
|
|
023c74d8d9 | ||
|
|
86fe867011 | ||
|
|
98829b8400 | ||
|
|
a7e98211f9 | ||
|
|
4642361969 | ||
|
|
093d0daf6f | ||
|
|
f8e0804dd2 | ||
|
|
fdb0650634 | ||
|
|
d1d6a8e64b | ||
|
|
bbf38499ca | ||
|
|
7657ad1b58 | ||
|
|
70399cb797 | ||
|
|
76c3b02e45 | ||
|
|
abf9b09495 | ||
|
|
6d09059911 | ||
|
|
894ae028b7 | ||
|
|
84a4ec223c | ||
|
|
8f10ac2916 | ||
|
|
bfb4e87a1e | ||
|
|
685682e1c8 | ||
|
|
40d301fd95 | ||
|
|
7d8df816a5 | ||
|
|
17f0f0eaf3 | ||
|
|
ce656a6275 | ||
|
|
8d8fcf9dc5 | ||
|
|
77e634ec2d | ||
|
|
22ece7215a | ||
|
|
4af4b39d6f | ||
|
|
a48002996e | ||
|
|
e9451f10f8 | ||
|
|
1de3608b1f | ||
|
|
3a25571105 | ||
|
|
139b4ca589 | ||
|
|
e431cf729e | ||
|
|
1d1c3ca928 | ||
|
|
f2f155744a | ||
|
|
d3a86ec6cb | ||
|
|
e053aa44ab | ||
|
|
a546a09565 | ||
|
|
12f182f0cf | ||
|
|
ffb99a3644 | ||
|
|
1ddc5610f9 | ||
|
|
d8c7b3162f | ||
|
|
9e1e4688e4 | ||
|
|
cd38672581 | ||
|
|
7fea236618 | ||
|
|
16a1b8fa35 | ||
|
|
a0dfe8acd0 | ||
|
|
95c47ec940 | ||
|
|
46f32f2ba9 | ||
|
|
8f794dee15 | ||
|
|
034b105ea1 | ||
|
|
c90874ad10 | ||
|
|
8468bb8e28 | ||
|
|
d30eee0cf8 | ||
|
|
59e56a75d3 | ||
|
|
5d6ce1d2c3 | ||
|
|
ce537b7494 | ||
|
|
b8f6e5f187 | ||
|
|
775f0864a8 | ||
|
|
8b6fb23bac | ||
|
|
1fb340561f | ||
|
|
8ed372d938 | ||
|
|
8a91a0ec6b | ||
|
|
802b70f5ee | ||
|
|
92c2439b56 | ||
|
|
781a46713b | ||
|
|
7c4725edef | ||
|
|
4fc99f76a5 | ||
|
|
25ab75f74f | ||
|
|
f6dd7e7aa8 | ||
|
|
8f5bee45c6 | ||
|
|
25964d9a3c | ||
|
|
d9cf98da73 | ||
|
|
8c153b5321 | ||
|
|
3c4112d080 | ||
|
|
aa28dd481b | ||
|
|
42187beee5 | ||
|
|
8cc17f1490 | ||
|
|
b4c1301600 | ||
|
|
2c556e12f1 | ||
|
|
6c82898225 | ||
|
|
a85508e0a4 | ||
|
|
cd78913e83 | ||
|
|
a0acada9b4 | ||
|
|
8eaa4afed1 | ||
|
|
1ce07e9e4e | ||
|
|
162b255e1b | ||
|
|
99a9cd10f5 | ||
|
|
7e1c25b649 | ||
|
|
9008e10651 | ||
|
|
d27fc14b31 | ||
|
|
dbdab01a2c | ||
|
|
cf12144230 | ||
|
|
ccc86342cd | ||
|
|
8d89695fcc | ||
|
|
6499dd7be9 | ||
|
|
75bbb82058 | ||
|
|
7fb13f2439 | ||
|
|
0b63bc746d | ||
|
|
6ee3c762df | ||
|
|
59f1c3eb58 | ||
|
|
55d0c25c90 | ||
|
|
cfc98e0137 | ||
|
|
32877cdeaa | ||
|
|
7e2d7b28b7 | ||
|
|
23886f40e8 | ||
|
|
57de75c1f8 | ||
|
|
9807334ac9 | ||
|
|
18448dd7c1 | ||
|
|
62fdef8601 | ||
|
|
61c29ee564 | ||
|
|
36bc8ab54a | ||
|
|
a8e5913254 | ||
|
|
b59aedad89 | ||
|
|
3f5e3558b9 | ||
|
|
bb2db0d30d | ||
|
|
e75846a507 | ||
|
|
05e56d67ea | ||
|
|
7164156c35 | ||
|
|
e67e1df9a5 | ||
|
|
e206b4f4a6 | ||
|
|
05bfa73509 | ||
|
|
d4313fb8e4 | ||
|
|
92db9206cc | ||
|
|
6195b521f5 |
No files matched your search
@@ -0,0 +1,99 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main # or your default branch
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: self-hosted
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # fetch full history
|
||||||
|
tags: true # fetch all tags
|
||||||
|
|
||||||
|
- name: Check/Create Tag
|
||||||
|
id: tag
|
||||||
|
run: |
|
||||||
|
VERSION=$(cat VERSION | tr -d '[:space:]') # read version and trim whitespace
|
||||||
|
echo "Version: $VERSION"
|
||||||
|
|
||||||
|
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
|
||||||
|
echo "Tag v$VERSION already exists. Skipping release."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Tag does not exist → create it
|
||||||
|
echo "Creating tag v$VERSION"
|
||||||
|
git tag "v$VERSION"
|
||||||
|
git push origin "v$VERSION"
|
||||||
|
|
||||||
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Extract version changelog
|
||||||
|
if: steps.tag.outputs.version
|
||||||
|
id: changelog
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.tag.outputs.version }}"
|
||||||
|
CHANGELOG_FILE=$(python3 scripts/get_changelog.py $VERSION)
|
||||||
|
|
||||||
|
if [[ $? != 0 ]]; then
|
||||||
|
echo "No changelog found for version $VERSION"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat "$CHANGELOG_FILE"
|
||||||
|
echo "changelog_file=$CHANGELOG_FILE" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Build Artifacts
|
||||||
|
if: steps.tag.outputs.version
|
||||||
|
run: |
|
||||||
|
chmod +x ./package
|
||||||
|
./package
|
||||||
|
ls -l dist/
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
if: steps.tag.outputs.version
|
||||||
|
id: create_release
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.tag.outputs.version }}"
|
||||||
|
CHANGELOG_FILE="${{ steps.changelog.outputs.changelog_file }}"
|
||||||
|
RESPONSE=$(jq -n \
|
||||||
|
--arg tag "v$VERSION" \
|
||||||
|
--arg name "wapp-v$VERSION" \
|
||||||
|
--rawfile body "$CHANGELOG_FILE" \
|
||||||
|
'{tag_name: $tag, name: $name, body: $body, draft: false, prerelease: false}' \
|
||||||
|
| curl -s -X POST \
|
||||||
|
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
--data-binary @- \
|
||||||
|
"https://git.thewizardapprentice.com/api/v1/repos/${{ gitea.repository }}/releases")
|
||||||
|
|
||||||
|
echo "$RESPONSE"
|
||||||
|
RELEASE_ID=$(echo $RESPONSE | jq -r '.id')
|
||||||
|
echo "release_id=$RELEASE_ID" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Upload Artifacts
|
||||||
|
if: steps.tag.outputs.version
|
||||||
|
run: |
|
||||||
|
RELEASE_ID="${{ steps.create_release.outputs.release_id }}"
|
||||||
|
for FILE in dist/*; do
|
||||||
|
FILENAME=$(basename "$FILE")
|
||||||
|
echo "Uploading $FILENAME"
|
||||||
|
curl -X POST \
|
||||||
|
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary @"$FILE" \
|
||||||
|
"https://git.thewizardapprentice.com/api/v1/repos/${{ gitea.repository }}/releases/$RELEASE_ID/assets?name=$FILENAME"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Cleanup
|
||||||
|
if: steps.tag.outputs.version
|
||||||
|
run: |
|
||||||
|
CHANGELOG_FILE="${{ steps.changelog.outputs.changelog_file }}"
|
||||||
|
rm -rf dist/*
|
||||||
|
rm -f "$CHANGELOG_FILE"
|
||||||
+12
-1
@@ -1,4 +1,15 @@
|
|||||||
.cache
|
.cache
|
||||||
.vscode
|
.vscode
|
||||||
|
.venv
|
||||||
|
test
|
||||||
|
test.*
|
||||||
|
*.dSYM
|
||||||
|
.DS_Store
|
||||||
|
*.pdb
|
||||||
|
*.obj
|
||||||
compile_commands.json
|
compile_commands.json
|
||||||
libwapp.so
|
libwapp-build
|
||||||
|
dist
|
||||||
|
scratchpad
|
||||||
|
*.vs
|
||||||
|
__pycache__
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [3.3.0] - 2026-10-24
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Containers for `b32`
|
||||||
|
|
||||||
|
## [3.2.0] - 2026-10-11
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `b32` alias for a 4-byte boolean
|
||||||
|
|
||||||
|
## [3.1.0] - 2026-10-05
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `wpLogMakeLogger` to be compile time constant
|
||||||
|
|
||||||
|
## [3.0.0] - 2026-09-30
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `temp_begin` and `temp_end` ops to the `WpAllocator` interface
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- `wpMemArenaAllocatorTempBegin` and `wpMemArenaAllocatorTempEnd`
|
||||||
|
|
||||||
|
## [2.6.0] - 2026-09-30
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Minimum length for log messages from 32 to 128
|
||||||
|
|
||||||
|
## [2.5.0] - 2026-09-24
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `wpMiscUtilsRotl64` utility
|
||||||
|
- `WpHasher` interface
|
||||||
|
- `wpCustomHasher` constructor for custom hasher with user-defined context
|
||||||
|
- `wpHasherGetHash128` and `wpHasherGetHash64` utilities
|
||||||
|
- `wpX64Mur3Hasher` and `wpX64Mur3HasherWithSeed` constructors for the hasher interface
|
||||||
|
- `wpX64Mur3GetHash128` implementation for the x64 128-bit variant of the MurmurHash3 algorithm
|
||||||
|
- `wpSip24Hasher`, `wpSip48Hasher`, `wpSip24HasherWithKey` and `wpSip48HasherWithKey` constructors for the hasher interface
|
||||||
|
- `wpSipGetHash128` implementation for the 128 variant of siphash
|
||||||
|
- `wpHashSet`, `wpHashSetCustomHasher`, `wpHashSetAlloc` and `wpHashSetCustomHasherAlloc` constructors
|
||||||
|
- `wpHashSetFind`, `wpHashSetInsertCapped`, `wpHashSetInsertAlloc`, `wpHashSetRemove`, `wpHashSetItems`, `wpHashSetCapacity`, `wpHashSetCount`, `wpHashSetItemSize`, `wpHashSetClear` and `wpHashSetDealloc` utilities
|
||||||
|
- `wpHashMap`, `wpHashMapCustomHasher`, `wpHashMapAlloc` and `wpHashMapCustomHasherAlloc` constructors
|
||||||
|
- `wpHashMapKeys`, `wpHashMapValues`, `wpHashMapCapacity`, `wpHashMapCount`, `wpHashMapKeySize`, `wpHashMapValueSize`, `wpHashMapFind`, `wpHashMapInsertCapped`, `wpHashMapInsertOrUpdateCapped`, `wpHashMapUpdate`, `wpHashMapInsertAlloc`, `wpHashMapInsertOrUpdateAlloc`, `wpHashMapRemove`, `wpHashMapClear` and `wpHashMapDealloc` utilities
|
||||||
|
- `wpU8Const`, `wpU16Const`, `wpU32Const`, `wpU64Const`, `wpI8Const`, `wpI16Const`, `wpI32Const`, and `wpI64Const` aliases
|
||||||
|
- `wpPrngXorshiftInitWithSeed` to allow for user control with PRNG
|
||||||
|
- `wpPrngXorshift256InRange`, `wpPrngXorshift256ssInRange`, `wpPrngXorshift256pInRange`, `wpPrngXorshift256Choice`, `wpPrngXorshift256ssChoice` and `wpPrngXorshift256pChoice` utilities
|
||||||
|
- `WpSplitmix64State`
|
||||||
|
- `wpPrngSplitmix64Init`, `wpPrngSplitmix64InitWithSeed`, `wpPrngSplitmix64`, `wpPrngSplitmix64InRange`, `wpPrngSplitmix64Choice`
|
||||||
|
- `wpStr8Clear`
|
||||||
|
- `wpArrayZero` to zero all elements in an array without setting the count to 0
|
||||||
|
- `wpArrayRemoveSwapEnd` to remove an array element using its index and swap the last element in
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Switched to using `wpMiscUtilsRotl64` in `xorshift`
|
||||||
|
- Remove all uses of names starting with underscore
|
||||||
|
- Ensure `wpArrayClear` memsets members to 0
|
||||||
|
- Removed '%' from `WP_STR8_SPEC`
|
||||||
|
|
||||||
|
## [2.4.0] - 2026-08-30
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `wpStreamHasCount`, `wpStreamPeekWithOffset` and `wpStreamConsumeCount` utilities
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Logic for peeking at stream contents
|
||||||
|
|
||||||
|
## [2.3.0] - 2026-08-29
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Compiler detection macros:
|
||||||
|
- `WP_PLATFORM_CLANG` for the clang compiler
|
||||||
|
- `WP_PLATFORM_CLANGPP` for the clang++ compiler
|
||||||
|
- `WP_PLATFORM_CLANG_MAJOR` Major version number for the clang/clang++ compiler
|
||||||
|
- `WP_PLATFORM_CLANG_MINOR` Minor version number for the clang/clang++ compiler
|
||||||
|
- `WP_PLATFORM_CLANG_PATCH` Patch version number for the clang/clang++ compiler
|
||||||
|
- `WP_PLATFORM_GCC` for the gcc compiler
|
||||||
|
- `WP_PLATFORM_GPP` for the g++ compiler
|
||||||
|
- `WP_PLATFORM_GCC_MAJOR` Major version number for the gcc/g++ compiler
|
||||||
|
- `WP_PLATFORM_GCC_MINOR` Minor version number for the gcc/g++ compiler
|
||||||
|
- `WP_PLATFORM_GCC_PATCH` Patch version number for the gcc/g++ compiler
|
||||||
|
- `WP_PLATFORM_MSVC` for Microsoft's Visual C++ compiler
|
||||||
|
- `WP_PLATFORM_MSVC_FULL_VERSION` Full version string for the MSVC compiler
|
||||||
|
- `WP_PLATFORM_MSVC_VERSION` Version string for the MSVC compiler
|
||||||
|
- Generic `WpStream` struct to wrap a stream of data
|
||||||
|
- `WpVoidPtrStream`, `WpC8Stream`, `WpC16Stream`, `WpC32Stream`, `WpU8Stream`, `WpU16Stream`, `WpU32Stream`, `WpU64Stream`, `WpB8Stream`, `WpI8Stream`, `WpI16Stream`, `WpI32Stream`, `WpI64Stream`, `WpF32Stream`, `WpF64Stream`, `WpF128Stream`, `WpUptrStream`, `WpIptrStream` and `WpStr8Stream` aliases for `WpStream` for readability
|
||||||
|
- Utilities for working with data streams:
|
||||||
|
- `wpStreamAtEnd`
|
||||||
|
- `wpStreamHasNext`
|
||||||
|
- `wpStreamPeek`
|
||||||
|
- `wpStreamConsume`
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [2026] [Abdelrahman Said]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
.PHONY: help full base os prng testing uuid all clean builddir build-test run-test install build-lib
|
||||||
|
|
||||||
|
# External variables
|
||||||
|
CC = clang
|
||||||
|
CXX = clang++
|
||||||
|
AR = ar
|
||||||
|
BUILD_TYPE = Debug
|
||||||
|
BUILD_DIR = libwapp-build/$(PLATFORM)-$(BUILD_TYPE)
|
||||||
|
INSTALL_PREFIX = dist
|
||||||
|
RUNTIME_ASSERT = true
|
||||||
|
|
||||||
|
# Internal variables
|
||||||
|
override CFLAGS = -Wall -Wextra -Werror -pedantic -Isrc
|
||||||
|
override LIBFLAGS = -fPIC
|
||||||
|
override CSTD := -std=gnu11
|
||||||
|
override CXXSTD := -std=gnu++11
|
||||||
|
override KERNEL := $(shell uname -s)
|
||||||
|
override MACHINE := $(shell uname -m)
|
||||||
|
override PLATFORM := $(KERNEL)_$(MACHINE)
|
||||||
|
override TEST_INCLUDE := -Isrc $(shell find tests -type d | xargs -I{} echo -n "-I{} ")
|
||||||
|
override TEST_SRC := $(shell find tests -type f -name "*.c" | xargs -I{} echo -n "{} ")
|
||||||
|
override TEST_C_SRC := src/wapp.c $(TEST_SRC)
|
||||||
|
override TEST_CXX_SRC := $(shell find tests -type f -name "*.cc" | xargs -I{} echo -n "{} ")
|
||||||
|
override LIB_BASENAME := wapp
|
||||||
|
override OBJ_OUT := $(BUILD_DIR)/$(LIB_BASENAME).o
|
||||||
|
override LIB_STATIC_NAME := lib$(LIB_BASENAME).a
|
||||||
|
override LIB_OUT := $(BUILD_DIR)/$(LIB_STATIC_NAME)
|
||||||
|
override TEST_C_OUT := $(BUILD_DIR)/wapptest
|
||||||
|
override TEST_CXX_OUT := $(BUILD_DIR)/wapptestcc
|
||||||
|
override ABS_INSTALL_PREFIX := $(shell mkdir -p $(INSTALL_PREFIX) && realpath $(INSTALL_PREFIX))
|
||||||
|
override INCLUDE_INSTALL := $(ABS_INSTALL_PREFIX)/include/$(LIB_BASENAME)
|
||||||
|
override LIB_INSTALL := $(ABS_INSTALL_PREFIX)/lib
|
||||||
|
override HEADER_INSTALL_CMD := scripts/header_install.sh
|
||||||
|
|
||||||
|
ifeq ($(origin BUILD_FLAGS), undefined)
|
||||||
|
ifeq ($(BUILD_TYPE),Debug)
|
||||||
|
BUILD_FLAGS += -g -fsanitize=address,undefined -DWP_DEBUG_ASSERT
|
||||||
|
else ifeq ($(BUILD_TYPE),RelWithDebInfo)
|
||||||
|
BUILD_FLAGS += -g -O2 -fsanitize=address,undefined -DWP_DEBUG_ASSERT
|
||||||
|
else ifeq ($(BUILD_TYPE),Release)
|
||||||
|
BUILD_FLAGS += -O3
|
||||||
|
else
|
||||||
|
$(error Invalid BUILD type '$(BUILD_TYPE)'. Use 'Debug', 'RelWithDebInfo' or 'Release')
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Disable runtime asserts
|
||||||
|
ifeq ($(RUNTIME_ASSERT), false)
|
||||||
|
override BUILD_FLAGS += -DWP_NO_RUNTIME_ASSERT
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(CC),gcc)
|
||||||
|
# Used to disable the "ASan runtime does not come first in initial library list" error when compiling with gcc
|
||||||
|
export ASAN_OPTIONS=verify_asan_link_order=0
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Escape sequences
|
||||||
|
BOLD = \033[1m
|
||||||
|
BLACK = \033[30m
|
||||||
|
BG_BLACK = \033[40m
|
||||||
|
RED = \033[31m
|
||||||
|
BG_RED = \033[41m
|
||||||
|
GREEN = \033[32m
|
||||||
|
BG_GREEN = \033[42m
|
||||||
|
YELLOW = \033[33m
|
||||||
|
BG_YELLOW = \033[43m
|
||||||
|
BLUE = \033[34m
|
||||||
|
BG_BLUE = \033[44m
|
||||||
|
MAGENTA = \033[35m
|
||||||
|
BG_MAGENTA = \033[45m
|
||||||
|
CYAN = \033[36m
|
||||||
|
BG_CYAN = \033[46m
|
||||||
|
WHITE = \033[37m
|
||||||
|
BG_WHITE = \033[47m
|
||||||
|
GRAY = \033[90m
|
||||||
|
BG_GRAY = \033[100m
|
||||||
|
BRIGHT_RED = \033[91m
|
||||||
|
BG_BRIGHT_RED = \033[101m
|
||||||
|
BRIGHT_GREEN = \033[92m
|
||||||
|
BG_BRIGHT_GREEN = \033[102m
|
||||||
|
BRIGHT_YELLOW = \033[93m
|
||||||
|
BG_BRIGHT_YELLOW = \033[103m
|
||||||
|
BRIGHT_BLUE = \033[94m
|
||||||
|
BG_BRIGHT_BLUE = \033[104m
|
||||||
|
BRIGHT_MAGENTA = \033[95m
|
||||||
|
BG_BRIGHT_MAGENTA = \033[105m
|
||||||
|
BRIGHT_CYAN = \033[96m
|
||||||
|
BG_BRIGHT_CYAN = \033[106m
|
||||||
|
BRIGHT_WHITE = \033[97m
|
||||||
|
BG_BRIGHT_WHITE = \033[107m
|
||||||
|
RESET = \033[0m
|
||||||
|
|
||||||
|
ECHO_E = echo -e
|
||||||
|
ifeq ($(KERNEL), Darwin)
|
||||||
|
ECHO_E = echo
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: clean builddir run-c-test full run-cc-test
|
||||||
|
|
||||||
|
help:
|
||||||
|
@$(ECHO_E) "$(BOLD)$(BLUE)Available build variables:$(RESET)"
|
||||||
|
@$(ECHO_E) " $(GREEN)CC$(RESET) C compiler to use $(YELLOW)(Default: clang)$(RESET)."
|
||||||
|
@$(ECHO_E) " $(GREEN)CXX$(RESET) C++ compiler to use $(YELLOW)(Default: clang++)$(RESET)."
|
||||||
|
@$(ECHO_E) " $(GREEN)AR$(RESET) Archiving utility to use for building static libraries $(YELLOW)(Default: ar)$(RESET)."
|
||||||
|
@$(ECHO_E) " $(GREEN)BUILD_TYPE$(RESET) Build type $(MAGENTA)[Debug | RelWithDebInfo | Release] $(YELLOW)(Default: Debug)$(RESET)."
|
||||||
|
@$(ECHO_E) " $(GREEN)BUILD_DIR$(RESET) Directory where build files will be written."
|
||||||
|
@$(ECHO_E) " $(GREEN)INSTALL_PREFIX$(RESET) Prefix where library and include files will be installed."
|
||||||
|
@$(ECHO_E) " $(GREEN)RUNTIME_ASSERT$(RESET) Whether runtime asserts are enabled $(MAGENTA)[true | false] $(YELLOW)(Default: true)$(RESET)."
|
||||||
|
@$(ECHO_E) " $(GREEN)$(RESET) $(BOLD)$(BG_RED)DISCLAIMER:$(RESET) Using this flag is not recommended as it disables safety checks"
|
||||||
|
@$(ECHO_E) " $(GREEN)$(RESET) potentially leading to Undefined Behaviour."
|
||||||
|
@$(ECHO_E)
|
||||||
|
@$(ECHO_E) "$(BOLD)$(BLUE)Available targets:$(RESET)"
|
||||||
|
@$(ECHO_E) " $(GREEN)make$(RESET) Build, install and test the full wapp library."
|
||||||
|
@$(ECHO_E) " $(GREEN)make full$(RESET) Build and install the full wapp library."
|
||||||
|
@$(ECHO_E) " $(GREEN)make base$(RESET) Build and install only the $(CYAN)base$(RESET) component of the wapp library with all its dependencies."
|
||||||
|
@$(ECHO_E) " $(GREEN)make os$(RESET) Build and install only the $(CYAN)os$(RESET) component of the wapp library with all its dependencies."
|
||||||
|
@$(ECHO_E) " $(GREEN)make prng$(RESET) Build and install only the $(CYAN)prng$(RESET) component of the wapp library with all its dependencies."
|
||||||
|
@$(ECHO_E) " $(GREEN)make uuid$(RESET) Build and install only the $(CYAN)uuid$(RESET) component of the wapp library with all its dependencies."
|
||||||
|
@$(ECHO_E) " $(GREEN)make testing$(RESET) Build and install only the $(CYAN)testing$(RESET) component of the wapp library with all its dependencies."
|
||||||
|
@$(ECHO_E) " $(GREEN)make clean$(RESET) Clean the build directory."
|
||||||
|
@$(ECHO_E) " $(GREEN)make help$(RESET) Print this help message and exit."
|
||||||
|
|
||||||
|
full: LIB_SRC = src/wapp.c
|
||||||
|
full: INCLUDES = common os base log prng testing uuid
|
||||||
|
full: install
|
||||||
|
|
||||||
|
base: LIB_SRC = src/base/wapp_base.c
|
||||||
|
base: INCLUDES = common base
|
||||||
|
base: install
|
||||||
|
|
||||||
|
log: LIB_SRC = src/log/wapp_log.c
|
||||||
|
log: INCLUDES = common os base log
|
||||||
|
log: install
|
||||||
|
|
||||||
|
os: LIB_SRC = src/os/wapp_os.c
|
||||||
|
os: INCLUDES = common os base
|
||||||
|
os: install
|
||||||
|
|
||||||
|
prng: LIB_SRC = src/prng/wapp_prng.c
|
||||||
|
prng: INCLUDES = common prng
|
||||||
|
prng: install
|
||||||
|
|
||||||
|
testing: LIB_SRC = src/testing/wapp_testing.c
|
||||||
|
testing: INCLUDES = common os testing
|
||||||
|
testing: install
|
||||||
|
|
||||||
|
uuid: LIB_SRC = src/uuid/wapp_uuid.c
|
||||||
|
uuid: INCLUDES = common base prng
|
||||||
|
uuid: install
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@rm -rf "$(BUILD_DIR)"
|
||||||
|
@rm -rf "$(INCLUDE_INSTALL)"
|
||||||
|
@rm -f "$(LIB_INSTALL)/$(LIB_STATIC_NAME)"
|
||||||
|
|
||||||
|
builddir:
|
||||||
|
@mkdir -p "$(BUILD_DIR)"
|
||||||
|
|
||||||
|
build-c-test:
|
||||||
|
bear -- $(CC) $(CSTD) $(CFLAGS) $(BUILD_FLAGS) $(TEST_INCLUDE) $(TEST_C_SRC) -o "$(TEST_C_OUT)"
|
||||||
|
|
||||||
|
run-c-test: build-c-test
|
||||||
|
@echo -e "\n\033[34;1mRUNNING C TESTS\033[0m"
|
||||||
|
@"$(TEST_C_OUT)"
|
||||||
|
@rm "$(TEST_C_OUT)"
|
||||||
|
|
||||||
|
build-cc-test:
|
||||||
|
bear -a -- $(CXX) $(CXXSTD) $(CFLAGS) $(BUILD_FLAGS) $(TEST_INCLUDE) $(TEST_CXX_SRC) "$(LIB_OUT)" -o "$(TEST_CXX_OUT)"
|
||||||
|
|
||||||
|
run-cc-test: build-cc-test
|
||||||
|
@echo -e "\n\033[34;1mRUNNING C++ TESTS\033[0m"
|
||||||
|
@export LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:"$(BUILD_DIR)" && "$(TEST_CXX_OUT)"
|
||||||
|
@rm "$(TEST_CXX_OUT)"
|
||||||
|
|
||||||
|
install: build-lib
|
||||||
|
@mkdir -p "$(LIB_INSTALL)"
|
||||||
|
@cp -v "$(LIB_OUT)" "$(LIB_INSTALL)"
|
||||||
|
@mkdir -p "$(INCLUDE_INSTALL)"
|
||||||
|
@bash $(HEADER_INSTALL_CMD) $(LIB_SRC) "$(INCLUDE_INSTALL)" $(INCLUDES)
|
||||||
|
|
||||||
|
build-lib: builddir
|
||||||
|
bear -a -- $(CC) -c $(CSTD) $(CFLAGS) $(BUILD_FLAGS) $(LIBFLAGS) $(LIB_SRC) -o "$(OBJ_OUT)"
|
||||||
|
$(AR) r "$(LIB_OUT)" "$(OBJ_OUT)"
|
||||||
|
@rm "$(OBJ_OUT)"
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
# Wizard Apprentice Standard Library
|
# Wizard Apprentice Standard Library
|
||||||
|
|
||||||
A collection of useful C/C++ utilities for my projects
|
**Wizard Apprentice Standard Library** (`wapp`) is a lightweight collection of reusable utilities for **C and C++** projects.
|
||||||
|
|
||||||
|
For more information about the library and how to use it, check the [wiki](https://git.thewizardapprentice.com/abdelrahman/wizapp-stdlib/wiki).
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
#ifndef ALIASES_H
|
|
||||||
#define ALIASES_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define u8 uint8_t
|
|
||||||
#define u16 uint16_t
|
|
||||||
#define u32 uint32_t
|
|
||||||
#define u64 uint64_t
|
|
||||||
|
|
||||||
#define i8 int8_t
|
|
||||||
#define i16 int16_t
|
|
||||||
#define i32 int32_t
|
|
||||||
#define i64 int64_t
|
|
||||||
|
|
||||||
#define f32 float
|
|
||||||
#define f64 double
|
|
||||||
#define f128 long double
|
|
||||||
|
|
||||||
#define uptr uintptr_t
|
|
||||||
#define iptr intptr_t
|
|
||||||
|
|
||||||
#define internal static
|
|
||||||
#define persistent static
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
#define class_mem static
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif // !ALIASES_H
|
|
||||||
@@ -1,3 +1,58 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
bear -- ./compile $@
|
# Colors
|
||||||
|
RED="\033[0;31m"
|
||||||
|
BOLD="\033[1m"
|
||||||
|
NC="\033[0m" # No Color
|
||||||
|
|
||||||
|
BUILD_TYPE="Debug"
|
||||||
|
ACCEPTED_BUILD_TYPES=("Debug" "RelWithDebInfo" "Release")
|
||||||
|
KERNEL="$(uname -s)"
|
||||||
|
ARGS=""
|
||||||
|
|
||||||
|
join_array_elements() {
|
||||||
|
local IFS=","
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
contains() {
|
||||||
|
local item="$1"; shift
|
||||||
|
local e
|
||||||
|
for e; do
|
||||||
|
[[ "$e" == "$item" ]] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
print_usage() {
|
||||||
|
echo -e "Usage: build [-b build_type] ..."
|
||||||
|
echo -e " Options:"
|
||||||
|
echo -e " -b, --build-type Choose from $(join_array_elements ${ACCEPTED_BUILD_TYPES[*]}) (Default: Debug)."
|
||||||
|
echo -e " -h, --help Print this message and exit"
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# > 0 ]];do
|
||||||
|
case $1 in
|
||||||
|
-b|--build-type)
|
||||||
|
BUILD_TYPE="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
print_usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*|-*|--*)
|
||||||
|
rest=("$@")
|
||||||
|
ARGS+=" ${rest[0]}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! contains ${BUILD_TYPE} "${ACCEPTED_BUILD_TYPES[@]}"; then
|
||||||
|
echo -e "${RED}${BOLD}Unknown build type: ${BUILD_TYPE}${NC}\n"
|
||||||
|
print_usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
make BUILD_TYPE=$BUILD_TYPE $ARGS
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
Param(
|
||||||
|
[switch]$Release
|
||||||
|
)
|
||||||
|
|
||||||
|
$Compiler = "cl.exe"
|
||||||
|
$Linker = "lib.exe"
|
||||||
|
|
||||||
|
$GeneralFlags = "/Wall /WX /wd4996 /wd4464 /wd5105 /EHs"
|
||||||
|
$CStd = "/std:c11"
|
||||||
|
$CppStd = "/std:c++14"
|
||||||
|
$LibraryFlags = "/c"
|
||||||
|
|
||||||
|
$Kernel = (Get-ChildItem Env:OS).Value
|
||||||
|
$Machine = (Get-ChildItem Env:PROCESSOR_ARCHITECTURE).Value
|
||||||
|
$Platform = "${Kernel}_${Machine}"
|
||||||
|
|
||||||
|
$IncludeDirs = "/I src"
|
||||||
|
$SrcFiles = "src/wapp.c"
|
||||||
|
|
||||||
|
$TestIncludeDirs = Get-ChildItem -Path tests -Recurse -Directory -ErrorAction SilentlyContinue -Force | %{$("/I " + '"' + $_.FullName + '"')}
|
||||||
|
$TestCSrcFiles = Get-ChildItem -Path tests -Recurse -Filter *.c -ErrorAction SilentlyContinue -Force | %{$('"' + $_.FullName + '"')}
|
||||||
|
$TestCppSrcFiles = Get-ChildItem -Path tests -Recurse -Filter *.cc -ErrorAction SilentlyContinue -Force | %{$('"' + $_.FullName + '"')}
|
||||||
|
|
||||||
|
If ($Release -eq $True) {
|
||||||
|
$GeneralFlags += " /O2 /Og"
|
||||||
|
$BuildType = "Release"
|
||||||
|
} Else {
|
||||||
|
$GeneralFlags += " /Zi /Od /fsanitize=address"
|
||||||
|
$BuildType = "Debug"
|
||||||
|
}
|
||||||
|
|
||||||
|
$BuildDir = "./libwapp-build/${Platform}-${BuildType}"
|
||||||
|
$ObjDir = "$BuildDir/objects"
|
||||||
|
$OutDir = "$BuildDir/output"
|
||||||
|
$TestsDir = "$BuildDir/tests"
|
||||||
|
|
||||||
|
$LibOutput = "$OutDir/libwapp.lib"
|
||||||
|
$Objects = "/Fo:$ObjDir/"
|
||||||
|
$LibOutputFlags = "/OUT:$LibOutput"
|
||||||
|
|
||||||
|
$TestCOutputBasename = "wapptest"
|
||||||
|
$TestCOutputFlags = "/Fo:$TestsDir/ /Fe:$TestsDir/$TestCOutputBasename"
|
||||||
|
|
||||||
|
$TestCppOutputBasename = "wapptestcc"
|
||||||
|
$TestCppOutputFlags = "/Fo:$TestsDir/ /Fe:$TestsDir/$TestCppOutputBasename"
|
||||||
|
|
||||||
|
If (Test-Path $BuildDir) {
|
||||||
|
Remove-Item $BuildDir -Recurse -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p $ObjDir > $null
|
||||||
|
mkdir -p $OutDir > $null
|
||||||
|
mkdir -p $TestsDir > $null
|
||||||
|
|
||||||
|
# Build and run C tests
|
||||||
|
Invoke-Expression "$Compiler $GeneralFlags $CStd $IncludeDirs $TestIncludeDirs $SrcFiles $TestCSrcFiles $TestCOutputFlags" -ErrorAction Stop
|
||||||
|
Invoke-Expression "$TestsDir/$TestCOutputBasename.exe"
|
||||||
|
|
||||||
|
$Status = $LASTEXITCODE
|
||||||
|
If ($Status -ne 0) {
|
||||||
|
Remove-Item $TestsDir -Recurse -Force
|
||||||
|
Write-Error "Tests failed"
|
||||||
|
Exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build library
|
||||||
|
Invoke-Expression "$Compiler $GeneralFlags $CStd $LibraryFlags $SrcFiles $Objects"
|
||||||
|
Invoke-Expression "$Linker $ObjDir/*.obj $LibOutputFlags"
|
||||||
|
|
||||||
|
# Build and run C++ tests
|
||||||
|
Invoke-Expression "$Compiler $GeneralFlags $CppStd $IncludeDirs $TestIncludeDirs $LibOutput $TestCppSrcFiles $TestCppOutputFlags" -ErrorAction Stop
|
||||||
|
Invoke-Expression "$TestsDir/$TestCppOutputBasename.exe"
|
||||||
|
|
||||||
|
Remove-Item $TestsDir -Recurse -Force
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
CC=clang
|
|
||||||
INCLUDE="\
|
|
||||||
-Ialiases \
|
|
||||||
-Icpath/include \
|
|
||||||
-Idstr/include \
|
|
||||||
$(find mem/include -type d | xargs -I{} echo -n "-I{} ") \
|
|
||||||
"
|
|
||||||
SRC="\
|
|
||||||
cpath/src/*.c \
|
|
||||||
dstr/src/*.c \
|
|
||||||
mem/src/*/*.c \
|
|
||||||
"
|
|
||||||
CFLAGS="-O3 -shared -fPIC -Wall -Werror -pedantic"
|
|
||||||
OUT="libwapp.so"
|
|
||||||
|
|
||||||
(set -x ; $CC $CFLAGS $INCLUDE $SRC -o $OUT)
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#ifndef PATH_UTILS_H
|
|
||||||
#define PATH_UTILS_H
|
|
||||||
|
|
||||||
#include "aliases.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#define NUMPARTS(...) \
|
|
||||||
(sizeof((const char *[]){"", __VA_ARGS__}) / sizeof(const char *) - 1)
|
|
||||||
|
|
||||||
#define wapp_cpath_join_path(DST, ...) \
|
|
||||||
join_path(DST, NUMPARTS(__VA_ARGS__), __VA_ARGS__)
|
|
||||||
#define wapp_cpath_dirname(DST, PATH) dirup(DST, 1, PATH)
|
|
||||||
#define wapp_cpath_dirup(DST, COUNT, PATH) dirup(DST, COUNT, PATH)
|
|
||||||
|
|
||||||
void join_path(char *dst, u64 count, ...);
|
|
||||||
void dirup(char *dst, u64 levels, const char *path);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif // !PATH_UTILS_H
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
#include "cpath.h"
|
|
||||||
#include "aliases.h"
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#if defined(__unix__) || defined(__APPLE__) || defined(__ANDROID__)
|
|
||||||
internal char path_sep = '/';
|
|
||||||
#elif defined(_WIN32) || defined(_WIN64)
|
|
||||||
internal char path_sep = '\\';
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void join_root_and_leaf(const char *root, const char *leaf, char *dst);
|
|
||||||
|
|
||||||
void join_path(char *dst, u64 count, ...) {
|
|
||||||
va_list args;
|
|
||||||
|
|
||||||
va_start(args, count);
|
|
||||||
|
|
||||||
for (u64 i = 0; i < count; ++i) {
|
|
||||||
join_root_and_leaf(dst, va_arg(args, const char *), dst);
|
|
||||||
}
|
|
||||||
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dirup(char *dst, u64 levels, const char *path) {
|
|
||||||
if (levels < 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 end_index = 0;
|
|
||||||
u64 sep_count = 0;
|
|
||||||
|
|
||||||
u64 full_length;
|
|
||||||
u64 length;
|
|
||||||
length = full_length = strlen(path);
|
|
||||||
|
|
||||||
if (path[length - 1] == path_sep) {
|
|
||||||
--length;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u64 i = length - 1; i >= 0; --i) {
|
|
||||||
if (path[i] == path_sep) {
|
|
||||||
++sep_count;
|
|
||||||
end_index = i;
|
|
||||||
|
|
||||||
if (sep_count == levels) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sep_count < levels) {
|
|
||||||
end_index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dst == path) {
|
|
||||||
memset(&dst[end_index], 0, full_length - end_index);
|
|
||||||
} else {
|
|
||||||
u64 dst_length = strlen(dst);
|
|
||||||
memset(dst, 0, dst_length);
|
|
||||||
strncpy(dst, path, end_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void join_root_and_leaf(const char *root, const char *leaf, char *dst) {
|
|
||||||
u64 root_length = strlen(root);
|
|
||||||
u64 root_end = root_length - 1;
|
|
||||||
|
|
||||||
u64 leaf_length = strlen(leaf);
|
|
||||||
u64 leaf_start = 0;
|
|
||||||
|
|
||||||
if (root[root_end] == path_sep) {
|
|
||||||
--root_end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (leaf[leaf_start] == path_sep) {
|
|
||||||
++leaf_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(dst, root, ++root_end);
|
|
||||||
|
|
||||||
dst[root_end] = path_sep;
|
|
||||||
|
|
||||||
memcpy(&(dst[++root_end]), &(leaf[leaf_start]), leaf_length - leaf_start);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# Hash Tables References
|
||||||
|
|
||||||
|
## SwissTable
|
||||||
|
|
||||||
|
- [Designing a Fast, Efficient, Cache-friendly Hash Table, Step by Step](https://www.youtube.com/watch?v=ncHmEUmJZf4)
|
||||||
|
- [Abseil blog announcement](https://abseil.io/blog/20180927-swisstables)
|
||||||
|
- [Abseil design notes](https://abseil.io/about/design/swisstables)
|
||||||
|
- [Faultlore hashbrown-TLDR](https://faultlore.com/blah/hashbrown-tldr)
|
||||||
|
- [hashbrown source](https://github.com/rust-lang/hashbrown/tree/master/src/raw)
|
||||||
|
- [Faster Go maps with Swiss Tables](https://go.dev/blog/swisstable)
|
||||||
|
|
||||||
|
### 64-bit hash from 128-bit hash
|
||||||
|
MurmurHash3 and SipHash return 128 bits. SwissTable expects 64 bits. XOR folding is the approach to use:
|
||||||
|
|
||||||
|
```c
|
||||||
|
uint64_t fold_128_to_64(uint128_t hash) {
|
||||||
|
return (uint64_t)(hash >> 64) ^ (uint64_t)(hash & 0xFFFFFFFFFFFFFFFF);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Robin Hood
|
||||||
|
|
||||||
|
- [Celis (1986) — original paper proving O(1) variance, Θ(log n) max probe](https://cs.uwaterloo.ca/research/tr/1986/CS-86-14.pdf)
|
||||||
|
- [Gankra/Faultlore — clearest walkthrough from first principles](https://faultlore.com/blah/robinhood-part-1)
|
||||||
|
- [On Worst Case Robin Hood Hashing](https://cglab.ca/~morin/publications/hashing/robinhood-siamjc.pdf)
|
||||||
|
- [Sebastian Sylvan — popularized Robin Hood in systems programming](https://www.sebastiansylvan.com/post/robin-hood-hashing-should-be-your-default-hash-table-implementation/)
|
||||||
|
- [Emmanuel Goossaert — detailed C++ implementation + backward-shift deletion](http://codecapsule.com/2013/11/11/robin-hood-hashing/)
|
||||||
|
- [Martinus (Ankerl) — most widely used C++ implementation](https://github.com/martinus/unordered_dense)
|
||||||
|
- [Tessil — clean header-only C++ implementation](https://github.com/Tessil/robin-map)
|
||||||
|
- [Malte Skarupke — Robin Hood with probe count limit](https://github.com/skarupke/flat_hash_map)
|
||||||
|
- [Robin Hood Hashing – Fairness in Collision Resolution](https://blog.hieunt.me/blog/robinhood-hashing)
|
||||||
|
|
||||||
|
## Robin Hood vs SwissTable
|
||||||
|
|
||||||
|
- [Rust migration PR](https://github.com/rust-lang/rust/pull/56241)
|
||||||
|
- [hashbrown deletion discussion](https://github.com/rust-lang/hashbrown/issues/503)
|
||||||
|
- [Jackson Allan benchmark](https://jacksonallan.github.io/c_cpp_hash_tables_benchmark/)
|
||||||
|
- [Richter et al. (VLDB 2015)](https://bigdata.uni-saarland.de/publications/p249-richter.pdf)
|
||||||
|
- [Poblete & Viola (2018)](https://www.cambridge.org/core/journals/combinatorics-probability-and-computing/article/abs/analysis-of-robin-hood-and-other-hashing-algorithms-under-the-random-probing-model/933D4F203E3C70EF15053287412242E0)
|
||||||
|
|
||||||
|
## Hybrid Open-Addressing/Separate-Chaining
|
||||||
|
|
||||||
|
- [Verstable](https://github.com/JacksonAllan/Verstable)
|
||||||
|
|
||||||
|
## Double Hashing
|
||||||
|
|
||||||
|
- Knuth, TAOCP Vol. 3 §6.4 — definitive analysis of double hashing probe counts
|
||||||
|
- Guibas & Szemerédi (1978) — proved double hashing is asymptotically equivalent to uniform probing
|
||||||
|
- [Heileman & Luo (2005) — "How Caching Affects Hashing" — cache-aware experimental comparison](http://siam.org/meetings/alenex05/papers/13gheileman.pdf)
|
||||||
|
- [VT Hashing Tutorial — excellent pedagogical walkthrough](https://research.cs.vt.edu/AVresearch/hashing/double.php)
|
||||||
|
- [Paul Khuong — "The Other Robin Hood Hashing" (original Celis double-hashing variant)](https://www.pvk.ca/Blog/2013/11/26/the-other-robin-hood-hashing/)
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#ifndef DSTR_H
|
|
||||||
#define DSTR_H
|
|
||||||
|
|
||||||
#include "aliases.h"
|
|
||||||
#include "mem_allocator.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
typedef struct dstr String;
|
|
||||||
|
|
||||||
String *wapp_dstr_with_capacity(u64 capacity, const Allocator *allocator);
|
|
||||||
String *wapp_dstr_from_string(const char *str, const Allocator *allocator);
|
|
||||||
void wapp_dstr_update(String **dst, const char *src);
|
|
||||||
void wapp_dstr_free(String **str);
|
|
||||||
void wapp_dstr_concat(String **dst, const char *src);
|
|
||||||
void wapp_dstr_append(String **dst, char c);
|
|
||||||
void wapp_dstr_resize(String **str);
|
|
||||||
void wapp_dstr_clear(String *str);
|
|
||||||
void wapp_dstr_print(const String *str);
|
|
||||||
i64 wapp_dstr_find(const String *str, const char *substr);
|
|
||||||
u64 wapp_dstr_length(const String *str);
|
|
||||||
u64 wapp_dstr_capacity(const String *str);
|
|
||||||
const char *wapp_dstr_to_cstr(const String *str);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif // !DSTR_H
|
|
||||||
-223
@@ -1,223 +0,0 @@
|
|||||||
#include "dstr.h"
|
|
||||||
#include "aliases.h"
|
|
||||||
#include "mem_allocator.h"
|
|
||||||
#include "mem_libc.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
// Use this scalar to allocate extra memory in order to avoid having to
|
|
||||||
// constantly reallocate
|
|
||||||
#define CAPACITY_SCALAR 8
|
|
||||||
|
|
||||||
struct dstr {
|
|
||||||
Allocator allocator;
|
|
||||||
u64 capacity;
|
|
||||||
u64 size;
|
|
||||||
char buf[];
|
|
||||||
};
|
|
||||||
|
|
||||||
String *wapp_dstr_with_capacity(u64 capacity, const Allocator *allocator) {
|
|
||||||
Allocator alloc;
|
|
||||||
if (allocator) {
|
|
||||||
alloc = *allocator;
|
|
||||||
} else {
|
|
||||||
alloc = wapp_mem_libc_allocator();
|
|
||||||
}
|
|
||||||
|
|
||||||
String *out =
|
|
||||||
(String *)wapp_mem_allocator_alloc(&alloc, sizeof(String) + capacity + 1);
|
|
||||||
if (!out) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
out->allocator = alloc;
|
|
||||||
out->capacity = capacity;
|
|
||||||
out->size = 0;
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
String *wapp_dstr_from_string(const char *str, const Allocator *allocator) {
|
|
||||||
if (!str) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 length = strlen(str);
|
|
||||||
|
|
||||||
u64 capacity = length * CAPACITY_SCALAR;
|
|
||||||
|
|
||||||
String *out = wapp_dstr_with_capacity(capacity, allocator);
|
|
||||||
if (!out) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
out->size = length;
|
|
||||||
strncpy(out->buf, str, length + 1);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_dstr_update(String **dst, const char *src) {
|
|
||||||
if (!dst || !(*dst)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 length = strlen(src);
|
|
||||||
|
|
||||||
String *str = *dst;
|
|
||||||
|
|
||||||
if (length < str->capacity) {
|
|
||||||
memset(str->buf, 0, str->capacity);
|
|
||||||
|
|
||||||
str->size = length;
|
|
||||||
|
|
||||||
strncpy(str->buf, src, length + 1);
|
|
||||||
} else {
|
|
||||||
u64 capacity = length * CAPACITY_SCALAR;
|
|
||||||
|
|
||||||
String *tmp = (String *)wapp_mem_allocator_realloc(
|
|
||||||
&(str->allocator), *dst, sizeof(String) + capacity + 1);
|
|
||||||
if (!tmp) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp->capacity = capacity;
|
|
||||||
tmp->size = length;
|
|
||||||
strncpy(tmp->buf, src, length + 1);
|
|
||||||
|
|
||||||
*dst = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_dstr_free(String **str) {
|
|
||||||
if (!str || !(*str)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String *str_ptr = *str;
|
|
||||||
wapp_mem_allocator_free(&(str_ptr->allocator), (void **)str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_dstr_concat(String **dst, const char *src) {
|
|
||||||
if (!dst || !(*dst)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 src_length = strlen(src);
|
|
||||||
if (src_length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 new_length = (*dst)->size + src_length;
|
|
||||||
|
|
||||||
char str[new_length + 1];
|
|
||||||
memset(str, 0, new_length + 1);
|
|
||||||
|
|
||||||
strncpy(str, (*dst)->buf, (*dst)->size);
|
|
||||||
strncat(str, src, new_length + 1 - (*dst)->size);
|
|
||||||
|
|
||||||
wapp_dstr_update(dst, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_dstr_append(String **dst, char c) {
|
|
||||||
if (!dst || !(*dst)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 new_length = (*dst)->size + 1;
|
|
||||||
|
|
||||||
char str[new_length + 1];
|
|
||||||
memset(str, 0, new_length + 1);
|
|
||||||
|
|
||||||
strncpy(str, (*dst)->buf, (*dst)->size);
|
|
||||||
str[(*dst)->size] = c;
|
|
||||||
|
|
||||||
wapp_dstr_update(dst, str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_dstr_resize(String **str) {
|
|
||||||
if (!str || !(*str)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String *str_ptr = *str;
|
|
||||||
u64 capacity = (*str)->size;
|
|
||||||
|
|
||||||
String *tmp = (String *)wapp_mem_allocator_realloc(
|
|
||||||
&(str_ptr->allocator), *str, sizeof(String) + capacity + 1);
|
|
||||||
if (!tmp) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp->capacity = capacity;
|
|
||||||
|
|
||||||
*str = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_dstr_clear(String *str) {
|
|
||||||
if (!str || str->size == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(str->buf, 0, str->capacity);
|
|
||||||
str->size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_dstr_print(const String *str) {
|
|
||||||
if (!str) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("%.*s\n", (i32)str->size, str->buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
i64 wapp_dstr_find(const String *str, const char *substr) {
|
|
||||||
if (!str || !substr) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 substr_length = strlen(substr);
|
|
||||||
if (substr_length == 0 || substr_length > str->size) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *s1;
|
|
||||||
for (u64 i = 0; i < str->size; ++i) {
|
|
||||||
if (i + substr_length > str->size) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
s1 = &(str->buf[i]);
|
|
||||||
|
|
||||||
if (strncmp(s1, substr, substr_length) == 0) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 wapp_dstr_length(const String *str) {
|
|
||||||
if (!str) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return str->size;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 wapp_dstr_capacity(const String *str) {
|
|
||||||
if (!str) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return str->capacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *wapp_dstr_to_cstr(const String *str) {
|
|
||||||
if (!str) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return str->buf;
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
#ifndef MEM_ALLOCATOR_H
|
|
||||||
#define MEM_ALLOCATOR_H
|
|
||||||
|
|
||||||
#include "aliases.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
typedef void *(MemAllocFunc)(u64 size, void *alloc_obj);
|
|
||||||
typedef void *(MemAllocAlignedFunc)(u64 size, u64 alignment, void *alloc_obj);
|
|
||||||
typedef void *(MemReallocFunc)(void *ptr, u64 size, void *alloc_obj);
|
|
||||||
typedef void *(MemReallocAlignedFunc)(void *ptr, u64 size, u64 alignment,
|
|
||||||
void *alloc_obj);
|
|
||||||
typedef void(MemFreeFunc)(void **ptr, void *alloc_obj);
|
|
||||||
|
|
||||||
typedef struct allocator Allocator;
|
|
||||||
struct allocator {
|
|
||||||
void *obj;
|
|
||||||
MemAllocFunc *alloc;
|
|
||||||
MemAllocAlignedFunc *alloc_aligned;
|
|
||||||
MemReallocFunc *realloc;
|
|
||||||
MemReallocAlignedFunc *realloc_aligned;
|
|
||||||
MemFreeFunc *free;
|
|
||||||
};
|
|
||||||
|
|
||||||
void *wapp_mem_allocator_alloc(const Allocator *allocator, u64 size);
|
|
||||||
void *wapp_mem_allocator_alloc_aligned(const Allocator *allocator, u64 size,
|
|
||||||
u64 alignment);
|
|
||||||
void *wapp_mem_allocator_realloc(const Allocator *allocator, void *ptr,
|
|
||||||
u64 size);
|
|
||||||
void *wapp_mem_allocator_realloc_aligned(const Allocator *allocator, void *ptr,
|
|
||||||
u64 size, u64 alignment);
|
|
||||||
void wapp_mem_allocator_free(const Allocator *allocator, void **ptr);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif // !MEM_ALLOCATOR_H
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef MEM_ARENA_H
|
|
||||||
#define MEM_ARENA_H
|
|
||||||
|
|
||||||
#include "aliases.h"
|
|
||||||
#include "mem_allocator.h"
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
typedef struct growing_arena Arena;
|
|
||||||
|
|
||||||
Allocator wapp_mem_arena_allocator(const Arena *arena);
|
|
||||||
bool wapp_mem_arena_init(Arena **arena, u64 base_capacity);
|
|
||||||
void *wapp_mem_arena_alloc(Arena *arena, u64 size);
|
|
||||||
void *wapp_mem_arena_alloc_aligned(Arena *arena, u64 size, u64 alignment);
|
|
||||||
void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 size);
|
|
||||||
void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 size,
|
|
||||||
u64 alignment);
|
|
||||||
void wapp_mem_arena_clear(Arena *arena);
|
|
||||||
void wapp_mem_arena_free(Arena **arena);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif // !MEM_ARENA_H
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
#ifndef MEM_CTX_H
|
|
||||||
#define MEM_CTX_H
|
|
||||||
|
|
||||||
#include "mem_allocator.h"
|
|
||||||
#include "mem_arena.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CTX_DEST_BUFFER_MAIN,
|
|
||||||
CTX_DEST_BUFFER_TEMP,
|
|
||||||
|
|
||||||
COUNT_CTX_DEST_BUFFER,
|
|
||||||
} CTXDestBuffer;
|
|
||||||
|
|
||||||
Allocator wapp_mem_ctx_allocator(CTXDestBuffer buffer);
|
|
||||||
void wapp_mem_ctx_init(u64 main_buf_capacity, u64 temp_buf_capacity);
|
|
||||||
void *wapp_mem_ctx_alloc(CTXDestBuffer buffer, u64 size);
|
|
||||||
void *wapp_mem_ctx_alloc_aligned(CTXDestBuffer buffer, u64 size, u64 alignment);
|
|
||||||
void *wapp_mem_ctx_realloc(CTXDestBuffer buffer, void *ptr, u64 size);
|
|
||||||
void *wapp_mem_ctx_realloc_aligned(CTXDestBuffer buffer, void *ptr, u64 size,
|
|
||||||
u64 alignment);
|
|
||||||
void wapp_mem_ctx_clear(CTXDestBuffer buffer);
|
|
||||||
void wapp_mem_ctx_free(void);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif // !MEM_CTX_H
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
#ifndef MEM_LIBC_H
|
|
||||||
#define MEM_LIBC_H
|
|
||||||
|
|
||||||
#include "mem_allocator.h"
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
Allocator wapp_mem_libc_allocator(void);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif // !MEM_LIBC_H
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#ifndef MEM_UTILS_H
|
|
||||||
#define MEM_UTILS_H
|
|
||||||
|
|
||||||
#include "aliases.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
void *wapp_mem_util_align_forward(void *ptr, u64 alignment);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif // !MEM_UTILS_H
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
#include "mem_allocator.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
void *wapp_mem_allocator_alloc(const Allocator *allocator, u64 size) {
|
|
||||||
if (!allocator || !(allocator->alloc)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return allocator->alloc(size, allocator->obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_allocator_alloc_aligned(const Allocator *allocator, u64 size,
|
|
||||||
u64 alignment) {
|
|
||||||
if (!allocator || !(allocator->alloc_aligned)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return allocator->alloc_aligned(size, alignment, allocator->obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_allocator_realloc(const Allocator *allocator, void *ptr,
|
|
||||||
u64 size) {
|
|
||||||
if (!allocator || !(allocator->realloc)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return allocator->realloc(ptr, size, allocator->obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_allocator_realloc_aligned(const Allocator *allocator, void *ptr,
|
|
||||||
u64 size, u64 alignment) {
|
|
||||||
if (!allocator || !(allocator->realloc_aligned)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return allocator->realloc_aligned(ptr, size, alignment, allocator->obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_mem_allocator_free(const Allocator *allocator, void **ptr) {
|
|
||||||
if (!allocator || !(allocator->free)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator->free(ptr, allocator->obj);
|
|
||||||
}
|
|
||||||
@@ -1,401 +0,0 @@
|
|||||||
#include "mem_arena.h"
|
|
||||||
#include "aliases.h"
|
|
||||||
#include "mem_allocator.h"
|
|
||||||
#include "mem_utils.h"
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifndef DEFAULT_ALIGNMENT
|
|
||||||
// Why 2 * sizeof(void *) instead of sizeof(void *)
|
|
||||||
// https://handmade.network/forums/t/6860-alignment_arena_allocator
|
|
||||||
#define DEFAULT_ALIGNMENT (2 * sizeof(void *))
|
|
||||||
#endif /* ifndef DEFAULT_ALIGNMENT */
|
|
||||||
|
|
||||||
#define HDR_MAGIC_BYTE_COUNT 8
|
|
||||||
#define HDR_MAGIC \
|
|
||||||
{ 0x57, 0x41, 0x41, 0x52, 0x4e, 0x48, 0x44, 0x52 }
|
|
||||||
#define MAX_HDR_SEARCH_LENGTH 256
|
|
||||||
|
|
||||||
typedef struct arena_alloc_hdr ArenaAllocHDR;
|
|
||||||
struct arena_alloc_hdr {
|
|
||||||
u8 magic[HDR_MAGIC_BYTE_COUNT];
|
|
||||||
u64 alloc_size;
|
|
||||||
u64 alignment;
|
|
||||||
u8 *alloc_start;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct base_arena BaseArena;
|
|
||||||
struct base_arena {
|
|
||||||
u8 *buf;
|
|
||||||
u8 *offset;
|
|
||||||
u64 capacity;
|
|
||||||
BaseArena *prev;
|
|
||||||
BaseArena *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct growing_arena {
|
|
||||||
BaseArena *active_arena;
|
|
||||||
u64 count;
|
|
||||||
u64 initial_capacity;
|
|
||||||
};
|
|
||||||
|
|
||||||
internal BaseArena *find_arena_from_pointer(const Arena *arena, void *ptr);
|
|
||||||
|
|
||||||
internal bool base_arena_init(BaseArena *arena, u64 capacity);
|
|
||||||
internal void *base_arena_alloc_aligned(BaseArena *arena, u64 size,
|
|
||||||
u64 alignment);
|
|
||||||
internal void base_arena_clear(BaseArena *arena);
|
|
||||||
internal void base_arena_free(BaseArena *arena);
|
|
||||||
internal ArenaAllocHDR *find_alloc_header(BaseArena *arena, void *alloc_ptr);
|
|
||||||
|
|
||||||
internal void *mem_arena_alloc(u64 size, void *alloc_obj);
|
|
||||||
internal void *mem_arena_alloc_aligned(u64 size, u64 alignment,
|
|
||||||
void *alloc_obj);
|
|
||||||
internal void *mem_arena_realloc(void *ptr, u64 size, void *alloc_obj);
|
|
||||||
internal void *mem_arena_realloc_aligned(void *ptr, u64 size, u64 alignment,
|
|
||||||
void *alloc_obj);
|
|
||||||
|
|
||||||
// PUBLIC API
|
|
||||||
|
|
||||||
Allocator wapp_mem_arena_allocator(const Arena *arena) {
|
|
||||||
return (Allocator){
|
|
||||||
.obj = (void *)arena,
|
|
||||||
.alloc = mem_arena_alloc,
|
|
||||||
.alloc_aligned = mem_arena_alloc_aligned,
|
|
||||||
.realloc = mem_arena_realloc,
|
|
||||||
.realloc_aligned = mem_arena_realloc_aligned,
|
|
||||||
.free = NULL,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wapp_mem_arena_init(Arena **arena, u64 base_capacity) {
|
|
||||||
if (!arena || *arena || base_capacity == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*arena = (Arena *)calloc(1, sizeof(Arena));
|
|
||||||
Arena *arena_ptr = *arena;
|
|
||||||
if (!arena_ptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
arena_ptr->active_arena = (BaseArena *)calloc(1, sizeof(BaseArena));
|
|
||||||
if (!(arena_ptr->active_arena)) {
|
|
||||||
wapp_mem_arena_free(arena);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!base_arena_init(arena_ptr->active_arena, base_capacity)) {
|
|
||||||
wapp_mem_arena_free(arena);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
arena_ptr->count = 1;
|
|
||||||
arena_ptr->initial_capacity = base_capacity;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_arena_alloc(Arena *arena, u64 size) {
|
|
||||||
return wapp_mem_arena_alloc_aligned(arena, size, DEFAULT_ALIGNMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_arena_alloc_aligned(Arena *arena, u64 size, u64 alignment) {
|
|
||||||
if (!arena || !(arena->active_arena)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *output = base_arena_alloc_aligned(arena->active_arena, size, alignment);
|
|
||||||
if (!output) {
|
|
||||||
if (arena->active_arena->next) {
|
|
||||||
arena->active_arena = arena->active_arena->next;
|
|
||||||
} else {
|
|
||||||
arena->active_arena->next = (BaseArena *)calloc(1, sizeof(BaseArena));
|
|
||||||
if (!(arena->active_arena->next)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!base_arena_init(arena->active_arena->next,
|
|
||||||
arena->initial_capacity)) {
|
|
||||||
free(arena->active_arena->next);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
arena->active_arena->next->prev = arena->active_arena;
|
|
||||||
arena->active_arena = arena->active_arena->next;
|
|
||||||
|
|
||||||
++(arena->count);
|
|
||||||
}
|
|
||||||
|
|
||||||
output = base_arena_alloc_aligned(arena->active_arena, size, alignment);
|
|
||||||
if (!output) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(output, 0, size);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 size) {
|
|
||||||
return wapp_mem_arena_realloc_aligned(arena, ptr, size, DEFAULT_ALIGNMENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 size,
|
|
||||||
u64 alignment) {
|
|
||||||
if (!arena) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseArena *base_arena = find_arena_from_pointer(arena, ptr);
|
|
||||||
if (!base_arena) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArenaAllocHDR *header = find_alloc_header(base_arena, ptr);
|
|
||||||
if (!header) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (header->alloc_start + header->alloc_size == base_arena->offset) {
|
|
||||||
// Avoid allocating new pointer and copying memory if pointer is at the end
|
|
||||||
// of the arena
|
|
||||||
i64 diff = size - header->alloc_size;
|
|
||||||
|
|
||||||
u8 *new_offset = base_arena->offset + diff;
|
|
||||||
|
|
||||||
u8 *clear_start = diff < 0 ? new_offset : base_arena->offset;
|
|
||||||
memset(clear_start, 0, llabs(diff));
|
|
||||||
|
|
||||||
header->alloc_size = size;
|
|
||||||
base_arena->offset = new_offset;
|
|
||||||
|
|
||||||
return header->alloc_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *new_alloc = wapp_mem_arena_alloc_aligned(arena, size, alignment);
|
|
||||||
if (!new_alloc) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 to_copy = size < header->alloc_size ? size : header->alloc_size;
|
|
||||||
|
|
||||||
memcpy(new_alloc, ptr, to_copy);
|
|
||||||
|
|
||||||
return new_alloc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_mem_arena_clear(Arena *arena) {
|
|
||||||
if (!arena) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseArena *last_active = NULL;
|
|
||||||
while (arena->active_arena) {
|
|
||||||
base_arena_clear(arena->active_arena);
|
|
||||||
|
|
||||||
last_active = arena->active_arena;
|
|
||||||
|
|
||||||
arena->active_arena = arena->active_arena->prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
arena->active_arena = last_active;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_mem_arena_free(Arena **arena) {
|
|
||||||
if (!arena) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Arena *arena_ptr = *arena;
|
|
||||||
if (!arena_ptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseArena *current;
|
|
||||||
BaseArena *next;
|
|
||||||
BaseArena *prev;
|
|
||||||
|
|
||||||
current = arena_ptr->active_arena->next;
|
|
||||||
while (current) {
|
|
||||||
next = current->next;
|
|
||||||
|
|
||||||
base_arena_free(current);
|
|
||||||
free(current);
|
|
||||||
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = arena_ptr->active_arena->prev;
|
|
||||||
while (current) {
|
|
||||||
prev = current->prev;
|
|
||||||
|
|
||||||
base_arena_free(current);
|
|
||||||
free(current);
|
|
||||||
|
|
||||||
current = prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
base_arena_free(arena_ptr->active_arena);
|
|
||||||
|
|
||||||
free(arena_ptr->active_arena);
|
|
||||||
arena_ptr->active_arena = NULL;
|
|
||||||
|
|
||||||
arena_ptr->count = 0;
|
|
||||||
arena_ptr->initial_capacity = 0;
|
|
||||||
|
|
||||||
free(*arena);
|
|
||||||
*arena = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// INTERNAL FUNCTIONS
|
|
||||||
|
|
||||||
internal BaseArena *find_arena_from_pointer(const Arena *arena, void *ptr) {
|
|
||||||
if (!arena || !ptr) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure pointer is not out of bounds
|
|
||||||
u8 *alloc = (u8 *)ptr;
|
|
||||||
BaseArena *active = arena->active_arena;
|
|
||||||
|
|
||||||
if (alloc > active->buf + arena->initial_capacity) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u64 i = 0; i < arena->count; ++i) {
|
|
||||||
if (alloc >= active->buf && alloc < active->buf + arena->initial_capacity) {
|
|
||||||
return (BaseArena *)active;
|
|
||||||
}
|
|
||||||
|
|
||||||
active = active->prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool base_arena_init(BaseArena *arena, u64 capacity) {
|
|
||||||
if (!arena || arena->buf || capacity == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
arena->buf = (u8 *)calloc(capacity, sizeof(u8));
|
|
||||||
if (!(arena->buf)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
arena->capacity = capacity;
|
|
||||||
arena->offset = arena->buf;
|
|
||||||
arena->prev = arena->next = NULL;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void *base_arena_alloc_aligned(BaseArena *arena, u64 size,
|
|
||||||
u64 alignment) {
|
|
||||||
if (!arena) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 *start_offset = arena->offset;
|
|
||||||
u8 *alloc_start = arena->offset + sizeof(ArenaAllocHDR);
|
|
||||||
|
|
||||||
u8 *output = wapp_mem_util_align_forward((void *)alloc_start, alignment);
|
|
||||||
if (output + size >= arena->buf + arena->capacity) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArenaAllocHDR *header = (ArenaAllocHDR *)start_offset;
|
|
||||||
*header = (ArenaAllocHDR){
|
|
||||||
.magic = HDR_MAGIC,
|
|
||||||
.alloc_size = size,
|
|
||||||
.alignment = alignment,
|
|
||||||
.alloc_start = output,
|
|
||||||
};
|
|
||||||
|
|
||||||
arena->offset = output + size;
|
|
||||||
|
|
||||||
return (void *)output;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void base_arena_clear(BaseArena *arena) {
|
|
||||||
if (!arena) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(arena->buf, 0, arena->offset - arena->buf);
|
|
||||||
arena->offset = arena->buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void base_arena_free(BaseArena *arena) {
|
|
||||||
if (!arena) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arena->buf) {
|
|
||||||
free(arena->buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
arena->buf = arena->offset = NULL;
|
|
||||||
arena->capacity = 0;
|
|
||||||
arena->prev = arena->next = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal ArenaAllocHDR *find_alloc_header(BaseArena *arena, void *alloc_ptr) {
|
|
||||||
persistent const u8 magic[HDR_MAGIC_BYTE_COUNT] = HDR_MAGIC;
|
|
||||||
|
|
||||||
u8 *current = (u8 *)alloc_ptr;
|
|
||||||
|
|
||||||
u8 *max_search_end = current - MAX_HDR_SEARCH_LENGTH;
|
|
||||||
u8 *arena_buf_start = arena->buf;
|
|
||||||
|
|
||||||
u8 *search_end =
|
|
||||||
max_search_end > arena_buf_start ? max_search_end : arena_buf_start;
|
|
||||||
|
|
||||||
bool match;
|
|
||||||
for (; current >= search_end; --current) {
|
|
||||||
match = true;
|
|
||||||
|
|
||||||
for (u64 i = 0; i < HDR_MAGIC_BYTE_COUNT; ++i) {
|
|
||||||
if (current[i] != magic[i]) {
|
|
||||||
match = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match) {
|
|
||||||
return (ArenaAllocHDR *)current;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void *mem_arena_alloc(u64 size, void *alloc_obj) {
|
|
||||||
Arena *arena = (Arena *)alloc_obj;
|
|
||||||
|
|
||||||
return wapp_mem_arena_alloc(arena, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void *mem_arena_alloc_aligned(u64 size, u64 alignment,
|
|
||||||
void *alloc_obj) {
|
|
||||||
Arena *arena = (Arena *)alloc_obj;
|
|
||||||
|
|
||||||
return wapp_mem_arena_alloc_aligned(arena, size, alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void *mem_arena_realloc(void *ptr, u64 size, void *alloc_obj) {
|
|
||||||
Arena *arena = (Arena *)alloc_obj;
|
|
||||||
|
|
||||||
return wapp_mem_arena_realloc(arena, ptr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void *mem_arena_realloc_aligned(void *ptr, u64 size, u64 alignment,
|
|
||||||
void *alloc_obj) {
|
|
||||||
Arena *arena = (Arena *)alloc_obj;
|
|
||||||
|
|
||||||
return wapp_mem_arena_realloc_aligned(arena, ptr, size, alignment);
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
#include "mem_ctx.h"
|
|
||||||
#include "aliases.h"
|
|
||||||
#include "mem_arena.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
typedef struct mem_ctx MemCTX;
|
|
||||||
struct mem_ctx {
|
|
||||||
Arena *main;
|
|
||||||
Arena *temp;
|
|
||||||
bool main_initialised;
|
|
||||||
bool temp_initialised;
|
|
||||||
};
|
|
||||||
|
|
||||||
internal MemCTX g_context = {0};
|
|
||||||
|
|
||||||
internal Arena *get_arena(CTXDestBuffer buffer);
|
|
||||||
|
|
||||||
Allocator wapp_mem_ctx_allocator(CTXDestBuffer buffer) {
|
|
||||||
Arena *arena = get_arena(buffer);
|
|
||||||
return wapp_mem_arena_allocator(arena);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_mem_ctx_init(u64 main_buf_capacity, u64 temp_buf_capacity) {
|
|
||||||
g_context.main_initialised =
|
|
||||||
wapp_mem_arena_init(&g_context.main, main_buf_capacity);
|
|
||||||
g_context.temp_initialised =
|
|
||||||
wapp_mem_arena_init(&g_context.temp, temp_buf_capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_ctx_alloc(CTXDestBuffer buffer, u64 size) {
|
|
||||||
Arena *arena = get_arena(buffer);
|
|
||||||
if (!arena) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wapp_mem_arena_alloc(arena, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_ctx_alloc_aligned(CTXDestBuffer buffer, u64 size,
|
|
||||||
u64 alignment) {
|
|
||||||
Arena *arena = get_arena(buffer);
|
|
||||||
if (!arena) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wapp_mem_arena_alloc_aligned(arena, size, alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_ctx_realloc(CTXDestBuffer buffer, void *ptr, u64 size) {
|
|
||||||
Arena *arena = get_arena(buffer);
|
|
||||||
if (!arena) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wapp_mem_arena_realloc(arena, ptr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *wapp_mem_ctx_realloc_aligned(CTXDestBuffer buffer, void *ptr, u64 size,
|
|
||||||
u64 alignment) {
|
|
||||||
Arena *arena = get_arena(buffer);
|
|
||||||
if (!arena) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wapp_mem_arena_realloc_aligned(arena, ptr, size, alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_mem_ctx_clear(CTXDestBuffer buffer) {
|
|
||||||
Arena *arena = get_arena(buffer);
|
|
||||||
if (!arena) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wapp_mem_arena_clear(arena);
|
|
||||||
}
|
|
||||||
|
|
||||||
void wapp_mem_ctx_free(void) {
|
|
||||||
wapp_mem_arena_free(&(g_context.main));
|
|
||||||
g_context.main_initialised = false;
|
|
||||||
|
|
||||||
wapp_mem_arena_free(&(g_context.temp));
|
|
||||||
g_context.temp_initialised = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Arena *get_arena(CTXDestBuffer buffer) {
|
|
||||||
Arena *output = NULL;
|
|
||||||
|
|
||||||
switch (buffer) {
|
|
||||||
case CTX_DEST_BUFFER_MAIN:
|
|
||||||
if (g_context.main_initialised) {
|
|
||||||
output = g_context.main;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CTX_DEST_BUFFER_TEMP:
|
|
||||||
if (g_context.temp_initialised) {
|
|
||||||
output = g_context.temp;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false && "Not all context destination buffers are handled");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
#include "mem_libc.h"
|
|
||||||
#include "aliases.h"
|
|
||||||
#include "mem_allocator.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
internal void *mem_libc_alloc(u64 size, void *alloc_obj);
|
|
||||||
internal void *mem_libc_alloc_aligned(u64 size, u64 alignment, void *alloc_obj);
|
|
||||||
internal void *mem_libc_realloc(void *ptr, u64 size, void *alloc_obj);
|
|
||||||
internal void mem_libc_free(void **ptr, void *alloc_obj);
|
|
||||||
|
|
||||||
Allocator wapp_mem_libc_allocator(void) {
|
|
||||||
return (Allocator){
|
|
||||||
.obj = NULL,
|
|
||||||
.alloc = mem_libc_alloc,
|
|
||||||
.alloc_aligned = mem_libc_alloc_aligned,
|
|
||||||
.realloc = mem_libc_realloc,
|
|
||||||
.realloc_aligned = NULL,
|
|
||||||
.free = mem_libc_free,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void *mem_libc_alloc(u64 size, void *alloc_obj) {
|
|
||||||
return calloc(1, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void *mem_libc_alloc_aligned(u64 size, u64 alignment,
|
|
||||||
void *alloc_obj) {
|
|
||||||
void *output = aligned_alloc(alignment, size);
|
|
||||||
if (output) {
|
|
||||||
memset(output, 0, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void *mem_libc_realloc(void *ptr, u64 size, void *alloc_obj) {
|
|
||||||
return realloc(ptr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void mem_libc_free(void **ptr, void *alloc_obj) {
|
|
||||||
if (!ptr || !(*ptr)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(*ptr);
|
|
||||||
*ptr = NULL;
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#include "mem_utils.h"
|
|
||||||
#include "aliases.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
internal bool is_power_of_two(u64 num) { return (num & (num - 1)) == 0; }
|
|
||||||
|
|
||||||
void *wapp_mem_util_align_forward(void *ptr, u64 alignment) {
|
|
||||||
if (!ptr) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(is_power_of_two(alignment));
|
|
||||||
|
|
||||||
uptr p = (uptr)ptr;
|
|
||||||
uptr align = (uptr)alignment;
|
|
||||||
|
|
||||||
// Similar to p % align, but it's a faster implementation that works fine
|
|
||||||
// because align is guaranteed to be a power of 2
|
|
||||||
uptr modulo = p & (align - 1);
|
|
||||||
|
|
||||||
if (modulo != 0) {
|
|
||||||
p += align - modulo;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (void *)p;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cp -r src wapp
|
||||||
|
cp CHANGELOG.md wapp
|
||||||
|
|
||||||
|
mkdir -p dist
|
||||||
|
|
||||||
|
ARCHIVE_NAME="wapp-v$(cat VERSION)"
|
||||||
|
tar -czvf dist/$ARCHIVE_NAME.tar.gz wapp
|
||||||
|
zip -r dist/$ARCHIVE_NAME.zip wapp
|
||||||
|
|
||||||
|
rm -rf wapp
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
VERSION_HEADER = re.compile(r"\[\d+.\d+.\d+\]")
|
||||||
|
SCRIPT_DIR = Path(__file__).absolute().parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("version")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
changelog = get_changelog(args.version)
|
||||||
|
outfilename = save_changelog(args.version, changelog)
|
||||||
|
|
||||||
|
print(outfilename)
|
||||||
|
|
||||||
|
|
||||||
|
def get_changelog(version: str) -> list[str]:
|
||||||
|
changelog = SCRIPT_DIR / "CHANGELOG.md"
|
||||||
|
|
||||||
|
with changelog.open("r") as infile:
|
||||||
|
lines = infile.readlines()
|
||||||
|
|
||||||
|
start = -1
|
||||||
|
end = -1
|
||||||
|
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if start == -1:
|
||||||
|
match = re.search(version, line)
|
||||||
|
if match is None:
|
||||||
|
continue
|
||||||
|
start = i
|
||||||
|
else:
|
||||||
|
match = VERSION_HEADER.search(line)
|
||||||
|
if match is not None:
|
||||||
|
end = i
|
||||||
|
break
|
||||||
|
|
||||||
|
if start == -1:
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
return lines[start:end] if end > -1 else lines[start:]
|
||||||
|
|
||||||
|
|
||||||
|
def save_changelog(version: str, lines: list[str]) -> str:
|
||||||
|
version_changelog = SCRIPT_DIR / f"wapp-changelog-{version}.md"
|
||||||
|
with version_changelog.open("w") as outfile:
|
||||||
|
outfile.writelines(lines)
|
||||||
|
return str(version_changelog)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(dirname $0)"
|
||||||
|
LIB_SRC="$1"
|
||||||
|
INSTALL_PREFIX="$2"
|
||||||
|
shift 2
|
||||||
|
INCLUDES="$@"
|
||||||
|
|
||||||
|
mkdir -p "$INSTALL_PREFIX"
|
||||||
|
|
||||||
|
BASE_INCLUDE_DIR="$(dirname "$LIB_SRC")"
|
||||||
|
find $BASE_INCLUDE_DIR -maxdepth 1 -type f -name "*.h" -exec cp -v {} "$INSTALL_PREFIX" \;
|
||||||
|
|
||||||
|
cd "$SCRIPT_DIR/../src"
|
||||||
|
for INCLUDE in $INCLUDES; do
|
||||||
|
for f in $(find "$INCLUDE" -type f -name "*.h"); do
|
||||||
|
DST="$INSTALL_PREFIX/$(dirname $f)"
|
||||||
|
mkdir -p "$DST"
|
||||||
|
cp -v "$f" "$DST"
|
||||||
|
done
|
||||||
|
done
|
||||||
@@ -0,0 +1,310 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#include "./array.h"
|
||||||
|
#include "../../common/assert/assert.h"
|
||||||
|
#include "../mem/allocator/mem_allocator.h"
|
||||||
|
#include "../../common/misc/misc_utils.h"
|
||||||
|
#include "../../common/aliases/aliases.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define arrayHeader(ARRAY) (WpArrayHeader *)(wpMiscUtilsOffsetPointer(ARRAY, (i64)sizeof(WpArrayHeader) * -1))
|
||||||
|
|
||||||
|
wp_persist inline void arrayValidate(const WpArray array, u64 item_size);
|
||||||
|
wp_persist inline void *arrayGetItemWithOffset(const WpArray array, u64 offset);
|
||||||
|
|
||||||
|
u64 arrayCount(WpArray array) {
|
||||||
|
wpDebugAssert(array != NULL, "`array` should not be NULL");
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
wpRuntimeAssert(WP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array");
|
||||||
|
|
||||||
|
return header->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 arrayCapacity(WpArray array) {
|
||||||
|
wpDebugAssert(array != NULL, "`array` should not be NULL");
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
wpRuntimeAssert(WP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array");
|
||||||
|
|
||||||
|
return header->capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 arrayItemSize(WpArray array) {
|
||||||
|
wpDebugAssert(array != NULL, "`array` should not be NULL");
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
wpRuntimeAssert(WP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array");
|
||||||
|
|
||||||
|
return header->item_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arraySetCount(WpArray array, u64 count) {
|
||||||
|
wpDebugAssert(array != NULL, "`array` should not be NULL");
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
wpRuntimeAssert(WP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array");
|
||||||
|
|
||||||
|
header->count = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *arrayGet(WpArray array, u64 index, u64 item_size) {
|
||||||
|
wpDebugAssert(array != NULL, "`array` should not be NULL");
|
||||||
|
arrayValidate(array, item_size);
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
wpRuntimeAssert(index < header->count, "`index` is out of bounds");
|
||||||
|
|
||||||
|
return arrayGetItemWithOffset(array, header->item_size * index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void arraySet(WpArray array, u64 index, void *value, u64 item_size) {
|
||||||
|
void *item = arrayGet(array, index, item_size);
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
memcpy(item, value, header->item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrayAppendCapped(WpArray array, void *value, u64 item_size) {
|
||||||
|
wpDebugAssert(array != NULL, "`array` should not be NULL");
|
||||||
|
arrayValidate(array, item_size);
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
if (header->count >= header->capacity) { return; }
|
||||||
|
|
||||||
|
u64 index = (header->count)++;
|
||||||
|
arraySet(array, index, value, item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrayExtendCappend(WpArray dst, const WpArray src, u64 item_size) {
|
||||||
|
wpDebugAssert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL");
|
||||||
|
arrayValidate(dst, item_size);
|
||||||
|
arrayValidate(src, item_size);
|
||||||
|
|
||||||
|
WpArrayHeader *src_header = arrayHeader(src);
|
||||||
|
WpArrayHeader *dst_header = arrayHeader(dst);
|
||||||
|
u64 remaining_capacity = dst_header->capacity - dst_header->count;
|
||||||
|
|
||||||
|
u64 copy_count = src_header->count < remaining_capacity ? src_header->count : remaining_capacity;
|
||||||
|
void *dst_ptr = wpMiscUtilsOffsetPointer(dst, dst_header->count * dst_header->item_size);
|
||||||
|
memcpy(dst_ptr, src, copy_count * src_header->item_size);
|
||||||
|
dst_header->count += copy_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrayCopyCapped(WpArray dst, const WpArray src, u64 item_size) {
|
||||||
|
wpDebugAssert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL");
|
||||||
|
arrayValidate(dst, item_size);
|
||||||
|
arrayValidate(src, item_size);
|
||||||
|
|
||||||
|
arrayClear(dst, item_size);
|
||||||
|
|
||||||
|
WpArrayHeader *src_header = arrayHeader(src);
|
||||||
|
WpArrayHeader *dst_header = arrayHeader(dst);
|
||||||
|
u64 copy_count = src_header->count < dst_header->capacity ? src_header->count : dst_header->capacity;
|
||||||
|
memcpy((void *)dst, (void *)src, copy_count * src_header->item_size);
|
||||||
|
dst_header->count = copy_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpArray arrayAppendAlloc(const WpAllocator *allocator, WpArray array, void *value,
|
||||||
|
WpArrayInitFlags flags, u64 item_size) {
|
||||||
|
wpDebugAssert(allocator != NULL && array != NULL, "`allocator` and `array` should not be NULL");
|
||||||
|
arrayValidate(array, item_size);
|
||||||
|
|
||||||
|
WpArray output = array;
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
if (header->count >= header->capacity) {
|
||||||
|
u64 new_capacity = wpMiscUtilsU64RoundUpPow2(header->capacity * 2);
|
||||||
|
output = (WpArray )arrayAllocCapacity(allocator, new_capacity, flags,
|
||||||
|
header->item_size);
|
||||||
|
if (!output) {
|
||||||
|
output = array;
|
||||||
|
goto RETURN_ARRAY_APPEND_ALLOC;
|
||||||
|
}
|
||||||
|
arrayCopyCapped(output, array, item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayAppendCapped(output, value, item_size);
|
||||||
|
|
||||||
|
if ((flags & WP_ARRAY_INIT_FILLED) == WP_ARRAY_INIT_FILLED) {
|
||||||
|
arraySetCount(output, arrayCapacity(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_ARRAY_APPEND_ALLOC:
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpArray arrayExtendAlloc(const WpAllocator *allocator, WpArray dst, const WpArray src,
|
||||||
|
WpArrayInitFlags flags, u64 item_size) {
|
||||||
|
wpDebugAssert(allocator != NULL && dst != NULL && src != NULL, "`allocator`, `dst` and `src` should not be NULL");
|
||||||
|
arrayValidate(dst, item_size);
|
||||||
|
arrayValidate(src, item_size);
|
||||||
|
|
||||||
|
WpArray output = dst;
|
||||||
|
|
||||||
|
WpArrayHeader *src_header = arrayHeader(src);
|
||||||
|
WpArrayHeader *dst_header = arrayHeader(dst);
|
||||||
|
u64 remaining_capacity = dst_header->capacity - dst_header->count;
|
||||||
|
if (src_header->count >= remaining_capacity) {
|
||||||
|
u64 new_capacity = wpMiscUtilsU64RoundUpPow2(dst_header->capacity * 2);
|
||||||
|
output = (WpArray )arrayAllocCapacity(allocator, new_capacity,
|
||||||
|
flags, dst_header->item_size);
|
||||||
|
if (!output) {
|
||||||
|
output = dst;
|
||||||
|
goto RETURN_ARRAY_EXTEND_ALLOC;
|
||||||
|
}
|
||||||
|
arrayCopyCapped(output, dst, item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayExtendCappend(output, src, item_size);
|
||||||
|
|
||||||
|
if ((flags & WP_ARRAY_INIT_FILLED) == WP_ARRAY_INIT_FILLED) {
|
||||||
|
arraySetCount(output, arrayCapacity(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_ARRAY_EXTEND_ALLOC:
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpArray arrayCopyAlloc(const WpAllocator *allocator, WpArray dst, const WpArray src,
|
||||||
|
WpArrayInitFlags flags, u64 item_size) {
|
||||||
|
wpDebugAssert(allocator != NULL && dst != NULL && src != NULL, "`allocator`, `dst` and `src` should not be NULL");
|
||||||
|
arrayValidate(dst, item_size);
|
||||||
|
arrayValidate(src, item_size);
|
||||||
|
|
||||||
|
WpArray output = dst;
|
||||||
|
|
||||||
|
WpArrayHeader *src_header = arrayHeader(src);
|
||||||
|
WpArrayHeader *dst_header = arrayHeader(dst);
|
||||||
|
if (src_header->count >= dst_header->capacity) {
|
||||||
|
u64 new_capacity = wpMiscUtilsU64RoundUpPow2(dst_header->capacity * 2);
|
||||||
|
output = (WpArray )arrayAllocCapacity(allocator, new_capacity,
|
||||||
|
flags, src_header->item_size);
|
||||||
|
if (!output) {
|
||||||
|
output = dst;
|
||||||
|
goto RETURN_ARRAY_COPY_ALLOC;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arrayCopyCapped(output, src, item_size);
|
||||||
|
|
||||||
|
if ((flags & WP_ARRAY_INIT_FILLED) == WP_ARRAY_INIT_FILLED) {
|
||||||
|
arraySetCount(output, arrayCapacity(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_ARRAY_COPY_ALLOC:
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrayRemoveSwapEnd(WpArray array, u64 index, u64 item_size) {
|
||||||
|
wpDebugAssert(array != NULL, "`array` should not be NULL");
|
||||||
|
arrayValidate(array, item_size);
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
if (header->count == 0 || index > header->count) { return; }
|
||||||
|
|
||||||
|
u64 last_item_index = header->count - 1;
|
||||||
|
|
||||||
|
if (index < last_item_index) {
|
||||||
|
void *cur_item = arrayGetItemWithOffset(array, header->item_size * index);
|
||||||
|
void *last_item = arrayGetItemWithOffset(array, header->item_size * last_item_index);
|
||||||
|
|
||||||
|
memcpy(cur_item, last_item, header->item_size);
|
||||||
|
memset(last_item, 0, header->item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
--(header->count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *arrayPop(WpArray array, u64 item_size) {
|
||||||
|
wpDebugAssert(array != NULL, "`array` should not be NULL");
|
||||||
|
arrayValidate(array, item_size);
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
if (header->count == 0) { return NULL; }
|
||||||
|
|
||||||
|
u64 index = header->count - 1;
|
||||||
|
void *out = arrayGetItemWithOffset(array, header->item_size * index);
|
||||||
|
--(header->count);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrayZero(WpArray array, u64 item_size) {
|
||||||
|
wpDebugAssert(array != NULL, "`array` should not be NULL");
|
||||||
|
arrayValidate(array, item_size);
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
memset(array, 0, header->capacity * header->item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrayClear(WpArray array, u64 item_size) {
|
||||||
|
wpDebugAssert(array != NULL, "`array` should not be NULL");
|
||||||
|
arrayValidate(array, item_size);
|
||||||
|
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
header->count = 0;
|
||||||
|
memset(array, 0, header->capacity * header->item_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 arrayCalcAllocSize(u64 capacity, u64 item_size) {
|
||||||
|
return sizeof(WpArrayHeader) + item_size * capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpArray arrayAllocCapacity(const WpAllocator *allocator, u64 capacity, WpArrayInitFlags flags,
|
||||||
|
u64 item_size) {
|
||||||
|
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
|
||||||
|
|
||||||
|
WpArray output = NULL;
|
||||||
|
|
||||||
|
u64 allocation_size = arrayCalcAllocSize(capacity, item_size);
|
||||||
|
void *buffer = wpMemAllocatorAlloc(allocator, allocation_size);
|
||||||
|
if (!buffer) {
|
||||||
|
goto RETURN_ARRAY_ALLOC;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = arrayFromPreallocatedBuffer(buffer, allocation_size, flags, item_size);
|
||||||
|
|
||||||
|
RETURN_ARRAY_ALLOC:
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WpArray arrayFromPreallocatedBuffer(void *buffer, u64 buffer_size, WpArrayInitFlags flags,
|
||||||
|
u64 item_size) {
|
||||||
|
wpDebugAssert(buffer != NULL, "`buffer` should not be NULL");
|
||||||
|
|
||||||
|
i64 data_buffer_size = (i64)buffer_size - (i64)(sizeof(WpArrayHeader));
|
||||||
|
if (data_buffer_size <= 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 item_capacity = (u64)data_buffer_size / item_size;
|
||||||
|
WpArrayHeader *header = (WpArrayHeader *)buffer;
|
||||||
|
WpArray output = (u8 *)(header + 1);
|
||||||
|
header->magic = WP_ARRAY_MAGIC;
|
||||||
|
header->count = flags & WP_ARRAY_INIT_FILLED ? item_capacity : 0;
|
||||||
|
header->capacity = item_capacity;
|
||||||
|
header->item_size = item_size;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void arrayDealloc(const WpAllocator *allocator, WpArray *array, u64 item_size) {
|
||||||
|
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
|
||||||
|
|
||||||
|
u64 capacity = wpArrayCapacity(*array);
|
||||||
|
u64 allocation_size = arrayCalcAllocSize(capacity, item_size);
|
||||||
|
void *header = (void *)arrayHeader(*array);
|
||||||
|
wpMemAllocatorFree(allocator, &header, allocation_size);
|
||||||
|
*array = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_persist inline void arrayValidate(const WpArray array, u64 item_size) {
|
||||||
|
WpArrayHeader *header = arrayHeader(array);
|
||||||
|
wpRuntimeAssert(WP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array");
|
||||||
|
wpRuntimeAssert(item_size == header->item_size, "Invalid item type provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_persist inline void *arrayGetItemWithOffset(const WpArray array, u64 offset) {
|
||||||
|
return wpMiscUtilsOffsetPointer(array, offset);
|
||||||
|
}
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#ifndef ARRAY_H
|
||||||
|
#define ARRAY_H
|
||||||
|
|
||||||
|
#include "../mem/allocator/mem_allocator.h"
|
||||||
|
#include "../../common/misc/misc_utils.h"
|
||||||
|
#include "../../common/aliases/aliases.h"
|
||||||
|
#include "../../common/platform/platform.h"
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
BEGIN_C_LINKAGE
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
#define WP_ARRAY_MAGIC wpU64Const(0x57415f415252)
|
||||||
|
|
||||||
|
#define calcArrayCount(TYPE, ...) wpMiscUtilsVaArgsCount(TYPE, __VA_ARGS__)
|
||||||
|
#define calcArrayCapacity(TYPE, ...) wpMiscUtilsU64RoundUpPow2(calcArrayCount(TYPE, __VA_ARGS__) * 2)
|
||||||
|
|
||||||
|
typedef struct WpStr8 WpStr8;
|
||||||
|
|
||||||
|
// NOTE (Abdelrahman): Typedefs to distinguish arrays from regular pointers
|
||||||
|
typedef void *WpArray;
|
||||||
|
typedef void **WpVoidPtrArray;
|
||||||
|
typedef c8 *WpC8Array;
|
||||||
|
typedef c16 *WpC16Array;
|
||||||
|
typedef c32 *WpC32Array;
|
||||||
|
typedef u8 *WpU8Array;
|
||||||
|
typedef u16 *WpU16Array;
|
||||||
|
typedef u32 *WpU32Array;
|
||||||
|
typedef u64 *WpU64Array;
|
||||||
|
typedef b8 *WpB8Array;
|
||||||
|
typedef b32 *WpB32Array;
|
||||||
|
typedef i8 *WpI8Array;
|
||||||
|
typedef i16 *WpI16Array;
|
||||||
|
typedef i32 *WpI32Array;
|
||||||
|
typedef i64 *WpI64Array;
|
||||||
|
typedef f32 *WpF32Array;
|
||||||
|
typedef f64 *WpF64Array;
|
||||||
|
typedef f128 *WpF128Array;
|
||||||
|
typedef uptr *WpUptrArray;
|
||||||
|
typedef iptr *WpIptrArray;
|
||||||
|
typedef WpStr8 *WpStr8Array;
|
||||||
|
|
||||||
|
typedef enum WpArrayInitFlags {
|
||||||
|
WP_ARRAY_INIT_NONE = 0,
|
||||||
|
WP_ARRAY_INIT_FILLED = 1 << 1,
|
||||||
|
} WpArrayInitFlags;
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
#define wpArray(TYPE, ...) ([&]() { \
|
||||||
|
u64 capacity = calcArrayCapacity(TYPE, __VA_ARGS__); \
|
||||||
|
\
|
||||||
|
TYPE items[calcArrayCapacity(TYPE, __VA_ARGS__)] = {__VA_ARGS__}; \
|
||||||
|
\
|
||||||
|
wp_persist u8 array[ \
|
||||||
|
sizeof(WpArrayHeader) + calcArrayCapacity(TYPE, __VA_ARGS__) * sizeof(TYPE) \
|
||||||
|
] = {0}; \
|
||||||
|
WpArrayHeader *header = (WpArrayHeader *)array; \
|
||||||
|
header->magic = WP_ARRAY_MAGIC; \
|
||||||
|
header->count = calcArrayCount(TYPE, __VA_ARGS__); \
|
||||||
|
header->capacity = calcArrayCapacity(TYPE, __VA_ARGS__); \
|
||||||
|
header->item_size = sizeof(TYPE); \
|
||||||
|
\
|
||||||
|
u8 *buf = (u8 *)(header + 1); \
|
||||||
|
memcpy(buf, items, capacity * sizeof(TYPE)); \
|
||||||
|
return (TYPE *)buf; \
|
||||||
|
}())
|
||||||
|
#define wpArrayWithCapacity(TYPE, CAPACITY, FLAGS) ([&]() { \
|
||||||
|
wp_persist u8 array[ \
|
||||||
|
sizeof(WpArrayHeader) + CAPACITY * sizeof(TYPE) \
|
||||||
|
] = {0}; \
|
||||||
|
WpArrayHeader *header = (WpArrayHeader *)array; \
|
||||||
|
header->magic = WP_ARRAY_MAGIC; \
|
||||||
|
header->count = (FLAGS & WP_ARRAY_INIT_FILLED) ? CAPACITY : 0; \
|
||||||
|
header->capacity = CAPACITY; \
|
||||||
|
header->item_size = sizeof(TYPE); \
|
||||||
|
\
|
||||||
|
return (TYPE *)(header + 1); \
|
||||||
|
}())
|
||||||
|
#define wpArrayPop(TYPE, ARRAY) ([&]() { \
|
||||||
|
if (ARRAY == NULL || arrayCount((WpArray)ARRAY) == 0) { \
|
||||||
|
TYPE result{}; \
|
||||||
|
return result; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
return *((TYPE *)arrayPop((WpArray)ARRAY, sizeof(TYPE))); \
|
||||||
|
}())
|
||||||
|
#else
|
||||||
|
#define stackArray(TYPE, SIZE) struct {WpArrayHeader header; \
|
||||||
|
TYPE items[SIZE]; \
|
||||||
|
wpMiscUtilsReservePadding(sizeof(WpArrayHeader) + \
|
||||||
|
sizeof(TYPE) * SIZE);}
|
||||||
|
#define wpArray(TYPE, ...) \
|
||||||
|
((TYPE *)( \
|
||||||
|
(stackArray(TYPE, calcArrayCapacity(TYPE, __VA_ARGS__))){ \
|
||||||
|
.header = { \
|
||||||
|
.magic = WP_ARRAY_MAGIC, \
|
||||||
|
.count = calcArrayCount(TYPE, __VA_ARGS__), \
|
||||||
|
.capacity = calcArrayCapacity(TYPE, __VA_ARGS__), \
|
||||||
|
.item_size = sizeof(TYPE), \
|
||||||
|
}, \
|
||||||
|
.items = {__VA_ARGS__}, \
|
||||||
|
}.items \
|
||||||
|
))
|
||||||
|
#define wpArrayWithCapacity(TYPE, CAPACITY, FLAGS) \
|
||||||
|
((TYPE *)( \
|
||||||
|
(stackArray(TYPE, CAPACITY)){ \
|
||||||
|
.header = { \
|
||||||
|
.magic = WP_ARRAY_MAGIC, \
|
||||||
|
.count = (FLAGS & WP_ARRAY_INIT_FILLED) ? CAPACITY : 0, \
|
||||||
|
.capacity = CAPACITY, \
|
||||||
|
.item_size = sizeof(TYPE), \
|
||||||
|
}, \
|
||||||
|
.items = {0}, \
|
||||||
|
}.items \
|
||||||
|
))
|
||||||
|
#define wpArrayPop(TYPE, ARRAY) \
|
||||||
|
(ARRAY == NULL || arrayCount((WpArray)ARRAY) == 0 ? \
|
||||||
|
(TYPE){0} : \
|
||||||
|
*((TYPE *)arrayPop((WpArray)ARRAY, sizeof(TYPE))) \
|
||||||
|
)
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
#define wpArrayCount(ARRAY) \
|
||||||
|
(arrayCount((WpArray)ARRAY))
|
||||||
|
#define wpArrayCapacity(ARRAY) \
|
||||||
|
(arrayCapacity((WpArray)ARRAY))
|
||||||
|
#define wpArrayItemSize(ARRAY) \
|
||||||
|
(arrayItemSize((WpArray)ARRAY))
|
||||||
|
#define wpArraySetCount(ARRAY, COUNT) \
|
||||||
|
(arraySetCount((WpArray)ARRAY, COUNT))
|
||||||
|
#define wpArrayGet(TYPE, ARRAY, INDEX) \
|
||||||
|
((TYPE *)arrayGet((WpArray)ARRAY, \
|
||||||
|
INDEX, \
|
||||||
|
sizeof(TYPE)))
|
||||||
|
#define wpArraySet(TYPE, ARRAY, INDEX, VALUE_PTR) \
|
||||||
|
(arraySet((WpArray)ARRAY, \
|
||||||
|
INDEX, \
|
||||||
|
(u8 *)VALUE_PTR, \
|
||||||
|
sizeof(TYPE)))
|
||||||
|
#define wpArrayAppendCapped(TYPE, ARRAY, VALUE_PTR) \
|
||||||
|
(arrayAppendCapped((WpArray)ARRAY, \
|
||||||
|
(u8 *)VALUE_PTR, \
|
||||||
|
sizeof(TYPE)))
|
||||||
|
#define wpArrayExtendCapped(TYPE, DST_ARRAY, SRC_ARRAY) \
|
||||||
|
(arrayExtendCappend((WpArray)DST_ARRAY, \
|
||||||
|
(WpArray)SRC_ARRAY, \
|
||||||
|
sizeof(TYPE)))
|
||||||
|
#define wpArrayCopyCapped(TYPE, DST_ARRAY, SRC_ARRAY) \
|
||||||
|
(arrayCopyCapped((WpArray)DST_ARRAY, \
|
||||||
|
(WpArray)SRC_ARRAY, \
|
||||||
|
sizeof(TYPE)))
|
||||||
|
#define wpArrayAppendAlloc(TYPE, ALLOCATOR_PTR, ARRAY, VALUE_PTR, FLAGS) \
|
||||||
|
((TYPE *)arrayAppendAlloc(ALLOCATOR_PTR, \
|
||||||
|
(WpArray)ARRAY, \
|
||||||
|
(u8 *)VALUE_PTR, \
|
||||||
|
FLAGS, \
|
||||||
|
sizeof(TYPE)))
|
||||||
|
#define wpArrayExtendAlloc(TYPE, ALLOCATOR_PTR, DST_ARRAY, SRC_ARRAY, FLAGS) \
|
||||||
|
((TYPE *)arrayExtendAlloc(ALLOCATOR_PTR, \
|
||||||
|
(WpArray)DST_ARRAY, \
|
||||||
|
(WpArray)SRC_ARRAY, \
|
||||||
|
FLAGS, \
|
||||||
|
sizeof(TYPE)))
|
||||||
|
#define wpArrayCopyAlloc(TYPE, ALLOCATOR_PTR, DST_ARRAY, SRC_ARRAY, FLAGS) \
|
||||||
|
((TYPE *)arrayCopyAlloc(ALLOCATOR_PTR, \
|
||||||
|
(WpArray)DST_ARRAY, \
|
||||||
|
(WpArray)SRC_ARRAY, \
|
||||||
|
FLAGS, \
|
||||||
|
sizeof(TYPE)))
|
||||||
|
#define wpArrayRemoveSwapEnd(TYPE, ARRAY, INDEX) arrayRemoveSwapEnd(ARRAY, INDEX, sizeof(TYPE))
|
||||||
|
#define wpArrayZero(TYPE, ARRAY) (arrayZero((WpArray)ARRAY, sizeof(TYPE)))
|
||||||
|
#define wpArrayClear(TYPE, ARRAY) (arrayClear((WpArray)ARRAY, sizeof(TYPE)))
|
||||||
|
#define wpArrayCalcAllocSize(TYPE, CAPACITY) arrayCalcAllocSize(CAPACITY, sizeof(TYPE))
|
||||||
|
#define wpArrayAllocCapacity(TYPE, ALLOCATOR_PTR, CAPACITY, FLAGS) \
|
||||||
|
((TYPE *)arrayAllocCapacity(ALLOCATOR_PTR, CAPACITY, FLAGS, sizeof(TYPE)))
|
||||||
|
#define wpArrayFromPreallcatedBuffer(TYPE, BUFFER, BUFFER_SIZE) \
|
||||||
|
((TYPE *)array_from_preallcated_buffer(BUFFER, BUFFER_SIZE, sizeof(TYPE)))
|
||||||
|
// Only needed for allocators like malloc where each allocation has to be freed on its own.
|
||||||
|
// No need to use it for allocators like Arena.
|
||||||
|
#define wpArrayDealloc(TYPE, ALLOCATOR_PTR, ARRAY_DPTR) \
|
||||||
|
(arrayDealloc(ALLOCATOR_PTR, (WpArray *)ARRAY_DPTR, sizeof(TYPE)))
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct WpArrayHeader WpArrayHeader;
|
||||||
|
struct WpArrayHeader {
|
||||||
|
u64 magic;
|
||||||
|
u64 count;
|
||||||
|
u64 capacity;
|
||||||
|
u64 item_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
u64 arrayCount(WpArray array);
|
||||||
|
u64 arrayCapacity(WpArray array);
|
||||||
|
u64 arrayItemSize(WpArray array);
|
||||||
|
void arraySetCount(WpArray array, u64 count);
|
||||||
|
void *arrayGet(WpArray array, u64 index, u64 item_size);
|
||||||
|
void arraySet(WpArray array, u64 index, void *value, u64 item_size);
|
||||||
|
void arrayAppendCapped(WpArray array, void *value, u64 item_size);
|
||||||
|
void arrayExtendCappend(WpArray dst, const WpArray src, u64 item_size);
|
||||||
|
void arrayCopyCapped(WpArray dst, const WpArray src, u64 item_size);
|
||||||
|
WpArray arrayAppendAlloc(const WpAllocator *allocator, WpArray array, void *value,
|
||||||
|
WpArrayInitFlags flags, u64 item_size);
|
||||||
|
WpArray arrayExtendAlloc(const WpAllocator *allocator, WpArray dst, const WpArray src,
|
||||||
|
WpArrayInitFlags flags, u64 item_size);
|
||||||
|
WpArray arrayCopyAlloc(const WpAllocator *allocator, WpArray dst, const WpArray src,
|
||||||
|
WpArrayInitFlags flags, u64 item_size);
|
||||||
|
void arrayRemoveSwapEnd(WpArray array, u64 index, u64 item_size);
|
||||||
|
void *arrayPop(WpArray array, u64 item_size);
|
||||||
|
void arrayZero(WpArray array, u64 item_size);
|
||||||
|
void arrayClear(WpArray array, u64 item_size);
|
||||||
|
u64 arrayCalcAllocSize(u64 capacity, u64 item_size);
|
||||||
|
WpArray arrayAllocCapacity(const WpAllocator *allocator, u64 capacity, WpArrayInitFlags flags,
|
||||||
|
u64 item_size);
|
||||||
|
WpArray arrayFromPreallocatedBuffer(void *buffer, u64 buffer_size, WpArrayInitFlags flags,
|
||||||
|
u64 item_size);
|
||||||
|
void arrayDealloc(const WpAllocator *allocator, WpArray *array, u64 item_size);
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
END_C_LINKAGE
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
#endif // !ARRAY_H
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#include "./dbl_list.h"
|
||||||
|
#include "../mem/allocator/mem_allocator.h"
|
||||||
|
#include "../../common/assert/assert.h"
|
||||||
|
#include "../../common/aliases/aliases.h"
|
||||||
|
#include "../../common/platform/platform.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
wp_intern WpDblList nodeToList(WpDblNode *node, u64 item_size);
|
||||||
|
wp_intern inline void dblListValidate(const WpDblList *list, u64 item_size);
|
||||||
|
wp_intern inline void dblListNodeValidate(const WpDblList *list, const WpDblNode *node, u64 item_size);
|
||||||
|
|
||||||
|
WpDblList *dblListAlloc(const WpAllocator *allocator, u64 item_size) {
|
||||||
|
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
|
||||||
|
|
||||||
|
WpDblList *list = wpMemAllocatorAlloc(allocator, sizeof(WpDblList));
|
||||||
|
if (!list) { goto DBL_LIST_ALLOC_RETURN; }
|
||||||
|
|
||||||
|
memset((void *)list, 0, sizeof(WpDblList));
|
||||||
|
list->magic = WP_DBL_LIST_MAGIC;
|
||||||
|
list->item_size = item_size;
|
||||||
|
|
||||||
|
DBL_LIST_ALLOC_RETURN:
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpDblNode *dblListNodeAlloc(const WpAllocator *allocator, void *item, u64 item_size) {
|
||||||
|
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
|
||||||
|
|
||||||
|
WpDblNode *node = wpMemAllocatorAlloc(allocator, sizeof(WpDblNode));
|
||||||
|
if (!node) { goto DBL_LIST_NODE_ALLOC_RETURN; }
|
||||||
|
|
||||||
|
memset((void *)node, 0, sizeof(WpDblNode));
|
||||||
|
node->item = item;
|
||||||
|
node->header.magic = WP_DBL_NODE_MAGIC;
|
||||||
|
node->header.item_size = item_size;
|
||||||
|
|
||||||
|
DBL_LIST_NODE_ALLOC_RETURN:
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpDblNode *dblListGet(const WpDblList *list, u64 index, u64 item_size) {
|
||||||
|
wpDebugAssert(list != NULL, "`list` should not be NULL");
|
||||||
|
dblListValidate(list, item_size);
|
||||||
|
wpRuntimeAssert(index < list->node_count, "`index` is out of bounds");
|
||||||
|
|
||||||
|
WpDblNode *output = NULL;
|
||||||
|
WpDblNode *current = list->first;
|
||||||
|
for (u64 i = 1; i <= index; ++i) {
|
||||||
|
current = current->header.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = current;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dblListPushFront(WpDblList *list, WpDblNode *node, u64 item_size) {
|
||||||
|
wpDebugAssert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
|
||||||
|
dblListValidate(list, item_size);
|
||||||
|
dblListNodeValidate(list, node, item_size);
|
||||||
|
|
||||||
|
WpDblList node_list = nodeToList(node, item_size);
|
||||||
|
|
||||||
|
if (list->node_count == 0) {
|
||||||
|
*list = node_list;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list->node_count += node_list.node_count;
|
||||||
|
|
||||||
|
WpDblNode *first = list->first;
|
||||||
|
if (first) {
|
||||||
|
first->header.prev = node_list.last;
|
||||||
|
}
|
||||||
|
|
||||||
|
list->first = node_list.first;
|
||||||
|
node_list.last->header.next = first;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dblListPushBack(WpDblList *list, WpDblNode *node, u64 item_size) {
|
||||||
|
wpDebugAssert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
|
||||||
|
dblListValidate(list, item_size);
|
||||||
|
dblListNodeValidate(list, node, item_size);
|
||||||
|
|
||||||
|
WpDblList node_list = nodeToList(node, item_size);
|
||||||
|
|
||||||
|
if (list->node_count == 0) {
|
||||||
|
*list = node_list;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
list->node_count += node_list.node_count;
|
||||||
|
|
||||||
|
WpDblNode *last = list->last;
|
||||||
|
if (last) {
|
||||||
|
last->header.next = node_list.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
list->last = node_list.last;
|
||||||
|
node_list.first->header.prev = last;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dblListInsert(WpDblList *list, WpDblNode *node, u64 index, u64 item_size) {
|
||||||
|
wpDebugAssert(list != NULL && node != NULL && (node->item) != NULL, "`list`, `node` and `node->item` should not be NULL");
|
||||||
|
dblListValidate(list, item_size);
|
||||||
|
dblListNodeValidate(list, node, item_size);
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
dblListPushFront(list, node, item_size);
|
||||||
|
return;
|
||||||
|
} else if (index == list->node_count) {
|
||||||
|
dblListPushBack(list, node, item_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpDblNode *dst_node = dblListGet(list, index, item_size);
|
||||||
|
if (!dst_node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpDblList node_list = nodeToList(node, item_size);
|
||||||
|
|
||||||
|
list->node_count += node_list.node_count;
|
||||||
|
|
||||||
|
WpDblNode *prev = dst_node->header.prev;
|
||||||
|
|
||||||
|
dst_node->header.prev = node_list.last;
|
||||||
|
prev->header.next = node_list.first;
|
||||||
|
|
||||||
|
node_list.first->header.prev = prev;
|
||||||
|
node_list.last->header.next = dst_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpDblNode *dblListPopFront(WpDblList *list, u64 item_size) {
|
||||||
|
wpDebugAssert(list != NULL, "`list` should not be NULL");
|
||||||
|
dblListValidate(list, item_size);
|
||||||
|
|
||||||
|
WpDblNode *output = NULL;
|
||||||
|
|
||||||
|
if (list->node_count == 0) {
|
||||||
|
goto RETURN_LIST_POP_FRONT;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = list->first;
|
||||||
|
|
||||||
|
if (list->node_count == 1) {
|
||||||
|
*list = (WpDblList){.magic = WP_DBL_LIST_MAGIC, .item_size = item_size};
|
||||||
|
goto RETURN_LIST_POP_FRONT;
|
||||||
|
}
|
||||||
|
|
||||||
|
--(list->node_count);
|
||||||
|
list->first = output->header.next;
|
||||||
|
|
||||||
|
output->header.prev = output->header.next = NULL;
|
||||||
|
|
||||||
|
RETURN_LIST_POP_FRONT:
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpDblNode *dblListPopBack(WpDblList *list, u64 item_size) {
|
||||||
|
wpDebugAssert(list != NULL, "`list` should not be NULL");
|
||||||
|
dblListValidate(list, item_size);
|
||||||
|
|
||||||
|
WpDblNode *output = NULL;
|
||||||
|
|
||||||
|
if (list->node_count == 0) {
|
||||||
|
goto RETURN_LIST_POP_BACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = list->last;
|
||||||
|
|
||||||
|
if (list->node_count == 1) {
|
||||||
|
*list = (WpDblList){.magic = WP_DBL_LIST_MAGIC, .item_size = item_size};
|
||||||
|
goto RETURN_LIST_POP_BACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
--(list->node_count);
|
||||||
|
list->last = output->header.prev;
|
||||||
|
|
||||||
|
output->header.prev = output->header.next = NULL;
|
||||||
|
|
||||||
|
RETURN_LIST_POP_BACK:
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
WpDblNode *dblListRemove(WpDblList *list, u64 index, u64 item_size) {
|
||||||
|
wpDebugAssert(list != NULL, "`list` should not be NULL");
|
||||||
|
dblListValidate(list, item_size);
|
||||||
|
|
||||||
|
WpDblNode *output = NULL;
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
output = dblListPopFront(list, item_size);
|
||||||
|
goto RETURN_LIST_REMOVE;
|
||||||
|
} else if (index == list->node_count) {
|
||||||
|
output = dblListPopBack(list, item_size);
|
||||||
|
goto RETURN_LIST_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
output = dblListGet(list, index, item_size);
|
||||||
|
if (!output) {
|
||||||
|
goto RETURN_LIST_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
output->header.prev->header.next = output->header.next;
|
||||||
|
output->header.next->header.prev = output->header.prev;
|
||||||
|
|
||||||
|
--(list->node_count);
|
||||||
|
|
||||||
|
output->header.prev = output->header.next = NULL;
|
||||||
|
|
||||||
|
RETURN_LIST_REMOVE:
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dblListEmpty(WpDblList *list, u64 item_size) {
|
||||||
|
wpDebugAssert(list != NULL, "`list` should not be NULL");
|
||||||
|
dblListValidate(list, item_size);
|
||||||
|
|
||||||
|
u64 count = list->node_count;
|
||||||
|
for (u64 i = 0; i < count; ++i) {
|
||||||
|
dblListPopBack(list, item_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_intern WpDblList nodeToList(WpDblNode *node, u64 item_size) {
|
||||||
|
WpDblList output = {
|
||||||
|
.magic = WP_DBL_LIST_MAGIC,
|
||||||
|
.first = node,
|
||||||
|
.last = node,
|
||||||
|
.node_count = 1,
|
||||||
|
.item_size = item_size,
|
||||||
|
};
|
||||||
|
|
||||||
|
while (output.first->header.prev != NULL) {
|
||||||
|
output.first = output.first->header.prev;
|
||||||
|
++(output.node_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (output.last->header.next != NULL) {
|
||||||
|
output.last = output.last->header.next;
|
||||||
|
++(output.node_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_intern inline void dblListValidate(const WpDblList *list, u64 item_size) {
|
||||||
|
wpRuntimeAssert(list->magic == WP_DBL_LIST_MAGIC, "`list` isn't a valid wp list type");
|
||||||
|
wpRuntimeAssert(list->item_size == item_size, "Invalid item provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_intern inline void dblListNodeValidate(const WpDblList *list, const WpDblNode *node, u64 item_size) {
|
||||||
|
wpRuntimeAssert(node->header.magic == WP_DBL_NODE_MAGIC, "`node` isn't a valid wp node type");
|
||||||
|
wpRuntimeAssert(list->item_size == node->header.item_size, "Mismatched `list` and `node` types");
|
||||||
|
wpRuntimeAssert(node->header.item_size == item_size, "Invalid item provided");
|
||||||
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#ifndef DBL_LIST_H
|
||||||
|
#define DBL_LIST_H
|
||||||
|
|
||||||
|
#include "../mem/allocator/mem_allocator.h"
|
||||||
|
#include "../../common/aliases/aliases.h"
|
||||||
|
#include "../../common/platform/platform.h"
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
BEGIN_C_LINKAGE
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
#define WP_DBL_LIST_MAGIC wpU64Const(0x57415f444c5354)
|
||||||
|
#define WP_DBL_NODE_MAGIC wpU64Const(0x57415f444e44)
|
||||||
|
|
||||||
|
typedef struct WpDblNode WpDblNode;
|
||||||
|
|
||||||
|
typedef struct WpDblNodeHeader {
|
||||||
|
u64 magic;
|
||||||
|
u64 item_size;
|
||||||
|
WpDblNode *prev;
|
||||||
|
WpDblNode *next;
|
||||||
|
} WpDblNodeHeader;
|
||||||
|
|
||||||
|
struct WpDblNode {
|
||||||
|
WpDblNodeHeader header;
|
||||||
|
void *item;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct WpDblList {
|
||||||
|
u64 magic;
|
||||||
|
u64 node_count;
|
||||||
|
u64 item_size;
|
||||||
|
WpDblNode *first;
|
||||||
|
WpDblNode *last;
|
||||||
|
} WpDblList;
|
||||||
|
|
||||||
|
// NOTE (Abdelrahman): WpDblList typedefs for readability
|
||||||
|
typedef WpDblList WpVoidPtrList;
|
||||||
|
typedef WpDblList WpC8List;
|
||||||
|
typedef WpDblList WpC16List;
|
||||||
|
typedef WpDblList WpC32List;
|
||||||
|
typedef WpDblList WpU8List;
|
||||||
|
typedef WpDblList WpU16List;
|
||||||
|
typedef WpDblList WpU32List;
|
||||||
|
typedef WpDblList WpU64List;
|
||||||
|
typedef WpDblList WpB8List;
|
||||||
|
typedef WpDblList WpB32List;
|
||||||
|
typedef WpDblList WpI8List;
|
||||||
|
typedef WpDblList WpI16List;
|
||||||
|
typedef WpDblList WpI32List;
|
||||||
|
typedef WpDblList WpI64List;
|
||||||
|
typedef WpDblList WpF32List;
|
||||||
|
typedef WpDblList WpF64List;
|
||||||
|
typedef WpDblList WpF128List;
|
||||||
|
typedef WpDblList WpUptrList;
|
||||||
|
typedef WpDblList WpIptrList;
|
||||||
|
typedef WpDblList WpStr8List;
|
||||||
|
|
||||||
|
// NOTE (Abdelrahman): WpDblNode typedefs for readability
|
||||||
|
typedef WpDblNode WpVoidPtrNode;
|
||||||
|
typedef WpDblNode WpC8Node;
|
||||||
|
typedef WpDblNode WpC16Node;
|
||||||
|
typedef WpDblNode WpC32Node;
|
||||||
|
typedef WpDblNode WpU8Node;
|
||||||
|
typedef WpDblNode WpU16Node;
|
||||||
|
typedef WpDblNode WpU32Node;
|
||||||
|
typedef WpDblNode WpU64Node;
|
||||||
|
typedef WpDblNode WpB8Node;
|
||||||
|
typedef WpDblNode WpB32Node;
|
||||||
|
typedef WpDblNode WpI8Node;
|
||||||
|
typedef WpDblNode WpI16Node;
|
||||||
|
typedef WpDblNode WpI32Node;
|
||||||
|
typedef WpDblNode WpI64Node;
|
||||||
|
typedef WpDblNode WpF32Node;
|
||||||
|
typedef WpDblNode WpF64Node;
|
||||||
|
typedef WpDblNode WpF128Node;
|
||||||
|
typedef WpDblNode WpUptrNode;
|
||||||
|
typedef WpDblNode WpIptrNode;
|
||||||
|
typedef WpDblNode WpStr8Node;
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
#define wpDblList(TYPE) \
|
||||||
|
WpDblList{WP_DBL_LIST_MAGIC, 0, sizeof(TYPE), nullptr, nullptr}
|
||||||
|
#define dblListNode(TYPE, ITEM_PTR) ([&]() { \
|
||||||
|
wp_persist WpDblNode node = { \
|
||||||
|
WpDblNodeHeader{WP_DBL_NODE_MAGIC, sizeof(TYPE), nullptr, nullptr}, \
|
||||||
|
ITEM_PTR, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
return &node; \
|
||||||
|
}())
|
||||||
|
#else
|
||||||
|
#define wpDblList(TYPE) ( \
|
||||||
|
(WpDblList){.magic = WP_DBL_LIST_MAGIC, .item_size = sizeof(TYPE)} \
|
||||||
|
)
|
||||||
|
#define dblListNode(TYPE, ITEM_PTR) ( \
|
||||||
|
&((WpDblNode){.header = {.magic = WP_DBL_NODE_MAGIC, .item_size = sizeof(TYPE)}, \
|
||||||
|
.item = ITEM_PTR}) \
|
||||||
|
)
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
#define wpDblListAlloc(TYPE, ALLOCATOR) \
|
||||||
|
(dblListAlloc(ALLOCATOR, sizeof(TYPE)))
|
||||||
|
#define wpDblListGet(TYPE, LIST_PTR, ITEM_INDEX) \
|
||||||
|
((TYPE *)(dblListGet(LIST_PTR, ITEM_INDEX, sizeof(TYPE))->item))
|
||||||
|
#define wpDblListGetNode(TYPE, LIST_PTR, ITEM_INDEX) \
|
||||||
|
(dblListGet(LIST_PTR, ITEM_INDEX, sizeof(TYPE)))
|
||||||
|
#define wpDblListGetNodeItem(TYPE, NODE_PTR) \
|
||||||
|
((TYPE *)( \
|
||||||
|
(NODE_PTR == NULL) ? \
|
||||||
|
NULL : \
|
||||||
|
(NODE_PTR)->item \
|
||||||
|
))
|
||||||
|
#define wpDblListPushFront(TYPE, LIST_PTR, ITEM_PTR) \
|
||||||
|
(dblListPushFront(LIST_PTR, dblListNode(TYPE, ITEM_PTR), sizeof(TYPE)))
|
||||||
|
#define wpDblListPushBack(TYPE, LIST_PTR, ITEM_PTR) \
|
||||||
|
(dblListPushBack(LIST_PTR, dblListNode(TYPE, ITEM_PTR), sizeof(TYPE)))
|
||||||
|
#define wpDblListInsert(TYPE, LIST_PTR, ITEM_PTR, ITEM_INDEX) \
|
||||||
|
(dblListInsert(LIST_PTR, dblListNode(TYPE, ITEM_PTR), \
|
||||||
|
ITEM_INDEX, sizeof(TYPE)))
|
||||||
|
#define wpDblListPushFrontAlloc(TYPE, ALLOCATOR, LIST_PTR, ITEM_PTR) \
|
||||||
|
(dblListPushFront(LIST_PTR, dblListNodeAlloc(ALLOCATOR, ITEM_PTR, sizeof(TYPE)), \
|
||||||
|
sizeof(TYPE)))
|
||||||
|
#define wpDblListPushBackAlloc(TYPE, ALLOCATOR, LIST_PTR, ITEM_PTR) \
|
||||||
|
(dblListPushBack(LIST_PTR, dblListNodeAlloc(ALLOCATOR, ITEM_PTR, sizeof(TYPE)), \
|
||||||
|
sizeof(TYPE)))
|
||||||
|
#define wpDblListInsertAlloc(TYPE, ALLOCATOR, LIST_PTR, ITEM_PTR, ITEM_INDEX) \
|
||||||
|
(dblListInsert(LIST_PTR, dblListNodeAlloc(ALLOCATOR, ITEM_PTR, sizeof(TYPE)), \
|
||||||
|
ITEM_INDEX, sizeof(TYPE)))
|
||||||
|
#define wpDblListPopFront(TYPE, LIST_PTR) \
|
||||||
|
((TYPE *)( \
|
||||||
|
(LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ? \
|
||||||
|
NULL : \
|
||||||
|
dblListPopFront(LIST_PTR, sizeof(TYPE))->item \
|
||||||
|
))
|
||||||
|
#define wpDblListPopBack(TYPE, LIST_PTR) \
|
||||||
|
((TYPE *)( \
|
||||||
|
(LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ? \
|
||||||
|
NULL : \
|
||||||
|
dblListPopBack(LIST_PTR, sizeof(TYPE))->item \
|
||||||
|
))
|
||||||
|
#define wpDblListRemove(TYPE, LIST_PTR, ITEM_INDEX) \
|
||||||
|
((TYPE *)( \
|
||||||
|
(LIST_PTR == NULL || (LIST_PTR)->node_count == 0 || ITEM_INDEX >= (LIST_PTR)->node_count) ? \
|
||||||
|
NULL : \
|
||||||
|
dblListRemove(LIST_PTR, ITEM_INDEX, sizeof(TYPE))->item \
|
||||||
|
))
|
||||||
|
#define wpDblListPopFrontNode(TYPE, LIST_PTR) \
|
||||||
|
( \
|
||||||
|
(LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ? \
|
||||||
|
NULL : \
|
||||||
|
dblListPopFront(LIST_PTR, sizeof(TYPE)) \
|
||||||
|
)
|
||||||
|
#define wpDblListPopBackNode(TYPE, LIST_PTR) \
|
||||||
|
( \
|
||||||
|
(LIST_PTR == NULL || (LIST_PTR)->node_count == 0) ? \
|
||||||
|
NULL : \
|
||||||
|
dblListPopBack(LIST_PTR, sizeof(TYPE)) \
|
||||||
|
)
|
||||||
|
#define wpDblListRemoveNode(TYPE, LIST_PTR, ITEM_INDEX) \
|
||||||
|
( \
|
||||||
|
(LIST_PTR == NULL || (LIST_PTR)->node_count == 0 || ITEM_INDEX >= (LIST_PTR)->node_count) ? \
|
||||||
|
NULL : \
|
||||||
|
dblListRemove(LIST_PTR, ITEM_INDEX, sizeof(TYPE)) \
|
||||||
|
)
|
||||||
|
#define wpDblListEmpty(TYPE, LIST_PTR) \
|
||||||
|
(dblListEmpty(LIST_PTR, sizeof(TYPE)))
|
||||||
|
|
||||||
|
WpDblList *dblListAlloc(const WpAllocator *allocator, u64 item_size);
|
||||||
|
WpDblNode *dblListNodeAlloc(const WpAllocator *allocator, void *item, u64 item_size);
|
||||||
|
WpDblNode *dblListGet(const WpDblList *list, u64 index, u64 item_size);
|
||||||
|
void dblListPushFront(WpDblList *list, WpDblNode *node, u64 item_size);
|
||||||
|
void dblListPushBack(WpDblList *list, WpDblNode *node, u64 item_size);
|
||||||
|
void dblListInsert(WpDblList *list, WpDblNode *node, u64 index, u64 item_size);
|
||||||
|
WpDblNode *dblListPopFront(WpDblList *list, u64 item_size);
|
||||||
|
WpDblNode *dblListPopBack(WpDblList *list, u64 item_size);
|
||||||
|
WpDblNode *dblListRemove(WpDblList *list, u64 index, u64 item_size);
|
||||||
|
void dblListEmpty(WpDblList *list, u64 item_size);
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
END_C_LINKAGE
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
#endif // !DBL_LIST_H
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#include "hasher.h"
|
||||||
|
#include "../../../common/assert/assert.h"
|
||||||
|
|
||||||
|
WpHasher wpCustomHasher(WpHashFunc func, void *ctx) {
|
||||||
|
return (WpHasher){
|
||||||
|
.func = func,
|
||||||
|
.ctx = (WpHasherCtx){
|
||||||
|
.type = WP_HASHER_CTX_USER_DEFINED,
|
||||||
|
.user = ctx,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
WpU128 wpHasherGetHash128(const WpHasher *hasher, WpU8Stream *stream) {
|
||||||
|
wpDebugAssert(hasher != NULL && stream != NULL, "hasher and stream shouldn't be NULL");
|
||||||
|
return hasher->func(stream, hasher->ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 wpHasherGetHash64(const WpHasher *hasher, WpU8Stream *stream) {
|
||||||
|
wpDebugAssert(hasher != NULL && stream != NULL, "hasher and stream shouldn't be NULL");
|
||||||
|
WpU128 result = hasher->func(stream, hasher->ctx);
|
||||||
|
return result.value[0] ^ result.value[1];
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#ifndef HASHER_H
|
||||||
|
#define HASHER_H
|
||||||
|
|
||||||
|
#include "../../stream/stream.h"
|
||||||
|
#include "../../../common/aliases/aliases.h"
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
BEGIN_C_LINKAGE
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
typedef struct WpU128 {
|
||||||
|
u64 value[2];
|
||||||
|
} WpU128;
|
||||||
|
|
||||||
|
typedef enum WpHasherCtxType {
|
||||||
|
WP_HASHER_CTX_MURMUR3,
|
||||||
|
WP_HASHER_CTX_SIPHASH,
|
||||||
|
WP_HASHER_CTX_USER_DEFINED,
|
||||||
|
|
||||||
|
COUNT_HASHER_CTX,
|
||||||
|
} WpHasherCtxType;
|
||||||
|
|
||||||
|
typedef struct WpSiphashCtx {
|
||||||
|
WpU128 key;
|
||||||
|
u64 compression;
|
||||||
|
u64 finalisation;
|
||||||
|
} WpSiphashCtx;
|
||||||
|
|
||||||
|
typedef struct WpHasherCtx {
|
||||||
|
WpHasherCtxType type;
|
||||||
|
union {
|
||||||
|
WpSiphashCtx siphash;
|
||||||
|
void *user;
|
||||||
|
u32 murmur3;
|
||||||
|
};
|
||||||
|
} WpHasherCtx;
|
||||||
|
|
||||||
|
typedef WpU128 (*WpHashFunc)(WpU8Stream *stream, WpHasherCtx ctx);
|
||||||
|
|
||||||
|
typedef struct WpHasher {
|
||||||
|
WpHashFunc func;
|
||||||
|
WpHasherCtx ctx;
|
||||||
|
} WpHasher;
|
||||||
|
|
||||||
|
WpHasher wpCustomHasher(WpHashFunc func, void *ctx);
|
||||||
|
WpU128 wpHasherGetHash128(const WpHasher *hasher, WpU8Stream *stream);
|
||||||
|
u64 wpHasherGetHash64(const WpHasher *hasher, WpU8Stream *stream);
|
||||||
|
|
||||||
|
#ifdef WP_PLATFORM_CPP
|
||||||
|
END_C_LINKAGE
|
||||||
|
#endif // !WP_PLATFORM_CPP
|
||||||
|
|
||||||
|
#endif // !HASHER_H
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
// vim:fileencoding=utf-8:foldmethod=marker
|
||||||
|
|
||||||
|
#include "murmur3.h"
|
||||||
|
#include "hasher.h"
|
||||||
|
#include "../../../common/aliases/aliases.h"
|
||||||
|
#include "../../../common/assert/assert.h"
|
||||||
|
#include "../../../common/misc/misc_utils.h"
|
||||||
|
#include "../../../prng/xorshift/xorshift.h"
|
||||||
|
|
||||||
|
wp_intern inline void validateMur3HasherCtx(const WpHasherCtx *ctx);
|
||||||
|
wp_intern inline u64 fmix64(u64 k);
|
||||||
|
|
||||||
|
WpHasher wpX64Mur3Hasher(void) {
|
||||||
|
WpXor256State state = wpPrngXorshiftInit();
|
||||||
|
u32 seed = (u32)wpPrngXorshift256(&state);
|
||||||
|
return wpX64Mur3HasherWithSeed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
WpHasher wpX64Mur3HasherWithSeed(u32 seed) {
|
||||||
|
return (WpHasher){
|
||||||
|
.func = wpX64Mur3GetHash128,
|
||||||
|
.ctx = (WpHasherCtx){
|
||||||
|
.type = WP_HASHER_CTX_MURMUR3,
|
||||||
|
.murmur3 = seed,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
WpU128 wpX64Mur3GetHash128(WpU8Stream *bytes, WpHasherCtx ctx) {
|
||||||
|
validateMur3HasherCtx(&ctx);
|
||||||
|
|
||||||
|
const i32 nblocks = (bytes->count * bytes->item_size) / 16;
|
||||||
|
|
||||||
|
u64 h1 = ctx.murmur3;
|
||||||
|
u64 h2 = ctx.murmur3;
|
||||||
|
|
||||||
|
const u64 c1 = wpU64Const(0x87c37b91114253d5);
|
||||||
|
const u64 c2 = wpU64Const(0x4cf5ad432745937f);
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// body
|
||||||
|
|
||||||
|
for(int i = 0; i < nblocks; ++i) {
|
||||||
|
u64 k1 = *((u64 *)wpStreamConsumeCount(u8, bytes, sizeof(u64)));
|
||||||
|
u64 k2 = *((u64 *)wpStreamConsumeCount(u8, bytes, sizeof(u64)));
|
||||||
|
|
||||||
|
k1 *= c1;
|
||||||
|
k1 = wpMiscUtilsRotl64(k1, 31);
|
||||||
|
k1 *= c2;
|
||||||
|
h1 ^= k1;
|
||||||
|
|
||||||
|
h1 = wpMiscUtilsRotl64(h1, 27);
|
||||||
|
h1 += h2;
|
||||||
|
h1 = h1 * 5 +0x52dce729;
|
||||||
|
|
||||||
|
k2 *= c2;
|
||||||
|
k2 = wpMiscUtilsRotl64(k2, 33);
|
||||||
|
k2 *= c1;
|
||||||
|
h2 ^= k2;
|
||||||
|
|
||||||
|
h2 = wpMiscUtilsRotl64(h2, 31);
|
||||||
|
h2 += h1;
|
||||||
|
h2 = h2*5+0x38495ab5;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------
|
||||||
|
// tail
|
||||||
|
|
||||||
|
const u8 *tail = wpStreamConsumeCount(u8, bytes, bytes->count - bytes->position);
|
||||||
|
|
||||||
|
u64 k1 = 0;
|
||||||
|
u64 k2 = 0;
|
||||||
|
|
||||||