Compare commits

..

204 Commits

Author SHA1 Message Date
abdelrahman 66acbaad9e Release v2.5.0
Release / release (push) Successful in 3s
2026-09-24 00:53:36 +01:00
abdelrahman 180292289d Add hash map implementation
Release / release (push) Successful in 3s
2026-09-24 00:52:43 +01:00
abdelrahman 68640dc5e2 Add hash set implementation
Release / release (push) Successful in 4s
2026-09-23 22:34:23 +01:00
Abdelrahman Said 20e890964f Add hash table for default hasher
Release / release (push) Successful in 3s
2026-09-23 00:21:51 +01:00
Abdelrahman Said c215486442 Restructure hasher implementation
Release / release (push) Successful in 3s
2026-09-23 00:02:37 +01:00
Abdelrahman Said 9881c0ac9a Update hasher data to context
Release / release (push) Successful in 4s
2026-09-22 21:47:46 +01:00
abdelrahman 17be4841e6 Update siphash comment
Release / release (push) Successful in 3s
2026-09-22 00:57:46 +01:00
abdelrahman 421728c137 Add internal hash table implementation
Release / release (push) Successful in 4s
2026-09-21 22:15:54 +01:00
abdelrahman b014ffa3b1 Add swap utility 2026-09-21 22:03:54 +01:00
abdelrahman 8da18774f0 Update array remove swap to handle deleting last index 2026-09-21 22:03:40 +01:00
abdelrahman ac5baef3b8 Switch array NULL pointer checks to debug asserts
Release / release (push) Successful in 2s
2026-09-21 07:41:46 +01:00
abdelrahman e90229db7a Reformat
Release / release (push) Successful in 3s
2026-09-21 07:38:16 +01:00
abdelrahman 4a92f55f17 Reformat
Release / release (push) Successful in 3s
2026-09-20 21:44:48 +01:00
abdelrahman 3c81feedbb Add wpArrayRemoveSwapEnd
Release / release (push) Successful in 3s
2026-09-19 22:41:10 +01:00
Abdelrahman Said 9029e21a4f Update str8 printf spec
Release / release (push) Successful in 4s
2026-09-19 11:37:55 +01:00
abdelrahman 605c1ddfae Add wpArrayZero
Release / release (push) Successful in 3s
2026-09-16 01:28:11 +01:00
Abdelrahman Said f73a870a34 Convert hasher to interface and remove spihash64
Release / release (push) Successful in 3s
2026-09-16 01:11:24 +01:00
Abdelrahman Said 8b3b1816b3 Reformat
Release / release (push) Successful in 3s
2026-09-16 00:18:01 +01:00
Abdelrahman Said f46c3ab38a Extract splitmix64 PRNG
Release / release (push) Successful in 4s
2026-09-16 00:12:50 +01:00
abdelrahman 9226891bb9 wpArrayClear memsets members to 0
Release / release (push) Successful in 3s
2026-09-13 21:14:11 +01:00
abdelrahman 6fdda743fb Fix ArenaStorageType label
Release / release (push) Successful in 2s
2026-09-13 02:32:59 +01:00
abdelrahman 575e0a52b1 Add labels to all typedef'd structs and enums
Release / release (push) Successful in 4s
2026-09-13 02:31:46 +01:00
abdelrahman 5df1a105c7 Update C++ hasher initialisers
Release / release (push) Successful in 2s
2026-09-13 00:39:52 +01:00
abdelrahman 34a1b2c1db Fix hasher initialisers
Release / release (push) Successful in 2s
2026-09-13 00:25:03 +01:00
abdelrahman f3a8045ecc Update hasher
Release / release (push) Successful in 3s
2026-09-13 00:20:18 +01:00
abdelrahman 6a3326e9a5 Add scratchpad to .gitignore
Release / release (push) Successful in 3s
2026-09-12 21:14:57 +01:00
Abdelrahman Said 222eb006e6 Add extra reference for robin hood hash tables
Release / release (push) Successful in 3s
2026-09-06 22:53:34 +01:00
abdelrahman 5e67c596b9 Remove all uses of names starting with underscore
Release / release (push) Successful in 3s
2026-09-06 18:15:23 +01:00
abdelrahman 9055c5c825 Add more xorshift utilities
Release / release (push) Successful in 2s
2026-09-06 13:40:13 +01:00
abdelrahman c83f9333de Update xorshift PRNG
Release / release (push) Successful in 4s
2026-09-06 13:31:37 +01:00
Abdelrahman Said 07e0034783 Update CHANGELOG
Release / release (push) Successful in 2s
2026-09-02 00:13:18 +01:00
Abdelrahman Said e015dfceda Add aliases for integer constants
Release / release (push) Successful in 3s
2026-09-02 00:11:23 +01:00
Abdelrahman Said 60467d56b9 Add paper for robin hood hashing
Release / release (push) Successful in 4s
2026-09-01 21:35:58 +01:00
Abdelrahman Said 2e943affb7 Add reference for open addressing/separate chaining
Release / release (push) Successful in 2s
2026-08-31 23:43:48 +01:00
Abdelrahman Said aa48942d91 Get most recent ankerl implementation
Release / release (push) Successful in 3s
2026-08-31 23:34:56 +01:00
abdelrahman 5ae4d7d6d1 Add documents
Release / release (push) Successful in 2s
2026-08-31 21:16:21 +01:00
abdelrahman 900c16415b Add implementation for siphash
Release / release (push) Successful in 4s
2026-08-31 02:15:01 +01:00
abdelrahman bca2e720ad Add Murmur3 validation
Release / release (push) Successful in 3s
2026-08-30 23:56:50 +01:00
abdelrahman 850f709a10 Update Murmur3 hasher IO
Release / release (push) Successful in 3s
2026-08-30 23:50:22 +01:00
abdelrahman 55ff77c4ea Add implementation for the murmur3 x64_128 hash
Release / release (push) Successful in 3s
2026-08-30 22:03:28 +01:00
abdelrahman d5578f009a Update stream utilities and fix peek logic
Release / release (push) Successful in 4s
2026-08-30 17:13:32 +01:00
abdelrahman b3830683f6 Print changelog contents to stdout
Release / release (push) Successful in 3s
2026-08-30 14:34:21 +01:00
abdelrahman 0060c51c12 Only extract changelog for new releases
Release / release (push) Successful in 3s
2026-08-30 14:31:11 +01:00
abdelrahman 23613d16b7 Update release workflow
Release / release (push) Successful in 4s
2026-08-30 14:20:47 +01:00
abdelrahman 8cd1d56b4a Save extracted changelog in workspace 2026-08-30 14:20:40 +01:00
abdelrahman 6b8fe820aa Remove empty line for testing
Release / release (push) Failing after 1s
2026-08-30 13:49:15 +01:00
abdelrahman 2554810306 Use python3 in release workflow
Release / release (push) Failing after 3s
2026-08-30 13:48:24 +01:00
abdelrahman 08029141d4 Extract version changelog in release process
Release / release (push) Failing after 4s
2026-08-30 13:45:40 +01:00
abdelrahman a1123b361d Include CHANGELOG file in release packages 2026-08-30 13:45:21 +01:00
abdelrahman f4d2e551b5 Add CHANGELOG 2026-08-30 12:39:03 +01:00
abdelrahman 1bbe124e3e Update VERSION
Release / release (push) Successful in 5s
2026-08-30 02:32:35 +01:00
abdelrahman c1b2e29c07 Add stream structure and utilities 2026-08-30 02:21:08 +01:00
abdelrahman a51686c8f0 Add compiler detection macros 2026-08-30 00:15:36 +01:00
Abdelrahman Said f599b5f425 Reformat
Release / release (push) Successful in 3s
2026-08-22 20:32:58 +01:00
abdelrahman 0063bb8641 Fix missing log in Makefile
Release / release (push) Successful in 4s
2026-08-17 05:24:10 +01:00
abdelrahman 2a20362b44 Add free array and queue utils
Release / release (push) Successful in 7s
2026-07-05 20:51:17 +01:00
Abdelrahman Said 02372cb644 Fix mem op supported bug
Release / release (push) Successful in 6s
2026-06-28 21:46:36 +01:00
Abdelrahman Said 5804c5d345 Add function to check memory op support for allocators
Release / release (push) Successful in 7s
2026-06-28 14:03:58 +01:00
Abdelrahman Said ffdeb5fdc2 Reformat
Release / release (push) Successful in 6s
2026-06-27 22:01:38 +01:00
abdelrahman a998f6b981 Standardize naming conventions (#12)
Release / release (push) Successful in 8s
## Summary

Standardize naming conventions across the entire wizapp-stdlib codebase by replacing inconsistent prefixes and snake_case with a unified `wp` prefix + CamelCase scheme.

## Changes

### Naming convention applied

| Pattern | Before | After |
|---|---|---|
| Public functions | `wapp_module_function` | `wpModuleFunction` |
| Public types | `GenericXxx`, bare `Xxx` | `WpXxx` |
| Constants / enum values | `WAPP_XXX`, `SHELL_XXX` | `WP_XXX`, `WP_SHELL_XXX` |
| Internal functions | `_module_function` | `_moduleFunction` |
| Storage-class macros | `wapp_extern`, `wapp_intern` | `wp_extern`, `wp_intern` |

### Modules affected

All 20 modules were renamed: `arena`, `array`, `dbl_list`, `queue`, `str8`, `mem_allocator`, `mem_utils`, `mem_os`, `file`, `cpath`, `log`, `shell_commander`, `shell_termcolour`, `shell_utils`, `prng/xorshift`, `uuid`, `tester`, `aliases`, `assert`, `misc_utils`, `platform` — plus their test files.

### Backward compatibility

Added `src/oldnames.h` with `#define OLD_NAME NEW_NAME` for every renamed symbol, organized by module under Constants → Types → Functions sections. Existing code that includes this file will compile without changes.

Reviewed-on: #12
Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com>
Co-committed-by: Abdelrahman <said.abdelrahman89@gmail.com>
2026-06-26 17:17:27 +00:00
Abdelrahman Said ea689e7357 Bump version number
Release / release (push) Successful in 8s
2026-06-23 23:48:44 +01:00
Abdelrahman Said a97b8f742a Update logging 2026-06-23 23:48:19 +01:00
Abdelrahman Said 99902cfa99 Add utilities to write strings to file 2026-06-23 23:48:02 +01:00
abdelrahman 7a314a0038 Fix include paths
Release / release (push) Successful in 4s
2026-05-25 17:41:52 +01:00
abdelrahman f073e5a21f v1.1.1
Release / release (push) Successful in 6s
2026-05-25 17:35:05 +01:00
abdelrahman 536a1a3b01 v1.1.1
Release / release (push) Has been cancelled
2026-05-25 16:38:40 +01:00
abdelrahman a6b697dd0e Pad logger struct for MSVC
Release / release (push) Successful in 3s
2026-05-17 17:46:50 +01:00
abdelrahman c67a448d00 Add basic logging functionality
Release / release (push) Successful in 6s
2026-05-17 18:40:10 +01:00
abdelrahman 2e5163ba33 Implement functions to get stdin, stdout & stderr
Release / release (push) Successful in 2s
2026-05-17 12:21:23 +01:00
abdelrahman 515493b963 Fix queue MSVC errors 2026-05-17 12:20:47 +01:00
abdelrahman 8061692801 Add standard streams
Release / release (push) Successful in 3s
2026-05-17 11:13:40 +01:00
abdelrahman 70997f091f Avoid wrapping whole Makefile in bear
Release / release (push) Successful in 6s
2026-05-17 10:17:54 +01:00
abdelrahman f61cb3cae0 Minor shell commander tweaks 2026-05-17 10:17:36 +01:00
abdelrahman 946bcc9b59 Replace extern 2026-05-17 09:44:11 +01:00
abdelrahman 7ffebe7dce Fix missing semi-colon
Release / release (push) Successful in 3s
2026-05-10 02:22:46 +01:00
abdelrahman 9c727950d8 Call UUID lambdas
Release / release (push) Successful in 3s
2026-05-10 02:21:38 +01:00
abdelrahman c7c4f88866 Fix wapp_uuid_gen_uuid4 for C++
Release / release (push) Successful in 5s
2026-05-10 02:19:34 +01:00
abdelrahman 6346765e32 Fix UUID for C++
Release / release (push) Successful in 5s
2026-05-10 02:13:41 +01:00
Abdelrahman Said 270dbfe5ca Update README
Release / release (push) Successful in 7s
2026-03-09 22:32:04 +00:00
Abdelrahman Said 7fd808fe7b Add LICENSE file
Release / release (push) Successful in 11s
2026-03-09 22:17:05 +00:00
abdelrahman 95deda1f59 v1.0.0
Release / release (push) Successful in 3s
2026-03-08 23:56:50 +00:00
abdelrahman 3f3d1e1e5d Ensure all tags are fetched when checking out the repo
Release / release (push) Successful in 3s
2026-03-08 23:55:19 +00:00
abdelrahman 877b8e9a04 Update release workflow
Release / release (push) Failing after 2s
2026-03-08 23:52:05 +00:00
abdelrahman 4ce59f537c Trigger workflow one more time
Release / release (push) Failing after 3s
2026-03-08 23:44:02 +00:00
abdelrahman 7d13bde13b Trigger workflow 2026-03-08 23:40:18 +00:00
abdelrahman 3aa792a620 Add release workflow 2026-03-08 23:34:09 +00:00
abdelrahman c0b667847c Update package script 2026-03-08 23:33:59 +00:00
abdelrahman ce2956f072 Update package script 2026-03-08 23:14:16 +00:00
abdelrahman a1182016af Add VERSION 2026-03-08 21:38:47 +00:00
abdelrahman 59423e294a Add packaging script 2026-03-08 21:38:41 +00:00
abdelrahman ef5c9376a9 Fix mistake when building with no runtime assert 2026-03-08 20:22:21 +00:00
abdelrahman eeed101228 Move large file source def to code 2026-03-08 13:10:17 +00:00
abdelrahman 6b88d7e3fe Add TODO for updating UUID implementation 2026-02-09 00:02:21 +00:00
abdelrahman 58dab46902 Revert "Update uuid C++ implementation"
This reverts commit 1cdb08a81a.
2026-02-08 23:57:09 +00:00
abdelrahman 269ee5d9ab Revert "Update uuid C++ implementation"
This reverts commit 610df6869f.
2026-02-08 23:57:05 +00:00
abdelrahman 610df6869f Update uuid C++ implementation 2026-02-08 23:53:30 +00:00
abdelrahman 1cdb08a81a Update uuid C++ implementation 2026-02-08 23:48:02 +00:00
abdelrahman 8d9ef89329 Fix bug with queue 2026-02-08 19:16:36 +00:00
Abdelrahman Said 3d3452f523 Update queue implementation to use ring buffer 2026-01-24 20:46:05 +00:00
Abdelrahman Said 8e41b627bc Add function to create array from preallocated buffer 2026-01-24 20:45:52 +00:00
Abdelrahman Said 7a54c28c0f Update array allocation functions 2026-01-24 12:17:15 +00:00
Abdelrahman Said bd659e64fc Ensure array count is set correctly when allocating 2026-01-22 06:09:43 +00:00
Abdelrahman Said 21ac756fad Pass init flags to allocating array utilities 2026-01-19 06:09:36 +00:00
Abdelrahman Said 243f04c0ca Handle lseek64 not existing on macOS 2026-01-19 06:00:09 +00:00
abdelrahman 4cc8cb3d25 Fix MSVC errors 2026-01-11 23:48:35 +00:00
abdelrahman a9f5b9c3c6 Add queue implementation 2026-01-11 23:46:23 +00:00
abdelrahman 9af9cedd51 Add pointer offset utility 2026-01-11 23:46:12 +00:00
abdelrahman 1e536cc3ba Rename array alloc size utility 2026-01-11 21:58:49 +00:00
abdelrahman e6f31e4f7b Update dbl list utilities 2026-01-11 21:46:00 +00:00
abdelrahman 6cd3c6f596 Add utility to get node item 2026-01-11 21:31:55 +00:00
abdelrahman 5a504c6791 Add extra utilities for dbl list 2026-01-11 21:26:17 +00:00
abdelrahman a4492cf8e8 Switch array fill to flags 2026-01-11 20:17:09 +00:00
abdelrahman ce76ac1e7c Add utility to calculate allocation size for array 2026-01-11 19:44:22 +00:00
abdelrahman cff418b9e9 Simplify dbl list API (#11)
Reviewed-on: #11
Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com>
Co-committed-by: Abdelrahman <said.abdelrahman89@gmail.com>
2026-01-11 18:22:54 +00:00
abdelrahman b88cb71aa8 Update array and dbl list macros 2026-01-05 03:57:50 +00:00
abdelrahman 8efcf14462 Switch numerical aliases to typedefs 2026-01-04 23:10:58 +00:00
abdelrahman f383fbb43e Reformat 2026-01-04 16:04:09 +00:00
abdelrahman 24069529c3 Add temp arena support 2026-01-04 01:21:42 +00:00
abdelrahman d2b4ec2052 Rename dbl list labels 2026-01-03 20:20:40 +00:00
abdelrahman 0a761eef05 Reformat 2026-01-03 19:53:43 +00:00
abdelrahman 821406315e Reformat 2026-01-03 19:51:26 +00:00
abdelrahman 8adbc1f841 Reformat 2026-01-03 19:17:10 +00:00
abdelrahman 1f3df20b7d Reformat 2026-01-03 19:13:42 +00:00
abdelrahman 458046a5d0 Reformat 2026-01-03 18:51:30 +00:00
abdelrahman 6d4a72aff9 Fix MSVC errors 2026-01-03 18:45:54 +00:00
abdelrahman 326265722e Add support for initialising arena with backing buffer 2026-01-03 18:36:30 +00:00
abdelrahman 83b879a180 Always reserve padding for structs that need it 2026-01-03 17:04:36 +00:00
abdelrahman b372447a46 Implement Windows file IO 2026-01-03 03:15:25 +00:00
abdelrahman fac9cb57eb Update windows include 2026-01-03 03:15:03 +00:00
abdelrahman abad2fa02a Add padding to stack array 2026-01-03 03:14:53 +00:00
abdelrahman 576699996f Update POSIX file IO 2026-01-03 03:13:42 +00:00
abdelrahman c2ca99cac4 Implement POSIX file IO 2026-01-02 22:57:12 +00:00
abdelrahman a07f05d1a3 Use 64 bit offsets for file seeking 2026-01-02 22:56:26 +00:00
abdelrahman ebf16c6ba3 Move is_power_of_two to misc_utils 2026-01-02 19:19:25 +00:00
abdelrahman 91138f9239 Reformat 2026-01-02 19:03:19 +00:00
abdelrahman ae8cb2e473 Add function to rename file 2026-01-02 19:02:17 +00:00
abdelrahman f5c2ed89a4 Add tests for file IO API 2026-01-02 18:56:19 +00:00
abdelrahman 989a5f60c4 Update file IO API 2026-01-02 18:56:09 +00:00
abdelrahman 659a3e457c Reformat 2026-01-02 17:17:25 +00:00
abdelrahman c2a156e256 Remove array structs (#10)
Reviewed-on: #10
2026-01-02 16:50:52 +00:00
abdelrahman f3ee1ee468 Unify struct names and tags 2026-01-02 14:00:00 +00:00
abdelrahman 7be1b42107 Reformat tests 2026-01-02 01:12:01 +00:00
abdelrahman 41cac2ef7f Reformat os 2026-01-02 00:49:21 +00:00
abdelrahman 24716505be Reformat prng 2026-01-02 00:38:44 +00:00
abdelrahman 7e21961943 Reformat uuid 2026-01-02 00:36:05 +00:00
abdelrahman 6f27d5ea91 Reformat common 2026-01-02 00:30:55 +00:00
abdelrahman 54691a8ede Reformat base 2026-01-02 00:29:24 +00:00
abdelrahman ab91bb2c9e Update Makefile 2025-12-29 23:40:49 +00:00
abdelrahman 52303c81ae Rename os memory functions 2025-12-29 23:29:40 +00:00
abdelrahman 2372ae6675 Remove inline 2025-12-29 23:13:46 +00:00
abdelrahman ad2de98093 Switch to using tabs instead of spaces (#9)
Reviewed-on: #9
2025-12-29 22:46:52 +00:00
abdelrahman aa6ef1ec2c Reformat 2025-12-29 15:44:34 +00:00
abdelrahman 5e939a7158 Add option to fill array when allocating 2025-12-28 18:49:27 +00:00
abdelrahman 1cbb148a1f Add ability to fill array with capacity 2025-12-28 18:31:49 +00:00
abdelrahman a065dfb573 Expand data size utilities 2025-12-24 08:53:12 +00:00
abdelrahman 3904b3a1ee Node validation checks against list 2025-12-17 05:20:27 +00:00
abdelrahman d15022603f Refactor array validation to function 2025-12-17 04:34:49 +00:00
abdelrahman debf6ed8eb Add vim info 2025-12-17 04:29:26 +00:00
abdelrahman 6c5352e9de Fix _dbl_list_pop_front bug 2025-12-17 04:23:57 +00:00
abdelrahman 4ea30f0762 No codegen doubly-linked list (#8)
Reviewed-on: #8
Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com>
Co-committed-by: Abdelrahman <said.abdelrahman89@gmail.com>
2025-12-17 03:53:13 +00:00
abdelrahman 4b95a681d4 Restructure the library layers 2025-12-16 17:36:50 +00:00
abdelrahman c0d901d7e9 Build and run C++ tests on MSVC 2025-12-15 23:40:30 +00:00
abdelrahman 31727dd6e7 Update va_arg_count C++ utility to support MSVC 2025-12-15 23:40:03 +00:00
abdelrahman af56a5be3f Reformat 2025-12-15 21:51:46 +00:00
abdelrahman e16abc2459 Fix MSVC errors 2025-12-15 21:48:30 +00:00
abdelrahman 05b7cfbbea Confirm WAPP_PLATFORM_C is defined before checking version 2025-12-15 21:17:10 +00:00
abdelrahman a5928b03c9 Ensure spaces are escaped in Makefile 2025-12-15 20:19:05 +00:00
abdelrahman a9c6b8ba72 Fix MSVC Spectre warnings 2025-12-15 20:06:34 +00:00
abdelrahman c6560ab7f5 Add windows pragma to slience warning in runtime assert 2025-12-15 19:54:04 +00:00
abdelrahman 04858b76c0 Add WIN32_LEAN_AND_MEAN 2025-12-15 19:53:20 +00:00
abdelrahman e3c1283017 Cast to avoid MSVC warnings 2025-12-15 19:48:58 +00:00
abdelrahman b55a3ff5d8 Update WUUID macros 2025-12-15 15:16:17 +00:00
abdelrahman 50b6a0b0f7 Change UUID to WUUID
Windows defines a UUID struct that causes clashes
2025-12-15 15:13:56 +00:00
abdelrahman 6b7421c25d Update README 2025-12-15 00:28:16 +00:00
abdelrahman ec0ab2170c Fix file reading 2025-12-15 00:09:48 +00:00
abdelrahman efadbf5fc2 Restore array structs but with no codegen (#7)
Reviewed-on: #7
2025-12-14 23:48:13 +00:00
abdelrahman 056dbbf3d7 Fix bug with C++ array_pop 2025-12-14 03:06:50 +00:00
abdelrahman b11ee05df4 Fix C++ array_pop 2025-12-14 02:31:25 +00:00
abdelrahman de9ce5791a Arrays without code generation (#6)
Co-authored-by: Abdelrahman Said <said.abdelrahman@flawlessai.com>
Reviewed-on: #6
Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com>
Co-committed-by: Abdelrahman <said.abdelrahman89@gmail.com>
2025-12-14 01:47:43 +00:00
abdelrahman 331f7e5e4e Remove build.c 2025-12-13 02:34:15 +00:00
abdelrahman ae7a8d4bab Fix installation prefix 2025-12-13 02:23:34 +00:00
abdelrahman 9c5b95c229 Change boolean size to 1 byte 2025-12-07 12:17:40 +00:00
Abdelrahman Said 432e4a48d5 Update Makefile to enable users to define per build type flags 2025-11-30 20:58:53 +00:00
abdelrahman 1bdc1ad955 Fix array pop for C++ pointer types 2025-11-08 20:45:41 +00:00
abdelrahman 0a00721c10 Add codegen as dependency for install target 2025-11-08 00:19:07 +00:00
abdelrahman 725955cb2e Allow user to pass code generation input to Makefile 2025-11-08 00:06:48 +00:00
abdelrahman 0354c7b485 Prefix static macros 2025-10-18 16:17:13 +01:00
abdelrahman 9f32891bbc Add test for str8 array 2025-09-28 00:05:17 +01:00
abdelrahman b3ebff3635 Remove ABS_INSTALL_PREFIX 2025-09-20 15:43:36 +01:00
abdelrahman 14bd6ce5fd File utilities and datatype implementation for a C-based code generator (#5)
Co-authored-by: Abdelrahman Said <said.abdelrahman@flawlessai.com>
Reviewed-on: #5
Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com>
Co-committed-by: Abdelrahman <said.abdelrahman89@gmail.com>
2025-09-20 13:48:08 +00:00
abdelrahman 09e96f8112 Update Makefile 2025-09-06 21:27:17 +01:00
Abdelrahman Said 9cbd0b29ef Fix build on macOS 2025-09-01 22:10:41 +01:00
abdelrahman 26fd329caa Update codegen to support accepting JSON file as input 2025-08-31 20:56:49 +01:00
abdelrahman 1e224702a3 Add default target as one of the available targets when make help is run 2025-08-31 14:21:55 +01:00
abdelrahman 74cca183e0 Make all the default target 2025-08-31 14:20:44 +01:00
abdelrahman e26bf613a5 Add support for release builds with debug info and using lib externally 2025-08-31 14:17:04 +01:00
Abdelrahman Said 81e3ab2c67 Switch to using intercept build since OSX [SIP](https://en.wikipedia.org/wiki/System_Integrity_Protection) stop bear from working 2025-08-16 13:25:38 +01:00
Abdelrahman Said 8ec0757b34 Add .venv to gitignore 2025-08-16 13:25:28 +01:00
Abdelrahman Said eb98de7c2b Unify build flags 2025-08-16 13:16:21 +01:00
abdelrahman d3fccd61b5 Reintroduce C++ support and add usage tests for C++ (#4)
Reviewed-on: #4
Co-authored-by: Abdelrahman <said.abdelrahman89@gmail.com>
Co-committed-by: Abdelrahman <said.abdelrahman89@gmail.com>
2025-08-10 22:33:40 +00:00
abdelrahman 011083ab83 Replace bool, true and false with aliases 2025-08-09 22:38:03 +01:00
abdelrahman b8c548ee4b Add static, runtime and debug assert utilities 2025-08-09 22:37:55 +01:00
abdelrahman 75be2316e0 Add vim encoding and folding marker 2025-08-09 14:35:28 +01:00
Abdelrahman Said d452225d02 Don't use uchar.h on macos 2025-05-20 16:52:12 +01:00
218 changed files with 13462 additions and 12548 deletions
+99
View File
@@ -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"
+3 -1
View File
@@ -1,5 +1,6 @@
.cache
.vscode
.venv
test
test.*
*.dSYM
@@ -8,6 +9,7 @@ test.*
*.obj
compile_commands.json
libwapp-build
libwapp.so
dist
scratchpad
*.vs
__pycache__
+76
View File
@@ -0,0 +1,76 @@
# 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]
## [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`
+201
View File
@@ -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.
+162 -44
View File
@@ -1,22 +1,52 @@
CC = clang
BUILD_TYPE = debug
CFLAGS = -Wall -Wextra -Werror -pedantic -std=gnu11 -Isrc
LIBFLAGS = -fPIC -shared
KERNEL = $(shell uname -s)
MACHINE = $(shell uname -m)
PLATFORM = $(KERNEL)_$(MACHINE)
TEST_INCLUDE = -Isrc $(shell find tests -type d | xargs -I{} echo -n "-I{} ")
TEST_SRC = src/wapp.c $(shell find tests -type f -name "*.c" | xargs -I{} echo -n "{} ")
BUILD_DIR = libwapp-build/$(PLATFORM)-$(BUILD_TYPE)
LIB_OUT = $(BUILD_DIR)/libwapp.so
TEST_OUT = $(BUILD_DIR)/wapptest
.PHONY: help full base os prng testing uuid all clean builddir build-test run-test install build-lib
ifeq ($(BUILD_TYPE),debug)
CFLAGS += -g -fsanitize=address,undefined
else ifeq ($(BUILD_TYPE),release)
CFLAGS += -O3
# 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' or 'release')
$(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)
@@ -24,43 +54,131 @@ ifeq ($(CC),gcc)
export ASAN_OPTIONS=verify_asan_link_order=0
endif
.PHONY: all clean builddir build-test run-test codegen build-lib full prng testing uuid core primitives
# 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
all: clean builddir codegen run-test full
ECHO_E = echo -e
ifeq ($(KERNEL), Darwin)
ECHO_E = echo
endif
clean:
@rm -rf $(BUILD_DIR)
all: clean builddir run-c-test full run-cc-test
builddir:
@mkdir -p $(BUILD_DIR)
build-test:
$(CC) $(CFLAGS) $(TEST_INCLUDE) $(TEST_SRC) -o $(TEST_OUT)
run-test: build-test
@$(TEST_OUT)
@rm $(TEST_OUT)
codegen:
python3 -m codegen
build-lib:
$(CC) $(CFLAGS) $(LIBFLAGS) $(LIB_SRC) -o $(LIB_OUT)
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: build-lib
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: build-lib
prng: INCLUDES = common prng
prng: install
testing: LIB_SRC = src/testing/wapp_testing.c
testing: build-lib
testing: INCLUDES = common os testing
testing: install
uuid: LIB_SRC = src/uuid/wapp_uuid.c
uuid: build-lib
uuid: INCLUDES = common base prng
uuid: install
core: LIB_SRC = src/core/wapp_core.c
core: build-lib
clean:
@rm -rf "$(BUILD_DIR)"
@rm -rf "$(INCLUDE_INSTALL)"
@rm -f "$(LIB_INSTALL)/$(LIB_STATIC_NAME)"
primitives: LIB_SRC = src/core/wapp_primitives.c
primitives: build-lib
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)"
+3 -1
View File
@@ -1,3 +1,5 @@
# Wizard Apprentice Standard Library
A collection of useful 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
View File
@@ -0,0 +1 @@
2.5.0
+43 -5
View File
@@ -1,13 +1,45 @@
#!/bin/bash
BUILD_TYPE="debug"
# 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
--release)
BUILD_TYPE="release"
shift
-b|--build-type)
BUILD_TYPE="$2"
shift 2
;;
-h|--help)
print_usage
exit 0
;;
*|-*|--*)
rest=("$@")
@@ -17,4 +49,10 @@ while [[ $# > 0 ]];do
esac
done
bear -- make BUILD_TYPE=$BUILD_TYPE $ARGS
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
+28 -19
View File
@@ -3,9 +3,12 @@ Param(
)
$Compiler = "cl.exe"
$Linker = "lib.exe"
$GeneralFlags = "/Wall /WX /wd4996 /wd4464 /wd5105 /std:c11"
$LibraryFlags = "/LD"
$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
@@ -15,14 +18,15 @@ $IncludeDirs = "/I src"
$SrcFiles = "src/wapp.c"
$TestIncludeDirs = Get-ChildItem -Path tests -Recurse -Directory -ErrorAction SilentlyContinue -Force | %{$("/I " + '"' + $_.FullName + '"')}
$TestSrcFiles = Get-ChildItem -Path tests -Recurse -Filter *.c -ErrorAction SilentlyContinue -Force | %{$('"' + $_.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"
$BuildType = "Release"
} Else {
$GeneralFlags += " /Zi /Od /fsanitize=address"
$BuildType = "debug"
$BuildType = "Debug"
}
$BuildDir = "./libwapp-build/${Platform}-${BuildType}"
@@ -30,12 +34,15 @@ $ObjDir = "$BuildDir/objects"
$OutDir = "$BuildDir/output"
$TestsDir = "$BuildDir/tests"
$OutBasename = "libwapp"
$LibOutput = "$OutDir/libwapp.lib"
$Objects = "/Fo:$ObjDir/"
$Outputs = "/Fd:$OutDir/$OutBasename /Fe:$OutDir/$OutBasename"
$LibOutputFlags = "/OUT:$LibOutput"
$TestOutBasename = "wapptest"
$TestOutputs = "/Fo:$TestsDir/ /Fe:$TestsDir/$TestOutBasename"
$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
@@ -45,21 +52,23 @@ mkdir -p $ObjDir > $null
mkdir -p $OutDir > $null
mkdir -p $TestsDir > $null
# Run code generation
Invoke-Expression "python -m codegen"
# Build and run C tests
Invoke-Expression "$Compiler $GeneralFlags $CStd $IncludeDirs $TestIncludeDirs $SrcFiles $TestCSrcFiles $TestCOutputFlags" -ErrorAction Stop
Invoke-Expression "$TestsDir/$TestCOutputBasename.exe"
# Build and run tests
Invoke-Expression "$Compiler $GeneralFlags $IncludeDirs $TestIncludeDirs $SrcFiles $TestSrcFiles $TestOutputs" -ErrorAction Stop
Invoke-Expression "$TestsDir/$TestOutBasename.exe"
$Status = $LASTEXITCODE
Remove-Item $TestsDir -Recurse -Force
If ($Status -ne 0) {
Remove-Item $TestsDir -Recurse -Force
Write-Error "Tests failed"
Exit 1
}
# Build library
Invoke-Expression "$Compiler $GeneralFlags $LibraryFlags $SrcFiles $Objects $Outputs"
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
-23
View File
@@ -1,23 +0,0 @@
from typing import Dict
from codegen.datatypes import CDataType
from codegen.dbl_list.make_dbl_list import DblListData, make_dbl_list
from codegen.array.make_array import ArrayData, make_array
def main():
gen_dbl_list()
gen_array()
def gen_dbl_list():
datatypes: Dict[CDataType, DblListData] = {}
make_dbl_list(datatypes)
def gen_array():
datatypes: Dict[CDataType, ArrayData] = {}
make_array(datatypes)
if __name__ == "__main__":
main()
-393
View File
@@ -1,393 +0,0 @@
from pathlib import Path
from dataclasses import dataclass, field
from typing import List, Dict
from codegen.constants import WAPP_SRC_ROOT
from codegen.utils import load_func_body_from_file, convert_to_relative
from codegen.datatypes import (
CDataType,
CMacro,
CStruct,
CFunc,
CHeader,
CSource,
CArg,
CType,
CPointer,
CPointerType,
CQualifier,
CInclude,
get_datatype_string,
)
@dataclass
class ArrayData:
array_typename: str
hdr_decl_types: List[CStruct] = field(default_factory=list)
src_decl_types: List[CStruct] = field(default_factory=list)
def make_array(user_datatypes: Dict[CDataType, ArrayData] = {}):
def __format_func_body(
filename: Path,
type_string: str,
type_string_upper: str,
type_string_lower: str,
array_typename: str
):
return load_func_body_from_file(filename).format(
T=type_string,
ArrayType=array_typename,
Tupper=type_string_upper,
Tlower=type_string_lower,
)
out_dir = WAPP_SRC_ROOT / "primitives" / "array"
out_dir.mkdir(parents=True, exist_ok=True)
common_includes: List[CInclude] = [
CInclude(header="stdbool.h"),
CInclude(
header=str(convert_to_relative(WAPP_SRC_ROOT / "primitives" / "mem_allocator" / "mem_allocator.h", out_dir)).replace("\\", "/"),
local=True,
),
CInclude(
header=str(convert_to_relative(WAPP_SRC_ROOT / "common" / "misc" / "misc_utils.h", out_dir)).replace("\\", "/"),
local=True,
),
]
common_decl_types: List[CStruct] = []
datatypes: dict[CDataType, ArrayData] = {
"Str8": ArrayData(
array_typename="Str8Array",
hdr_decl_types=[
CStruct(name="str8", cargs=[], typedef_name="Str8"),
],
),
}
for _type in CType:
if _type == CType.VOID:
datatypes["void *"] = ArrayData(
array_typename="VoidPArray",
)
continue
elif _type == CType.BOOL:
datatypes[_type.value] = ArrayData(
array_typename="BoolArray",
)
continue
type_title = _type.value.title()
datatypes[_type.value] = ArrayData(
array_typename=f"{type_title}Array",
)
datatypes.update(user_datatypes)
snippets_dir = Path(__file__).parent / "snippets"
header = CHeader(
name="array",
decl_types=[*common_decl_types],
includes=[],
types=[],
funcs=[]
)
source = CSource(
name=header.name,
decl_types=[*common_decl_types],
includes=[
CInclude(header, local=True, same_dir=True),
CInclude(header="stddef.h"),
CInclude(header="assert.h"),
],
internal_funcs=[],
funcs=header.funcs
)
if len(common_includes) > 0:
header.includes.extend(common_includes)
source.includes.extend(common_includes)
generic_funcs = []
for _type, array_data in datatypes.items():
type_string = get_datatype_string(_type)
clean_type_string = type_string.replace(" ", "").replace("*", "_ptr")
type_string_upper = clean_type_string.upper()
type_string_lower = clean_type_string.lower()
array = CStruct(
name=array_data.array_typename,
cargs=[
CArg(name="items", _type=type_string, pointer=CPointer(_type=CPointerType.SINGLE)),
CArg(name="count", _type=CType.U64),
CArg(name="capacity", _type=CType.U64),
CArg(name="item_size", _type=CType.U64),
],
)
if isinstance(_type, str) and _type == "void *":
alloc_capacity_func = CFunc(
name=f"_array_alloc_capacity",
ret_type=array,
args=[
CArg(name="allocator", _type="Allocator", pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
CArg(name="capacity", _type=CType.U64),
CArg(name="item_size", _type=CType.U64),
],
body=__format_func_body(
filename=snippets_dir / "alloc_capacity",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
pointer=CPointer(CPointerType.SINGLE),
)
generic_funcs.append(alloc_capacity_func)
stack_array_macro = CMacro(
name=f"wapp_{type_string_lower}_array(...)",
value=__format_func_body(
filename=snippets_dir / "stack_array",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
)
stack_capacity_array_macro = CMacro(
name=f"wapp_{type_string_lower}_array_with_capacity(CAPACITY)",
value=__format_func_body(
filename=snippets_dir / "stack_capacity_array",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
)
alloc_capacity_array_macro = CMacro(
name=f"wapp_{type_string_lower}_array_alloc_capacity(ALLOCATOR_PTR, CAPACITY)",
value=__format_func_body(
filename=snippets_dir / "alloc_capacity_macro",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
)
array_pop_macro = CMacro(
name=f"wapp_{type_string_lower}_array_pop(ARRAY_PTR)",
value=__format_func_body(
filename=snippets_dir / "array_pop_macro",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
)
get_func = CFunc(
name=f"wapp_{type_string_lower}_array_get",
ret_type=type_string,
args=[
CArg(name="array", _type=array, pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
CArg(name="index", _type=CType.U64),
],
body=__format_func_body(
filename=snippets_dir / "array_get",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
pointer=CPointer(CPointerType.SINGLE),
)
set_func = CFunc(
name=f"wapp_{type_string_lower}_array_set",
ret_type=CType.VOID,
args=[
CArg(name="array", _type=array, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="index", _type=CType.U64),
CArg(name="item", _type=type_string, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "array_set",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
)
append_capped_func = CFunc(
name=f"wapp_{type_string_lower}_array_append_capped",
ret_type=CType.VOID,
args=[
CArg(name="array", _type=array, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="item", _type=type_string, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "append_capped",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
)
extend_capped_func = CFunc(
name=f"wapp_{type_string_lower}_array_extend_capped",
ret_type=CType.VOID,
args=[
CArg(name="array", _type=array, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="other", _type=array, pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
],
body=__format_func_body(
filename=snippets_dir / "extend_capped",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
)
clear_func = CFunc(
name=f"wapp_{type_string_lower}_array_clear",
ret_type=CType.VOID,
args=[
CArg(name="array", _type=array, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "clear",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
)
copy_capped_func = CFunc(
name=f"wapp_{type_string_lower}_array_copy_capped",
ret_type=CType.VOID,
args=[
CArg(name="src", _type=array, pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
CArg(name="dst", _type=array, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "copy_capped",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
)
append_alloc_func = CFunc(
name=f"wapp_{type_string_lower}_array_append_alloc",
ret_type=array,
args=[
CArg(name="allocator", _type="Allocator", pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
CArg(name="array", _type=array, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="item", _type=type_string, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "append_alloc",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
pointer=CPointer(CPointerType.SINGLE),
)
extend_alloc_func = CFunc(
name=f"wapp_{type_string_lower}_array_extend_alloc",
ret_type=array,
args=[
CArg(name="allocator", _type="Allocator", pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
CArg(name="array", _type=array, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="other", _type=array, pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
],
body=__format_func_body(
filename=snippets_dir / "extend_alloc",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
pointer=CPointer(CPointerType.SINGLE),
)
copy_alloc_func = CFunc(
name=f"wapp_{type_string_lower}_array_copy_alloc",
ret_type=array,
args=[
CArg(name="allocator", _type="Allocator", pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
CArg(name="src", _type=array, pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
CArg(name="dst", _type=array, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "copy_alloc",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
pointer=CPointer(CPointerType.SINGLE),
)
pop_func = CFunc(
name=f"_{type_string_lower}_array_pop",
ret_type=type_string,
args=[
CArg(name="array", _type=array, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "array_pop",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
array_typename=array_data.array_typename,
),
pointer=CPointer(CPointerType.SINGLE),
)
header.decl_types.extend(array_data.hdr_decl_types)
header.macros.extend([
stack_array_macro,
stack_capacity_array_macro,
alloc_capacity_array_macro,
array_pop_macro,
])
header.types.extend([array])
header.funcs.extend([
get_func,
set_func,
append_capped_func,
extend_capped_func,
clear_func,
copy_capped_func,
append_alloc_func,
extend_alloc_func,
copy_alloc_func,
pop_func,
])
source.decl_types.extend(array_data.src_decl_types)
source.funcs = header.funcs
header.funcs.extend(generic_funcs)
header.save(out_dir)
source.save(out_dir)
-15
View File
@@ -1,15 +0,0 @@
assert(allocator != NULL);
u64 allocation_size = sizeof({ArrayType}) + item_size * capacity;
{ArrayType} *array = wapp_mem_allocator_alloc(allocator, allocation_size);
if (!array) {{
goto RETURN_GENERIC_ARRAY_ALLOC;
}}
array->items = ({T} *)((u8 *)array + sizeof({ArrayType}));
array->count = 0;
array->capacity = capacity;
array->item_size = item_size;
RETURN_GENERIC_ARRAY_ALLOC:
return array;
@@ -1 +0,0 @@
(({ArrayType} *)_array_alloc_capacity(ALLOCATOR_PTR, CAPACITY, sizeof({T})))
-18
View File
@@ -1,18 +0,0 @@
assert(allocator != NULL && array != NULL);
{ArrayType} *output = array;
if (array->count >= array->capacity) {{
u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(array->capacity * 2);
output = ({ArrayType} *)_array_alloc_capacity(allocator, new_capacity, array->item_size);
if (!output) {{
output = array;
goto RETURN_{Tupper}_ARRAY_APPEND_ALLOC;
}}
wapp_{Tlower}_array_copy_capped(array, output);
}}
wapp_{Tlower}_array_append_capped(output, item);
RETURN_{Tupper}_ARRAY_APPEND_ALLOC:
return output;
-4
View File
@@ -1,4 +0,0 @@
assert(array != NULL && array->count < array->capacity);
u64 index = (array->count)++;
wapp_{Tlower}_array_set(array, index, item);
-4
View File
@@ -1,4 +0,0 @@
assert(array != NULL && index < array->count);
u8 *ptr = (u8 *)(array->items) + (array->item_size * index);
return ({T} *)ptr;
-4
View File
@@ -1,4 +0,0 @@
u64 index = array->count - 1;
{T} *out = wapp_{Tlower}_array_get(array, index);
--(array->count);
return out;
-4
View File
@@ -1,4 +0,0 @@
(ARRAY_PTR != NULL && (ARRAY_PTR)->count > 0 ? \
*_{Tlower}_array_pop(ARRAY_PTR) : \
({T}){{0}} \
)
-3
View File
@@ -1,3 +0,0 @@
{T} *ptr = wapp_{Tlower}_array_get(array, index);
memcpy((void *)ptr, (void *)item, array->item_size);
-2
View File
@@ -1,2 +0,0 @@
assert(array != NULL);
array->count = 0;
-18
View File
@@ -1,18 +0,0 @@
assert(allocator != NULL && src != NULL && dst != NULL);
{ArrayType} *output = dst;
if (src->count >= dst->capacity) {{
u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(dst->capacity * 2);
output = ({ArrayType} *)_array_alloc_capacity(allocator, new_capacity, src->item_size);
if (!output) {{
output = dst;
goto RETURN_{Tupper}_ARRAY_COPY_ALLOC;
}}
}}
wapp_{Tlower}_array_clear(output);
wapp_{Tlower}_array_copy_capped(src, output);
RETURN_{Tupper}_ARRAY_COPY_ALLOC:
return output;
-22
View File
@@ -1,22 +0,0 @@
assert(src != NULL && dst != NULL);
wapp_{Tlower}_array_clear(dst);
{T} *item;
// NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of
// MSVC Spectre mitigation warnings
u64 to_copy = src->count < dst->capacity ? src->count : dst->capacity;
u64 item_index = 0;
bool running = true;
while (running) {{
item = wapp_{Tlower}_array_get(src, item_index);
++item_index;
running = item_index < to_copy;
if (!item) {{
continue;
}}
wapp_{Tlower}_array_append_capped(dst, item);
}}
-19
View File
@@ -1,19 +0,0 @@
assert(allocator != NULL && array != NULL && other != NULL);
{ArrayType} *output = array;
u64 remaining_capacity = array->capacity - array->count;
if (other->count >= remaining_capacity) {{
u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(array->capacity * 2);
output = ({ArrayType} *)_array_alloc_capacity(allocator, new_capacity, array->item_size);
if (!output) {{
output = array;
goto RETURN_{Tupper}_ARRAY_EXTEND_ALLOC;
}}
wapp_{Tlower}_array_copy_capped(array, output);
}}
wapp_{Tlower}_array_extend_capped(output, other);
RETURN_{Tupper}_ARRAY_EXTEND_ALLOC:
return output;
-23
View File
@@ -1,23 +0,0 @@
assert(array != NULL && other != NULL);
u64 remaining_capacity = array->capacity - array->count;
assert(other->count < remaining_capacity);
{T} *item;
// NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of
// MSVC Spectre mitigation warnings
u64 items_to_add = other->count;
u64 item_index = 0;
bool running = true;
while (running) {{
item = wapp_{Tlower}_array_get(other, item_index);
++item_index;
running = item_index < items_to_add;
if (!item) {{
continue;
}}
wapp_{Tlower}_array_append_capped(array, item);
}}
-6
View File
@@ -1,6 +0,0 @@
(({ArrayType}){{ \
.items = ({T}[wapp_misc_utils_u64_round_up_pow2(wapp_misc_utils_va_args_count({T}, __VA_ARGS__) * 2)]){{__VA_ARGS__}}, \
.count = wapp_misc_utils_va_args_count({T}, __VA_ARGS__), \
.capacity = wapp_misc_utils_u64_round_up_pow2(wapp_misc_utils_va_args_count({T}, __VA_ARGS__) * 2), \
.item_size = sizeof({T}) \
}})
@@ -1 +0,0 @@
(({ArrayType}){{.items = ({T}[CAPACITY]){{0}}, .count = 0, .capacity = CAPACITY, .item_size = sizeof({T})}})
-5
View File
@@ -1,5 +0,0 @@
from pathlib import Path
PACKAGE_DIR = Path(__file__).parent.resolve()
WAPP_SRC_ROOT = PACKAGE_DIR.parent / "src"
-350
View File
@@ -1,350 +0,0 @@
from enum import Enum
from pathlib import Path
from typing import Optional, Union, List
from dataclasses import dataclass, field
from codegen.constants import WAPP_SRC_ROOT
from codegen.utils import convert_to_relative
class CType(Enum):
VOID = "void"
BOOL = "bool"
CHAR = "char"
C8 = "c8"
C16 = "c16"
C32 = "c32"
I8 = "i8"
I16 = "i16"
I32 = "i32"
I64 = "i64"
U8 = "u8"
U16 = "u16"
U32 = "u32"
U64 = "u64"
F32 = "f32"
F64 = "f64"
F128 = "f128"
IPTR = "iptr"
UPTR = "uptr"
def __str__(self) -> str:
return self.value
class CQualifier(Enum):
NONE = ""
CONST = "const "
EXTERNAL = "external "
INTERNAL = "internal "
PERSISTENT = "persistent "
def __str__(self) -> str:
return self.value
class CPointerType(Enum):
NONE = ""
SINGLE = "*"
DOUBLE = "**"
def __str__(self) -> str:
return self.value
@dataclass
class CPointer:
_type: CPointerType = CPointerType.NONE
qualifier: CQualifier = CQualifier.NONE
def __str__(self) -> str:
return str(self._type) + str(self.qualifier)
@dataclass
class CEnumVal:
name: str
value: Optional[int] = None
def __str__(self) -> str:
return self.name + "" if self.value is None else f" = {self.value}"
@dataclass
class CEnum:
name: str
values: List[CEnumVal]
typedef: bool = False
def __str__(self) -> str:
if self.typedef:
header = "typedef enum {\n"
footer = f"}} {self.name};\n"
else:
header = f"enum {self.name} {{\n"
footer = "};\n"
values = ""
for value in self.values:
values += f" {str(value)},\n"
return header + values + footer
@dataclass
class CMacro:
name: str
value: str
def __str__(self) -> str:
return f"#define {self.name} {self.value}\n"
@dataclass
class CStruct:
name: str
cargs: List["CArg"]
typedef_name: Optional[str] = None
def __str__(self) -> str:
return self.declare() + self.define()
def declare(self) -> str:
declaration = f"typedef struct {self.name} {self.typedef_name if self.typedef_name is not None else self.name};\n"
return declaration
def define(self):
definition = f"struct {self.name} {{\n"
args = ""
for arg in self.cargs:
args += f" {str(arg)};\n"
footer = "};\n"
return definition + args + footer;
CUserType = Union[CStruct, CEnum]
CDataType = Union[CType, CUserType, str]
@dataclass
class CArg:
name: str
_type: CDataType
array: bool = False
pointer: CPointer = field(default_factory=CPointer)
qualifier: CQualifier = CQualifier.NONE
def __str__(self) -> str:
qualifier = str(self.qualifier)
_type = get_datatype_string(self._type) + " "
pointer = str(self.pointer)
array = "[]" if self.array else ""
return qualifier + _type + pointer + self.name + array
@dataclass
class CFunc:
name: str
ret_type: CDataType
args: List[CArg]
body: str
pointer: CPointer = field(default_factory=CPointer)
qualifiers: List[CQualifier] = field(default_factory=list)
def __str__(self) -> str:
qualifiers = ""
for qualifier in self.qualifiers:
if qualifier == CQualifier.NONE:
continue
if len(qualifiers) > 0:
qualifiers += " "
qualifiers += f"{str(qualifier)}"
args = ""
for i, arg in enumerate(self.args):
args += f"{str(arg)}"
if i + 1 < len(self.args):
args += ", "
return qualifiers + get_datatype_string(self.ret_type) + " " + str(self.pointer) + self.name + f"({args})"
def declare(self) -> str:
return f"{str(self)};\n"
def define(self) -> str:
return f"{str(self)} {{\n{self.body}\n}}\n\n"
@dataclass
class CInclude:
header: Union[str, "CHeader"]
local: bool = False
same_dir: bool = False
def __str__(self) -> str:
if isinstance(self.header, CHeader):
name = f"{self.header.name}.{self.header.extension}"
else:
name = self.header
if self.local:
open_symbol = '"'
close_symbol = '"'
if self.same_dir:
name = f"./{name}"
else:
open_symbol = '<'
close_symbol = '>'
return f"#include {open_symbol}{name}{close_symbol}\n"
@dataclass
class CFile:
name: str
extension: str
includes: List[CInclude] = field(default_factory=list)
types: List[CUserType] = field(default_factory=list)
funcs: List[CFunc] = field(default_factory=list)
decl_types: List[CStruct] = field(default_factory=list)
macros: List[CMacro] = field(default_factory=list)
def save(self, output_dir: Path):
self.includes.extend(
[
CInclude(
header=str(convert_to_relative(WAPP_SRC_ROOT / "common" / "aliases" / "aliases.h", output_dir)).replace("\\", "/"),
local=True,
),
CInclude(
header=str(convert_to_relative(WAPP_SRC_ROOT / "common" / "platform" / "platform.h", output_dir)).replace("\\", "/"),
local=True,
)
]
)
output_file = output_dir / f"{self.name}.{self.extension}"
with open(output_file, "w+") as outfile:
outfile.write(str(self))
def __str__(self) -> str:
return """\
/**
* THIS FILE IS AUTOMATICALLY GENERATED. ANY MODIFICATIONS TO IT WILL BE OVERWRITTEN
*/
"""
@dataclass
class CHeader(CFile):
extension: str = "h"
def __str__(self) -> str:
name_upper = self.name.upper()
header_guard_name = f"{name_upper}_H"
header_guard_open = f"#ifndef {header_guard_name}\n#define {header_guard_name}\n\n"
header_guard_close = f"\n#endif // !{header_guard_name}\n"
includes = _get_includes_string(self.includes)
macros = ""
for macro in self.macros:
macros += str(macro)
if len(macros) > 0:
macros += "\n"
forward_declarations = ""
for _type in self.decl_types:
forward_declarations += _type.declare()
if len(forward_declarations) > 0:
forward_declarations += "\n"
types = ""
for _type in self.types:
types += str(_type) + "\n"
funcs = ""
for func in self.funcs:
funcs += func.declare()
return (
super().__str__() +
header_guard_open +
includes +
macros +
forward_declarations +
types +
funcs +
header_guard_close
)
@dataclass
class CSource(CFile):
extension: str = "c"
internal_funcs: List[CFunc] = field(default_factory=list)
def __str__(self) -> str:
includes = _get_includes_string(self.includes)
macros = ""
for macro in self.macros:
macros += str(macro)
if len(macros) > 0:
macros += "\n"
forward_declarations = ""
for _type in self.decl_types:
forward_declarations += _type.declare()
if len(forward_declarations) > 0:
forward_declarations += "\n"
types = ""
for _type in self.types:
types += str(_type) + "\n"
internal_funcs_decl = ""
internal_funcs_def = ""
for func in self.internal_funcs:
internal_funcs_decl += func.declare()
internal_funcs_def += func.define()
if len(internal_funcs_decl) > 0:
internal_funcs_decl += "\n"
funcs = ""
for func in self.funcs:
funcs += func.define()
return (
super().__str__() +
includes +
macros +
forward_declarations +
types +
internal_funcs_decl +
funcs +
internal_funcs_def
)
def get_datatype_string(_type: CDataType) -> str:
if isinstance(_type, CType):
return str(_type)
elif isinstance(_type, CStruct) or isinstance(_type, CEnum):
return _type.name
elif isinstance(_type, str):
return _type
def _get_includes_string(includes: List[CInclude]) -> str:
output = ""
for include in sorted(includes, key=lambda inc: inc.local, reverse=True):
output += str(include)
if len(output) > 0:
output += "\n"
return output
-321
View File
@@ -1,321 +0,0 @@
from pathlib import Path
from dataclasses import dataclass, field
from typing import List, Dict
from codegen.constants import WAPP_SRC_ROOT
from codegen.utils import load_func_body_from_file
from codegen.datatypes import (
CDataType,
CMacro,
CStruct,
CFunc,
CHeader,
CSource,
CArg,
CType,
CPointer,
CPointerType,
CQualifier,
CInclude,
get_datatype_string,
)
@dataclass
class DblListData:
node_typename: str
list_typename: str
hdr_decl_types: List[CStruct] = field(default_factory=list)
src_decl_types: List[CStruct] = field(default_factory=list)
def make_dbl_list(user_datatypes: Dict[CDataType, DblListData] = {}):
def __format_func_body(
filename: Path,
type_string: str,
type_string_upper: str,
type_string_lower: str,
node_typename: str,
list_typename: str
):
return load_func_body_from_file(filename).format(
T=type_string,
NodeType=node_typename,
ListType=list_typename,
Tupper=type_string_upper,
Tlower=type_string_lower,
)
out_dir = WAPP_SRC_ROOT / "primitives" / "dbl_list"
out_dir.mkdir(parents=True, exist_ok=True)
common_includes: List[CInclude] = [
CInclude(header="stdbool.h")
]
common_decl_types: List[CStruct] = []
datatypes: dict[CDataType, DblListData] = {
"Str8": DblListData(
node_typename="Str8Node",
list_typename="Str8List",
hdr_decl_types=[
CStruct(name="str8", cargs=[], typedef_name="Str8"),
],
),
}
for _type in CType:
if _type == CType.VOID:
datatypes["void *"] = DblListData(
node_typename="VoidPNode",
list_typename="VoidPList",
)
continue
type_title = _type.value.title()
datatypes[_type.value] = DblListData(
node_typename=f"{type_title}Node",
list_typename=f"{type_title}List",
)
datatypes.update(user_datatypes)
snippets_dir = Path(__file__).parent / "snippets"
header = CHeader(
name="dbl_list",
decl_types=[*common_decl_types],
includes=[],
types=[],
funcs=[]
)
source = CSource(
name=header.name,
decl_types=[*common_decl_types],
includes=[
CInclude(header, local=True, same_dir=True),
CInclude(header="stddef.h"),
CInclude(header="assert.h"),
],
internal_funcs=[],
funcs=header.funcs
)
if len(common_includes) > 0:
header.includes.extend(common_includes)
source.includes.extend(common_includes)
for _type, dbl_list_data in datatypes.items():
type_string = get_datatype_string(_type)
clean_type_string = type_string.replace(" ", "").replace("*", "_ptr")
type_string_upper = clean_type_string.upper()
type_string_lower = clean_type_string.lower()
node = CStruct(
name=dbl_list_data.node_typename,
cargs=[
CArg(name="item", _type=type_string, pointer=CPointer(_type=CPointerType.SINGLE)),
],
)
node.cargs.extend([
CArg(name="prev", _type=node, pointer=CPointer(_type=CPointerType.SINGLE)),
CArg(name="next", _type=node, pointer=CPointer(_type=CPointerType.SINGLE)),
])
dl_list = CStruct(
name=dbl_list_data.list_typename,
cargs=[
CArg(name="first", _type=node, pointer=CPointer(_type=CPointerType.SINGLE)),
CArg(name="last", _type=node, pointer=CPointer(_type=CPointerType.SINGLE)),
CArg(name="node_count", _type=CType.U64),
],
)
node_macro = CMacro(
name=f"wapp_{type_string_lower}_list_node(ITEM_PTR)",
value=__format_func_body(
filename=snippets_dir / "list_node",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
get_func = CFunc(
name=f"wapp_{type_string_lower}_list_get",
ret_type=node,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE), qualifier=CQualifier.CONST),
CArg(name="index", _type=CType.U64),
],
body=__format_func_body(
filename=snippets_dir / "list_get",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
pointer=CPointer(CPointerType.SINGLE),
)
push_front_func = CFunc(
name=f"wapp_{type_string_lower}_list_push_front",
ret_type=CType.VOID,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="node", _type=node, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "list_push_front",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
push_back_func = CFunc(
name=f"wapp_{type_string_lower}_list_push_back",
ret_type=CType.VOID,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="node", _type=node, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "list_push_back",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
insert_func = CFunc(
name=f"wapp_{type_string_lower}_list_insert",
ret_type=CType.VOID,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="node", _type=node, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="index", _type=CType.U64),
],
body=__format_func_body(
filename=snippets_dir / "list_insert",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
pop_front_func = CFunc(
name=f"wapp_{type_string_lower}_list_pop_front",
ret_type=node,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "list_pop_front",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
pointer=CPointer(CPointerType.SINGLE),
)
pop_back_func = CFunc(
name=f"wapp_{type_string_lower}_list_pop_back",
ret_type=node,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "list_pop_back",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
pointer=CPointer(CPointerType.SINGLE),
)
remove_func = CFunc(
name=f"wapp_{type_string_lower}_list_remove",
ret_type=node,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
CArg(name="index", _type=CType.U64),
],
body=__format_func_body(
filename=snippets_dir / "list_remove",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
pointer=CPointer(CPointerType.SINGLE),
)
empty_func = CFunc(
name=f"wapp_{type_string_lower}_list_empty",
ret_type=CType.VOID,
args=[
CArg(name="list", _type=dl_list, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "list_empty",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
)
node_to_list_func = CFunc(
name=f"{type_string_lower}_node_to_list",
ret_type=dl_list,
args=[
CArg(name="node", _type=node, pointer=CPointer(CPointerType.SINGLE)),
],
body=__format_func_body(
filename=snippets_dir / "node_to_list",
type_string=type_string,
type_string_upper=type_string_upper,
type_string_lower=type_string_lower,
node_typename=dbl_list_data.node_typename,
list_typename=dbl_list_data.list_typename
),
qualifiers=[CQualifier.INTERNAL],
)
header.decl_types.extend(dbl_list_data.hdr_decl_types)
header.macros.append(node_macro)
header.types.extend([node, dl_list])
header.funcs.extend([
get_func,
push_front_func,
push_back_func,
insert_func,
pop_front_func,
pop_back_func,
remove_func,
empty_func,
])
source.decl_types.extend(dbl_list_data.src_decl_types)
source.internal_funcs.append(node_to_list_func)
source.funcs = header.funcs
header.save(out_dir)
source.save(out_dir)
-6
View File
@@ -1,6 +0,0 @@
assert(list != NULL);
u64 count = list->node_count;
for (u64 i = 0; i < count; ++i) {{
wapp_{Tlower}_list_pop_back(list);
}}
-11
View File
@@ -1,11 +0,0 @@
assert(index < list->node_count);
{NodeType} *output = NULL;
{NodeType} *current = list->first;
for (u64 i = 1; i <= index; ++i) {{
current = current->next;
}}
output = current;
return output;
-26
View File
@@ -1,26 +0,0 @@
assert(list != NULL && node != NULL && (node->item) != NULL);
if (index == 0) {{
wapp_{Tlower}_list_push_front(list, node);
return;
}} else if (index == list->node_count) {{
wapp_{Tlower}_list_push_back(list, node);
return;
}}
{NodeType} *dst_node = wapp_{Tlower}_list_get(list, index);
if (!dst_node) {{
return;
}}
{ListType} node_list = {Tlower}_node_to_list(node);
list->node_count += node_list.node_count;
{NodeType} *prev = dst_node->prev;
dst_node->prev = node_list.last;
prev->next = node_list.first;
node_list.first->prev = prev;
node_list.last->next = dst_node;
-1
View File
@@ -1 +0,0 @@
(({NodeType}){{.item = ITEM_PTR}})
-22
View File
@@ -1,22 +0,0 @@
assert(list != NULL);
{NodeType} *output = NULL;
if (list->node_count == 0) {{
goto RETURN_{Tupper}_LIST_POP_BACK;
}}
output = list->last;
if (list->node_count == 1) {{
*list = ({ListType}){{0}};
goto RETURN_{Tupper}_LIST_POP_BACK;
}}
--(list->node_count);
list->last = output->prev;
output->prev = output->next = NULL;
RETURN_{Tupper}_LIST_POP_BACK:
return output;
-22
View File
@@ -1,22 +0,0 @@
assert(list != NULL);
{NodeType} *output = NULL;
if (list->node_count == 0) {{
goto RETURN_{Tupper}_LIST_POP_FRONT;
}}
output = list->first;
if (list->node_count == 1) {{
*list = ({ListType}){{0}};
goto RETURN_{Tupper}_LIST_POP_FRONT;
}}
--(list->node_count);
list->first = output->next;
output->prev = output->next = NULL;
RETURN_{Tupper}_LIST_POP_FRONT:
return output;
-18
View File
@@ -1,18 +0,0 @@
assert(list != NULL && node != NULL && (node->item) != NULL);
{ListType} node_list = {Tlower}_node_to_list(node);
if (list->node_count == 0) {{
*list = node_list;
return;
}}
list->node_count += node_list.node_count;
{NodeType} *last = list->last;
if (last) {{
last->next = node_list.first;
}}
list->last = node_list.last;
node_list.first->prev = last;
-18
View File
@@ -1,18 +0,0 @@
assert(list != NULL && node != NULL && (node->item) != NULL);
{ListType} node_list = {Tlower}_node_to_list(node);
if (list->node_count == 0) {{
*list = node_list;
return;
}}
list->node_count += node_list.node_count;
{NodeType} *first = list->first;
if (first) {{
first->prev = node_list.last;
}}
list->first = node_list.first;
node_list.last->next = first;
-26
View File
@@ -1,26 +0,0 @@
assert(list != NULL);
{NodeType} *output = NULL;
if (index == 0) {{
output = wapp_{Tlower}_list_pop_front(list);
goto RETURN_{Tupper}_LIST_REMOVE;
}} else if (index == list->node_count) {{
output = wapp_{Tlower}_list_pop_back(list);
goto RETURN_{Tupper}_LIST_REMOVE;
}}
output = wapp_{Tlower}_list_get(list, index);
if (!output) {{
goto RETURN_{Tupper}_LIST_REMOVE;
}}
output->prev->next = output->next;
output->next->prev = output->prev;
--(list->node_count);
output->prev = output->next = NULL;
RETURN_{Tupper}_LIST_REMOVE:
return output;
-13
View File
@@ -1,13 +0,0 @@
{ListType} output = {{.first = node, .last = node, .node_count = 1}};
while (output.first->prev != NULL) {{
output.first = output.first->prev;
++(output.node_count);
}}
while (output.last->next != NULL) {{
output.last = output.last->next;
++(output.node_count);
}}
return output;
-18
View File
@@ -1,18 +0,0 @@
import os
import sys
from pathlib import Path
def load_func_body_from_file(filename: Path) -> str:
with open(filename, "r") as infile:
return infile.read().rstrip()
def convert_to_relative(path: Path, target: Path) -> Path:
major = sys.version_info.major
minor = sys.version_info.minor
if major >= 3 and minor >= 12:
return path.relative_to(target, walk_up=True)
else:
return Path(os.path.relpath(str(path), start=str(target)))
+51
View File
@@ -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/)
Executable
+12
View File
@@ -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
+56
View File
@@ -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()
+21
View File
@@ -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
+310
View File
@@ -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);
}
+223
View File
@@ -0,0 +1,223 @@
// 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 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
+259
View File
@@ -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");
}
+184
View File
@@ -0,0 +1,184 @@
// 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 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 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
+25
View File
@@ -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];
}
+55
View File
@@ -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
+126
View File
@@ -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;
u64 len = bytes->count * bytes->item_size;
switch(len & 15) {
case 15: k2 ^= ((u64)tail[14]) << 48;
case 14: k2 ^= ((u64)tail[13]) << 40;
case 13: k2 ^= ((u64)tail[12]) << 32;
case 12: k2 ^= ((u64)tail[11]) << 24;
case 11: k2 ^= ((u64)tail[10]) << 16;
case 10: k2 ^= ((u64)tail[ 9]) << 8;
case 9: k2 ^= ((u64)tail[ 8]) << 0;
k2 *= c2; k2 = wpMiscUtilsRotl64(k2,33); k2 *= c1; h2 ^= k2;
case 8: k1 ^= ((u64)tail[ 7]) << 56;
case 7: k1 ^= ((u64)tail[ 6]) << 48;
case 6: k1 ^= ((u64)tail[ 5]) << 40;
case 5: k1 ^= ((u64)tail[ 4]) << 32;
case 4: k1 ^= ((u64)tail[ 3]) << 24;
case 3: k1 ^= ((u64)tail[ 2]) << 16;
case 2: k1 ^= ((u64)tail[ 1]) << 8;
case 1: k1 ^= ((u64)tail[ 0]) << 0;
k1 *= c1; k1 = wpMiscUtilsRotl64(k1,31); k1 *= c2; h1 ^= k1;
};
//----------
// finalization
h1 ^= len; h2 ^= len;
h1 += h2;
h2 += h1;
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 += h2;
h2 += h1;
return (WpU128){ .value = { [0] = h1, [1] = h2 } };
}
wp_intern inline void validateMur3HasherCtx(const WpHasherCtx *ctx) {
wpRuntimeAssert(ctx->type == WP_HASHER_CTX_MURMUR3, "Invalid Murmur3 hash context");
}
wp_intern inline u64 fmix64(u64 k) {
k ^= k >> 33;
k *= wpU64Const(0xff51afd7ed558ccd);
k ^= k >> 33;
k *= wpU64Const(0xc4ceb9fe1a85ec53);
k ^= k >> 33;
return k;
}
+28
View File
@@ -0,0 +1,28 @@
// vim:fileencoding=utf-8:foldmethod=marker
/**
* An implementation of the x64 128-bit variant of the MurmurHash3 algorithm
* based on https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp
*/
#ifndef MURMUR3_H
#define MURMUR3_H
#include "hasher.h"
#include "../../stream/stream.h"
#include "../../../common/aliases/aliases.h"
#include "../../../common/platform/platform.h"
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
WpHasher wpX64Mur3Hasher(void);
WpHasher wpX64Mur3HasherWithSeed(u32 seed);
WpU128 wpX64Mur3GetHash128(WpU8Stream *bytes, WpHasherCtx ctx);
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !MURMUR3_H
+190
View File
@@ -0,0 +1,190 @@
// vim:fileencoding=utf-8:foldmethod=marker
#include "siphash.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 siphash(WpStream *bytes, const WpHasherCtx *ctx, u8 *out, u64 outlen);
wp_intern inline void validateSipHasherCtx(const WpHasherCtx *ctx);
wp_intern inline b8 checkRounds(const WpHasherCtx *ctx);
#define _u32To8LE(p, v) \
(p)[0] = (u8)((v)); \
(p)[1] = (u8)((v) >> 8); \
(p)[2] = (u8)((v) >> 16); \
(p)[3] = (u8)((v) >> 24);
#define _u64To8LE(p, v) \
_u32To8LE((p), (u32)((v))); \
_u32To8LE((p) + 4, (u32)((v) >> 32));
#define _u8To64LE(p) \
(((u64)((p)[0])) | ((u64)((p)[1]) << 8) | \
((u64)((p)[2]) << 16) | ((u64)((p)[3]) << 24) | \
((u64)((p)[4]) << 32) | ((u64)((p)[5]) << 40) | \
((u64)((p)[6]) << 48) | ((u64)((p)[7]) << 56))
#define _sipRound \
do { \
v0 += v1; \
v1 = wpMiscUtilsRotl64(v1, 13); \
v1 ^= v0; \
v0 = wpMiscUtilsRotl64(v0, 32); \
v2 += v3; \
v3 = wpMiscUtilsRotl64(v3, 16); \
v3 ^= v2; \
v0 += v3; \
v3 = wpMiscUtilsRotl64(v3, 21); \
v3 ^= v0; \
v2 += v1; \
v1 = wpMiscUtilsRotl64(v1, 17); \
v1 ^= v2; \
v2 = wpMiscUtilsRotl64(v2, 32); \
} while (0)
WpHasher wpSip24Hasher(void) {
WpXor256State state = wpPrngXorshiftInit();
WpU128 key = { .value = { wpPrngXorshift256(&state), wpPrngXorshift256(&state) } };
return wpSip24HasherWithKey(key);
}
WpHasher wpSip48Hasher(void) {
WpXor256State state = wpPrngXorshiftInit();
WpU128 key = { .value = { wpPrngXorshift256(&state), wpPrngXorshift256(&state) } };
return wpSip48HasherWithKey(key);
}
WpHasher wpSip24HasherWithKey(WpU128 key) {
return (WpHasher){
.func = wpSipGetHash128,
.ctx = (WpHasherCtx){
.type = WP_HASHER_CTX_SIPHASH,
.siphash = (WpSiphashCtx){ .key = key, .compression = 2, .finalisation = 4 },
},
};
}
WpHasher wpSip48HasherWithKey(WpU128 key) {
return (WpHasher){
.func = wpSipGetHash128,
.ctx = (WpHasherCtx){
.type = WP_HASHER_CTX_SIPHASH,
.siphash = (WpSiphashCtx){ .key = key, .compression = 4, .finalisation = 8 },
},
};
}
WpU128 wpSipGetHash128(WpStream *bytes, WpHasherCtx ctx) {
validateSipHasherCtx(&ctx);
WpU128 output = {0};
siphash(bytes, &ctx, (u8 *)&output.value, sizeof(u64) * 2);
return output;
}
/**
* Implementation of the 128-variant of the siphash algorithm
*/
wp_intern inline void siphash(WpStream *bytes, const WpHasherCtx *ctx, u8 *out, u64 outlen) {
const u8 *kk = (const u8 *)&ctx->siphash.key;
u64 inlen = bytes->count * bytes->item_size;
wpRuntimeAssert(outlen == 16, "outlen should be 16");
u64 v0 = wpU64Const(0x736f6d6570736575);
u64 v1 = wpU64Const(0x646f72616e646f6d);
u64 v2 = wpU64Const(0x6c7967656e657261);
u64 v3 = wpU64Const(0x7465646279746573);
u64 k0 = _u8To64LE(kk);
u64 k1 = _u8To64LE(kk + 8);
u64 m;
u64 i;
const i32 left = inlen & 7;
u64 b = ((u64)inlen) << 56;
v3 ^= k1;
v2 ^= k0;
v1 ^= k1;
v0 ^= k0;
v1 ^= 0xee;
u8 *data;
while (wpStreamHasCount(bytes, sizeof(u64))) {
data = wpStreamConsumeCount(u8, bytes, sizeof(u64));
m = _u8To64LE(data);
v3 ^= m;
for (i = 0; i < ctx->siphash.compression; ++i) {
_sipRound;
}
v0 ^= m;
}
data = wpStreamConsume(u8, bytes);
switch (left) {
case 7:
b |= ((u64)data[6]) << 48;
/* FALLTHRU */
case 6:
b |= ((u64)data[5]) << 40;
/* FALLTHRU */
case 5:
b |= ((u64)data[4]) << 32;
/* FALLTHRU */
case 4:
b |= ((u64)data[3]) << 24;
/* FALLTHRU */
case 3:
b |= ((u64)data[2]) << 16;
/* FALLTHRU */
case 2:
b |= ((u64)data[1]) << 8;
/* FALLTHRU */
case 1:
b |= ((u64)data[0]);
break;
case 0:
break;
}
v3 ^= b;
for (i = 0; i < ctx->siphash.compression; ++i) {
_sipRound;
}
v0 ^= b;
v2 ^= 0xee;
for (i = 0; i < ctx->siphash.finalisation; ++i) {
_sipRound;
}
b = v0 ^ v1 ^ v2 ^ v3;
_u64To8LE(out, b);
v1 ^= 0xdd;
for (i = 0; i < ctx->siphash.finalisation; ++i) {
_sipRound;
}
b = v0 ^ v1 ^ v2 ^ v3;
_u64To8LE(out + 8, b);
}
wp_intern inline void validateSipHasherCtx(const WpHasherCtx *ctx) {
b8 magic_match = ctx->type == WP_HASHER_CTX_SIPHASH;
b8 rounds = checkRounds(ctx);
wpRuntimeAssert(magic_match && rounds, "Invalid Siphash context");
}
wp_intern inline b8 checkRounds(const WpHasherCtx *ctx) {
b8 rounds24 = ctx->siphash.compression == 2 && ctx->siphash.finalisation == 4;
b8 rounds48 = ctx->siphash.compression == 4 && ctx->siphash.finalisation == 8;
return rounds24 || rounds48;
}
+29
View File
@@ -0,0 +1,29 @@
// vim:fileencoding=utf-8:foldmethod=marker
/**
* Implementations of the SipHash-2-4-128, SipHash4-8-128 algorithms based on
* https://github.com/veorq/SipHash/blob/master/siphash.c
*/
#ifndef SIPHASH_H
#define SIPHASH_H
#include "hasher.h"
#include "../../stream/stream.h"
#include "../../../common/platform/platform.h"
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
WpHasher wpSip24Hasher(void);
WpHasher wpSip48Hasher(void);
WpHasher wpSip24HasherWithKey(WpU128 key);
WpHasher wpSip48HasherWithKey(WpU128 key);
WpU128 wpSipGetHash128(WpU8Stream *bytes, WpHasherCtx ctx);
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !SIPHASH_H
+83
View File
@@ -0,0 +1,83 @@
// vim:fileencoding=utf-8:foldmethod=marker
#include "map.h"
#include "../table/table.h"
WpHashMap hashMapAlloc(const WpAllocator *allocator, WpU8StreamEncoder encoder, WpKeyEqualTest key_eq,
u64 capacity, u64 key_size, u64 value_size) {
return hashMapAllocWithCustomHasher(allocator, WP_HASH_TABLE_DEFAULT_HASHER, encoder, key_eq,
capacity, key_size, value_size);
}
WpHashMap hashMapAllocWithCustomHasher(const WpAllocator *allocator, WpHasher hasher,
WpU8StreamEncoder encoder, WpKeyEqualTest key_eq,
u64 capacity, u64 key_size, u64 value_size) {
WpHashMap map = {0};
hashTableAlloc(allocator, &map, hasher, encoder, key_eq, capacity, key_size, value_size, true);
return map;
}
WpHashLookupResult hashMapFind(WpHashMap *map, void *key, u64 key_size) {
u64 hash = hashTableCalcHash(map, key, key_size);
WpHashBucketRef ref = hashTableBucketRef(map, hash);
return hashTableLookup(map, key, ref, key_size);
}
void hashMapUpdate(WpHashMap *map, void *key, void *value, u64 key_size, u64 value_size) {
WpHashLookupResult result = hashMapFind(map, key, key_size);
if (result.found) {
hashTableUpdate(map, value, result.ref, value_size);
}
}
void hashMapInsertCapped(WpHashMap *map, void *key, void *value, u64 key_size, u64 value_size) {
WpHashLookupResult result = hashMapFind(map, key, key_size);
if (!result.found && map->load_factor < WP_HASH_MAX_LOAD_FACTOR) {
hashTableInsert(map, key, value, result.ref, key_size, value_size);
}
}
void hashMapInsertOrUpdateCapped(WpHashMap *map, void *key, void *value, u64 key_size, u64 value_size) {
WpHashLookupResult result = hashMapFind(map, key, key_size);
if (result.found) {
hashTableUpdate(map, value, result.ref, value_size);
} else if (map->load_factor < WP_HASH_MAX_LOAD_FACTOR) {
hashTableInsert(map, key, value, result.ref, key_size, value_size);
}
}
void hashMapInsertAlloc(const WpAllocator *allocator, WpHashMap *map, void *key, void *value,
u64 key_size, u64 value_size) {
WpHashLookupResult result = hashMapFind(map, key, key_size);
if (!result.found) {
if (map->load_factor >= WP_HASH_MAX_LOAD_FACTOR) {
hashTableGrow(allocator, map, key_size, value_size);
// Rerun key lookup after growing
result = hashMapFind(map, key, key_size);
}
hashTableInsert(map, key, value, result.ref, key_size, value_size);
}
}
void hashMapInsertOrUpdateAlloc(const WpAllocator *allocator, WpHashMap *map, void *key, void *value,
u64 key_size, u64 value_size) {
WpHashLookupResult result = hashMapFind(map, key, key_size);
if (result.found) {
hashTableUpdate(map, value, result.ref, value_size);
} else {
if (map->load_factor >= WP_HASH_MAX_LOAD_FACTOR) {
hashTableGrow(allocator, map, key_size, value_size);
// Rerun key lookup after growing
result = hashMapFind(map, key, key_size);
}
hashTableInsert(map, key, value, result.ref, key_size, value_size);
}
}
void hashMapRemove(WpHashMap *map, void *key, u64 key_size, u64 value_size) {
WpHashLookupResult result = hashMapFind(map, key, key_size);
if (result.found) {
hashTableRemove(map, result.ref, key_size, value_size);
}
}
+134
View File
@@ -0,0 +1,134 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef MAP_H
#define MAP_H
#include "../table/table.h"
#include "../../../common/misc/misc_utils.h"
#include "../../../common/platform/platform.h"
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
typedef WpHashTable WpHashMap;
#ifdef WP_PLATFORM_CPP
#define wpHashMap(KEY_TYPE, VALUE_TYPE, U8_STREAM_ENCODER, KEY_EQ_TEST, CAPACITY) \
(WpHashMap{ \
WP_HASH_TABLE_DEFAULT_HASHER, \
U8_STREAM_ENCODER, \
KEY_EQ_TEST, \
wpArrayWithCapacity(KEY_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \
wpArrayWithCapacity(VALUE_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \
wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_FILLED), \
WP_HASH_TABLE_MAGIC, \
wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \
0.0f, \
true, \
})
#define wpHashMapCustomHasher(KEY_TYPE, VALUE_TYPE, HASHER, U8_STREAM_ENCODER, KEY_EQ_TEST, \
CAPACITY) \
(WpHashMap{ \
HASHER, \
U8_STREAM_ENCODER, \
KEY_EQ_TEST, \
wpArrayWithCapacity(KEY_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \
wpArrayWithCapacity(VALUE_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \
wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_FILLED), \
WP_HASH_TABLE_MAGIC, \
wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \
0.0f, \
true, \
})
#else
#define wpHashMap(KEY_TYPE, VALUE_TYPE, U8_STREAM_ENCODER, KEY_EQ_TEST, CAPACITY) \
((WpHashMap){ \
.hasher = WP_HASH_TABLE_DEFAULT_HASHER, \
.encoder = U8_STREAM_ENCODER, \
.key_eq = KEY_EQ_TEST, \
.keys = wpArrayWithCapacity(KEY_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), \
WP_ARRAY_INIT_NONE), \
.values = wpArrayWithCapacity(VALUE_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), \
WP_ARRAY_INIT_NONE), \
.buckets = wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), \
WP_ARRAY_INIT_FILLED), \
.magic = WP_HASH_TABLE_MAGIC, \
.bucket_mask = wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \
.has_values = true, \
})
#define wpHashMapCustomHasher(KEY_TYPE, VALUE_TYPE, HASHER, U8_STREAM_ENCODER, KEY_EQ_TEST, \
CAPACITY) \
((WpHashMap){ \
.hasher = HASHER, \
.encoder = U8_STREAM_ENCODER, \
.key_eq = KEY_EQ_TEST, \
.keys = wpArrayWithCapacity(KEY_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), \
WP_ARRAY_INIT_NONE), \
.values = wpArrayWithCapacity(VALUE_TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), \
WP_ARRAY_INIT_NONE), \
.buckets = wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), \
WP_ARRAY_INIT_FILLED), \
.magic = WP_HASH_TABLE_MAGIC, \
.bucket_mask = wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \
.has_values = true, \
})
#endif
#define wpHashMapAlloc(KEY_TYPE, VALUE_TYPE, ALLOCATOR_PTR, U8_STREAM_ENCODER, KEY_EQ_TEST, \
CAPACITY) \
hashMapAlloc(ALLOCATOR_PTR, U8_STREAM_ENCODER, KEY_EQ_TEST, CAPACITY, sizeof(KEY_TYPE), \
sizeof(VALUE_TYPE))
#define wpHashMapCustomHasherAlloc(KEY_TYPE, VALUE_TYPE, ALLOCATOR_PTR, HASHER, U8_STREAM_ENCODER, \
KEY_EQ_TEST, CAPACITY) \
hashMapAllocWithCustomHasher(ALLOCATOR_PTR, HASHER, U8_STREAM_ENCODER, KEY_EQ_TEST, CAPACITY, \
sizeof(KEY_TYPE), sizeof(VALUE_TYPE))
#define wpHashMapKeys(KEY_TYPE, MAP_PTR) ((KEY_TYPE *)hashTableKeys(MAP_PTR, sizeof(KEY_TYPE)))
#define wpHashMapValues(VALUE_TYPE, MAP_PTR) \
((VALUE_TYPE *)hashTableValues(MAP_PTR, sizeof(VALUE_TYPE)))
#define wpHashMapCapacity(MAP_PTR) hashTableCapacity(MAP_PTR)
#define wpHashMapCount(MAP_PTR) hashTableCount(MAP_PTR)
#define wpHashMapKeySize(MAP_PTR) hashTableKeySize(MAP_PTR)
#define wpHashMapValueSize(MAP_PTR) hashTableValueSize(MAP_PTR)
#define wpHashMapFind(KEY_TYPE, MAP_PTR, KEY_PTR) hashMapFind(MAP_PTR, KEY_PTR, sizeof(KEY_TYPE))
#define wpHashMapInsertCapped(KEY_TYPE, VALUE_TYPE, MAP_PTR, KEY_PTR, VALUE_PTR) \
hashMapInsertCapped(MAP_PTR, KEY_PTR, VALUE_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE))
#define wpHashMapInsertOrUpdateCapped(KEY_TYPE, VALUE_TYPE, MAP_PTR, KEY_PTR, VALUE_PTR) \
hashMapInsertOrUpdateCapped(MAP_PTR, KEY_PTR, VALUE_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE))
#define wpHashMapUpdate(KEY_TYPE, VALUE_TYPE, MAP_PTR, KEY_PTR, VALUE_PTR) \
hashMapUpdate(MAP_PTR, KEY_PTR, VALUE_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE))
#define wpHashMapInsertAlloc(KEY_TYPE, VALUE_TYPE, ALLOCATOR_PTR, MAP_PTR, KEY_PTR, VALUE_PTR) \
hashMapInsertAlloc(ALLOCATOR_PTR, MAP_PTR, KEY_PTR, VALUE_PTR, sizeof(KEY_TYPE), \
sizeof(VALUE_TYPE))
#define wpHashMapInsertOrUpdateAlloc(KEY_TYPE, VALUE_TYPE, ALLOCATOR_PTR, MAP_PTR, KEY_PTR, \
VALUE_PTR) \
hashMapInsertOrUpdateAlloc(ALLOCATOR_PTR, MAP_PTR, KEY_PTR, VALUE_PTR, sizeof(KEY_TYPE), \
sizeof(VALUE_TYPE))
#define wpHashMapRemove(KEY_TYPE, VALUE_TYPE, MAP_PTR, KEY_PTR) \
hashMapRemove(MAP_PTR, KEY_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE))
#define wpHashMapClear(KEY_TYPE, VALUE_TYPE, MAP_PTR) \
hashTableClear(MAP_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE))
#define wpHashMapDealloc(KEY_TYPE, VALUE_TYPE, ALLOCATOR_PTR, MAP_PTR) \
hashTableDealloc(ALLOCATOR_PTR, MAP_PTR, sizeof(KEY_TYPE), sizeof(VALUE_TYPE))
WpHashMap hashMapAlloc(const WpAllocator *allocator, WpU8StreamEncoder encoder,
WpKeyEqualTest key_eq, u64 capacity, u64 key_size, u64 value_size);
WpHashMap hashMapAllocWithCustomHasher(const WpAllocator *allocator, WpHasher hasher,
WpU8StreamEncoder encoder, WpKeyEqualTest key_eq,
u64 capacity, u64 key_size, u64 value_size);
WpHashLookupResult hashMapFind(WpHashMap *map, void *key, u64 key_size);
void hashMapUpdate(WpHashMap *map, void *key, void *value, u64 key_size, u64 value_size);
void hashMapInsertCapped(WpHashMap *map, void *key, void *value, u64 key_size, u64 value_size);
void hashMapInsertOrUpdateCapped(WpHashMap *map, void *key, void *value, u64 key_size,
u64 value_size);
void hashMapInsertAlloc(const WpAllocator *allocator, WpHashMap *map, void *key,
void *value, u64 key_size, u64 value_size);
void hashMapInsertOrUpdateAlloc(const WpAllocator *allocator, WpHashMap *map, void *key,
void *value, u64 key_size, u64 value_size);
void hashMapRemove(WpHashMap *map, void *key, u64 key_size, u64 value_size);
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !MAP_H
+51
View File
@@ -0,0 +1,51 @@
// vim:fileencoding=utf-8:foldmethod=marker
#include "set.h"
#include "../table/table.h"
WpHashSet hashSetAlloc(const WpAllocator *allocator, WpU8StreamEncoder encoder, WpKeyEqualTest key_eq,
u64 capacity, u64 key_size) {
return hashSetAllocWithCustomHasher(allocator, WP_HASH_TABLE_DEFAULT_HASHER, encoder, key_eq,
capacity, key_size);
}
WpHashSet hashSetAllocWithCustomHasher(const WpAllocator *allocator, WpHasher hasher,
WpU8StreamEncoder encoder, WpKeyEqualTest key_eq, u64 capacity,
u64 key_size) {
WpHashSet set = {0};
hashTableAlloc(allocator, &set, hasher, encoder, key_eq, capacity, key_size, 0, false);
return set;
}
WpHashLookupResult hashSetFind(WpHashSet *set, void *item, u64 item_size) {
u64 hash = hashTableCalcHash(set, item, item_size);
WpHashBucketRef ref = hashTableBucketRef(set, hash);
return hashTableLookup(set, item, ref, item_size);
}
void hashSetInsertCapped(WpHashSet *set, void *item, u64 item_size) {
WpHashLookupResult result = hashSetFind(set, item, item_size);
if (!result.found && set->load_factor < WP_HASH_MAX_LOAD_FACTOR) {
hashTableInsert(set, item, NULL, result.ref, item_size, 0);
}
}
void hashSetInsertAlloc(const WpAllocator *allocator, WpHashSet *set, void *item, u64 item_size) {
WpHashLookupResult result = hashSetFind(set, item, item_size);
if (!result.found) {
if (set->load_factor >= WP_HASH_MAX_LOAD_FACTOR) {
hashTableGrow(allocator, set, item_size, 0);
// Rerun item lookup after growing
result = hashSetFind(set, item, item_size);
}
hashTableInsert(set, item, NULL, result.ref, item_size, 0);
}
}
void hashSetRemove(WpHashSet *set, void *item, u64 item_size) {
WpHashLookupResult result = hashSetFind(set, item, item_size);
if (result.found) {
hashTableRemove(set, result.ref, item_size, 0);
}
}
+105
View File
@@ -0,0 +1,105 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef SET_H
#define SET_H
#include "../table/table.h"
#include "../../../common/misc/misc_utils.h"
#include "../../../common/platform/platform.h"
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
typedef WpHashTable WpHashSet;
#ifdef WP_PLATFORM_CPP
#define wpHashSet(TYPE, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY) \
(WpHashSet{ \
WP_HASH_TABLE_DEFAULT_HASHER, \
U8_STREAM_ENCODER, \
ITEM_EQ_TEST, \
wpArrayWithCapacity(TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \
NULL, \
wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_FILLED), \
WP_HASH_TABLE_MAGIC, \
wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \
0.0f, \
false, \
})
#define wpHashSetCustomHasher(TYPE, HASHER, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY) \
(WpHashSet{ \
HASHER, \
U8_STREAM_ENCODER, \
ITEM_EQ_TEST, \
wpArrayWithCapacity(TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_NONE), \
NULL, \
wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), WP_ARRAY_INIT_FILLED), \
WP_HASH_TABLE_MAGIC, \
wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \
0.0f, \
false, \
})
#else
#define wpHashSet(TYPE, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY) \
((WpHashSet){ \
.hasher = WP_HASH_TABLE_DEFAULT_HASHER, \
.encoder = U8_STREAM_ENCODER, \
.key_eq = ITEM_EQ_TEST, \
.keys = wpArrayWithCapacity(TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), \
WP_ARRAY_INIT_NONE), \
.buckets = wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), \
WP_ARRAY_INIT_FILLED), \
.magic = WP_HASH_TABLE_MAGIC, \
.bucket_mask = wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \
})
#define wpHashSetCustomHasher(TYPE, HASHER, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY) \
((WpHashSet){ \
.hasher = HASHER, \
.encoder = U8_STREAM_ENCODER, \
.key_eq = ITEM_EQ_TEST, \
.keys = wpArrayWithCapacity(TYPE, wpMiscUtilsU64RoundUpPow2(CAPACITY), \
WP_ARRAY_INIT_NONE), \
.buckets = wpArrayWithCapacity(WpHashBucket, wpMiscUtilsU64RoundUpPow2(CAPACITY), \
WP_ARRAY_INIT_FILLED), \
.magic = WP_HASH_TABLE_MAGIC, \
.bucket_mask = wpMiscUtilsU64RoundUpPow2(CAPACITY) - 1, \
})
#endif
#define wpHashSetAlloc(TYPE, ALLOCATOR_PTR, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY) \
hashSetAlloc(ALLOCATOR_PTR, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY, sizeof(TYPE))
#define wpHashSetCustomHasherAlloc(TYPE, ALLOCATOR_PTR, HASHER, U8_STREAM_ENCODER, ITEM_EQ_TEST, \
CAPACITY) \
hashSetAllocWithCustomHasher(ALLOCATOR_PTR, HASHER, U8_STREAM_ENCODER, ITEM_EQ_TEST, CAPACITY, \
sizeof(TYPE))
#define wpHashSetItems(TYPE, SET_PTR) ((TYPE *)hashTableKeys(SET_PTR, sizeof(TYPE)))
#define wpHashSetCapacity(SET_PTR) hashTableCapacity(SET_PTR)
#define wpHashSetCount(SET_PTR) hashTableCount(SET_PTR)
#define wpHashSetItemSize(SET_PTR) hashTableKeySize(SET_PTR)
#define wpHashSetFind(TYPE, SET_PTR, ITEM_PTR) hashSetFind(SET_PTR, ITEM_PTR, sizeof(TYPE))
#define wpHashSetInsertCapped(TYPE, SET_PTR, ITEM_PTR) \
hashSetInsertCapped(SET_PTR, ITEM_PTR, sizeof(TYPE))
#define wpHashSetInsertAlloc(TYPE, ALLOCATOR_PTR, SET_PTR, ITEM_PTR) \
hashSetInsertAlloc(ALLOCATOR_PTR, SET_PTR, ITEM_PTR, sizeof(TYPE))
#define wpHashSetRemove(TYPE, SET_PTR, ITEM_PTR) hashSetRemove(SET_PTR, ITEM_PTR, sizeof(TYPE))
#define wpHashSetClear(TYPE, SET_PTR) hashTableClear(SET_PTR, sizeof(TYPE), 0)
#define wpHashSetDealloc(TYPE, ALLOCATOR_PTR, SET_PTR) \
hashTableDealloc(ALLOCATOR_PTR, SET_PTR, sizeof(TYPE), 0)
WpHashSet hashSetAlloc(const WpAllocator *allocator, WpU8StreamEncoder encoder,
WpKeyEqualTest key_eq, u64 capacity, u64 key_size);
WpHashSet hashSetAllocWithCustomHasher(const WpAllocator *allocator, WpHasher hasher,
WpU8StreamEncoder encoder, WpKeyEqualTest key_eq,
u64 capacity, u64 key_size);
WpHashLookupResult hashSetFind(WpHashSet *set, void *item, u64 item_size);
void hashSetInsertCapped(WpHashSet *set, void *item, u64 item_size);
void hashSetInsertAlloc(const WpAllocator *allocator, WpHashSet *set, void *item,
u64 item_size);
void hashSetRemove(WpHashSet *set, void *item, u64 item_size);
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !SET_H
+292
View File
@@ -0,0 +1,292 @@
// vim:fileencoding=utf-8:foldmethod=marker
#include "table.h"
#include "../hasher/hasher.h"
#include "../../mem/allocator/mem_allocator.h"
#include "../../stream/stream.h"
#include "../../../common/aliases/aliases.h"
#include "../../../common/assert/assert.h"
#include "../../../common/misc/misc_utils.h"
wp_persist inline void *hashTableGetKeyFromBucket(WpHashTable *table, WpHashBucket bucket, u64 key_size);
wp_persist inline void *hashTableGetValueFromBucket(WpHashTable *table, WpHashBucket bucket, u64 value_size);
wp_persist inline u64 hashTableGetDist(u64 dist_and_tag);
wp_persist inline u64 hashTableNextBucket(WpHashTable *table, u64 bucket_index);
wp_persist inline u64 hashTableDistInc(u64 dist_and_tag);
wp_persist inline u64 hashTableDistDec(u64 dist_and_tag);
wp_persist inline void hashTableValidate(const WpHashTable *table, u64 key_size, u64 value_size);
void hashTableAlloc(const WpAllocator *allocator, WpHashTable *table, WpHasher hasher,
WpU8StreamEncoder encoder, WpKeyEqualTest key_eq, u64 capacity, u64 key_size,
u64 value_size, b8 has_values) {
wpDebugAssert(allocator != NULL && table != NULL && encoder != NULL && key_eq != NULL,
"`allocator`, `table`, `encoder` and `key_eq` should not be NULL");
u64 table_capacity = wpMiscUtilsU64RoundUpPow2(capacity);
table->hasher = hasher;
table->encoder = encoder;
table->key_eq = key_eq;
table->keys = arrayAllocCapacity(allocator, table_capacity, WP_ARRAY_INIT_NONE, key_size);;
table->buckets = wpArrayAllocCapacity(WpHashBucket, allocator, table_capacity, WP_ARRAY_INIT_FILLED);
table->magic = WP_HASH_TABLE_MAGIC;
table->bucket_mask = table_capacity - 1;
table->load_factor = WP_HASH_MAX_LOAD_FACTOR;
table->has_values = has_values;
wpRuntimeAssert(table->keys != NULL && table->buckets != NULL, "Failed to allocate keys, buckets or both");
if (has_values) {
table->values = arrayAllocCapacity(allocator, table_capacity, WP_ARRAY_INIT_NONE, value_size);;
wpRuntimeAssert(table->values != NULL, "Failed to allocate values");
}
}
u64 hashTableCalcHash(WpHashTable *table, void *key, u64 key_size) {
hashTableValidate(table, key_size, 0);
wpDebugAssert(key != NULL, "`key` should not be NULL");
WpU8Stream stream = table->encoder(key);
return wpHasherGetHash64(&table->hasher, &stream);
}
WpHashBucketRef hashTableBucketRef(WpHashTable *table, u64 hash) {
hashTableValidate(table, 0, 0);
return (WpHashBucketRef){
.dist_and_tag = WP_HASH_DIST_INC | (hash & WP_HASH_TAG_MASK),
.bucket_index = hash & table->bucket_mask,
};
}
void *hashTableKeys(WpHashTable *table, u64 key_size) {
hashTableValidate(table, key_size, 0);
return table->keys;
}
void *hashTableValues(WpHashTable *table, u64 value_size) {
hashTableValidate(table, 0, value_size);
return table->values;
}
void *hashTableGetKeyFromBucketRef(WpHashTable *table, WpHashBucketRef ref, u64 key_size) {
return hashTableGetKeyFromBucket(table, table->buckets[ref.bucket_index], key_size);
}
void *hashTableGetValueFromBucketRef(WpHashTable *table, WpHashBucketRef ref, u64 value_size) {
return hashTableGetValueFromBucket(table, table->buckets[ref.bucket_index], value_size);
}
u64 hashTableCapacity(WpHashTable *table) {
hashTableValidate(table, 0, 0);
return wpArrayCapacity(table->keys);
}
u64 hashTableCount(WpHashTable *table) {
hashTableValidate(table, 0, 0);
return wpArrayCount(table->keys);
}
u64 hashTableKeySize(WpHashTable *table) {
hashTableValidate(table, 0, 0);
return wpArrayItemSize(table->keys);
}
u64 hashTableValueSize(WpHashTable *table) {
hashTableValidate(table, 0, 0);
return table->has_values ? wpArrayItemSize(table->values) : 0;
}
void hashTableGrow(const WpAllocator *allocator, WpHashTable *table, u64 key_size, u64 value_size) {
hashTableValidate(table, key_size, value_size);
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
u64 capacity = wpMiscUtilsU64RoundUpPow2(hashTableCapacity(table) * 2);
u64 mask = capacity - wpU64Const(1);
WpArray keys = arrayAllocCapacity(allocator, capacity, WP_ARRAY_INIT_NONE, key_size);
WpArray values = NULL;
WpHashBucketArray buckets = wpArrayAllocCapacity(WpHashBucket, allocator, capacity, WP_ARRAY_INIT_FILLED);
wpRuntimeAssert(keys != NULL && buckets != NULL, "Failed to allocate keys, buckets or both");
if (table->has_values) {
values = arrayAllocCapacity(allocator, capacity, WP_ARRAY_INIT_NONE, value_size);
wpRuntimeAssert(values != NULL, "Failed to allocate values");
}
WpArray old_keys = table->keys;
WpArray old_values = table->values;
table->keys = keys;
table->values = values;
table->buckets = buckets;
table->bucket_mask = mask;
for (u64 i = 0; i < wpArrayCount(old_keys); ++i) {
void *key = arrayGet(old_keys, i, key_size);
void *value = table->has_values ? arrayGet(old_values, i, value_size) : NULL;
u64 hash = hashTableCalcHash(table, key, key_size);
WpHashBucketRef ref = hashTableBucketRef(table, hash);
hashTableInsert(table, key, value, ref, key_size, value_size);
}
}
WpHashLookupResult hashTableLookup(WpHashTable *table, void *key, WpHashBucketRef ref, u64 key_size) {
hashTableValidate(table, key_size, 0);
wpDebugAssert(key != NULL, "`key` should not be NULL");
while (true) {
WpHashBucket *bucket = &table->buckets[ref.bucket_index];
if (ref.dist_and_tag == bucket->dist_and_tag) {
b8 key_equal = table->key_eq(key, arrayGet(table->keys, bucket->item_index, key_size));
return (WpHashLookupResult){
.ref.dist_and_tag = ref.dist_and_tag,
.ref.bucket_index = ref.bucket_index,
.found = key_equal,
};
} else if (hashTableGetDist(ref.dist_and_tag) > hashTableGetDist(bucket->dist_and_tag)) {
return (WpHashLookupResult){
.ref.dist_and_tag = ref.dist_and_tag,
.ref.bucket_index = ref.bucket_index,
.found = false,
};
}
ref.dist_and_tag = hashTableDistInc(ref.dist_and_tag);
ref.bucket_index = hashTableNextBucket(table, ref.bucket_index);
}
}
void hashTableUpdate(WpHashTable *table, void *value, WpHashBucketRef ref, u64 value_size) {
hashTableValidate(table, 0, value_size);
if (!(table->has_values)) {
wpRuntimeAssert(0, "Attempting to update a hash table that has no values");
}
wpDebugAssert(value != NULL, "`value` should not be NULL");
WpHashBucket *bucket = &table->buckets[ref.bucket_index];
arraySet(table->values, bucket->item_index, value, value_size);
}
void hashTableInsert(WpHashTable *table, void *key, void *value, WpHashBucketRef ref, u64 key_size,
u64 value_size) {
hashTableValidate(table, key_size, value_size);
wpDebugAssert(key != NULL, "`key` should not be NULL");
u64 index = wpArrayCount(table->keys);
while (true) {
WpHashBucket *bucket = &table->buckets[ref.bucket_index];
if (bucket->dist_and_tag == 0) {
bucket->dist_and_tag = ref.dist_and_tag;
bucket->item_index = index;
break;
} else if (hashTableGetDist(ref.dist_and_tag) > hashTableGetDist(bucket->dist_and_tag)) {
wpMiscUtilsSwap(u64, &ref.dist_and_tag, &bucket->dist_and_tag);
wpMiscUtilsSwap(u64, &index, &bucket->item_index);
}
ref.dist_and_tag = hashTableDistInc(ref.dist_and_tag);
ref.bucket_index = hashTableNextBucket(table, ref.bucket_index);
}
arrayAppendCapped(table->keys, key, key_size);
if (table->has_values) {
arrayAppendCapped(table->values, value, value_size);
}
table->load_factor = (f32)wpArrayCount(table->keys) / (f32)wpArrayCapacity(table->keys);
}
void hashTableRemove(WpHashTable *table, WpHashBucketRef ref, u64 key_size, u64 value_size) {
hashTableValidate(table, key_size, value_size);
WpHashBucket *bucket = &table->buckets[ref.bucket_index];
u64 last_item_index = wpArrayCount(table->keys) - 1;
if (bucket->item_index < last_item_index) {
// Update the bucket for the last item before swapping
u64 hash = hashTableCalcHash(table, arrayGet(table->keys, last_item_index, key_size), key_size);
WpHashLookupResult result = hashTableLookup(table, arrayGet(table->keys, last_item_index, key_size),
hashTableBucketRef(table, hash), key_size);
wpRuntimeAssert(result.found, "Hash table item should exist but it doesn't");
table->buckets[result.ref.bucket_index].item_index = bucket->item_index;
arrayRemoveSwapEnd(table->keys, bucket->item_index, key_size);
if (table->has_values) {
arrayRemoveSwapEnd(table->values, bucket->item_index, value_size);
}
}
while (bucket->dist_and_tag > 0) {
u64 next_index = hashTableNextBucket(table, ref.bucket_index);
WpHashBucket *next = &table->buckets[next_index];
if (hashTableGetDist(next->dist_and_tag) > WP_HASH_DIST_INC) {
bucket->dist_and_tag = hashTableDistDec(next->dist_and_tag);
bucket->item_index = next->item_index;
ref.bucket_index = next_index;
bucket = &table->buckets[ref.bucket_index];
} else {
bucket->dist_and_tag = 0;
bucket->item_index = 0;
}
}
}
void hashTableClear(WpHashTable *table, u64 key_size, u64 value_size) {
hashTableValidate(table, key_size, value_size);
arrayClear(table->keys, key_size);
if (table->has_values) {
arrayClear(table->values, value_size);
}
wpArrayZero(WpHashBucket, table->buckets);
}
void hashTableDealloc(const WpAllocator *allocator, WpHashTable *table, u64 key_size, u64 value_size) {
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
hashTableValidate(table, key_size, value_size);
arrayDealloc(allocator, table->keys, key_size);
wpArrayDealloc(WpHashBucket, allocator, table->buckets);
if (table->has_values) {
arrayDealloc(allocator, table->values, value_size);
}
}
wp_persist inline void *hashTableGetKeyFromBucket(WpHashTable *table, WpHashBucket bucket, u64 key_size) {
hashTableValidate(table, key_size, 0);
return arrayGet(table->keys, bucket.item_index, key_size);
}
wp_persist inline void *hashTableGetValueFromBucket(WpHashTable *table, WpHashBucket bucket, u64 value_size) {
hashTableValidate(table, 0, value_size);
return table->has_values ? arrayGet(table->values, bucket.item_index, value_size) : NULL;
}
wp_persist inline u64 hashTableGetDist(u64 dist_and_tag) {
return dist_and_tag & (~WP_HASH_TAG_MASK);
}
wp_persist inline u64 hashTableNextBucket(WpHashTable *table, u64 bucket_index) {
return (++bucket_index) & table->bucket_mask;
}
wp_persist inline u64 hashTableDistInc(u64 dist_and_tag) {
return dist_and_tag + WP_HASH_DIST_INC;
}
wp_persist inline u64 hashTableDistDec(u64 dist_and_tag) {
return dist_and_tag - WP_HASH_DIST_INC;
}
wp_persist inline void hashTableValidate(const WpHashTable *table, u64 key_size, u64 value_size) {
wpDebugAssert(table != NULL, "`table` should not be NULL");
wpRuntimeAssert(WP_HASH_TABLE_MAGIC == table->magic, "`table` is not a valid wapp hash table");
if (key_size > 0) {
wpRuntimeAssert(key_size == wpArrayItemSize(table->keys), "Invalid key type provided");
}
if (value_size > 0) {
wpRuntimeAssert(value_size == wpArrayItemSize(table->values), "Invalid value type provided");
}
}
+101
View File
@@ -0,0 +1,101 @@
// vim:fileencoding=utf-8:foldmethod=marker
/**
* A hash table implementation with Robin Hood open addressing for collision resolution.
*
* The implementation is based on a version of this C++ implementation:
* https://github.com/martinus/unordered_dense
*
* Extra references:
* - https://faultlore.com/blah/robinhood-part-1/
* - https://blog.hieunt.me/blog/robinhood-hashing
*
* IMPORTANT:
* This is the internal hash table implementation and it is not intended to be used directly.
* Please check ../map/map.h and ../set/set.h for the public APIs.
*/
#ifndef TABLE_H
#define TABLE_H
#include "../hasher/hasher.h"
#include "../hasher/murmur3.h"
#include "../../array/array.h"
#include "../../mem/allocator/mem_allocator.h"
#include "../../stream/stream.h"
#include "../../../common/aliases/aliases.h"
#include "../../../common/platform/platform.h"
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#define WP_HASH_TABLE_MAGIC wpU64Const(0x57504854424c)
#define WP_HASH_TAG_BITS wpU64Const(32)
#define WP_HASH_DIST_INC (wpU64Const(1) << WP_HASH_TAG_BITS)
#define WP_HASH_TAG_MASK (WP_HASH_DIST_INC - wpU64Const(1))
#define WP_HASH_MAX_LOAD_FACTOR 0.65f
#define WP_HASH_TABLE_DEFAULT_HASHER wpX64Mur3Hasher()
typedef WpU8Stream (*WpU8StreamEncoder)(void *data);
typedef b8 (*WpKeyEqualTest)(void *a, void *b);
typedef struct WpHashBucket {
u64 dist_and_tag;
u64 item_index;
} WpHashBucket;
typedef WpHashBucket *WpHashBucketArray;
typedef struct WpHashTable {
WpHasher hasher;
WpU8StreamEncoder encoder;
WpKeyEqualTest key_eq;
WpArray keys;
WpArray values;
WpHashBucketArray buckets;
u64 magic;
u64 bucket_mask;
f32 load_factor;
b8 has_values;
} WpHashTable;
typedef struct WpHashBucketRef {
u64 dist_and_tag;
u64 bucket_index;
} WpHashBucketRef;
typedef struct WpHashLookupResult {
WpHashBucketRef ref;
b8 found;
} WpHashLookupResult;
void hashTableAlloc(const WpAllocator *allocator, WpHashTable *table,
WpHasher hasher, WpU8StreamEncoder encoder,
WpKeyEqualTest key_eq, u64 capacity, u64 key_size,
u64 value_size, b8 has_values);
u64 hashTableCalcHash(WpHashTable *table, void *key, u64 key_size);
WpHashBucketRef hashTableBucketRef(WpHashTable *table, u64 hash);
void *hashTableKeys(WpHashTable *table, u64 key_size);
void *hashTableValues(WpHashTable *table, u64 value_size);
void *hashTableGetKeyFromBucketRef(WpHashTable *table, WpHashBucketRef ref, u64 key_size);
void *hashTableGetValueFromBucketRef(WpHashTable *table, WpHashBucketRef ref, u64 value_size);
u64 hashTableCapacity(WpHashTable *table);
u64 hashTableCount(WpHashTable *table);
u64 hashTableKeySize(WpHashTable *table);
u64 hashTableValueSize(WpHashTable *table);
void hashTableGrow(const WpAllocator *allocator, WpHashTable *table, u64 key_size,
u64 value_size);
WpHashLookupResult hashTableLookup(WpHashTable *table, void *key, WpHashBucketRef ref, u64 key_size);
void hashTableUpdate(WpHashTable *table, void *value, WpHashBucketRef ref, u64 value_size);
void hashTableInsert(WpHashTable *table, void *key, void *value, WpHashBucketRef ref,
u64 key_size, u64 value_size);
void hashTableRemove(WpHashTable *table, WpHashBucketRef ref, u64 key_size, u64 value_size);
void hashTableClear(WpHashTable *table, u64 key_size, u64 value_size);
void hashTableDealloc(const WpAllocator *allocator, WpHashTable *table, u64 key_size,
u64 value_size);
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !TABLE_H
+78
View File
@@ -0,0 +1,78 @@
// vim:fileencoding=utf-8:foldmethod=marker
#include "mem_allocator.h"
#include "../../../common/aliases/aliases.h"
#include "../../../common/assert/assert.h"
#include <stdlib.h>
void *wpMemAllocatorAlloc(const WpAllocator *allocator, u64 size) {
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
if (!wpMemAllocatorOpSupported(allocator, WP_MEM_OP_ALLOC)) {
return NULL;
}
return allocator->alloc(size, allocator->obj);
}
void *wpMemAllocatorAllocAligned(const WpAllocator *allocator, u64 size, u64 alignment) {
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
if (!wpMemAllocatorOpSupported(allocator, WP_MEM_OP_ALLOC_ALIGNED)) {
return NULL;
}
return allocator->alloc_aligned(size, alignment, allocator->obj);
}
void *wpMemAllocatorRealloc(const WpAllocator *allocator, void *ptr, u64 old_size, u64 new_size) {
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
if (!wpMemAllocatorOpSupported(allocator, WP_MEM_OP_REALLOC)) {
return NULL;
}
return allocator->realloc(ptr, old_size, new_size, allocator->obj);
}
void *wpMemAllocatorReallocAligned(const WpAllocator *allocator, void *ptr, u64 old_size,
u64 new_size, u64 alignment) {
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
if (!wpMemAllocatorOpSupported(allocator, WP_MEM_OP_REALLOC_ALIGNED)) {
return NULL;
}
return allocator->realloc_aligned(ptr, old_size, new_size, alignment, allocator->obj);
}
void wpMemAllocatorFree(const WpAllocator *allocator, void **ptr, u64 size) {
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
if (!wpMemAllocatorOpSupported(allocator, WP_MEM_OP_FREE)) {
return;
}
allocator->free(ptr, size, allocator->obj);
}
b8 wpMemAllocatorOpSupported(const WpAllocator *allocator, WpMemOp op) {
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
switch (op) {
case WP_MEM_OP_ALLOC:
return allocator->alloc != NULL;
case WP_MEM_OP_ALLOC_ALIGNED:
return allocator->alloc_aligned != NULL;
case WP_MEM_OP_REALLOC:
return allocator->realloc != NULL;
case WP_MEM_OP_REALLOC_ALIGNED:
return allocator->realloc_aligned != NULL;
case WP_MEM_OP_FREE:
return allocator->free != NULL;
default:
break;
}
return false;
}
+61
View File
@@ -0,0 +1,61 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef MEM_ALLOCATOR_H
#define MEM_ALLOCATOR_H
#include "../../../common/aliases/aliases.h"
#include "../../../common/platform/platform.h"
#include <string.h>
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
typedef enum WpMemOp {
WP_MEM_OP_ALLOC,
WP_MEM_OP_ALLOC_ALIGNED,
WP_MEM_OP_REALLOC,
WP_MEM_OP_REALLOC_ALIGNED,
WP_MEM_OP_FREE,
COUNT_MEM_OPS
} WpMemOp;
typedef void *(WpMemAllocFunc)(u64 size, void *alloc_obj);
typedef void *(WpMemAllocAlignedFunc)(u64 size, u64 alignment, void *alloc_obj);
typedef void *(WpMemReallocFunc)(void *ptr, u64 old_size, u64 new_size, void *alloc_obj);
typedef void *(WpMemReallocAlignedFunc)(void *ptr, u64 old_size, u64 new_size, u64 alignment, void *alloc_obj);
typedef void (WpMemFreeFunc)(void **ptr, u64 size, void *alloc_obj);
typedef struct WpAllocator WpAllocator;
struct WpAllocator {
void *obj;
WpMemAllocFunc *alloc;
WpMemAllocAlignedFunc *alloc_aligned;
WpMemReallocFunc *realloc;
WpMemReallocAlignedFunc *realloc_aligned;
WpMemFreeFunc *free;
};
#ifdef WP_PLATFORM_CPP
#define wpMemAllocatorInvalid(ALLOCATOR) ([&]() { \
WpAllocator alloc{}; \
return memcmp(ALLOCATOR, &alloc, sizeof(WpAllocator)) == 0; \
}())
#else
#define wpMemAllocatorInvalid(ALLOCATOR) (memcmp(ALLOCATOR, &((WpAllocator){0}), sizeof(WpAllocator)) == 0)
#endif // !WP_PLATFORM_CPP
void *wpMemAllocatorAlloc(const WpAllocator *allocator, u64 size);
void *wpMemAllocatorAllocAligned(const WpAllocator *allocator, u64 size, u64 alignment);
void *wpMemAllocatorRealloc(const WpAllocator *allocator, void *ptr, u64 old_size, u64 new_size);
void *wpMemAllocatorReallocAligned(const WpAllocator *allocator, void *ptr, u64 old_size,
u64 new_size, u64 alignment);
void wpMemAllocatorFree(const WpAllocator *allocator, void **ptr, u64 size);
b8 wpMemAllocatorOpSupported(const WpAllocator *allocator, WpMemOp op);
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !MEM_ALLOCATOR_H
+25
View File
@@ -0,0 +1,25 @@
// vim:fileencoding=utf-8:foldmethod=marker
#include "mem_utils.h"
#include "../../../common/aliases/aliases.h"
#include "../../../common/assert/assert.h"
#include "../../../common/misc/misc_utils.h"
#include <stddef.h>
void *wpMemUtilAlignForward(void *ptr, u64 alignment) {
wpDebugAssert(ptr != NULL, "`ptr` should not be NULL");
wpRuntimeAssert(wpMiscUtilsIsPowerOfTwo(alignment), "`alignment` value is not a power of two");
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;
}
+19
View File
@@ -0,0 +1,19 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef MEM_UTILS_H
#define MEM_UTILS_H
#include "../../../common/aliases/aliases.h"
#include "../../../common/platform/platform.h"
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
void *wpMemUtilAlignForward(void *ptr, u64 alignment);
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !MEM_UTILS_H
+108
View File
@@ -0,0 +1,108 @@
// vim:fileencoding=utf-8:foldmethod=marker
#include "queue.h"
#include "../array/array.h"
#include "../../common/assert/assert.h"
#include "../../common/misc/misc_utils.h"
#include <string.h>
void queuePush(WpQueue *queue, void *item, u64 item_size) {
wpDebugAssert(queue != NULL, "`queue` should not be NULL");
wpRuntimeAssert(item_size == wpArrayItemSize(queue->items), "Invalid type");
u64 capacity = wpArrayCapacity(queue->items);
if (queue->count >= capacity) { return; }
u64 index = (queue->back)++;
arraySet(queue->items, index, item, item_size);
++(queue->count);
if (queue->back >= capacity) {
queue->back = 0;
}
}
WpQueue *queuePushAlloc(const WpAllocator *allocator, WpQueue *queue, void *item, u64 item_size) {
wpDebugAssert(allocator != NULL && queue != NULL && item != NULL,
"`allocator`, `queue` and `item` should not be NULL");
wpRuntimeAssert(item_size == wpArrayItemSize(queue->items), "Invalid type");
WpQueue *output = queue;
u64 capacity = wpArrayCapacity(queue->items);
// NOTE (Abdelrahman): Extracted into variable to fix MSVC error
b8 queue_full = queue->count >= capacity;
if (queue_full) {
u64 new_capacity = wpMiscUtilsU64RoundUpPow2(capacity * 2);
u64 array_size = arrayCalcAllocSize(new_capacity, item_size);
u64 alloc_size = sizeof(WpQueue) + array_size;
void *buffer = wpMemAllocatorAlloc(allocator, alloc_size);
if (!buffer) {
goto RETURN_QUEUE_PUSH_ALLOC;
}
memset((void *)buffer, 0, alloc_size);
output = (WpQueue *)buffer;
output->items = arrayFromPreallocatedBuffer((void *)(output + 1), array_size, WP_ARRAY_INIT_FILLED, item_size);
// NOTE (Abdelrahman): When the queue is full, the front and back indices should
// always be the same
u64 front_count = capacity - queue->front;
u64 back_count = queue->back;
void *copy_boundary = (void *)((uptr)(queue->items) + (queue->front * item_size));
memcpy(output->items, copy_boundary, front_count * item_size);
/**
* NOTE (Abdelrahman): Since this is a ring buffer, the elements at the beginning of the array
* aren't always the ones at the front of the queue. When that's the case, the memcpy above
* will only copy a subset of the elements. This is why we need to copy the remaining ones.
*
* Example: Take a queue that looks like this with a capacity of 5 elements
*
* 0 1 | 2 3 4
* ---------------|-----------------------
* | * | * | * | * | * |
* ---------------|-----------------------
* |
* queue_front = 2
* queue_back = 2
*
* In this case, the first memcpy will only copy elements 2-4. The memcpy below will be
* responsible for copying elements 0-1.
*/
b8 items_left_to_copy = back_count > 0;
if (items_left_to_copy) {
void *back_copy_dst = (void *)((uptr)(output->items) + (front_count * item_size));
memcpy(back_copy_dst, queue->items, back_count * item_size);
}
output->front = 0;
output->back = front_count + back_count;
output->count = queue->count;
}
queuePush(output, item, item_size);
RETURN_QUEUE_PUSH_ALLOC:
return output;
}
void *queuePop(WpQueue *queue, u64 item_size) {
wpDebugAssert(queue != NULL, "`queue` should not be NULL");
wpRuntimeAssert(item_size == wpArrayItemSize(queue->items), "Invalid type");
if (queue->count == 0) { return NULL; }
u64 index = (queue->front)++;
--(queue->count);
u64 capacity = wpArrayCapacity(queue->items);
if (queue->front >= capacity) {
queue->front = 0;
}
return arrayGet(queue->items, index, item_size);
}
+105
View File
@@ -0,0 +1,105 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef QUEUE_H
#define QUEUE_H
#include "../array/array.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
typedef struct WpQueue {
WpArray items;
u64 front;
u64 back;
u64 count;
} WpQueue;
// NOTE (Abdelrahman): WpQueue typedefs for readability
typedef WpQueue WpVoidPtrQueue;
typedef WpQueue WpC8Queue;
typedef WpQueue WpC16Queue;
typedef WpQueue WpC32Queue;
typedef WpQueue WpU8Queue;
typedef WpQueue WpU16Queue;
typedef WpQueue WpU32Queue;
typedef WpQueue WpU64Queue;
typedef WpQueue WpB8Queue;
typedef WpQueue WpI8Queue;
typedef WpQueue WpI16Queue;
typedef WpQueue WpI32Queue;
typedef WpQueue WpI64Queue;
typedef WpQueue WpF32Queue;
typedef WpQueue WpF64Queue;
typedef WpQueue WpF128Queue;
typedef WpQueue WpUptrQueue;
typedef WpQueue WpIptrQueue;
typedef WpQueue WpStr8Queue;
#ifdef WP_PLATFORM_CPP
#define wpQueue(TYPE, CAPACITY) ([&]() { \
wp_persist WpArray arr = wpArrayWithCapacity(TYPE, CAPACITY, WP_ARRAY_INIT_FILLED); \
wp_persist WpQueue queue = { \
arr, \
0, \
0, \
0, \
}; \
\
return queue; \
}())
#define wpQueueAlloc(TYPE, ALLOCATOR_PTR, CAPACITY) ([&]() { \
wp_persist WpQueue queue = { \
wpArrayAllocCapacity(TYPE, ALLOCATOR_PTR, CAPACITY, WP_ARRAY_INIT_FILLED), \
0, \
0, \
0, \
}; \
\
return queue; \
}())
#else
#define wpQueue(TYPE, CAPACITY) ((WpQueue){ \
.items = wpArrayWithCapacity(TYPE, CAPACITY, WP_ARRAY_INIT_FILLED), \
.front = 0, \
.back = 0, \
.count = 0, \
})
#define wpQueueAlloc(TYPE, ALLOCATOR_PTR, CAPACITY) ((WpQueue){ \
.items = wpArrayAllocCapacity(TYPE, ALLOCATOR_PTR, CAPACITY, WP_ARRAY_INIT_FILLED), \
.front = 0, \
.back = 0, \
.count = 0, \
})
#endif // !WP_PLATFORM_CPP
#define wpQueueCapacity(QUEUE_PTR) (wpArrayCapacity((QUEUE_PTR)->items))
#define wpQueueItemSize(QUEUE_PTR) (wpArrayItemSize((QUEUE_PTR)->items))
#define wpQueuePush(TYPE, QUEUE_PTR, VALUE_PTR) ( \
queuePush(QUEUE_PTR, VALUE_PTR, sizeof(TYPE)) \
)
#define wpQueuePushAlloc(TYPE, ALLOCATOR_PTR, QUEUE_PTR, VALUE_PTR) ( \
queuePushAlloc(ALLOCATOR_PTR, QUEUE_PTR, VALUE_PTR, sizeof(TYPE)) \
)
#define wpQueuePop(TYPE, QUEUE_PTR) ( \
(TYPE *)queuePop(QUEUE_PTR, sizeof(TYPE)) \
)
#define wpQueueDealloc(TYPE, ALLOCATOR_PTR, QUEUE_PTR) \
(wpArrayDealloc(TYPE, ALLOCATOR_PTR, &((QUEUE_PTR)->items)), \
(QUEUE_PTR)->front = 0, \
(QUEUE_PTR)->back = 0, \
(QUEUE_PTR)->count = 0)
void queuePush(WpQueue *queue, void *item, u64 item_size);
WpQueue *queuePushAlloc(const WpAllocator *allocator, WpQueue *queue, void *item, u64 item_size);
void *queuePop(WpQueue *queue, u64 item_size);
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !QUEUE_H
+43
View File
@@ -0,0 +1,43 @@
// vim:fileencoding=utf-8:foldmethod=marker
#include "stream.h"
#include "../../common/aliases/aliases.h"
#include "../../common/assert/assert.h"
void streamValidate(const WpStream *stream, u64 item_size);
b8 wpStreamAtEnd(const WpStream *stream) {
return stream->position >= stream->count;
}
b8 streamHasCount(const WpStream *stream, u64 count) {
return stream->position + count <= stream->count;
}
void *streamPeekWithOffset(const WpStream *stream, u64 offset, u64 item_size) {
streamValidate(stream, item_size);
if (streamHasCount(stream, offset + 1)) {
u64 index = (stream->position + offset) * stream->item_size;
return (void *)(&((u8 *)(stream->data))[index]);
}
return NULL;
}
void *streamConsumeCount(WpStream *stream, u64 count, u64 item_size) {
streamValidate(stream, item_size);
if (streamHasCount(stream, count)) {
u64 index = stream->position * stream->item_size;
stream->position += count;
return (void *)(&((u8 *)(stream->data))[index]);
}
return NULL;
}
void streamValidate(const WpStream *stream, u64 item_size) {
wpDebugAssert(stream != NULL, "`stream` should not be NULL");
wpRuntimeAssert(stream->item_size == item_size, "Invalid item type provided");
}
+80
View File
@@ -0,0 +1,80 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef STREAM_H
#define STREAM_H
#include "../../common/aliases/aliases.h"
#include "../../common/platform/platform.h"
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
typedef struct WpStream {
void *data;
u64 count;
u64 position;
u64 item_size;
} WpStream;
// NOTE (Abdelrahman): WpStream typedefs for readability
typedef WpStream WpVoidPtrStream;
typedef WpStream WpC8Stream;
typedef WpStream WpC16Stream;
typedef WpStream WpC32Stream;
typedef WpStream WpU8Stream;
typedef WpStream WpU16Stream;
typedef WpStream WpU32Stream;
typedef WpStream WpU64Stream;
typedef WpStream WpB8Stream;
typedef WpStream WpI8Stream;
typedef WpStream WpI16Stream;
typedef WpStream WpI32Stream;
typedef WpStream WpI64Stream;
typedef WpStream WpF32Stream;
typedef WpStream WpF64Stream;
typedef WpStream WpF128Stream;
typedef WpStream WpUptrStream;
typedef WpStream WpIptrStream;
typedef WpStream WpStr8Stream;
#ifdef WP_PLATFORM_CPP
#define wpStream(TYPE, DATA, SIZE) ([&]() { \
return WpStream{ \
DATA, \
SIZE / sizeof(TYPE), \
0, \
sizeof(TYPE) \
}; \
}())
#else
#define wpStream(TYPE, DATA, SIZE) ( \
(WpStream){ \
.data = DATA, \
.count = SIZE / sizeof(TYPE), \
.position = 0, \
.item_size = sizeof(TYPE) \
} \
)
#endif
#define wpStreamHasNext(STREAM) streamHasCount(STREAM, 1)
#define wpStreamHasCount(STREAM, COUNT) streamHasCount(STREAM, COUNT)
#define wpStreamPeek(TYPE, STREAM) ((TYPE *)streamPeekWithOffset(STREAM, 0, sizeof(TYPE)))
#define wpStreamPeekWithOffset(TYPE, STREAM, OFFSET) \
((TYPE *)streamPeekWithOffset(STREAM, OFFSET, sizeof(TYPE)))
#define wpStreamConsume(TYPE, STREAM) ((TYPE *)streamConsumeCount(STREAM, 1, sizeof(TYPE)))
#define wpStreamConsumeCount(TYPE, STREAM, COUNT) \
((TYPE *)streamConsumeCount(STREAM, COUNT, sizeof(TYPE)))
b8 wpStreamAtEnd(const WpStream *stream);
b8 streamHasCount(const WpStream *stream, u64 count);
void *streamPeekWithOffset(const WpStream *stream, u64 offset, u64 item_size);
void *streamConsumeCount(WpStream *stream, u64 count, u64 item_size);
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !STREAM_H
+485
View File
@@ -0,0 +1,485 @@
// vim:fileencoding=utf-8:foldmethod=marker
#include "str8.h"
#include "../../array/array.h"
#include "../../mem/allocator/mem_allocator.h"
#include "../../../common/aliases/aliases.h"
#include "../../../common/assert/assert.h"
#include <ctype.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define STR8_BUF_ALLOC_SIZE(CAPACITY) (sizeof(WpStr8) + sizeof(c8) * CAPACITY)
WpStr8 *wpStr8AllocBuf(const WpAllocator *allocator, u64 capacity) {
wpDebugAssert(allocator != NULL, "`allocator` should not be NULL");
WpStr8 *str = wpMemAllocatorAlloc(allocator, STR8_BUF_ALLOC_SIZE(capacity));
if (!str) {
goto RETURN_STR8;
}
str->buf = (u8 *)str + sizeof(WpStr8);
str->size = 0;
str->capacity = capacity;
RETURN_STR8:
return str;
}
WpStr8 *wpStr8AllocAndFillBuf(const WpAllocator *allocator, u64 capacity) {
WpStr8 *out = wpStr8AllocBuf(allocator, capacity);
if (out) {
memset(out->buf, 0, capacity);
out->size = capacity;
}
return out;
}
WpStr8 *wpStr8AllocCstr(const WpAllocator *allocator, const char *str) {
wpDebugAssert(allocator != NULL && str != NULL, "`allocator` and `str` should not be NULL");
u64 length = strlen(str);
WpStr8 *output = wpStr8AllocBuf(allocator, length * 2);
if (!output) {
goto RETURN_ALLOC_CSTR;
}
output->size = length;
memcpy(output->buf, str, length);
RETURN_ALLOC_CSTR:
return output;
}
WpStr8 *wpStr8AllocStr8(const WpAllocator *allocator, WpStr8RO *str) {
wpDebugAssert(allocator != NULL && str != NULL, "`allocator` and `str` should not be NULL");
WpStr8 *output = wpStr8AllocBuf(allocator, str->capacity);
if (!output) {
goto RETURN_ALLOC_STR8;
}
output->size = str->size;
memcpy(output->buf, str->buf, str->size);
RETURN_ALLOC_STR8:
return output;
}
WpStr8 *wpStr8AllocSubstr(const WpAllocator *allocator, WpStr8RO *str, u64 start, u64 end) {
wpDebugAssert(allocator != NULL && str != NULL, "`allocator` and `str` should not be NULL");
WpStr8 *output = NULL;
if (start >= str->size || start >= end) {
goto RETURN_ALLOC_SUBSTR;
}
if (end > str->size) {
end = str->size;
}
output = wpStr8AllocBuf(allocator, str->capacity);
if (!output) {
goto RETURN_ALLOC_SUBSTR;
}
output->size = end - start;
memcpy(output->buf, str->buf + start, output->size);
RETURN_ALLOC_SUBSTR:
return output;
}
void wpStr8DeallocBuf(const WpAllocator *allocator, WpStr8 **str) {
wpDebugAssert(allocator != NULL && str != NULL && (*str) != NULL, "Either `allocator` is NULL or `str` is an invalid double pointer");
wpMemAllocatorFree(allocator, (void **)str, STR8_BUF_ALLOC_SIZE((*str)->capacity));
}
c8 wpStr8Get(const WpStr8 *str, u64 index) {
if (index >= str->size) {
return '\0';
}
return str->buf[index];
}
void wpStr8Set(WpStr8 *str, u64 index, c8 c) {
if (index >= str->size) {
return;
}
str->buf[index] = c;
}
void wpStr8Clear(WpStr8 *str) {
memset(str->buf, 0, str->capacity);
str->size = 0;
}
void wpStr8PushBack(WpStr8 *str, c8 c) {
if (!(str->size < str->capacity)) {
return;
}
u64 index = (str->size)++;
wpStr8Set(str, index, c);
}
b8 wpStr8Equal(WpStr8RO *s1, WpStr8RO *s2) {
if (s1->size != s2->size) {
return false;
}
return wpStr8EqualToCount(s1, s2, s1->size);
}
b8 wpStr8EqualToCount(WpStr8RO* s1, WpStr8RO* s2, u64 count) {
if (!s1 || !s2) {
return false;
}
return memcmp(s1->buf, s2->buf, count) == 0;
}
WpStr8 wpStr8Slice(WpStr8RO *str, u64 start, u64 end) {
if (start >= str->size || start >= end) {
start = str->size;
end = str->size;
}
if (end > str->size) {
end = str->size;
}
return (WpStr8RO){
.capacity = end - start,
.size = end - start,
.buf = str->buf + start,
};
}
WpStr8 *wpStr8AllocConcat(const WpAllocator *allocator, WpStr8 *dst, WpStr8RO *src) {
wpDebugAssert(allocator != NULL && dst != NULL && src != NULL, "`allocator`, `dst` and `src` should not be NULL");
WpStr8 *output = NULL;
u64 remaining = dst->capacity - dst->size;
if (src->size <= remaining) {
output = dst;
goto SOURCE_STRING_STR8_CONCAT;
}
u64 capacity = dst->capacity + src->size;
output = wpStr8AllocBuf(allocator, capacity);
if (!output) {
goto RETURN_STR8_CONCAT;
}
wpStr8ConcatCapped(output, dst);
SOURCE_STRING_STR8_CONCAT:
wpStr8ConcatCapped(output, src);
RETURN_STR8_CONCAT:
return output;
}
void wpStr8ConcatCapped(WpStr8 *dst, WpStr8RO *src) {
wpDebugAssert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL");
u64 remaining = dst->capacity - dst->size;
u64 to_copy = remaining < src->size ? remaining : src->size;
memcpy(dst->buf + dst->size, src->buf, to_copy);
dst->size += to_copy;
}
void wpStr8CopyCstrCapped(WpStr8 *dst, const char *src) {
wpDebugAssert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL");
u64 length = strlen(src);
u64 to_copy = length <= dst->capacity ? length : dst->capacity;
memset(dst->buf, 0, dst->size);
memcpy(dst->buf, src, to_copy);
dst->size = to_copy;
}
void wpStr8CopyStr8Capped(WpStr8 *dst, WpStr8RO *src) {
wpDebugAssert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL");
u64 to_copy = src->size <= dst->capacity ? src->size : dst->capacity;
memset(dst->buf, 0, dst->size);
memcpy(dst->buf, src->buf, to_copy);
dst->size = to_copy;
}
void wpStr8CopyToCstr(char *dst, WpStr8RO *src, u64 dst_capacity) {
wpDebugAssert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL");
u64 to_copy = src->size < dst_capacity ? src->size : dst_capacity - 1;
memset(dst, 0, dst_capacity);
memcpy(dst, src->buf, to_copy);
}
void wpStr8Format(WpStr8 *dst, const char *format, ...) {
wpDebugAssert(dst != NULL && format != NULL, "`dst` and `format` should not be NULL");
va_list args1;
va_list args2;
va_start(args1, format);
va_copy(args2, args1);
u64 total_size = vsnprintf(NULL, 0, format, args1);
dst->size = total_size <= dst->capacity ? total_size : dst->capacity;
vsnprintf((char *)(dst->buf), dst->capacity, format, args2);
va_end(args1);
va_end(args2);
}
void wpStr8ToLower(WpStr8 *dst, WpStr8RO *src) {
wpDebugAssert(src != NULL && dst != NULL, "`dst` and `src` should not be NULL");
wpDebugAssert(dst->capacity >= src->capacity, "`dst` does not have enough capacity");
dst->size = src->size;
// NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of
// MSVC Spectre mitigation warnings
u64 index = 0;
b8 running = true;
while (running) {
wpStr8Set(dst, index, (u8)tolower(wpStr8Get(src, index)));
++index;
running = index < src->size;
}
}
void wpStr8ToUpper(WpStr8 *dst, WpStr8RO *src) {
wpDebugAssert(src != NULL && dst != NULL, "`dst` and `src` should not be NULL");
wpDebugAssert(dst->capacity >= src->capacity, "`dst` does not have enough capacity");
dst->size = src->size;
// NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of
// MSVC Spectre mitigation warnings
u64 index = 0;
b8 running = true;
while (running) {
wpStr8Set(dst, index, (u8)toupper(wpStr8Get(src, index)));
++index;
running = index < src->size;
}
}
void wpStr8FromBytes(WpStr8 *dst, const WpU8Array src) {
wpDebugAssert(src != NULL && dst != NULL, "`dst` and `src` should not be NULL");
u64 size = wpArrayCount(src) * wpArrayItemSize(src);
wpDebugAssert(dst->capacity >= size, "`dst` does not have enough capacity");
dst->size = size;
memcpy(dst->buf, src, size);
}
i64 wpStr8Find(WpStr8RO *str, WpStr8RO substr) {
if (!str || substr.size > str->size) {
return -1;
}
// NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of
// MSVC Spectre mitigation warnings
u64 char_index = 0;
b8 running = char_index < str->size;
while (running) {
const c8 *sub = str->buf + char_index;
if (memcmp(sub, substr.buf, substr.size) == 0) {
return char_index;
}
++char_index;
running = char_index < str->size;
}
return -1;
}
i64 wpStr8Rfind(WpStr8RO *str, WpStr8RO substr) {
if (!str || substr.size > str->size) {
return -1;
}
// NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of
// MSVC Spectre mitigation warnings
i64 char_index = str->size - substr.size;
b8 running = char_index >= 0;
while (running) {
const c8 *sub = str->buf + char_index;
if (memcmp(sub, substr.buf, substr.size) == 0) {
return char_index;
}
--char_index;
running = char_index >= 0;
}
return -1;
}
WpStr8List *wpStr8SplitWithMax(const WpAllocator *allocator, WpStr8RO *str, WpStr8RO *delimiter, i64 max_splits) {
wpDebugAssert(allocator != NULL && str != NULL && delimiter != NULL, "`allocator`, `str` and `delimiter` should not be NULL");
WpStr8List *output = wpDblListAlloc(WpStr8, allocator);
if (delimiter->size > str->size) {
WpStr8 *full = wpStr8AllocStr8(allocator, str);
if (full) {
wpDblListPushBackAlloc(WpStr8, allocator, output, full);
}
goto RETURN_STR8_SPLIT;
}
i64 start = 0;
i64 end = 0;
i64 splits = 0;
WpStr8 *rest = wpStr8AllocStr8(allocator, str);
WpStr8 *before_str;
while ((end = wpStr8Find(rest, *delimiter)) != -1) {
if (max_splits > 0 && splits >= max_splits) {
break;
}
before_str = wpStr8AllocSubstr(allocator, str, start, start + end);
if (before_str) {
wpDblListPushBackAlloc(WpStr8, allocator, output, before_str);
}
wpMemAllocatorFree(allocator, (void **)&rest, sizeof(WpStr8));
rest = wpStr8AllocSubstr(allocator, str, start + end + delimiter->size, str->size);
start += end + delimiter->size;
++splits;
}
// Ensure the last part of the string after the delimiter is added to the list
rest = wpStr8AllocSubstr(allocator, str, start, str->size);
if (rest) {
wpDblListPushBackAlloc(WpStr8, allocator, output, rest);
}
RETURN_STR8_SPLIT:
return output;
}
WpStr8List *wpStr8RsplitWithMax(const WpAllocator *allocator, WpStr8RO *str, WpStr8RO *delimiter, i64 max_splits) {
wpDebugAssert(allocator != NULL && str != NULL && delimiter != NULL, "`allocator`, `str` and `delimiter` should not be NULL");
WpStr8List *output = wpDblListAlloc(WpStr8, allocator);
if (delimiter->size > str->size) {
WpStr8 *full = wpStr8AllocStr8(allocator, str);
if (full) {
wpDblListPushBackAlloc(WpStr8, allocator, output, full);
}
goto RETURN_STR8_SPLIT;
}
i64 end = 0;
i64 splits = 0;
WpStr8 *rest = wpStr8AllocStr8(allocator, str);
WpStr8 *after_str;
while ((end = wpStr8Rfind(rest, *delimiter)) != -1) {
if (max_splits > 0 && splits >= max_splits) {
break;
}
after_str = wpStr8AllocSubstr(allocator, rest, end + delimiter->size, str->size);
if (after_str) {
wpDblListPushFrontAlloc(WpStr8, allocator, output, after_str);
}
wpMemAllocatorFree(allocator, (void **)&rest, sizeof(WpStr8));
rest = wpStr8AllocSubstr(allocator, rest, 0, end);
++splits;
}
rest = wpStr8AllocSubstr(allocator, str, 0, rest->size);
if (rest) {
wpDblListPushFrontAlloc(WpStr8, allocator, output, rest);
}
RETURN_STR8_SPLIT:
return output;
}
WpStr8 *wpStr8Join(const WpAllocator *allocator, const WpStr8List *list, WpStr8RO *delimiter) {
wpDebugAssert(allocator != NULL && list != NULL && delimiter != NULL, "`allocator`, `list` and `delimiter` should not be NULL");
u64 capacity = wpStr8ListTotalSize(list) + (delimiter->size * (list->node_count - 1));
WpStr8 *output = wpStr8AllocBuf(allocator, capacity * 2);
// NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of
// MSVC Spectre mitigation warnings
WpStr8 *node;
u64 node_index = 0;
b8 running = node_index < list->node_count;
while (running) {
node = wpDblListGet(WpStr8, list, node_index);
if (!node) {
break;
}
wpStr8ConcatCapped(output, node);
// NOTE (Abdelrahman): Comparison extracted to variable to silence
// MSVC Spectre mitigation warnings
b8 not_last = node_index + 1 < list->node_count;
if (not_last) {
wpStr8ConcatCapped(output, delimiter);
}
++node_index;
running = node_index < list->node_count;
}
return output;
}
u64 wpStr8ListTotalSize(const WpStr8List *list) {
if (!list) {
return 0;
}
// NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of
// MSVC Spectre mitigation warnings
WpStr8 *node;
u64 node_index = 0;
u64 output = 0;
b8 running = node_index < list->node_count;
while (running) {
node = wpDblListGet(WpStr8, list, node_index);
if (!node) {
break;
}
output += node->size;
++node_index;
running = node_index < list->node_count;
}
return output;
}
+131
View File
@@ -0,0 +1,131 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef STR8_H
#define STR8_H
#include "../../../common/aliases/aliases.h"
#include "../../../common/assert/assert.h"
#include "../../../common/platform/platform.h"
#include "../../array/array.h"
#include "../../dbl_list/dbl_list.h"
#include "../../mem/allocator/mem_allocator.h"
#include <string.h>
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
typedef struct WpStr8 WpStr8;
struct WpStr8 {
u64 capacity;
u64 size;
c8 *buf;
};
typedef const WpStr8 WpStr8RO;
/**
* Utilities to be used with printf functions
*/
#define WP_STR8_SPEC ".*s"
#define wpStr8Varg(STRING) (int)((STRING).size), (STRING).buf
/**
* WpStr8 stack buffers
*/
#ifdef WP_PLATFORM_CPP
// Uses a lambda to achieve the same behaviour achieved by the C macro
#define wpStr8Buf(CAPACITY) ([&](){ \
wp_persist c8 buf[CAPACITY] = {}; \
memset(buf, 0, CAPACITY); \
return WpStr8{CAPACITY, 0, buf}; \
}())
// Uses a lambda to achieve the same behaviour achieved by the C macro
#define wpStr8Lit(STRING) ([&]() { \
wp_persist c8 buf[sizeof(STRING) * 2] = {}; \
memcpy(buf, STRING, sizeof(STRING)); \
return WpStr8{(sizeof(STRING) - 1) * 2, sizeof(STRING) - 1, buf}; \
}())
#define wpStr8LitRo(STRING) WpStr8RO{sizeof(STRING) - 1, sizeof(STRING) - 1, (c8 *)STRING}
#define wpStr8LitRoInitialiserList(STRING) {sizeof(STRING) - 1, sizeof(STRING) - 1, (c8 *)STRING}
#else
#define wpStr8Buf(CAPACITY) ((WpStr8){.capacity = CAPACITY, .size = 0, .buf = (c8[CAPACITY]){0}})
// Utilises the fact that memcpy returns pointer to dest buffer and that getting
// address of compound literals is valid in C to create a string on the stack
#define wpStr8Lit(STRING) ((WpStr8){.capacity = (sizeof(STRING) - 1) * 2, \
.size = sizeof(STRING) - 1, \
.buf = memcpy(&((c8 [sizeof(STRING) * 2]){0}), \
STRING, \
sizeof(STRING))})
#define wpStr8LitRo(STRING) ((WpStr8RO){.capacity = sizeof(STRING) - 1, \
.size = sizeof(STRING) - 1, \
.buf = (c8 *)STRING})
// To be used only when initialising a static storage variable in compilers that don't support
// initialisers with the syntax of wpStr8LitRo (e.g. gcc). Should only be used when necessary
// and only be assigned to a WpStr8RO variable to avoid any attempt at modifying the string
#define wpStr8LitRoInitialiserList(STRING) {.capacity = sizeof(STRING) - 1, \
.size = sizeof(STRING) - 1, \
.buf = (c8 *)STRING}
#endif // !WP_PLATFORM_CPP
/**
* WpStr8 allocated buffers
*/
WpStr8 *wpStr8AllocBuf(const WpAllocator *allocator, u64 capacity);
WpStr8 *wpStr8AllocAndFillBuf(const WpAllocator *allocator, u64 capacity);
WpStr8 *wpStr8AllocCstr(const WpAllocator *allocator, const char *str);
WpStr8 *wpStr8AllocStr8(const WpAllocator *allocator, WpStr8RO *str);
WpStr8 *wpStr8AllocSubstr(const WpAllocator *allocator, WpStr8RO *str, u64 start, u64 end);
WpStr8 *wpStr8AllocConcat(const WpAllocator *allocator, WpStr8 *dst, WpStr8RO *src);
// 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.
void wpStr8DeallocBuf(const WpAllocator *allocator, WpStr8 **str);
/**
* WpStr8 utilities
*/
c8 wpStr8Get(WpStr8RO *str, u64 index);
void wpStr8Set(WpStr8 *str, u64 index, c8 c);
void wpStr8Clear(WpStr8 *str);
void wpStr8PushBack(WpStr8 *str, c8 c);
b8 wpStr8Equal(WpStr8RO *s1, WpStr8RO *s2);
b8 wpStr8EqualToCount(WpStr8RO* s1, WpStr8RO* s2, u64 count);
WpStr8 wpStr8Slice(WpStr8RO *str, u64 start, u64 end);
void wpStr8ConcatCapped(WpStr8 *dst, WpStr8RO *src);
void wpStr8CopyCstrCapped(WpStr8 *dst, const char *src);
void wpStr8CopyStr8Capped(WpStr8 *dst, WpStr8RO *src);
void wpStr8CopyToCstr(char *dst, WpStr8RO *src, u64 dst_capacity);
void wpStr8Format(WpStr8 *dst, const char *format, ...);
void wpStr8ToLower(WpStr8 *dst, WpStr8RO *src);
void wpStr8ToUpper(WpStr8 *dst, WpStr8RO *src);
void wpStr8FromBytes(WpStr8 *dst, const WpU8Array src);
/**
* WpStr8 find functions
*/
i64 wpStr8Find(WpStr8RO *str, WpStr8RO substr);
i64 wpStr8Rfind(WpStr8RO *str, WpStr8RO substr);
/**
* WpStr8 split and join
*/
#define wpStr8Split(ALLOCATOR, STR, DELIMITER) wpStr8SplitWithMax(ALLOCATOR, STR, DELIMITER, -1)
#define wpStr8Rsplit(ALLOCATOR, STR, DELIMITER) wpStr8RsplitWithMax(ALLOCATOR, STR, DELIMITER, -1)
WpStr8List *wpStr8SplitWithMax(const WpAllocator *allocator, WpStr8RO *str, WpStr8RO *delimiter, i64 max_splits);
WpStr8List *wpStr8RsplitWithMax(const WpAllocator *allocator, WpStr8RO *str, WpStr8RO *delimiter, i64 max_splits);
WpStr8 *wpStr8Join(const WpAllocator *allocator, const WpStr8List *list, WpStr8RO *delimiter);
/**
* WpStr8 list utilities
*/
u64 wpStr8ListTotalSize(const WpStr8List *list);
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !STR8_H
+21
View File
@@ -0,0 +1,21 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef WAPP_BASE_C
#define WAPP_BASE_C
#include "wapp_base.h"
#include "array/array.c"
#include "dbl_list/dbl_list.c"
#include "hash/hasher/hasher.c"
#include "hash/hasher/murmur3.c"
#include "hash/hasher/siphash.c"
#include "hash/table/table.c"
#include "hash/set/set.c"
#include "hash/map/map.c"
#include "queue/queue.c"
#include "stream/stream.c"
#include "mem/allocator/mem_allocator.c"
#include "mem/utils/mem_utils.c"
#include "strings/str8/str8.c"
#endif // !WAPP_BASE_C
+21
View File
@@ -0,0 +1,21 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef WAPP_BASE_H
#define WAPP_BASE_H
#include "array/array.h"
#include "dbl_list/dbl_list.h"
#include "hash/hasher/hasher.h"
#include "hash/hasher/murmur3.h"
#include "hash/hasher/siphash.h"
#include "hash/table/table.h"
#include "hash/set/set.h"
#include "hash/map/map.h"
#include "queue/queue.h"
#include "stream/stream.h"
#include "mem/allocator/mem_allocator.h"
#include "mem/utils/mem_utils.h"
#include "strings/str8/str8.h"
#include "../common/wapp_common.h"
#endif // !WAPP_BASE_H
+59 -27
View File
@@ -1,45 +1,77 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef ALIASES_H
#define ALIASES_H
#include "../platform/platform.h"
#include <stdint.h>
#if WAPP_PLATFORM_C_VERSION >= WAPP_PLATFORM_C11_VERSION
#if defined(WP_PLATFORM_C) && WP_PLATFORM_C_VERSION >= WP_PLATFORM_C11_VERSION && !defined(WP_PLATFORM_APPLE)
#include <uchar.h>
#if WAPP_PLATFORM_C_VERSION >= WAPP_PLATFORM_C23_VERSION
#define c8 char8_t
#if WP_PLATFORM_C_VERSION >= WP_PLATFORM_C23_VERSION
typedef char8_t c8;
#else
#define c8 uint8_t
#endif // !WAPP_PLATFORM_C23_VERSION
typedef uint8_t c8;
#endif // !WP_PLATFORM_C23_VERSION
#define c16 char16_t
#define c32 char32_t
typedef char16_t c16;
typedef char32_t c32;
#else
#define c8 uint8_t
#define c16 uint16_t
#define c32 uint32_t
#endif // !WAPP_PLATFORM_C11_VERSION
typedef uint8_t c8;
typedef uint16_t c16;
typedef uint32_t c32;
#endif // !WP_PLATFORM_C
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define u64 uint64_t
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#define i8 int8_t
#define i16 int16_t
#define i32 int32_t
#define i64 int64_t
#define wpU8Const UINT8_C
#define wpU16Const UINT16_C
#define wpU32Const UINT32_C
#define wpU64Const UINT64_C
#define f32 float
#define f64 double
#define f128 long double
typedef uint8_t b8;
#define uptr uintptr_t
#define iptr intptr_t
#ifndef WP_PLATFORM_CPP
#define external extern
#define internal static
#define persistent static
#ifndef false
#define false (b8)0
#endif // !false
#ifndef true
#define true (b8)1
#endif // !true
#endif // !WP_PLATFORM_CPP
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
#define wpI8Const INT8_C
#define wpI16Const INT16_C
#define wpI32Const INT32_C
#define wpI64Const INT64_C
typedef float f32;
typedef double f64;
typedef long double f128;
typedef uintptr_t uptr;
typedef intptr_t iptr;
#define wp_extern extern
#define wp_intern static
#define wp_persist static
#ifdef WP_PLATFORM_CPP
#define wp_class_mem static
#define BEGIN_C_LINKAGE wp_extern "C" {
#define END_C_LINKAGE }
#endif // WP_PLATFORM_CPP
#endif // !ALIASES_H
+61
View File
@@ -0,0 +1,61 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef WAPP_ASSERT_H
#define WAPP_ASSERT_H
#include "../aliases/aliases.h"
#include "../platform/platform.h"
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#define wpStaticAssert(EXPR, MSG) wp_extern char ASSERTION_FAILED[EXPR ? 1 : -1]
#ifndef WP_NO_RUNTIME_ASSERT
#define wpRuntimeAssert(EXPR, MSG) runtimeAssert(EXPR, MSG)
#else
#define wpRuntimeAssert(EXPR, MSG)
#endif
#ifdef WP_DEBUG_ASSERT
#define wpDebugAssert(EXPR, MSG) wpRuntimeAssert(EXPR, MSG)
#else
#define wpDebugAssert(EXPR, MSG)
#endif
#ifdef WP_PLATFORM_WINDOWS
#define runtimeAssert(EXPR, MSG) do { \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
if (!(EXPR)) { \
__pragma(warning(pop)) \
runtimeAssertFailed(EXPR, MSG); \
} \
} while(false)
#else
#define runtimeAssert(EXPR, MSG) do { \
if (!(EXPR)) { \
runtimeAssertFailed(EXPR, MSG); \
} \
} while(false)
#endif // !WP_PLATFORM_WINDOWS
#define runtimeAssertFailed(EXPR, MSG) do { \
fprintf( \
stderr, \
"%s:%d (In function `%s`): Assertion failed (%" PRIu32 ")\nDiagnostic: %s\n\n", \
__FILE__, __LINE__, __func__, \
EXPR, MSG \
); \
abort(); \
} while(false)
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#endif // !WAPP_ASSERT_H
+42 -7
View File
@@ -1,14 +1,29 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef MISC_UTILS_H
#define MISC_UTILS_H
#include "../aliases/aliases.h"
#define KB(SIZE) (SIZE * 1024ull)
#define MB(SIZE) (KB(SIZE) * 1024)
#define GB(SIZE) (MB(SIZE) * 1024)
#define TB(SIZE) (GB(SIZE) * 1024)
#ifdef WP_PLATFORM_CPP
BEGIN_C_LINKAGE
#endif // !WP_PLATFORM_CPP
#define wapp_misc_utils_padding_size(SIZE) u8 reserved_padding[sizeof(void *) - ((SIZE) % sizeof(void *))]
#define KiB(SIZE) (((u64)SIZE) << 10)
#define MiB(SIZE) (((u64)SIZE) << 20)
#define GiB(SIZE) (((u64)SIZE) << 30)
#define TiB(SIZE) (((u64)SIZE) << 40)
#define PiB(SIZE) (((u64)SIZE) << 50)
#define EiB(SIZE) (((u64)SIZE) << 60)
#define KB(SIZE) (((u64)SIZE) * 1000llu)
#define MB(SIZE) (KB(SIZE) * 1000llu)
#define GB(SIZE) (MB(SIZE) * 1000llu)
#define TB(SIZE) (GB(SIZE) * 1000llu)
#define PB(SIZE) (TB(SIZE) * 1000llu)
#define EB(SIZE) (PB(SIZE) * 1000llu)
#define wpMiscUtilsReservePadding(SIZE) u8 reserved_padding[sizeof(void *) - ((SIZE) % sizeof(void *))]
#define U64_RSHIFT_OR_1(X) (((u64)X) | (((u64)X) >> 1))
#define U64_RSHIFT_OR_2(X) (((u64)X) | (((u64)X) >> 2))
@@ -16,7 +31,7 @@
#define U64_RSHIFT_OR_8(X) (((u64)X) | (((u64)X) >> 8))
#define U64_RSHIFT_OR_16(X) (((u64)X) | (((u64)X) >> 16))
#define U64_RSHIFT_OR_32(X) (((u64)X) | (((u64)X) >> 32))
#define wapp_misc_utils_u64_round_up_pow2(X) ( \
#define wpMiscUtilsU64RoundUpPow2(X) ( \
( \
U64_RSHIFT_OR_32( \
U64_RSHIFT_OR_16( \
@@ -32,6 +47,26 @@
) + 1 \
)
#define wapp_misc_utils_va_args_count(T, ...) (sizeof((T[]){__VA_ARGS__})/sizeof(T))
#define wpMiscUtilsIsPowerOfTwo(NUM) ((NUM & (NUM - 1)) == 0)
#define wpMiscUtilsOffsetPointer(PTR, OFFSET) ((void *)((uptr)(PTR) + (OFFSET)))
#define wpMiscUtilsSwap(TYPE, A_PTR, B_PTR) do { \
TYPE tmp = *(A_PTR); \
*(A_PTR) = *(B_PTR); \
*(B_PTR) = tmp; \
} while(0)
wp_intern inline u64 wpMiscUtilsRotl64(u64 x, i8 rotation) {
return (x << rotation) | (x >> (64 - rotation));
}
#ifdef WP_PLATFORM_CPP
END_C_LINKAGE
#include <tuple>
#define wpMiscUtilsVaArgsCount(T, ...) (std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value)
#else
#define wpMiscUtilsVaArgsCount(T, ...) (sizeof((T[]){__VA_ARGS__})/sizeof(T))
#endif // !WP_PLATFORM_CPP
#endif // !MISC_UTILS_H
+110 -51
View File
@@ -1,88 +1,147 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef PLATFORM_H
#define PLATFORM_H
// Operating system
#if defined(__ANDROID__)
#define WAPP_PLATFORM_ANDROID
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_ANDROID
#define WP_PLATFORM_POSIX
#elif defined(__FreeBSD__)
#define WAPP_PLATFORM_FREE_BSD
#define WAPP_PLATFORM_BSD
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_FREE_BSD
#define WP_PLATFORM_BSD
#define WP_PLATFORM_POSIX
#elif defined(__NetBSD__)
#define WAPP_PLATFORM_NET_BSD
#define WAPP_PLATFORM_BSD
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_NET_BSD
#define WP_PLATFORM_BSD
#define WP_PLATFORM_POSIX
#elif defined(__OpenBSD__)
#define WAPP_PLATFORM_OPEN_BSD
#define WAPP_PLATFORM_BSD
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_OPEN_BSD
#define WP_PLATFORM_BSD
#define WP_PLATFORM_POSIX
#elif defined(__DragonFly__)
#define WAPP_PLATFORM_DRAGON_FLY
#define WAPP_PLATFORM_BSD
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_DRAGON_FLY
#define WP_PLATFORM_BSD
#define WP_PLATFORM_POSIX
#elif defined(__bsdi__)
#define WAPP_PLATFORM_BSD
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_BSD
#define WP_PLATFORM_POSIX
#elif defined(__linux__) || defined(linux) || defined(__linux) || defined(__gnu_linux__)
#define WAPP_PLATFORM_LINUX
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_LINUX
#define WP_PLATFORM_POSIX
#elif defined(__GNU__) || defined(__gnu_hurd__)
#define WAPP_PLATFORM_GNU
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_GNU
#define WP_PLATFORM_POSIX
#elif defined(__APPLE__) || defined(__MACH__)
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#define WAPP_PLATFORM_IOS
#define WAPP_PLATFORM_APPLE
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_IOS
#define WP_PLATFORM_APPLE
#define WP_PLATFORM_POSIX
#elif TARGET_OS_MAC
#define WAPP_PLATFORM_MACOS
#define WAPP_PLATFORM_APPLE
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_MACOS
#define WP_PLATFORM_APPLE
#define WP_PLATFORM_POSIX
#else
#error "Unrecognised Apple platform"
#endif
#elif defined(_WIN64)
#define WAPP_PLATFORM_WINDOWS64
#define WAPP_PLATFORM_WINDOWS
#define WP_PLATFORM_WINDOWS64
#define WP_PLATFORM_WINDOWS
#elif defined(_WIN32)
#define WAPP_PLATFORM_WINDOWS32
#define WAPP_PLATFORM_WINDOWS
#define WP_PLATFORM_WINDOWS32
#define WP_PLATFORM_WINDOWS
#elif defined(__CYGWIN__)
#define WAPP_PLATFORM_CYGWIN
#define WAPP_PLATFORM_WINDOWS
#define WP_PLATFORM_CYGWIN
#define WP_PLATFORM_WINDOWS
#elif defined(__unix__) || defined(__unix)
#define WAPP_PLATFORM_UNIX
#define WAPP_PLATFORM_POSIX
#define WP_PLATFORM_UNIX
#define WP_PLATFORM_POSIX
#else
#error "Unrecognised platform"
#endif
#ifdef __cplusplus
#error "WAPP is a C only library"
// Compiler
#if defined(__clang__)
#define WP_PLATFORM_CLANG
#if defined(__GNUG__)
#define WP_PLATFORM_CLANGPP
#endif
#define WP_PLATFORM_CLANG_MAJOR __clang_major__
#define WP_PLATFORM_CLANG_MINOR __clang_minor__
#define WP_PLATFORM_CLANG_PATCH __clang_patchlevel__
#elif defined(__GNUC__) || defined(__GNUG__)
#define WP_PLATFORM_GCC
#if defined(__GNUG__)
#define WP_PLATFORM_GPP
#endif
#define WP_PLATFORM_GCC_MAJOR __GNUC__
#define WP_PLATFORM_GCC_MINOR __GNUC_MINOR__
#if defined(__GNUC_PATCHLEVEL__)
#define WP_PLATFORM_GCC_PATCH __GNUC_PATCHLEVEL__
#endif
#elif defined(_MSC_VER)
#define WP_PLATFORM_MSVC
#define WP_PLATFORM_MSVC_FULL_VERSION _MSC_FULL_VER
#define WP_PLATFORM_MSVC_VERSION _MSC_VER
#else
#define WAPP_PLATFORM_C
#error "Unrecognised compiler"
#endif
// Language
#ifdef __cplusplus
#define WP_PLATFORM_CPP
#define WP_PLATFORM_CPP_VERSION __cplusplus
#define WP_PLATFORM_CPP98_VERSION 199711L
#define WP_PLATFORM_CPP11_VERSION 201103L
#define WP_PLATFORM_CPP14_VERSION 201402L
#define WP_PLATFORM_CPP17_VERSION 201703L
#define WP_PLATFORM_CPP20_VERSION 202002L
#define WP_PLATFORM_CPP23_VERSION 202302L
#if WP_PLATFORM_CPP_VERSION == WP_PLATFORM_CPP98_VERSION
#define WP_PLATFORM_CPP98
#elif WP_PLATFORM_CPP_VERSION == WP_PLATFORM_CPP11_VERSION
#define WP_PLATFORM_CPP11
#elif WP_PLATFORM_CPP_VERSION == WP_PLATFORM_CPP14_VERSION
#define WP_PLATFORM_CPP14
#elif WP_PLATFORM_CPP_VERSION == WP_PLATFORM_CPP17_VERSION
#define WP_PLATFORM_CPP17
#elif WP_PLATFORM_CPP_VERSION == WP_PLATFORM_CPP20_VERSION
#define WP_PLATFORM_CPP20
#elif WP_PLATFORM_CPP_VERSION == WP_PLATFORM_CPP23_VERSION
#define WP_PLATFORM_CPP23
#else
#error "Unrecognised C++ version"
#endif
#else
#define WP_PLATFORM_C
#if defined(__STDC_VERSION__)
#define WAPP_PLATFORM_C_VERSION __STDC_VERSION__
#define WAPP_PLATFORM_C99_VERSION 199901L
#define WAPP_PLATFORM_C11_VERSION 201112L
#define WAPP_PLATFORM_C17_VERSION 201710L
#define WAPP_PLATFORM_C23_VERSION 202311L
#define WP_PLATFORM_C_VERSION __STDC_VERSION__
#define WP_PLATFORM_C99_VERSION 199901L
#define WP_PLATFORM_C11_VERSION 201112L
#define WP_PLATFORM_C17_VERSION 201710L
#define WP_PLATFORM_C23_VERSION 202311L
#if WAPP_PLATFORM_C_VERSION == WAPP_PLATFORM_C99_VERSION
#define WAPP_PLATFORM_C99
#elif WAPP_PLATFORM_C_VERSION == WAPP_PLATFORM_C11_VERSION
#define WAPP_PLATFORM_C11
#elif WAPP_PLATFORM_C_VERSION == WAPP_PLATFORM_C17_VERSION
#define WAPP_PLATFORM_C17
#elif WAPP_PLATFORM_C_VERSION == WAPP_PLATFORM_C23_VERSION
#define WAPP_PLATFORM_C23
#if WP_PLATFORM_C_VERSION == WP_PLATFORM_C99_VERSION
#define WP_PLATFORM_C99
#elif WP_PLATFORM_C_VERSION == WP_PLATFORM_C11_VERSION
#define WP_PLATFORM_C11
#elif WP_PLATFORM_C_VERSION == WP_PLATFORM_C17_VERSION
#define WP_PLATFORM_C17
#elif WP_PLATFORM_C_VERSION == WP_PLATFORM_C23_VERSION
#define WP_PLATFORM_C23
#else
#error "Unrecognised C version"
#endif
#else
#define WAPP_PLATFORM_C89
#define WP_PLATFORM_C89
#endif
#endif // !__cplusplus
+3
View File
@@ -1,7 +1,10 @@
// vim:fileencoding=utf-8:foldmethod=marker
#ifndef WAPP_COMMON_H
#define WAPP_COMMON_H
#include "aliases/aliases.h"
#include "assert/assert.h"
#include "misc/misc_utils.h"
#include "platform/platform.h"
-145
View File
@@ -1,145 +0,0 @@
#include "mem_arena.h"
#include "../utils/mem_utils.h"
#include "../../../common/aliases/aliases.h"
#include "../../../common/misc/misc_utils.h"
#include "../../os/mem/mem_os.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <assert.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 ARENA_MINIMUM_CAPACITY KB(16) // Allocate minimum of 4 pages
struct arena {
u8 *buf;
u8 *offset;
u64 capacity;
bool committed;
#ifdef WAPP_PLATFORM_WINDOWS
wapp_misc_utils_padding_size(sizeof(u8 *) * 2 + sizeof(u64) + sizeof(bool));
#endif // ifdef WAPP_PLATFORM_WINDOWS
};
bool wapp_mem_arena_init_custom(Arena **arena, u64 base_capacity, MemAllocFlags flags, bool zero_buffer) {
if (!arena || *arena || base_capacity == 0) {
return false;
}
*arena = (Arena *)calloc(1, sizeof(Arena));
Arena *arena_ptr = *arena;
if (!arena_ptr) {
return false;
}
u64 arena_capacity = wapp_misc_utils_u64_round_up_pow2(
base_capacity >= ARENA_MINIMUM_CAPACITY ?
base_capacity :
ARENA_MINIMUM_CAPACITY
);
arena_ptr->buf = (u8 *)wapp_mem_util_alloc(NULL, arena_capacity, WAPP_MEM_ACCESS_READ_WRITE, flags,
zero_buffer ? WAPP_MEM_INIT_INITIALISED : WAPP_MEM_INIT_UNINITIALISED);
if (!(arena_ptr->buf)) {
wapp_mem_arena_destroy(arena);
return false;
}
arena_ptr->capacity = arena_capacity;
arena_ptr->offset = arena_ptr->buf;
arena_ptr->committed = (flags & WAPP_MEM_ALLOC_COMMIT) == WAPP_MEM_ALLOC_COMMIT;
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) {
assert(arena != NULL);
u8 *alloc_start = arena->offset;
u8 *output = wapp_mem_util_align_forward((void *)alloc_start, alignment);
if (output + size >= arena->buf + arena->capacity) {
return NULL;
}
arena->offset = output + size;
#ifdef WAPP_PLATFORM_WINDOWS
if (!(arena->committed)) {
wapp_mem_util_alloc(alloc_start, (uptr)(arena->offset) - (uptr)(alloc_start),
WAPP_MEM_ACCESS_READ_WRITE, WAPP_MEM_ALLOC_COMMIT,
WAPP_MEM_INIT_UNINITIALISED);
}
#endif // ifdef WAPP_PLATFORM_WINDOWS
memset(output, 0, size);
return (void *)output;
}
void *wapp_mem_arena_realloc(Arena *arena, void *ptr, u64 old_size, u64 new_size) {
if ((u8*)ptr < arena->buf || (u8*)ptr > arena->offset ||
arena->offset + new_size >= arena->buf + arena->capacity) {
return NULL;
}
void *new_ptr = wapp_mem_arena_alloc(arena, new_size);
if (!new_ptr) {
return NULL;
}
u64 copy_size = new_size <= old_size ? new_size : old_size;
memcpy(new_ptr, ptr, copy_size);
return new_ptr;
}
void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 old_size, u64 new_size, u64 alignment) {
if ((u8*)ptr < arena->buf || (u8*)ptr > arena->offset ||
arena->offset + new_size >= arena->buf + arena->capacity) {
return NULL;
}
void *new_ptr = wapp_mem_arena_alloc_aligned(arena, new_size, alignment);
if (!new_ptr) {
return NULL;
}
u64 copy_size = new_size <= old_size ? new_size : old_size;
memcpy(new_ptr, ptr, copy_size);
return new_ptr;
}
void wapp_mem_arena_clear(Arena *arena) {
assert(arena != NULL);
memset(arena->buf, 0, arena->offset - arena->buf);
arena->offset = arena->buf;
}
void wapp_mem_arena_destroy(Arena **arena) {
assert(arena != NULL && (*arena) != NULL);
Arena *arena_ptr = *arena;
if (arena_ptr->buf) {
wapp_mem_util_free(arena_ptr->buf, arena_ptr->capacity);
}
arena_ptr->buf = arena_ptr->offset = NULL;
arena_ptr->capacity = 0;
free(*arena);
*arena = NULL;
}
-33
View File
@@ -1,33 +0,0 @@
#ifndef MEM_ARENA_H
#define MEM_ARENA_H
#include "../../../common/aliases/aliases.h"
#include "../../../common/platform/platform.h"
#include "../../os/mem/mem_os.h"
#include <stdbool.h>
typedef struct arena Arena;
#define wapp_mem_arena_init(arena_dptr, base_capacity) \
(wapp_mem_arena_init_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE, false))
#define wapp_mem_arena_init_commit(arena_dptr, base_capacity) \
(wapp_mem_arena_init_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, false))
#define wapp_mem_arena_init_zero(arena_dptr, base_capacity) \
(wapp_mem_arena_init_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE, true))
#define wapp_mem_arena_init_commit_and_zero(arena_dptr, base_capacity) \
(wapp_mem_arena_init_custom(arena_dptr, base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, true))
/**
* Arena initialisation function. `wapp_mem_arena_init_custom` provides the most
* control over how the Arena is initialised. Wrapper macros are provided for
* easier use.
*/
bool wapp_mem_arena_init_custom(Arena **arena, u64 base_capacity, MemAllocFlags flags, bool zero_buffer);
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 old_size, u64 new_size);
void *wapp_mem_arena_realloc_aligned(Arena *arena, void *ptr, u64 old_size, u64 new_size, u64 alignment);
void wapp_mem_arena_clear(Arena *arena);
void wapp_mem_arena_destroy(Arena **arena);
#endif // !MEM_ARENA_H
-57
View File
@@ -1,57 +0,0 @@
#include "mem_arena_allocator.h"
#include "mem_arena.h"
#include "../../../common/aliases/aliases.h"
#include "../../os/mem/mem_os.h"
internal inline void *mem_arena_alloc(u64 size, void *alloc_obj);
internal inline void *mem_arena_alloc_aligned(u64 size, u64 alignment, void *alloc_obj);
internal inline void *mem_arena_realloc(void *ptr, u64 old_size, u64 new_size, void *alloc_obj);
internal inline void *mem_arena_realloc_aligned(void *ptr, u64 old_size, u64 new_size, u64 alignment,
void *alloc_obj);
Allocator wapp_mem_arena_allocator_init_custom(u64 base_capacity, MemAllocFlags flags, bool zero_buffer) {
Allocator allocator = {0};
bool initialised = wapp_mem_arena_init_custom((Arena **)(&allocator.obj), base_capacity, flags, zero_buffer);
if (!initialised) {
return allocator;
}
allocator.alloc = mem_arena_alloc;
allocator.alloc_aligned = mem_arena_alloc_aligned;
allocator.realloc = mem_arena_realloc;
allocator.realloc_aligned = mem_arena_realloc_aligned;
return allocator;
}
void wapp_mem_arena_allocator_clear(Allocator *allocator) {
wapp_mem_arena_clear((Arena *)(allocator->obj));
}
void wapp_mem_arena_allocator_destroy(Allocator *allocator) {
wapp_mem_arena_destroy((Arena **)(&(allocator->obj)));
*allocator = (Allocator){0};
}
internal inline void *mem_arena_alloc(u64 size, void *alloc_obj) {
Arena *arena = (Arena *)alloc_obj;
return wapp_mem_arena_alloc(arena, size);
}
internal inline 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 inline void *mem_arena_realloc(void *ptr, u64 old_size, u64 new_size, void *alloc_obj) {
Arena *arena = (Arena *)alloc_obj;
return wapp_mem_arena_realloc(arena, ptr, old_size, new_size);
}
internal inline void *mem_arena_realloc_aligned(void *ptr, u64 old_size, u64 new_size, u64 alignment,
void *alloc_obj) {
Arena *arena = (Arena *)alloc_obj;
return wapp_mem_arena_realloc_aligned(arena, ptr, old_size, new_size, alignment);
}
-34
View File
@@ -1,34 +0,0 @@
#ifndef MEM_ARENA_ALLOCATOR_H
#define MEM_ARENA_ALLOCATOR_H
#include "../../../common/aliases/aliases.h"
#include "../../../common/platform/platform.h"
#include "../../../primitives/mem_allocator/mem_allocator.h"
#include "../../os/mem/mem_os.h"
#include <stdbool.h>
#define wapp_mem_arena_allocator_init(base_capacity) \
(wapp_mem_arena_allocator_init_custom(base_capacity, WAPP_MEM_ALLOC_RESERVE, false))
#define wapp_mem_arena_allocator_init_commit(base_capacity) \
(wapp_mem_arena_allocator_init_custom(base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, false))
#define wapp_mem_arena_allocator_init_zero(base_capacity) \
(wapp_mem_arena_allocator_init_custom(base_capacity, WAPP_MEM_ALLOC_RESERVE, true))
#define wapp_mem_arena_allocator_init_commit_and_zero(base_capacity) \
(wapp_mem_arena_allocator_init_custom(base_capacity, WAPP_MEM_ALLOC_RESERVE | WAPP_MEM_ALLOC_COMMIT, true))
/**
* Wraps an Arena in an Allocator object. It attempts to initialise the Arena
* and, if successful, defines the operations supported by it to be used by the
* Allocator.
*
* An Arena allocator only supports normal allocation and aligned allocation.
* Reallocation, aligned reallocation and freeing aren't implemented.
*
* The `wapp_mem_arena_allocator_init_custom` provides the most control over how
* the Arena is initialised. Wrapper macros are provided for easier use.
*/
Allocator wapp_mem_arena_allocator_init_custom(u64 base_capacity, MemAllocFlags flags, bool zero_buffer);
void wapp_mem_arena_allocator_clear(Allocator *allocator);
void wapp_mem_arena_allocator_destroy(Allocator *allocator);
#endif // !MEM_ARENA_ALLOCATOR_H
-25
View File
@@ -1,25 +0,0 @@
#include "mem_utils.h"
#include "../../../common/aliases/aliases.h"
#include <stdbool.h>
#include <stddef.h>
#include <assert.h>
internal bool is_power_of_two(u64 num) { return (num & (num - 1)) == 0; }
void *wapp_mem_util_align_forward(void *ptr, u64 alignment) {
assert(ptr != 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;
}
-9
View File
@@ -1,9 +0,0 @@
#ifndef MEM_UTILS_H
#define MEM_UTILS_H
#include "../../../common/aliases/aliases.h"
#include "../../../common/platform/platform.h"
void *wapp_mem_util_align_forward(void *ptr, u64 alignment);
#endif // !MEM_UTILS_H
-134
View File
@@ -1,134 +0,0 @@
#include "cpath.h"
#include "../../../common/aliases/aliases.h"
#include "../../../common/misc/misc_utils.h"
#include "../../mem/arena/mem_arena_allocator.h"
#include "../../../primitives/dbl_list/dbl_list.h"
#include "../../../primitives/mem_allocator/mem_allocator.h"
#include "../../../primitives/strings/str8/str8.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
u32 wapp_cpath_join_path(Str8 *dst, const Str8List *parts) {
if (!dst || !parts) {
return CPATH_JOIN_INVALID_ARGS;
}
if (parts->node_count == 0) {
return CPATH_JOIN_EMPTY_PARTS;
}
Str8 separator = wapp_str8_buf(4);
wapp_str8_push_back(&separator, PATH_SEP);
u64 required_capacity = parts->node_count * separator.size + wapp_str8_list_total_size(parts);
if (dst->capacity < required_capacity) {
return CPATH_JOIN_INSUFFICIENT_DST_CAPACITY;
}
// Handle first node
const Str8Node *first_node = wapp_str8_list_get(parts, 0);
wapp_str8_copy_str8_capped(dst, first_node->item);
// NOTE (Abdelrahman): Uses a while loop instead of a for loop to get rid of
// MSVC Spectre mitigation warnings
const Str8Node *node = first_node;
u64 node_index = 1;
bool running = node_index < parts->node_count;
while (running && node->next) {
node = node->next;
if (node->item->size == 0) {
continue;
}
if (dst->size > 0) {
char dst_last = wapp_str8_get(dst, dst->size - 1);
char node_start = wapp_str8_get(node->item, 0);
bool add_path_sep = dst_last != PATH_SEP && node_start != PATH_SEP;
if (add_path_sep) {
wapp_str8_concat_capped(dst, &separator);
}
}
wapp_str8_concat_capped(dst, node->item);
++node_index;
running = node_index < parts->node_count;
}
return CPATH_JOIN_SUCCESS;
}
Str8 *dirup(const Allocator *allocator, Str8RO *path, u64 levels) {
Str8 *output = NULL;
if (!allocator || !path) {
goto RETURN_DIRUP;
}
bool absolute = wapp_str8_get(path, 0) == PATH_SEP;
Str8 separator = wapp_str8_buf(4);
wapp_str8_push_back(&separator, PATH_SEP);
if (path->size == 0) {
output = wapp_str8_alloc_buf(allocator, 16);
if (!output) {
goto RETURN_DIRUP;
}
wapp_str8_push_back(output, absolute ? PATH_SEP : '.');
goto RETURN_DIRUP;
}
if (levels < 1) {
output = wapp_str8_alloc_str8(allocator, path);
goto RETURN_DIRUP;
}
Allocator tmp_arena = wapp_mem_arena_allocator_init(MB(8));
if (wapp_mem_allocator_invalid(&tmp_arena)) {
goto RETURN_DIRUP;
}
Str8List *parts = wapp_str8_split(&tmp_arena, path, &separator);
if (!parts) {
goto RETURN_DIRUP;
}
if (levels >= parts->node_count) {
output = wapp_str8_alloc_buf(allocator, 16);
if (!output) {
goto LIST_CLEANUP_DIRUP;
}
wapp_str8_push_back(output, absolute ? PATH_SEP : '.');
} else {
for (u64 i = 0; i < levels; ++i) {
wapp_str8_list_pop_back(parts);
}
u64 alignment = sizeof(void *) * 2;
u64 alloc_size = wapp_str8_list_total_size(parts) + parts->node_count * separator.size;
u64 modulo = alloc_size & (alignment - 1);
alloc_size += alignment - modulo;
output = wapp_str8_alloc_buf(allocator, alloc_size);
if (output) {
if (absolute) {
wapp_str8_push_back(output, PATH_SEP);
}
Str8 *joined = wapp_str8_join(&tmp_arena, parts, &separator);
if (joined) {
wapp_str8_concat_capped(output, joined);
}
}
}
LIST_CLEANUP_DIRUP:
wapp_mem_arena_allocator_destroy(&tmp_arena);
RETURN_DIRUP:
return output;
}
-30
View File
@@ -1,30 +0,0 @@
#ifndef CPATH_H
#define CPATH_H
#include "../../../common/aliases/aliases.h"
#include "../../../common/platform/platform.h"
#include "../../../primitives/mem_allocator/mem_allocator.h"
#include "../../../primitives/strings/str8/str8.h"
#ifdef WAPP_PLATFORM_POSIX
#define PATH_SEP '/'
#elif defined(WAPP_PLATFORM_WINDOWS)
#define PATH_SEP '\\'
#else
#error "Unrecognised platform"
#endif
#define wapp_cpath_dirname(ALLOCATOR, PATH) dirup(ALLOCATOR, PATH, 1)
#define wapp_cpath_dirup(ALLOCATOR, PATH, COUNT) dirup(ALLOCATOR, PATH, COUNT)
enum {
CPATH_JOIN_SUCCESS = 0,
CPATH_JOIN_INVALID_ARGS,
CPATH_JOIN_EMPTY_PARTS,
CPATH_JOIN_INSUFFICIENT_DST_CAPACITY,
};
u32 wapp_cpath_join_path(Str8 *dst, const Str8List *parts);
Str8 *dirup(const Allocator *allocator, Str8RO *path, u64 levels);
#endif // !CPATH_H
-29
View File
@@ -1,29 +0,0 @@
#include "mem_os.h"
#include "mem_os_ops.h"
#include "../../../common/aliases/aliases.h"
#include "../../../common/platform/platform.h"
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#if defined(WAPP_PLATFORM_WINDOWS)
#include "win/mem_os_win.h"
#elif defined(WAPP_PLATFORM_POSIX)
#include "posix/mem_os_posix.h"
#else
#error "Unrecognised platform"
#endif
void *wapp_mem_util_alloc(void *addr, u64 size, MemAccess access, MemAllocFlags flags, MemInitType type) {
void *output = mem_util_allocate(addr, size, access, flags, type);
if (type == WAPP_MEM_INIT_INITIALISED) {
memset(output, 0, size);
}
return output;
}
void wapp_mem_util_free(void *ptr, u64 size) {
mem_util_free(ptr, size);
}
-23
View File
@@ -1,23 +0,0 @@
#ifndef MEM_OS_H
#define MEM_OS_H
#include "../../../common/aliases/aliases.h"
#include "../../../common/platform/platform.h"
#include "mem_os_ops.h"
#if defined(WAPP_PLATFORM_WINDOWS)
#include "win/mem_os_win.h"
#elif defined(WAPP_PLATFORM_POSIX)
#include "posix/mem_os_posix.h"
#else
#error "Unrecognised platform"
#endif
void *wapp_mem_util_alloc(void *addr, u64 size, MemAccess access, MemAllocFlags flags, MemInitType type);
void wapp_mem_util_free(void *ptr, u64 size);
external void *mem_util_allocate(void *addr, u64 size, MemAccess access, MemAllocFlags flags, MemInitType type);
external void mem_util_free(void *ptr, u64 size);
#endif // !MEM_OS_H
-20
View File
@@ -1,20 +0,0 @@
#ifndef MEM_OS_OPS_H
#define MEM_OS_OPS_H
#include "../../../common/platform/platform.h"
typedef enum mem_access {
WAPP_MEM_ACCESS_NONE,
WAPP_MEM_ACCESS_READ_ONLY,
WAPP_MEM_ACCESS_EXEC_ONLY,
WAPP_MEM_ACCESS_READ_WRITE,
WAPP_MEM_ACCESS_READ_EXEC,
WAPP_MEM_ACCESS_READ_WRITE_EXEC,
} MemAccess;
typedef enum mem_init_type {
WAPP_MEM_INIT_UNINITIALISED,
WAPP_MEM_INIT_INITIALISED,
} MemInitType;
#endif // !MEM_OS_OPS_H
-34
View File
@@ -1,34 +0,0 @@
#include "../../../../common/aliases/aliases.h"
#include "../../../../common/platform/platform.h"
#ifdef WAPP_PLATFORM_POSIX
#include "mem_os_posix.h"
#include "../mem_os_ops.h"
#include <sys/mman.h>
internal const i32 access_types[] = {
[WAPP_MEM_ACCESS_NONE] = PROT_NONE,
[WAPP_MEM_ACCESS_READ_ONLY] = PROT_READ,
[WAPP_MEM_ACCESS_EXEC_ONLY] = PROT_EXEC,
[WAPP_MEM_ACCESS_READ_WRITE] = PROT_READ | PROT_WRITE,
[WAPP_MEM_ACCESS_READ_EXEC] = PROT_READ | PROT_EXEC,
[WAPP_MEM_ACCESS_READ_WRITE_EXEC] = PROT_READ | PROT_WRITE | PROT_EXEC,
};
void *mem_util_allocate(void *addr, u64 size, MemAccess access, MemAllocFlags flags, MemInitType type) {
(void)type;
i32 alloc_flags = flags | MAP_ANON | MAP_PRIVATE;
#if defined(WAPP_PLATFORM_LINUX) || defined(WAPP_PLATFORM_GNU) || defined(WAPP_PLATFORM_NET_BSD)
alloc_flags |= MAP_NORESERVE;
#endif
return mmap(addr, size, access_types[access], alloc_flags, -1, 0);
}
void mem_util_free(void *ptr, u64 size) {
munmap(ptr, size);
}
#endif // !WAPP_PLATFORM_POSIX
-25
View File
@@ -1,25 +0,0 @@
#ifndef MEM_OS_POSIX_H
#define MEM_OS_POSIX_H
#include "../../../../common/platform/platform.h"
#ifdef WAPP_PLATFORM_POSIX
#include <sys/mman.h>
typedef enum mem_alloc_flags {
#if defined(WAPP_PLATFORM_LINUX) || defined(WAPP_PLATFORM_GNU)
WAPP_MEM_ALLOC_RESERVE = 0,
WAPP_MEM_ALLOC_COMMIT = MAP_POPULATE,
#elif defined(WAPP_PLATFORM_FREE_BSD)
WAPP_MEM_ALLOC_RESERVE = 0,
WAPP_MEM_ALLOC_COMMIT = MAP_PREFAULT_READ,
#elif defined(WAPP_PLATFORM_BSD) || defined(WAPP_PLATFORM_UNIX) || defined(WAPP_PLATFORM_APPLE)
WAPP_MEM_ALLOC_RESERVE = 0,
WAPP_MEM_ALLOC_COMMIT = 0,
#endif
} MemAllocFlags;
#endif // !WAPP_PLATFORM_POSIX
#endif // !MEM_OS_POSIX_H
-35
View File
@@ -1,35 +0,0 @@
#include "../../../../common/aliases/aliases.h"
#include "../../../../common/platform/platform.h"
#ifdef WAPP_PLATFORM_WINDOWS
#include "mem_os_win.h"
#include "../mem_os_ops.h"
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <memoryapi.h>
internal const i32 access_types[] = {
[WAPP_MEM_ACCESS_NONE] = PAGE_NOACCESS,
[WAPP_MEM_ACCESS_READ_ONLY] = PAGE_READONLY,
[WAPP_MEM_ACCESS_EXEC_ONLY] = PAGE_EXECUTE,
[WAPP_MEM_ACCESS_READ_WRITE] = PAGE_READWRITE,
[WAPP_MEM_ACCESS_READ_EXEC] = PAGE_EXECUTE_READ,
[WAPP_MEM_ACCESS_READ_WRITE_EXEC] = PAGE_EXECUTE_READWRITE,
};
void *mem_util_allocate(void *addr, u64 size, MemAccess access, MemAllocFlags flags, MemInitType type) {
// Ensure memory is committed if it's meant to be initialised
if (type == WAPP_MEM_INIT_INITIALISED) {
flags |= WAPP_MEM_ALLOC_COMMIT;
}
return VirtualAlloc(addr, (SIZE_T)size, flags, access_types[access]);
}
void mem_util_free(void *ptr, u64 size) {
VirtualFree(ptr, size, MEM_RELEASE);
}
#endif // !WAPP_PLATFORM_WINDOWS
-19
View File
@@ -1,19 +0,0 @@
#ifndef MEM_OS_WIN_H
#define MEM_OS_WIN_H
#include "../../../../common/platform/platform.h"
#ifdef WAPP_PLATFORM_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <memoryapi.h>
typedef enum mem_alloc_flags {
WAPP_MEM_ALLOC_RESERVE = MEM_RESERVE,
WAPP_MEM_ALLOC_COMMIT = MEM_COMMIT,
} MemAllocFlags;
#endif // !WAPP_PLATFORM_WINDOWS
#endif // !MEM_OS_WIN_H
-100
View File
@@ -1,100 +0,0 @@
#include "commander.h"
#include "commander_output.h"
#include "../utils/shell_utils.h"
#include "../../../mem/arena/mem_arena_allocator.h"
#include "../../../../common/aliases/aliases.h"
#include "../../../../common/misc/misc_utils.h"
#include "../../../../primitives/dbl_list/dbl_list.h"
#include "../../../../primitives/mem_allocator/mem_allocator.h"
#include "../../../../primitives/strings/str8/str8.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CMD_BUF_LEN 8192
#define OUT_BUF_LEN 4096
internal inline CMDResult execute_command(Str8RO *cmd, CMDOutHandling out_handling, Str8 *out_buf);
internal inline CMDError get_command_output(FILE *fp, CMDOutHandling out_handling, Str8 *out_buf);
CMDResult wapp_shell_commander_execute(CMDOutHandling out_handling, Str8 *out_buf, const Str8List *cmd) {
if (!cmd) {
return CMD_NO_EXIT(SHELL_ERR_INVALID_ARGS);
}
Allocator arena = wapp_mem_arena_allocator_init(KB(500));
Str8 *cmd_str = wapp_str8_join(&arena, cmd, &wapp_str8_lit_ro(" "));
if (!cmd_str) {
wapp_mem_arena_allocator_destroy(&arena);
return CMD_NO_EXIT(SHELL_ERR_ALLOCATION_FAIL);
}
// Redirect output
cmd_str = wapp_str8_alloc_concat(&arena, cmd_str, &wapp_str8_lit_ro(" 2>&1"));
CMDResult output = execute_command(cmd_str, out_handling, out_buf);
wapp_mem_arena_allocator_destroy(&arena);
return output;
}
internal inline CMDResult execute_command(Str8RO *cmd, CMDOutHandling out_handling, Str8 *out_buf) {
char cmd_buf[CMD_BUF_LEN] = {0};
wapp_str8_copy_to_cstr(cmd_buf, cmd, CMD_BUF_LEN);
FILE *fp = wapp_shell_utils_popen(cmd_buf, "r");
if (!fp) {
return CMD_NO_EXIT(SHELL_ERR_PROC_START_FAIL);
}
CMDResult output;
CMDError err = get_command_output(fp, out_handling, out_buf);
if (err > SHELL_ERR_NO_ERROR) {
output = CMD_NO_EXIT(err);
goto EXECUTE_COMMAND_CLOSE;
}
i32 st = EXIT_SUCCESS;
err = get_output_status(fp, &st);
if (err > SHELL_ERR_NO_ERROR) {
output = CMD_NO_EXIT(err);
goto EXECUTE_COMMAND_CLOSE;
}
// Process is already closed in get_output_status
fp = NULL;
output = (CMDResult){
.exited = true,
.exit_code = st,
.error = SHELL_ERR_NO_ERROR,
};
EXECUTE_COMMAND_CLOSE:
if (fp) {
wapp_shell_utils_pclose(fp);
}
return output;
}
internal inline CMDError get_command_output(FILE *fp, CMDOutHandling out_handling, Str8 *out_buf) {
Str8 out = wapp_str8_buf(OUT_BUF_LEN);
out.size = fread((void *)out.buf, sizeof(u8), out.capacity, fp);
if (out_handling == SHELL_OUTPUT_CAPTURE && out_buf != NULL) {
if (out.size >= out_buf->capacity) {
return SHELL_ERR_OUT_BUF_FULL;
}
wapp_str8_concat_capped(out_buf, &out);
} else if (out_handling == SHELL_OUTPUT_PRINT) {
printf(WAPP_STR8_SPEC, wapp_str8_varg(out));
}
return SHELL_ERR_NO_ERROR;
}
-18
View File
@@ -1,18 +0,0 @@
#ifndef COMMANDER_H
#define COMMANDER_H
#include "commander_output.h"
#include "../../../../common/aliases/aliases.h"
#include "../../../../common/platform/platform.h"
#include "../../../../primitives/strings/str8/str8.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#define CMD_NO_EXIT(ERR) ((CMDResult){.exited = false, .exit_code = EXIT_FAILURE, .error = ERR})
CMDResult wapp_shell_commander_execute(CMDOutHandling out_handling, Str8 *out_buf, const Str8List *cmd);
external CMDError get_output_status(FILE *fp, i32 *status_out);
#endif // !COMMANDER_H
@@ -1,35 +0,0 @@
#ifndef COMMANDER_OUTPUT_H
#define COMMANDER_OUTPUT_H
#include "../../../../common/aliases/aliases.h"
#include "../../../../common/platform/platform.h"
#include <stdbool.h>
typedef enum {
SHELL_OUTPUT_DISCARD,
SHELL_OUTPUT_PRINT,
SHELL_OUTPUT_CAPTURE,
} CMDOutHandling;
typedef enum {
SHELL_ERR_NO_ERROR,
SHELL_ERR_INVALID_ARGS,
SHELL_ERR_ALLOCATION_FAIL,
SHELL_ERR_PROC_START_FAIL,
SHELL_ERR_OUT_BUF_FULL,
SHELL_ERR_PROC_EXIT_FAIL,
} CMDError;
typedef struct commander_result CMDResult;
struct commander_result {
i32 exit_code;
CMDError error;
bool exited;
#ifdef WAPP_PLATFORM_WINDOWS
#include "../../../../common/misc/misc_utils.h"
wapp_misc_utils_padding_size(sizeof(bool) + sizeof(i32) + sizeof(CMDError));
#endif // !WAPP_PLATFORM_WINDOWS
};
#endif // !COMMANDER_OUTPUT_H
@@ -1,23 +0,0 @@
#include "../../../../../common/aliases/aliases.h"
#include "../../../../../common/platform/platform.h"
#ifdef WAPP_PLATFORM_POSIX
#include "../commander_output.h"
#include "../../utils/shell_utils.h"
#include <stdio.h>
#include <stdlib.h>
CMDError get_output_status(FILE *fp, i32 *status_out) {
*status_out = wapp_shell_utils_pclose(fp);
if (!WIFEXITED(*status_out)) {
return SHELL_ERR_PROC_EXIT_FAIL;
}
*status_out = WEXITSTATUS(*status_out);
return SHELL_ERR_NO_ERROR;
}
#endif // !WAPP_PLATFORM_POSIX
@@ -1,22 +0,0 @@
#include "../../../../../common/aliases/aliases.h"
#include "../../../../../common/platform/platform.h"
#ifdef WAPP_PLATFORM_WINDOWS
#include "../commander_output.h"
#include "../../utils/shell_utils.h"
#include <stdio.h>
CMDError get_output_status(FILE *fp, i32 *status_out) {
if (!feof(fp)) {
// Ensure process is closed on failure
wapp_shell_utils_pclose(fp);
return SHELL_ERR_PROC_EXIT_FAIL;
}
*status_out = wapp_shell_utils_pclose(fp);
return SHELL_ERR_NO_ERROR;
}
#endif // !WAPP_PLATFORM_WINDOWS
@@ -1,34 +0,0 @@
#include "../../../../../common/aliases/aliases.h"
#include "../../../../../common/platform/platform.h"
#include "../../../../../primitives/strings/str8/str8.h"
#ifdef WAPP_PLATFORM_POSIX
#include "../terminal_colours.h"
#include <stdio.h>
internal Str8RO colours[COUNT_TERM_COLOUR] = {
[WAPP_TERM_COLOUR_FG_BLACK] = wapp_str8_lit_ro_initialiser_list("\033[30m"),
[WAPP_TERM_COLOUR_FG_RED] = wapp_str8_lit_ro_initialiser_list("\033[31m"),
[WAPP_TERM_COLOUR_FG_GREEN] = wapp_str8_lit_ro_initialiser_list("\033[32m"),
[WAPP_TERM_COLOUR_FG_BLUE] = wapp_str8_lit_ro_initialiser_list("\033[34m"),
[WAPP_TERM_COLOUR_FG_CYAN] = wapp_str8_lit_ro_initialiser_list("\033[36m"),
[WAPP_TERM_COLOUR_FG_MAGENTA] = wapp_str8_lit_ro_initialiser_list("\033[35m"),
[WAPP_TERM_COLOUR_FG_YELLOW] = wapp_str8_lit_ro_initialiser_list("\033[33m"),
[WAPP_TERM_COLOUR_FG_WHITE] = wapp_str8_lit_ro_initialiser_list("\033[37m"),
[WAPP_TERM_COLOUR_FG_BR_BLACK] = wapp_str8_lit_ro_initialiser_list("\033[90m"),
[WAPP_TERM_COLOUR_FG_BR_RED] = wapp_str8_lit_ro_initialiser_list("\033[91m"),
[WAPP_TERM_COLOUR_FG_BR_GREEN] = wapp_str8_lit_ro_initialiser_list("\033[92m"),
[WAPP_TERM_COLOUR_FG_BR_BLUE] = wapp_str8_lit_ro_initialiser_list("\033[94m"),
[WAPP_TERM_COLOUR_FG_BR_CYAN] = wapp_str8_lit_ro_initialiser_list("\033[96m"),
[WAPP_TERM_COLOUR_FG_BR_MAGENTA] = wapp_str8_lit_ro_initialiser_list("\033[95m"),
[WAPP_TERM_COLOUR_FG_BR_YELLOW] = wapp_str8_lit_ro_initialiser_list("\033[93m"),
[WAPP_TERM_COLOUR_FG_BR_WHITE] = wapp_str8_lit_ro_initialiser_list("\033[97m"),
[WAPP_TERM_COLOUR_CLEAR] = wapp_str8_lit_ro_initialiser_list("\033[0m"),
};
void print_coloured_text(Str8RO *text, TerminalColour colour) {
printf(WAPP_STR8_SPEC WAPP_STR8_SPEC, wapp_str8_varg(colours[colour]), wapp_str8_varg((*text)));
}
#endif // !WAPP_PLATFORM_POSIX
-16
View File
@@ -1,16 +0,0 @@
#include "termcolour.h"
#include "terminal_colours.h"
#include "../../../../primitives/strings/str8/str8.h"
void wapp_shell_termcolour_print_text(Str8RO *text, TerminalColour colour) {
if (colour < WAPP_TERM_COLOUR_FG_BLACK || colour > WAPP_TERM_COLOUR_FG_BR_WHITE) {
return;
}
print_coloured_text(text, colour);
}
void wapp_shell_termcolour_clear_colour(void) {
Str8RO empty = wapp_str8_lit_ro("");
print_coloured_text(&empty, WAPP_TERM_COLOUR_CLEAR);
}
-14
View File
@@ -1,14 +0,0 @@
#ifndef TERM_COLOUR_H
#define TERM_COLOUR_H
#include "terminal_colours.h"
#include "../../../../common/aliases/aliases.h"
#include "../../../../common/platform/platform.h"
#include "../../../../primitives/strings/str8/str8.h"
void wapp_shell_termcolour_print_text(Str8RO *text, TerminalColour colour);
void wapp_shell_termcolour_clear_colour(void);
external void print_coloured_text(Str8RO *text, TerminalColour colour);
#endif // !TERM_COLOUR_H

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