Author SHA1 Message Date
ed 9086233d89 compile fixes 2024-05-26 11:16:20 -04:00
ed ad1b2fa23d Merge tag 'v0.9.10-alpha' into odin 2024-05-26 11:12:40 -04:00
ed f67098f54b Merge remote-tracking branch 'EpicGames/master' into odin
# Conflicts:
#	src/df/core/df_core.mdesk
#	src/df/core/generated/df_core.meta.c
2024-05-18 12:03:19 -04:00
ed ea4e671a44 Merge remote-tracking branch 'EpicGames/master' into odin 2024-05-14 22:27:18 -04:00
ed 0c4c18c64f Fixes for latest merge into with last changes for odin support from gingerBill 2024-05-07 18:44:00 -04:00
ed ba9d68951d Merge branch 'master' into odin
# Conflicts:
#	src/df/core/generated/df_core.meta.c
#	src/df/gfx/df_gfx.mdesk
#	src/df/gfx/df_view_rules.c
#	src/df/gfx/generated/df_gfx.meta.c
#	src/df/gfx/generated/df_gfx.meta.h
2024-05-07 18:27:10 -04:00
ed 340d504f10 Merge remote-tracking branch 'gingerbill/master' into odin 2024-04-02 15:48:04 -04:00
gingerBill e8f03562bf Add hash row under key 2024-04-01 15:42:20 +01:00
gingerBillandGitHub c946f88b2a Merge pull request #1 from EpicGamesExt/master
Update
2024-04-01 15:14:55 +01:00
gingerBill 56e7bae038 Minor clean up 2024-04-01 15:13:22 +01:00
gingerBill c1741b4f39 odin_map visualization with nested key-value pairs 2024-04-01 14:51:30 +01:00
ed ba41543543 Merge remote-tracking branch 'gingerbill/master' into odin
# Conflicts:
#	src/df/core/df_core.mdesk
#	src/df/core/generated/df_core.meta.c
#	src/df/core/generated/df_core.meta.h
#	src/df/gfx/df_gfx.c
#	src/df/gfx/df_view_rule_hooks.c
#	src/font_cache/font_cache.c
#	src/txti/txti.c
#	src/txti/txti.h
2024-03-30 13:02:37 -04:00
gingerBill 5ae60aedc5 Correct brace style 2024-03-30 14:13:06 +00:00
gingerBill f23979e20a Mock out logic for map iteration 2024-03-30 14:00:27 +00:00
gingerBill 57fb0eb833 VERY basic map support with map cell info 2024-03-30 13:47:18 +00:00
gingerBill 5d2d13d9d9 Dumb map rendering showing keys only (including dummy ones) 2024-03-30 11:59:04 +00:00
gingerBill 8342cfbd53 Add slice view 2024-03-30 10:46:51 +00:00
ed 2721adf088 remove hmh comment from build script (remnant of copy & paste) 2024-02-12 11:52:08 -05:00
ed ffcecd659e Merge remote-tracking branch 'gingerbill/master' into odin 2024-02-12 11:32:35 -05:00
gingerBill 0ccfb51102 Very bad odin_map view rule, only renders keys currently 2024-02-12 15:51:31 +00:00
gingerBill f41f8dd719 Remove dbg_printf 2024-02-12 14:33:57 +00:00
gingerBill fa6b04ab24 Support String8 like strings 2024-02-12 14:32:49 +00:00
gingerBill 156615a3ba Add view rule slice 2024-02-12 14:16:23 +00:00
gingerBill 57a35c6b28 Make tabs 4-spaces (1 was too little) 2024-02-12 12:35:43 +00:00
ed 76ce8a79c2 Made ease of use scripts for building and cleaning 2024-02-09 17:50:42 -05:00
ed 932db741fa Merge remote-tracking branch 'leidegre/feature/odin' into build_scripts 2024-02-09 16:08:40 -05:00
John LeidegrenandGitHub 0add3ef72c Merge branch 'master' into feature/odin 2024-01-21 15:59:23 +01:00
John LeidegrenandGitHub e417a96ce4 Fixed typo 2024-01-20 17:53:49 +01:00
John Leidegren b2262532a7 Fixed mistakes 2024-01-20 15:21:55 +01:00
John Leidegren b24326f502 Fixed formatting 2024-01-20 14:27:38 +01:00
John Leidegren 66c1fa337c Adds syntax colors to .odin files 2024-01-20 14:23:23 +01:00
605 changed files with 183613 additions and 379199 deletions
-1
View File
@@ -1 +0,0 @@
*.sh text=auto eol=lf
+8 -30
View File
@@ -13,17 +13,6 @@ jobs:
runs-on: windows-2022 runs-on: windows-2022
strategy: strategy:
fail-fast: false fail-fast: false
matrix:
target:
- raddbg
- radlink
- radbin
compiler:
- msvc
- clang
mode:
- debug
- release
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
@@ -31,22 +20,11 @@ jobs:
shell: cmd shell: cmd
run: | run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
call build "${{ matrix.target }}" "${{ matrix.compiler }}" "${{ matrix.mode }}" || exit /b 1 call build raddbg msvc debug || exit /b 1
call build raddbgi_from_pdb msvc debug || exit /b 1
run-torture: call build raddbgi_from_dwarf msvc debug || exit /b 1
runs-on: windows-2022 call build raddbgi_dump msvc debug || exit /b 1
steps: call build raddbg clang debug || exit /b 1
# - name: Install ASAN call build raddbgi_from_pdb clang debug || exit /b 1
# shell: cmd call build raddbgi_from_dwarf clang debug || exit /b 1
# run: | call build raddbgi_dump clang debug || exit /b 1
# "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe" modify --installPath "C:\Program Files\Microsoft Visual Studio\2022\Enterprise" --quiet --force --norestart --add Microsoft.VisualStudio.Component.VC.ASAN
- name: checkout
uses: actions/checkout@v2
- name: run-torture
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
call build radlink asan debug no_meta || exit /b 1
call build torture debug no_meta || exit /b 1
cd build
torture -l:radlink || exit /b 1
-1
View File
@@ -1,4 +1,3 @@
/build/ /build/
/local/ /local/
*~ *~
.vscode/settings.json
-137
View File
@@ -1,137 +0,0 @@
# v0.9.24-alpha
## Debugger Changes
- Added the ability for the debugger to load, use, and evaluate using debug
info, even when not actively debugging. The debugger will now keep a process'
debug info loaded, even after the process ends. It stores the set of loaded
debug info files in the project configuration file, meaning it will also
automatically load the same debug info across many runs. Debug info can also
be loaded manually (without ever launching a process) with the
`Load Debug Info` command. There is also a new tab, `Debug Info`, which allows
viewing and managing the set of loaded debug info files.
- Improved the debugger's behavior when used as a drag & drop target, to allow
for debug info loading as an option (when relevant), and to better handle the
case where many files (potentially of different types) are dropped together.
- Improved debug info searching performance and reponsiveness in large projects.
- Fixed some crashes and incorrect results with the new `list` view.
- Fixed some cases where RDIs did not contain some basic types from their
originating PDBs.
- Allowed `.` and `->` operators to be used with array types.
- Fixed the debugger's treatment of quoted command line arguments when building
targets. In previous versions, calling `raddbg main.exe "foo bar baz"` would
create a target `main.exe` with arguments `foo bar baz` (dropping the quotes).
This is now fixed, such that the target's arguments string will also contain
the quotes, and pass them to the target when launched.
- Fixed the debugger not correctly responding (through font and UI scale) to DPI
changes.
- Fixed the debugger incorrectly generating conflicting source line info records
in PDB -> RDI conversion, which in some scenarios was preventing source line
maps from working (leading to breakpoint resolution failing).
- Other small fixes, improvements, and tweaks.
# v0.9.23-alpha
## Debugger Changes
- Further improved PDB -> RDI conversion performance & memory usage.
- Adjusted limits for the amount of PDB -> RDI conversion work that the
debugger will kick off, to prevent PDB -> RDI conversion interfering with
debuggee performance.
- Reintroduced the `list` lens, which gathers all nodes in a linked list, and
visualizes them as a flat list of pointers (the same as how an array of
pointers is visualized).
- Fixed a crash when closing empty Geometry 3D views. (#652, #657)
- Fixed the debugger not visualizing `enum` types when evaluated through a
bitfield type. (#655)
- Fixed the PDB -> RDI conversion not correctly generating location information
for function parameters, when the EXE/PDB were built to include support for
Edit and Continue (`-ZI` switch). (#656)
## Binary Utility Changes
- Fixed a crash when using the `--compress` option, when generating RDI files.
# v0.9.22-alpha
## Debugger Changes
- Further improved PDB -> RDI conversion performance.
- Capped the number of additional threads / processes spawned for PDB -> RDI
conversion.
- Prioritize PDB -> RDI conversion based on what is actually found to be
necessary by the debugger, rather than converting all PDBs in the order in
which they're discovered.
- Added preliminary support for DWARF -> RDI conversion on Windows.
- The debugger now relies on source file checksums to determine whether or not
a source file is out-of-date with respect to what was compiled when debug info
was produced, rather than just the modification timestamp.
- The debugger now will rely on debug info to detect the language of source code
files, if it cannot infer from the source file's extension, or view settings.
This will enable features like syntax highlighting and hover evaluation in
cases like `.inl` files being included in C++ projects.
- The debugger now will restore the last focused window when continuing
automatically. (#245, #596)
- Watch tables have been simplified in that they no longer have a separate
column for evaluation types, since this was usually taking a lot more space
than it deserved. The type of evaluations is still displayed in watch table
cells, and it can always be evaluated directly via `typeof`.
- Type evaluations have been simplified in watch tables as well; they no longer
have untitled columns for sizes and offsets, this is instead displayed as
an extra note by default. Similar behavior to the original behavior can still
be obtained using the `columns` view, if needed.
- The debugger no longer uses complex `union` types for most registers, and
instead just displays the register value plainly.
- The hover evaluation UI has been made larger when needed.
- The debugger now prefers matching global, function, and type identifiers to
the most relevant debug info and module in context; this fixes evaluation in
some multi-process debugging contexts. (#581)
- Fixed the debugger unnecessarily stripping `enum` type information when
accessed through array operators. (#634)
- The debugger now understands a standalone `unsigned` keyword as an
`unsigned int` type, to match C rules.
- The debugger now uses the current working directory to form the working
directory for targets specified on the command line, to match behavior when
running a command from the command line without the debugger.
- Improved call stack computation performance.
- Improved debugger memory usage over long periods of time.
- Fixed string-pointer comparison not working with not-equal (`!=`) operations.
- Fixed a bug which was causing bad debuggee performance on some threads after
some interactions with the debugger controller.
- Fixed incorrect results when adding two register values. (#642)
- Fixed the interpretation of register expressions in visualizers. (#649)
- Fixed "forever loading" states in disassembly views in some cases. (#643)
- Fixed jittering on window resizing. (#636)
- Fixed the bitmap visualizer crashing in some circumstances relating to
unsupported bitmap sizes. (#444, #563)
- Fixed a crash when an empty `cast()` expression would be evaluated. (#625)
- Fixed a crash when an invalid expression would be visualized using the `text`
view. (#647)
## Linker Changes
- Changed symbol resolution in libaries to match MSVC behavior.
- Optimized image building step to reduce memory usage.
- Linker memory maps all input files by default to lower memory usage.
(`/RAD_MEMORY_MAP_FILES`)
- If debug info is available, linker uses it to show file and line number for
unresolved relocations.
- Improved base relocation build performance for large images, cutting build
time by 70%.
- Added stubs for `/Brepro`, `/D2`, and /ErrorReport to improve compatability
with existing response files
- Implemented section garbage collection (`/OPT:REF`)
- Fixed bug where thread local variables pointed to incorrect types.
- Changed rules for weak and undefined symbols, now weak symbol is not allowed
to replace an undefined symbol.
- Linker no longer creates thunks for imports that don't require them.
## Binary Utility Changes
- The binary utility, like the debugger, now can convert DWARF debug info to
RDI files. When both DWARF and PDB info is present, it can now convert both,
and produce a single final RDI file with all information.
- Textual dumping of RDI files is now done in parallel, massively improving
dumping performance.
- PDB -> Breakpad conversion performance has now been parallelized to a greater
degree, improving performance.
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) Epic Games Tools Copyright (c) 2024 Epic Games Tools
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in this software and associated documentation files (the “Software”), to deal in
+123 -224
View File
@@ -1,93 +1,45 @@
# The RAD Debugger Project # The RAD Debugger Project
_**NOTE:** This README does not document usage instructions and tips for the _**Note:** This README does not document usage instructions and tips for the
debugger itself, and is intended as a technical overview of the project. The debugger itself, and is intended as a technical overview of the project. The
debugger's README, which includes usage instructions and tips, can be found debugger's README, which includes usage instructions and tips, can be found
packaged along with debugger releases, or within the `build` folder after a packaged along with debugger releases, or within the `build` folder after a
local copy has been built. You can find pre-built release binaries local copy has been built._
[here](https://github.com/EpicGamesExt/raddebugger/releases)._
The RAD Debugger is a native, user-mode, multi-process, graphical debugger. It The RAD Debugger is a native, user-mode, multi-process, graphical debugger. It
currently only supports local-machine Windows x64 debugging with PDBs, with currently only supports local-machine Windows x64 debugging with PDBs, with
plans to expand and port in the future. In the future we'll expand to also plans to expand and port in the future. In the future we'll expand to also
support native Linux debugging and DWARF debug info. support native Linux debugging and DWARF debug info.
The debugger is currently in *ALPHA*. In order to get the debugger The RAD Debugger is currently in *ALPHA*. In order to get the debugger bullet-
bullet-proof, it'd greatly help out if you submitted the issues you find proof, it'd greatly help out if you submitted the issues you find here, along
[here](https://github.com/EpicGamesExt/raddebugger/issues), along with any with any information you can gather, like dump files (along with the build you
information you can gather, like dump files (along with the build you used), used), instructions to reproduce, test executables, and so on.
instructions to reproduce, test executables, and so on.
In addition to the debugger, we aim to further improve the toolchain with two You can download pre-built binaries for the debugger
additional related technologies: **(1)** the RAD Debug Info (RDI) format, and [here](https://github.com/EpicGames/raddebugger/releases).
**(2)** the RAD Linker.
## The RAD Debug Info (RDI) Format The RAD Debugger project aims to simplify the debugger by simplifying and
unifying the underlying debug info format. In that pursuit we've built the RAD
The RAD Debug Info (RDI) format is our custom debug information format, which Debug Info (RDI) format, which is what the debugger parses and uses. To work
the debugger parses and uses, rather than the debug information natively with existing toolchains, we convert PDB (and eventually PE/ELF files with
produced by toolchains, like PDB or DWARF. To work with these existing embedded DWARF) into the RDI format on-demand.
toolchains, we convert PDB (and eventually PE/ELF files with embedded DWARF)
into the RDI format on-demand.
The RDI format is currently specified in code, in the files within the The RDI format is currently specified in code, in the files within the
`src/lib_rdi` folder. In [`rdi.h`](src/lib_rdi/rdi.h) and `src/lib_rdi_format` folder. The other relevant folders for working with the
[`rdi.c`](src/lib_rdi/rdi.c), the types and functions which define the format format are:
itself are specified. In [`rdi_parse.h`](src/lib_rdi/rdi_parse.h) and
[`rdi_parse.c`](src/lib_rdi/rdi_parse.c), helpers for parsing the format are
included.
We also have an in-progress library for constructing and serializing RDI data, - `lib_rdi_make`: The "RAD Debug Info Make" library, for making RDI debug info.
located within the `src/lib_rdi_make` folder. - `rdi_from_pdb`: Our PDB-to-RDI converter. Can be used as a helper codebase
layer, or built as an executable with a command line interface frontend.
- `rdi_from_dwarf`: Our in-progress DWARF-to-RDI converter.
- `rdi_dump`: Our RDI textual dumping utility.
Our `radbin` utility (accessible through the debugger too, via the `--bin` ## Development Setup Instructions
command line argument) is capable of converting native debug information formats
to RDI, and of producing textual dumps of contents stored within RDI files.
## The RAD Linker **Note: Currently, only x64 Windows development is supported.**
The RAD Linker is a new performance linker for generating x64 PE/COFF binaries. ### 1. Installing the Required Tools (MSVC & Windows SDK)
It is designed to be very fast when creating gigantic executables. It generates
standard PDB files for debugging, but it can also (optionally) natively create
RAD Debug Info too, which is useful both to eliminate on-demand conversion time
when debugging, but also for huge executables that otherwise create broken
PDBs that overflow internal 32-bit tables.
The RAD Linker is primarily optimized to handle huge linking projects. In our
test cases (where debug info is multiple gigabytes), we see 50% faster link
times.
The command line syntax is fully compatible with MSVC; you can get a full list
of implemented switches from `/help`.
Our current designed-for use case for the linker is to help with the
compile-debug cycle of huge projects. We don't yet have support for
link-time-optimizations, but this feature is on the road map.
By default, the linker spawns as many threads as there are cores, so if you plan
to run multiple linkers in parallel, you can limit the number of thread workers
via `/rad_workers`.
We also have support for large memory pages, which, when enabled, reduce link
time by another 25%. To link with large pages, you need to explicitly request
them via `/rad_large_pages`. Large pages are off by default, since Windows
support for large pages is a bit buggy; we recommend they only be used in Docker
or VM images where the environment is reset after each link. In a standard
Windows environment, using large pages otherwise will fragment memory quickly,
forcing a reboot. We are working on a Linux port of the linker that will be able
to build with large pages robustly.
A benchmark of the linker's performance is below:
![AMD Ryzen Threadripper PRO 3995WX 64-Cores, 256 GiB RAM (Windows x64)](https://github.com/user-attachments/assets/a95b382a-76b4-4a4c-b809-b61fe25e667a)
---
# Project Development Setup Instructions
**NOTE: Currently, only x64 Windows development is supported for the project.**
## 1. Installing the Required Tools (MSVC & Windows SDK)
In order to work with the codebase, you'll need the [Microsoft C/C++ Build Tools In order to work with the codebase, you'll need the [Microsoft C/C++ Build Tools
v15 (2017) or later](https://aka.ms/vs/17/release/vs_BuildTools.exe), for both v15 (2017) or later](https://aka.ms/vs/17/release/vs_BuildTools.exe), for both
@@ -96,7 +48,7 @@ the Windows SDK and the MSVC compiler and linker.
If the Windows SDK is installed (e.g. via installation of the Microsoft C/C++ If the Windows SDK is installed (e.g. via installation of the Microsoft C/C++
Build Tools), you may also build with [Clang](https://releases.llvm.org/). Build Tools), you may also build with [Clang](https://releases.llvm.org/).
## 2. Build Environment Setup ### 2. Build Environment Setup
Building the codebase can be done in a terminal which is equipped with the Building the codebase can be done in a terminal which is equipped with the
ability to call either MSVC or Clang from command line. ability to call either MSVC or Clang from command line.
@@ -140,63 +92,33 @@ You should see the following output:
[msvc compile] [msvc compile]
[default mode, assuming `raddbg` build] [default mode, assuming `raddbg` build]
metagen_main.c metagen_main.c
searching C:\devel\raddebugger/src... 458 files found searching C:\devel\raddebugger/src... 299 files found
parsing metadesk... 16 metadesk files parsed parsing metadesk... 12 metadesk files parsed
gathering tables... 97 tables found gathering tables... 37 tables found
generating layer code... generating layer code...
raddbg_main.c raddbg.cpp
``` ```
If everything worked correctly, there will be a `build` folder in the root If everything worked correctly, there will be a `build` folder in the root
level of the codebase, and it will contain a freshly-built `raddbg.exe`. level of the codebase, and it will contain a freshly-built `raddbg.exe`.
This `raddbg.exe` will have been built in **debug mode**, which is not built ## Short-To-Medium-Term Roadmap
with optimizations, and may perform worse. To produce a
**release mode executable**, run `build.bat` with a `release` argument:
```
build release
```
This build will take significantly longer.
By default, `build.bat` only builds the debugger if no arguments (or just
`release`) are passed, but additional arguments can be passed to build the RAD
Linker, or the `radbin` CLI binary file utility:
```
build radlink release
build radbin release
```
---
# Project Roadmap
### The Initial Alpha Battle-Testing Phase ### The Initial Alpha Battle-Testing Phase
The first priority for the project is to ensure that the most crucial components The first priority for the project is to ensure that the most crucial debugger
are functioning extremely reliably for local, x64, Windows development. components are functioning extremely reliably for local, x64, Windows
For the debugger, this would include parts like debug info conversion, debug debugging. This would include parts like debug info conversion, debug info
info loading, process control, stepping, evaluation (correct usage of both loading, process control, stepping, evaluation (correct usage of both location
location info and type info), and a robust frontend which ensures the lower info and type info), and a robust frontend which ensures the lower level parts
level parts are usable. For the linker, this is a matter of reliability and are usable.
convergence with existing linker behavior.
We feel that we've already come a long way in all of these respects, but given We feel that the debugger has already come a long way in all of these respects,
the massive set of possible combinations of languages, build settings, but given the massive set of possible combinations of languages, build
toolchains, used language features, and patterns of generated code, we still settings, toolchains, used language features, and patterns of generated code,
expect some issues, and are prioritizing these issues being resolved first. there are still cases where the debugger has not been tested, and so there are
still issues. So, we feel that the top priority is eliminating these issues,
We also hope to continue to improve performance in this phase. For the debugger, such that the debugging experience is rock solid.
this primarily includes frontend performance, introducing caches when economical
to do so, and tightening existing systems up. For the linker, it has been mostly
tuned thus far for giant projects, and so we'd like to improve linking speed for
small-to-mid sized projects as well.
For the linker, there are also a number of features to come, like
dead-code-elimination (`/opt:ref`), and link-time-optimizations with the help
of `clang` (we won't support LTCG from MSVC, since it is undocumented).
### Local x64 Linux Debugging Phase ### Local x64 Linux Debugging Phase
@@ -237,10 +159,6 @@ like remote debugging, porting to different architectures, further improving
the debugger's features (like improving the visualization engine), and so on. the debugger's features (like improving the visualization engine), and so on.
But for now, we're mostly focused on those first two phases. But for now, we're mostly focused on those first two phases.
---
# Codebase Introduction
## Top-Level Directory Descriptions ## Top-Level Directory Descriptions
- `data`: Small binary files which are used when building, either to embed - `data`: Small binary files which are used when building, either to embed
@@ -251,10 +169,9 @@ After setting up the codebase and building, the following directories will
also exist: also exist:
- `build`: All build artifacts. Not checked in to version control. - `build`: All build artifacts. Not checked in to version control.
- `local`: Local files, used for local build configuration input files. Not - `local`: Local files, used for local build configuration input files.
checked in to version control.
## Layer Descriptions ## Codebase Introduction
The codebase is organized into *layers*. Layers are separated either to isolate The codebase is organized into *layers*. Layers are separated either to isolate
certain problems, and to allow inclusion into various builds without needing to certain problems, and to allow inclusion into various builds without needing to
@@ -282,85 +199,69 @@ so in other words, layers are arranged into a directed acyclic graph.
A few layers are built to be used completely independently from the rest of the A few layers are built to be used completely independently from the rest of the
codebase, as libraries in other codebases and projects. As such, these layers do codebase, as libraries in other codebases and projects. As such, these layers do
not depend on any other layers in the codebase. The folders which contain these not depend on any other layers in the codebase. The folders which contain these
layers are prefixed with `lib_`, like `lib_rdi`. layers are prefixed with `lib_`, like `lib_rdi_format`.
A list of the layers in the codebase and their associated namespaces is below: A list of the layers in the codebase and their associated namespaces is below:
- `artifact_cache` (`AC_`): Implements an asynchronously-filled cache of
computation artifacts, which are automatically evicted when not accessed. Used
for asynchronously streaming and caching process memory and file system
contents, as well as asynchronously preparing visualizer data.
- `base` (no namespace): Universal, codebase-wide constructs. Strings, math, - `base` (no namespace): Universal, codebase-wide constructs. Strings, math,
memory allocators, helper macros, command-line parsing, and so on. Requires memory allocators, helper macros, command-line parsing, and so on. Depends
no other codebase layers. on no other codebase layers.
- `codeview` (`CV_`): Code for parsing and writing the CodeView format. - `codeview` (`CV_`): Code for parsing and/or writing the CodeView format.
- `coff` (`COFF_`): Code for parsing and writing the COFF (Common Object File - `coff` (`COFF_`): Code for parsing and/or writing the COFF (Common Object File
Format) file format. Format) file format.
- `content` (`C_`): Implements a cache for general data blobs, keyed by a
128-bit hash of the data. Also implements a keying system on top, where keys
refer to a unique identity which corresponds to a history of 128-bit hashes.
Used as a general data store by other layers.
- `ctrl` (`CTRL_`): The debugger's "control system" layer. Implements - `ctrl` (`CTRL_`): The debugger's "control system" layer. Implements
asynchronous process control, stepping, and breakpoints for all attached asynchronous process control, stepping, and breakpoints for all attached
processes. Runs in lockstep with attached processes. When it runs, attached processes. Runs in lockstep with attached processes. When it runs, attached
processes are halted. When attached processes are running, it is halted. processes are halted. When attached processes are running, it is halted.
Driven by a debugger frontend on another thread. Driven by a debugger frontend on another thread.
- `dbg_engine` (`D_`): Implements the core debugger system, without any - `dasm` (`DASM_`): An asynchronous disassembly decoder and cache. Users ask for
graphical components. This contains top-level logic for things like stepping, disassembly for a particular virtual address range in a process, and threads
launching, freezing threads, mid-run breakpoint addition, some caches, and so implemented in this layer decode and cache the disassembly for that range.
on. - `dbgi` (`DI_`): An asynchronous debug info loader and cache. Loads debug info
- `dbg_info` (`DI_`): Implements asynchronous debug info conversion and loading. stored in the RDI format. Users ask for debug info for a particular path, and
Maintains a cache for loaded debug info. Loads RAD Debug Info (RDI) files. on separate threads, this layer loads the associated debug info file. If
Launches separate processes for on-demand conversion to the RDI format if necessary, it will launch a separate conversion process to convert original
necessary. Also provides various asynchronous operations for using debug info, debug info into the RDI format.
like fuzzy searching across all records in loaded debug info. - `demon` (`DEMON_`): An abstraction layer for local-machine, low-level process
- `demon` (`DMN_`): An abstraction layer for local-machine, low-level process
control. The abstraction is used to provide a common interface for process control. The abstraction is used to provide a common interface for process
control on target platforms. Used to implement part of `ctrl`. control on target platforms. Used to implement part of `ctrl`.
- `disasm` (`DASM_`): Implements disassembly generation, including exposing the - `df/core` (`DF_`): The debugger's non-graphical frontend. Implements a
ability to compute and cache disassembly asynchronously. debugger "entity cache" (where "entities" include processes, threads, modules,
- `draw` (`DR_`): Implements a high-level graphics drawing API for the breakpoints, source files, targets, and so on). Implements a command loop
debugger's purposes, using the underlying `render` abstraction layer. Provides for driving process control, which is used to implement stepping commands and
high-level APIs for various draw commands, but takes care of batching them, user breakpoints. Implements extractors and caches for various entity-related
and so on. data, like full thread unwinds and local variable maps. Also implements core
- `dwarf` (`DW_`): Code for parsing the DWARF format. building blocks for evaluation and evaluation visualization.
- `eh` (`EH_`): Code for parsing the EH frame format. - `df/gfx` (`DF_`): The debugger's graphical frontend. Builds on top of
- `elf` (`ELF_`): Code for parsing the ELF format. `df/core` to provide all graphical features, including windows, panels, all
- `eval` (`E_`): A compiler for an expression language, built for evaluation of of the various debugger interfaces, and evaluation visualization.
variables, registers, types, and more, from debugger-attached processes, - `draw` (`D_`): Implements a high-level graphics drawing API for the debugger's
debug info, debugger state, and files. Broken into several phases mostly purposes, using the underlying `render` abstraction layer. Provides high-level
corresponding to traditional compiler phases: lexer, parser, type-checker, IR APIs for various draw commands, but takes care of batching them, and so on.
generation, and IR evaluation. - `eval` (`EVAL_`): Implements a compiler for an expression language built for
- `eval_visualization` (`EV_`): Implements the core non-graphical evaluation evaluation of variables, registers, and so on from debugger-attached processes
visualization engine, which can be used to visualize evaluations (provided by and/or debug info. Broken into several phases mostly corresponding to
the `eval` layer) in a number of ways. Implements core data structures and traditional compiler phases - lexer, parser, type-checker, IR generation, and
transforms for watch tables. IR evaluation.
- `file_stream` (`FS_`): Implements asynchronous file streaming, storing the - `font_cache` (`F_`): Implements a cache of rasterized font data, both in CPU-
artifacts inside of the cache implemented by the `content` and side data for text shaping, and in GPU texture atlases for rasterized glyphs.
`artifact_cache` layers, hot-reloading the contents of files when they change. All cache information is sourced from the `font_provider` abstraction layer.
Allows callers to map file paths to data hashes, which can then be used to
obtain the file's data.
- `font_cache` (`FNT_`): Implements a cache of rasterized font data, both in
CPU-side data for text shaping, and in GPU texture atlases for rasterized
glyphs. All cache information is sourced from the `font_provider` abstraction
layer.
- `font_provider` (`FP_`): An abstraction layer for various font file decoding - `font_provider` (`FP_`): An abstraction layer for various font file decoding
and font rasterization backends. and font rasterization backends.
- `geo_cache` (`GEO_`): Implements an asynchronously-filled cache for GPU
geometry data, filled by data sourced in the `hash_store` layer's cache. Used
for asynchronously preparing data for memory visualization in the debugger.
- `hash_store` (`HS_`): Implements a cache for general data blobs, keyed by a
128-bit hash of the data. Used as a general data store by other layers.
- `lib_raddbg_markup` (`RADDBG_`): Standalone library for marking up user - `lib_raddbg_markup` (`RADDBG_`): Standalone library for marking up user
programs to work with various features in the debugger. Does not depend on programs to work with various features in the `raddbg` debugger. Does not
`base`, and can be independently relocated to other codebases. depend on `base`, and can be independently relocated to other codebases.
- `lib_rdi` (`RDI_`): Standalone library which defines the core RDI types
and helper functions for reading and writing the RDI debug info file format.
Does not depend on `base`, and can be independently relocated to other
codebases.
- `lib_rdi_make` (`RDIM_`): Standalone library for constructing RDI debug info - `lib_rdi_make` (`RDIM_`): Standalone library for constructing RDI debug info
data. Does not depend on `base`, and can be independently relocated data. Does not depend on `base`, and can be independently relocated
to other codebases. to other codebases.
- `linker` (`LNK_`): The layer which implements the RAD Linker executable - `lib_rdi_format` (`RDI_`): Standalone library which defines the core RDI types
itself. and helper functions for reading and writing the RDI debug info file format.
- `mdesk` (`MD_`): Code for parsing Metadesk files (stored as `.mdesk`), which Does not depend on `base`, and can be independently relocated to other
is the JSON-like (technically a JSON superset) text format used for the codebases.
debugger's user and project configuration files and metacode, which is parsed
and used to generate code with the `metagen` layer.
- `metagen` (`MG_`): A metaprogram which is used to generate primarily code and - `metagen` (`MG_`): A metaprogram which is used to generate primarily code and
data tables. Consumes Metadesk files, stored with the extension `.mdesk`, and data tables. Consumes Metadesk files, stored with the extension `.mdesk`, and
generates C code which is then included by hand-written C code. Currently, it generates C code which is then included by hand-written C code. Currently, it
@@ -369,13 +270,15 @@ A list of the layers in the codebase and their associated namespaces is below:
tables, which are then used to produce e.g. C `enum`s and a number of tables, which are then used to produce e.g. C `enum`s and a number of
associated data tables. There are also a number of other generation features, associated data tables. There are also a number of other generation features,
like embedding binary files or complex multi-line strings into source code. like embedding binary files or complex multi-line strings into source code.
- `msf` (`MSF_`): Code for parsing and writing the MSF file format. This layer cannot depend on any other layer in the codebase directly,
- `msvc_crt` (`MSCRT_`): Code for parsing that's specific to the MSVC CRT. including `base`, because it may be used to generate code for those layers. To
still use `base` and `os` layer features in the `metagen` program, a separate,
duplicate version of `base` and `os` are included in this layer. They are
updated manually, as needed. This is to ensure the stability of the
metaprogram.
- `msf` (`MSF_`): Code for parsing and/or writing the MSF file format.
- `mule` (no namespace): Test executables for battle testing debugger - `mule` (no namespace): Test executables for battle testing debugger
functionality. functionality.
- `mutable_text` (`MTX_`): Implements an asynchronously-filled-and-mutated
cache for text buffers which are mutated across time. In the debugger, this is
used to implement the `Output` log.
- `natvis` (no namespace): NatVis files for type visualization of the codebase's - `natvis` (no namespace): NatVis files for type visualization of the codebase's
types in other debuggers. types in other debuggers.
- `os/core` (`OS_`): An abstraction layer providing core, non-graphical - `os/core` (`OS_`): An abstraction layer providing core, non-graphical
@@ -384,29 +287,20 @@ A list of the layers in the codebase and their associated namespaces is below:
- `os/gfx` (`OS_`): An abstraction layer, building on `os/core`, providing - `os/gfx` (`OS_`): An abstraction layer, building on `os/core`, providing
graphical operating system features under an abstract API, which is graphical operating system features under an abstract API, which is
implemented per-target-operating-system. implemented per-target-operating-system.
- `pdb` (`PDB_`): Code for parsing and writing the PDB file format. - `os/socket` (`OS_`): An abstraction layer, building on `os/core`, providing
- `pe` (`PE_`): Code for parsing and writing the PE (Portable Executable) file networking operating system features under an abstract API, which is
format. implemented per-target-operating-system.
- `radbin` (`RB_`): The layer implementing the `radbin` binary utility - `pdb` (`PDB_`): Code for parsing and/or writing the PDB file format.
executable. - `pe` (`PE_`): Code for parsing and/or writing the PE (Portable Executable)
- `raddbg` (`RD_`): The layer which ties everything together for the main file format.
graphical debugger executable. Implements the debugger's graphical frontend, - `raddbg` (no namespace): The layer which ties everything together for the main
all of the debugger-specific UI, the debugger executable's command line graphical debugger. Not much "meat", just drives `df`, implements command line
interface, and all of the built-in visualizers. options, and so on.
- `rdi` (`RDI_`): A layer which includes the `lib_rdi` layer and bundles it with - `rdi_from_pdb` (`P2R_`): Our implementation of PDB-to-RDI conversion.
codebase-specific helpers, to easily include the library in codebase programs, - `rdi_from_dwarf` (`D2R_`): Our in-progress implementation of DWARF-to-RDI
and have it be integrated with codebase constructs. conversion.
- `rdi_from_coff` (`C2R_`): Code for converting information in COFF files to the - `rdi_dump` (no namespace): A dumper utility program for dumping
equivalent RDI data. textualizations of RDI debug info files.
- `rdi_from_dwarf` (`D2R_`): In-progress code for converting DWARF to the
equivalent RDI data.
- `rdi_from_elf` (`E2R_`)): Code for converting ELF data to the equivalent RDI
data.
- `rdi_from_pdb` (`P2R_`): Code for converting PDB data to the equivalent RDI
data.
- `rdi_make` (`RDIM_`): A layer which includes the `lib_rdi_make` layer and
bundles it with codebase-specific helpers, to easily include the library in
codebase programs, and have it be integrated with codebase constructs.
- `regs` (`REGS_`): Types, helper functions, and metadata for registers on - `regs` (`REGS_`): Types, helper functions, and metadata for registers on
supported architectures. Used in reading/writing registers in `demon`, or in supported architectures. Used in reading/writing registers in `demon`, or in
looking up register metadata. looking up register metadata.
@@ -415,14 +309,19 @@ A list of the layers in the codebase and their associated namespaces is below:
level drawing API - this layer is strictly for minimally abstracting on an level drawing API - this layer is strictly for minimally abstracting on an
as-needed basis. Higher level drawing features are implemented in the `draw` as-needed basis. Higher level drawing features are implemented in the `draw`
layer. layer.
- `scratch` (no namespace): Scratch space for small and transient test programs. - `scratch` (no namespace): Scratch space for small and transient test or sample
- `tester` (no namespace): A program used for automated testing. programs.
- `text` (`TXT_`): Implements text processing functions, like parsing line - `texture_cache` (`TEX_`): Implements an asynchronously-filled cache for GPU
breaks, and lexing and parsing source code. Also offers an API to do this texture data, filled by data sourced in the `hash_store` layer's cache. Used
asynchronously. for asynchronously preparing data for memory visualization in the debugger.
- `third_party` (no namespace): External code from other projects, which some - `txti` (`TXTI_`): Machinery for asynchronously-loaded, asynchronously hot-
layers in the codebase depend on. All external code is included and built reloaded, asynchronously parsed, and asynchronously mutated source code files.
directly within the codebase. Used by the debugger to visualize source code files. Users ask for text lines,
tokens, and metadata, and it is prepared on background threads.
- `type_graph` (`TG_`): Code for analyzing and navigating type structures from
RDI debug info files, with the additional capability of constructing
synthetic types *not* found in debug info. Used in `eval` and for various
visualization features.
- `ui` (`UI_`): Machinery for building graphical user interfaces. Provides a - `ui` (`UI_`): Machinery for building graphical user interfaces. Provides a
core immediate mode hierarchical user interface data structure building core immediate mode hierarchical user interface data structure building
API, and has helper layers for building some higher-level widgets. API, and has helper layers for building some higher-level widgets.
+40 -84
View File
@@ -1,15 +1,14 @@
@echo off @echo off
setlocal enabledelayedexpansion setlocal
cd /D "%~dp0" cd /D "%~dp0"
:restart
:: --- Usage Notes (2024/1/10) ------------------------------------------------ :: --- Usage Notes (2024/1/10) ------------------------------------------------
:: ::
:: This is a central build script for the RAD Debugger project, for use in :: This is a central build script for the RAD Debugger project. It takes a list
:: Windows development environments. It takes a list of simple alphanumeric- :: of simple alphanumeric-only arguments which control (a) what is built, (b)
:: only arguments which control (a) what is built, (b) which compiler & linker :: which compiler & linker are used, and (c) extra high-level build options. By
:: are used, and (c) extra high-level build options. By default, if no options :: default, if no options are passed, then the main "raddbg" graphical debugger
:: are passed, then the main "raddbg" graphical debugger is built. :: is built.
:: ::
:: Below is a non-exhaustive list of possible ways to use the script: :: Below is a non-exhaustive list of possible ways to use the script:
:: `build raddbg` :: `build raddbg`
@@ -25,73 +24,44 @@ cd /D "%~dp0"
:: ::
:: - `asan`: enable address sanitizer :: - `asan`: enable address sanitizer
:: - `telemetry`: enable RAD telemetry profiling support :: - `telemetry`: enable RAD telemetry profiling support
:: - `spall`: enable spall profiling support
:: --- Unpack Arguments ------------------------------------------------------- :: --- Unpack Arguments -------------------------------------------------------
for %%a in (%*) do set "%%~a=1" for %%a in (%*) do set "%%a=1"
if not "%msvc%"=="1" if not "%clang%"=="1" set msvc=1 if not "%msvc%"=="1" if not "%clang%"=="1" set msvc=1
if not "%release%"=="1" set debug=1 if not "%release%"=="1" set debug=1
if "%debug%"=="1" set release=0 && echo [debug mode] if "%debug%"=="1" set release=0 && echo [debug mode]
if "%release%"=="1" set debug=0 && echo [release mode] if "%release%"=="1" set debug=0 && echo [release mode]
if "%msvc%"=="1" set clang=0 && echo [msvc compile] if "%msvc%"=="1" set clang=0 && echo [msvc compile]
if "%clang%"=="1" set msvc=0 && echo [clang compile] if "%clang%"=="1" set msvc=0 && echo [clang compile]
if "%~1"=="" echo [default mode, assuming `raddbg` build] && set raddbg=1 if "%~1"=="" echo [default mode, assuming `raddbg` build] && set raddbg=1
if "%~1"=="release" if "%~2"=="" echo [default mode, assuming `raddbg` build] && set raddbg=1
:: --- Unpack Command Line Build Arguments ------------------------------------ :: --- Unpack Command Line Build Arguments ------------------------------------
set auto_compile_flags= set auto_compile_flags=
if "%telemetry%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_TELEMETRY=1 && echo [telemetry profiling enabled] if "%telemetry%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_TELEMETRY=1 && echo [telemetry profiling enabled]
if "%spall%"=="1" set auto_compile_flags=%auto_compile_flags% -DPROFILE_SPALL=1 && echo [spall profiling enabled] if "%asan%"=="1" set auto_compile_flags=%auto_compile_flags% -fsanitize=address && echo [asan enabled]
if "%asan%"=="1" set auto_compile_flags=%auto_compile_flags% -fsanitize=address && echo [asan enabled]
if "%opengl%"=="1" set auto_compile_flags=%auto_compile_flags% -DR_BACKEND=R_BACKEND_OPENGL && echo [opengl render backend]
if "%dwarf%"=="1" if "%clang%"=="1" set auto_compile_flags=%auto_compile_flags% -gdwarf && echo [dwarf debug info]
if "%dwarf%"=="" if "%clang%"=="1" set auto_compile_flags=%auto_compile_flags% -gcodeview
if "%pgo%"=="1" (
where llvm-profdata /q || echo llvm-profdata is not in the PATH || exit /b 1
if "%clang%"=="1" (
if "%pgo_run%" == "1" (
call llvm-profdata merge %LLVM_PROFILE_FILE% -output=%~dp0build\build.profdata || exit /b 1
set auto_compile_flags=%auto_compile_flags% -fprofile-use=%~dp0build\build.profdata
set pgo_run=0
) else (
echo [pgo enabled]
set auto_compile_flags=%auto_compile_flags% -fprofile-generate -mllvm -vp-counters-per-site=5
set LLVM_PROFILE_FILE=%~dp0build\build.profraw
set pgo_run=1
)
) else (
echo ERROR: PGO build is not supported with current compiler
exit /b 1
)
)
:: --- Compile/Link Line Definitions ------------------------------------------ :: --- Compile/Link Line Definitions ------------------------------------------
set cl_common= /I..\src\ /I..\local\ /nologo /FC /Z7 set cl_common= /I..\src\ /I..\local\ /nologo /FC /Z7
set cl_debug= call cl /Od /Ob1 /DBUILD_DEBUG=1 %cl_common% %auto_compile_flags% set clang_common= -I..\src\ -I..\local\ -gcodeview -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-writable-strings -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf
set cl_debug= call cl /Od /DBUILD_DEBUG=1 %cl_common% %auto_compile_flags%
set cl_release= call cl /O2 /DBUILD_DEBUG=0 %cl_common% %auto_compile_flags% set cl_release= call cl /O2 /DBUILD_DEBUG=0 %cl_common% %auto_compile_flags%
set cl_link= /link /MANIFEST:EMBED /INCREMENTAL:NO /pdbaltpath:%%%%_PDB%%%% /NATVIS:"%~dp0\src\natvis\base.natvis" /noexp /nocoffgrpinfo /opt:ref /opt:icf
set cl_out= /out:
set cl_linker=
set clang_common= -I..\src\ -I..\local\ -fdiagnostics-absolute-paths -Wall -Wno-unknown-warning-option -Wno-missing-braces -Wno-unused-function -Wno-unused-parameter -Wno-writable-strings -Wno-missing-field-initializers -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Wno-compare-distinct-pointer-types -Wno-initializer-overrides -Wno-incompatible-pointer-types-discards-qualifiers -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf -ferror-limit=10000 -mcx16 -msha
set clang_debug= call clang -g -O0 -DBUILD_DEBUG=1 %clang_common% %auto_compile_flags% set clang_debug= call clang -g -O0 -DBUILD_DEBUG=1 %clang_common% %auto_compile_flags%
set clang_release= call clang -g -O2 -DBUILD_DEBUG=0 %clang_common% %auto_compile_flags% set clang_release= call clang -g -O2 -DBUILD_DEBUG=0 %clang_common% %auto_compile_flags%
set clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /pdbaltpath:%%%%_PDB%%%% -Xlinker /NATVIS:"%~dp0\src\natvis\base.natvis" -Xlinker /opt:ref -Xlinker /opt:icf set cl_link= /link /MANIFEST:EMBED /INCREMENTAL:NO /natvis:"%~dp0\src\natvis\base.natvis" logo.res
set clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /natvis:"%~dp0\src\natvis\base.natvis" logo.res
set cl_out= /out:
set clang_out= -o set clang_out= -o
set clang_linker= -Xlinker
:: --- Per-Build Settings ----------------------------------------------------- :: --- Per-Build Settings -----------------------------------------------------
set link_dll=-DLL set link_dll=-DLL
set link_icon=logo.res
if "%msvc%"=="1" set linker=%cl_linker%
if "%clang%"=="1" set linker=%clang_linker%
if "%msvc%"=="1" set only_compile=/c if "%msvc%"=="1" set only_compile=/c
if "%clang%"=="1" set only_compile=-c if "%clang%"=="1" set only_compile=-c
if "%msvc%"=="1" set EHsc=/EHsc if "%msvc%"=="1" set EHsc=/EHsc
if "%clang%"=="1" set EHsc= if "%clang%"=="1" set EHsc=
if "%msvc%"=="1" set no_aslr=/DYNAMICBASE:NO if "%msvc%"=="1" set no_aslr=/DYNAMICBASE:NO
if "%clang%"=="1" set no_aslr=-Wl,/DYNAMICBASE:NO if "%clang%"=="1" set no_aslr=
if "%msvc%"=="1" set rc=call rc if "%msvc%"=="1" set rc=rc.exe
if "%clang%"=="1" set rc=call llvm-rc if "%clang%"=="1" set rc=llvm-rc.exe
:: --- Choose Compile/Link Lines ---------------------------------------------- :: --- Choose Compile/Link Lines ----------------------------------------------
if "%msvc%"=="1" set compile_debug=%cl_debug% if "%msvc%"=="1" set compile_debug=%cl_debug%
@@ -115,12 +85,11 @@ pushd build
popd popd
:: --- Get Current Git Commit Id ---------------------------------------------- :: --- Get Current Git Commit Id ----------------------------------------------
for /f %%i in ('call git describe --always --dirty') do set compile=%compile% -DBUILD_GIT_HASH=\"%%i\" for /f %%i in ('call git describe --always --dirty') do set compile=%compile% -DBUILD_GIT_HASH=\"%%i\"
for /f %%i in ('call git rev-parse HEAD') do set compile=%compile% -DBUILD_GIT_HASH_FULL=\"%%i\"
:: --- Build & Run Metaprogram ------------------------------------------------ :: --- Build & Run Metaprogram ------------------------------------------------
if "%meta%"=="1" ( if "%no_meta%"=="1" echo [skipping metagen]
echo [doing metagen] if not "%no_meta%"=="1" (
pushd build pushd build
%compile_debug% ..\src\metagen\metagen_main.c %compile_link% %out%metagen.exe || exit /b 1 %compile_debug% ..\src\metagen\metagen_main.c %compile_link% %out%metagen.exe || exit /b 1
metagen.exe || exit /b 1 metagen.exe || exit /b 1
@@ -129,24 +98,18 @@ if "%meta%"=="1" (
:: --- Build Everything (@build_targets) -------------------------------------- :: --- Build Everything (@build_targets) --------------------------------------
pushd build pushd build
if "%raddbg%"=="1" set didbuild=1 && %compile% ..\src\raddbg\raddbg_main.c %compile_link% %link_icon% %out%raddbg.exe || exit /b 1 if "%raddbg%"=="1" %compile% %gfx% ..\src\raddbg\raddbg_main.cpp %compile_link% %out%raddbg.exe || exit /b 1
if "%radlink%"=="1" set didbuild=1 && %compile% ..\src\linker\lnk.c %compile_link% %linker% /NOIMPLIB %linker% /NATVIS:"%~dp0\src\linker\linker.natvis" %out%radlink.exe || exit /b 1 if "%rdi_from_pdb%"=="1" %compile% ..\src\rdi_from_pdb\rdi_from_pdb_main.c %compile_link% %out%rdi_from_pdb.exe || exit /b 1
if "%radbin%"=="1" set didbuild=1 && %compile% ..\src\radbin\radbin_main.c %compile_link% %out%radbin.exe || exit /b 1 if "%rdi_from_dwarf%"=="1" %compile% ..\src\rdi_from_dwarf\rdi_from_dwarf.c %compile_link% %out%rdi_from_dwarf.exe || exit /b 1
if "%raddump%"=="1" set didbuild=1 && %compile% ..\src\raddump\raddump_main.c %compile_link% %out%raddump.exe || exit /b 1 if "%rdi_dump%"=="1" %compile% ..\src\rdi_dump\rdi_dump_main.c %compile_link% %out%rdi_dump.exe || exit /b 1
if "%tester%"=="1" set didbuild=1 && %compile% ..\src\tester\tester_main.c %compile_link% %out%tester.exe || exit /b 1 if "%rdi_breakpad_from_pdb%"=="1" %compile% ..\src\rdi_breakpad_from_pdb\rdi_breakpad_from_pdb_main.c %compile_link% %out%rdi_breakpad_from_pdb.exe || exit /b 1
if "%ryan_scratch%"=="1" set didbuild=1 && %compile% ..\src\scratch\ryan_scratch.c %compile_link% %out%ryan_scratch.exe || exit /b 1 if "%ryan_scratch%"=="1" %compile% ..\src\scratch\ryan_scratch.c %compile_link% %out%ryan_scratch.exe || exit /b 1
if "%eval_scratch%"=="1" set didbuild=1 && %compile% ..\src\scratch\eval_scratch.c %compile_link% %out%eval_scratch.exe || exit /b 1 if "%cpp_tests%"=="1" %compile% ..\src\scratch\i_hate_c_plus_plus.cpp %compile_link% %out%cpp_tests.exe || exit /b 1
if "%textperf%"=="1" set didbuild=1 && %compile% ..\src\scratch\textperf.c %compile_link% %out%textperf.exe || exit /b 1 if "%look_at_raddbg%"=="1" %compile% ..\src\scratch\look_at_raddbg.c %compile_link% %out%look_at_raddbg.exe || exit /b 1
if "%convertperf%"=="1" set didbuild=1 && %compile% ..\src\scratch\convertperf.c %compile_link% %out%convertperf.exe || exit /b 1 if "%mule_main%"=="1" del vc*.pdb mule*.pdb && %compile_release% %only_compile% ..\src\mule\mule_inline.cpp && %compile_release% %only_compile% ..\src\mule\mule_o2.cpp && %compile_debug% %EHsc% ..\src\mule\mule_main.cpp ..\src\mule\mule_c.c mule_inline.obj mule_o2.obj %compile_link% %no_aslr% %out%mule_main.exe || exit /b 1
if "%debugstringperf%"=="1" set didbuild=1 && %compile% ..\src\scratch\debugstringperf.c %compile_link% %out%debugstringperf.exe || exit /b 1 if "%mule_module%"=="1" %compile% ..\src\mule\mule_module.cpp %compile_link% %link_dll% %out%mule_module.dll || exit /b 1
if "%parse_inline_sites%"=="1" set didbuild=1 && %compile% ..\src\scratch\parse_inline_sites.c %compile_link% %out%parse_inline_sites.exe || exit /b 1 if "%mule_hotload%"=="1" %compile% ..\src\mule\mule_hotload_main.c %compile_link% %out%mule_hotload.exe & %compile% ..\src\mule\mule_hotload_module_main.c %compile_link% %link_dll% %out%mule_hotload_module.dll || exit /b 1
if "%strip_lib_debug%"=="1" set didbuild=1 && %compile% ..\src\strip_lib_debug\strip_lib_debug.c %compile_link% %out%strip_lib_debug.exe || exit /b 1
if "%mule_main%"=="1" set didbuild=1 && del vc*.pdb mule*.pdb && %compile_release% %only_compile% ..\src\mule\mule_inline.cpp && %compile_release% %only_compile% ..\src\mule\mule_o2.cpp && %compile_debug% %EHsc% ..\src\mule\mule_main.cpp ..\src\mule\mule_c.c mule_inline.obj mule_o2.obj %compile_link% %no_aslr% %out%mule_main.exe || exit /b 1
if "%mule_module%"=="1" set didbuild=1 && %compile% ..\src\mule\mule_module.cpp %compile_link% %link_dll% %out%mule_module.dll || exit /b 1
if "%mule_hotload%"=="1" set didbuild=1 && %compile% ..\src\mule\mule_hotload_main.c %compile_link% %out%mule_hotload.exe & %compile% ..\src\mule\mule_hotload_module_main.c %compile_link% %link_dll% %out%mule_hotload_module.dll || exit /b 1
if "%torture%"=="1" set didbuild=1 && %compile% ..\src\torture\torture.c %compile_link% %out%torture.exe || exit /b1
if "%mule_peb_trample%"=="1" ( if "%mule_peb_trample%"=="1" (
set didbuild=1
if exist mule_peb_trample.exe move mule_peb_trample.exe mule_peb_trample_old_%random%.exe if exist mule_peb_trample.exe move mule_peb_trample.exe mule_peb_trample_old_%random%.exe
if exist mule_peb_trample_new.pdb move mule_peb_trample_new.pdb mule_peb_trample_old_%random%.pdb if exist mule_peb_trample_new.pdb move mule_peb_trample_new.pdb mule_peb_trample_old_%random%.pdb
if exist mule_peb_trample_new.rdi move mule_peb_trample_new.rdi mule_peb_trample_old_%random%.rdi if exist mule_peb_trample_new.rdi move mule_peb_trample_new.rdi mule_peb_trample_old_%random%.rdi
@@ -155,18 +118,11 @@ if "%mule_peb_trample%"=="1" (
) )
popd popd
:: --- Warn On No Builds ------------------------------------------------------ :: --- Unset ------------------------------------------------------------------
if "%didbuild%"=="" ( for %%a in (%*) do set "%%a=0"
echo [WARNING] no valid build target specified; must use build target names as arguments to this script, like `build raddbg` or `build rdi_from_pdb`. set raddbg=
exit /b 1 set compile=
) set compile_link=
set out=
:: --- PGO Run ---------------------------------------------------------------- set msvc=
if "%pgo_run%"=="1" ( set debug=
if "%radlink%"=="1" (
pushd local\lyra_pgo
call %~dp0build\radlink @lyra.rsp /rad_alt_pch_dir:%~dp0local\lyra_pgo || exit /b 1
popd
)
goto restart
)
-81
View File
@@ -1,81 +0,0 @@
#!/bin/bash
set -eu
cd "$(dirname "$0")"
# --- Unpack Arguments --------------------------------------------------------
for arg in "$@"; do declare $arg='1'; done
if [ ! -v gcc ]; then clang=1; fi
if [ ! -v release ]; then debug=1; fi
if [ -v debug ]; then echo "[debug mode]"; fi
if [ -v release ]; then echo "[release mode]"; fi
if [ -v clang ]; then compiler="${CC:-clang}"; echo "[clang compile]"; fi
if [ -v gcc ]; then compiler="${CC:-gcc}"; echo "[gcc compile]"; fi
# --- Unpack Command Line Build Arguments -------------------------------------
auto_compile_flags=''
# --- Get Current Git Commit Id -----------------------------------------------
git_hash=$(git describe --always --dirty)
git_hash_full=$(git rev-parse HEAD)
# --- Compile/Link Line Definitions -------------------------------------------
clang_common="-I../src/ -I/usr/include/freetype2/ -I../local/ -g -DBUILD_GIT_HASH=\"$git_hash\" -DBUILD_GIT_HASH_FULL=\"$git_hash_full\" -Wno-unknown-warning-option -fdiagnostics-absolute-paths -Wall -Wno-missing-braces -Wno-unused-function -Wno-writable-strings -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-register -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-single-bit-bitfield-constant-conversion -Wno-compare-distinct-pointer-types -Wno-initializer-overrides -Wno-incompatible-pointer-types-discards-qualifiers -Wno-for-loop-analysis -Xclang -flto-visibility-public-std -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf"
clang_debug="$compiler -g -O0 -DBUILD_DEBUG=1 ${clang_common} ${auto_compile_flags}"
clang_release="$compiler -g -O2 -DBUILD_DEBUG=0 ${clang_common} ${auto_compile_flags}"
clang_link="-lpthread -lm -lrt -ldl"
clang_out="-o"
gcc_common="-I../src/ -I../local/ -g -DBUILD_GIT_HASH=\"$git_hash\" -DBUILD_GIT_HASH_FULL=\"$git_hash_full\" -Wno-unknown-warning-option -Wall -Wno-missing-braces -Wno-unused-function -Wno-attributes -Wno-unused-value -Wno-unused-variable -Wno-unused-local-typedef -Wno-deprecated-declarations -Wno-unused-but-set-variable -Wno-compare-distinct-pointer-types -D_USE_MATH_DEFINES -Dstrdup=_strdup -Dgnu_printf=printf"
gcc_debug="$compiler -g -O0 -DBUILD_DEBUG=1 ${gcc_common} ${auto_compile_flags}"
gcc_release="$compiler -g -O2 -DBUILD_DEBUG=0 ${gcc_common} ${auto_compile_flags}"
gcc_link="-lpthread -lm -lrt -ldl"
gcc_out="-o"
# --- Per-Build Settings ------------------------------------------------------
link_dll="-fPIC"
link_os_gfx="-lX11 -lXext"
link_render="-lGL -lEGL"
link_font_provider="-lfreetype"
# --- Choose Compile/Link Lines -----------------------------------------------
if [ -v gcc ]; then compile_debug="$gcc_debug"; fi
if [ -v gcc ]; then compile_release="$gcc_release"; fi
if [ -v gcc ]; then compile_link="$gcc_link"; fi
if [ -v gcc ]; then out="$gcc_out"; fi
if [ -v clang ]; then compile_debug="$clang_debug"; fi
if [ -v clang ]; then compile_release="$clang_release"; fi
if [ -v clang ]; then compile_link="$clang_link"; fi
if [ -v clang ]; then out="$clang_out"; fi
if [ -v debug ]; then compile="$compile_debug"; fi
if [ -v release ]; then compile="$compile_release"; fi
# --- Prep Directories --------------------------------------------------------
mkdir -p build
mkdir -p local
# --- Build & Run Metaprogram -------------------------------------------------
if [ -v no_meta ]; then echo "[skipping metagen]"; fi
if [ ! -v no_meta ]
then
cd build
$compile_debug ../src/metagen/metagen_main.c $compile_link $out metagen
./metagen
cd ..
fi
# --- Build Everything (@build_targets) ---------------------------------------
cd build
if [ -v raddbg ]; then didbuild=1 && $compile ../src/raddbg/raddbg_main.c $compile_link $link_os_gfx $link_render $link_font_provider $out raddbg; fi
if [ -v radlink ]; then didbuild=1 && $compile ../src/linker/lnk.c $compile_link $out radlink; fi
if [ -v rdi_from_pdb ]; then didbuild=1 && $compile ../src/rdi_from_pdb/rdi_from_pdb_main.c $compile_link $out rdi_from_pdb; fi
if [ -v rdi_from_dwarf ]; then didbuild=1 && $compile ../src/rdi_from_dwarf/rdi_from_dwarf.c $compile_link $out rdi_from_dwarf; fi
if [ -v rdi_dump ]; then didbuild=1 && $compile ../src/rdi_dump/rdi_dump_main.c $compile_link $out rdi_dump; fi
if [ -v rdi_breakpad_from_pdb ]; then didbuild=1 && $compile ../src/rdi_breakpad_from_pdb/rdi_breakpad_from_pdb_main.c $compile_link $out rdi_breakpad_from_pdb; fi
if [ -v ryan_scratch ]; then didbuild=1 && $compile ../src/scratch/ryan_scratch.c $compile_link $link_os_gfx $link_render $link_font_provider $out ryan_scratch; fi
cd ..
# --- Warn On No Builds -------------------------------------------------------
if [ ! -v didbuild ]
then
echo "[WARNING] no valid build target specified; must use build target names as arguments to this script, like \`./build.sh raddbg\` or \`./build.sh rdi_from_pdb\`."
exit 1
fi
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+115 -58
View File
@@ -45,73 +45,130 @@ load_paths =
commands = commands =
{ {
//- rjf: [raddbg] .rjf_f1 =
.f1 = { .win = "raddbg_stable --ipc kill_all && build raddbg meta telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, {
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .win = "build raddbg telemetry",
.linux = "",
//- rjf: [raddbg wsl] .out = "*compilation*",
// .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .footer_panel = true,
.save_dirty_files = true,
//- rjf: [scratch] .cursor_at_end = false,
.f2 = { .win = "build radbin debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, },
.rjf_f2 =
//- rjf: [textperf] {
// .f1 = { .win = "raddbg_stable --ipc kill_all && build no_meta telemetry textperf && raddbg_stable --ipc bring_to_front && raddbg_stable --ipc run", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .win = "build mule_peb_trample",
.linux = "",
//- rjf: [tester] .out = "*compilation*",
// .f1 = { .win = "raddbg_stable --ipc kill_all && build no_meta tester", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .footer_panel = true,
.save_dirty_files = true,
//- rjf: [radbin] .cursor_at_end = false,
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, },
.rjf_f3 =
//- rjf: running target {
.f3 = { .win = "raddbg_stable --ipc run", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .win = "pushd build && raddbg.exe --user:local_dev.raddbg_user --profile:local_dev.raddbg_profile && popd",
// .f3 = { .win = "wsl ./build/raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .linux = "",
// .f3 = { .win = "pushd build && raddbg --user:dev.raddbg_user && popd", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .out = "*compilation*",
// .f3 = { .win = "C:/devel/raddebugger/build/raddbg.exe --capture --user:C:/devel/raddebugger/build/local_dev.raddbg_user --project:C:/devel/raddebugger/build/local_dev.raddbg_project", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .footer_panel = true,
// .f3 = { .win = "wsl_launch /mnt/c/devel/raddebugger/build/raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .save_dirty_files = true,
.cursor_at_end = false,
//- rjf: local target builds },
.build_raddbg = { .win = "build raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .rjf_f4 =
.build_rdi_from_pdb = { .win = "build rdi_from_pdb", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, {
.build_rdi_from_dwarf = { .win = "build rdi_from_dwarf", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .win = "build rdi_from_pdb release telemetry && pushd build && rdi_from_pdb.exe --exe:UnrealEditorFortnite.exe --pdb:UnrealEditorFortnite.pdb --out:UnrealEditorFortnite.rdi --capture && popd",
.build_rdi_dump = { .win = "build rdi_dump", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .linux = "",
.build_rdi_breakpad_from_pdb = { .win = "build rdi_breakpad_from_pdb", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .out = "*compilation*",
.build_ryan_scratch = { .win = "build ryan_scratch", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .footer_panel = true,
.build_mule_main = { .win = "build mule_main", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .save_dirty_files = true,
.build_mule_module = { .win = "build mule_module", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .cursor_at_end = false,
.build_mule_hotload = { .win = "build mule_hotload", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, },
.build_mule_peb_trample = { .win = "build mule_peb_trample", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .rjf_f5 =
{
//- rjf: wsl target builds .win = "pushd build && rdi_from_pdb.exe --exe:raddbg.exe --pdb:raddbg.pdb --out:raddbg.rdi --capture && popd",
.build_raddbg_wsl = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .linux = "",
.build_rdi_from_pdb_wsl = { .win = "wsl ./build.sh rdi_from_pdb", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .out = "*compilation*",
.build_rdi_from_dwarf_wsl = { .win = "wsl ./build.sh rdi_from_dwarf", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .footer_panel = true,
.build_rdi_dump_wsl = { .win = "wsl ./build.sh rdi_dump", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .save_dirty_files = true,
.build_rdi_breakpad_from_pdb_wsl = { .win = "wsl ./build.sh rdi_breakpad_from_pdb", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .cursor_at_end = false,
.build_ryan_scratch_wsl = { .win = "wsl ./build.sh ryan_scratch", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, },
.build_mule_main_wsl = { .win = "wsl ./build.sh mule_main", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .build_raddbg =
.build_mule_module_wsl = { .win = "wsl ./build.sh mule_module", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, {
.build_mule_hotload_wsl = { .win = "wsl ./build.sh mule_hotload", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .win = "build raddbg",
.build_mule_peb_trample_wsl = { .win = "wsl ./build.sh mule_peb_trample", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .linux = "",
.out = "*compilation*",
//- rjf: local target runs .footer_panel = true,
.run_raddbg = { .win = "pushd build && raddbg.exe && popd", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, }, .save_dirty_files = true,
.cursor_at_end = false,
},
.build_raddbg_release_telemetry =
{
.win = "build raddbg release telemetry",
.linux = "",
.out = "*compilation*",
.footer_panel = true,
.save_dirty_files = true,
.cursor_at_end = false,
},
.build_rdi_from_pdb =
{
.win = "build rdi_from_pdb",
.linux = "",
.out = "*compilation*",
.footer_panel = true,
.save_dirty_files = true,
.cursor_at_end = false,
},
.build_rdi_dump =
{
.win = "build rdi_dump",
.linux = "",
.out = "*compilation*",
.footer_panel = true,
.save_dirty_files = true,
.cursor_at_end = false,
},
.build_mule_main =
{
.win = "build mule_main",
.linux = "",
.out = "*compilation*",
.footer_panel = true,
.save_dirty_files = true,
.cursor_at_end = false,
},
.build_ryan_scratch =
{
.win = "build ryan_scratch",
.linux = "",
.out = "*compilation*",
.footer_panel = true,
.save_dirty_files = true,
.cursor_at_end = false,
},
.run_raddbg =
{
.win = "pushd build && raddbg.exe && popd",
.linux = "",
.out = "*compilation*",
.footer_panel = true,
.save_dirty_files = true,
.cursor_at_end = false,
},
}; };
fkey_command = fkey_command =
{ {
.F1 = "f1", .F1 = "build_raddbg",
.F2 = "f2", .F3 = "run_raddbg",
.F3 = "f3",
}; };
fkey_command_override = fkey_command_override =
{ {
.rjf = .rjf =
{ {
.F1 = "f1", .F1 = "rjf_f1",
.F2 = "f2", .F2 = "rjf_f2",
.F3 = "f3", .F3 = "rjf_f3",
.F4 = "rjf_f4",
.F5 = "rjf_f5",
}, },
}; };
-21
View File
@@ -1,21 +0,0 @@
@echo off
setlocal
cd /D "%~dp0"
echo --- getting test data folder path ---------------------------------------------
if not exist .\local\test_data_path.txt (
echo error: You must first store the full path of your test data folder inside of `local/test_data_path.txt`.
goto :EOF
)
set /p test_data_folder=<.\local\test_data_path.txt
echo test data path: %test_data_folder%
echo:
echo --- building all testing executables ------------------------------------------
call build rdi_from_pdb rdi_dump raddbg radlink tester
echo:
echo --- running tests -------------------------------------------------------------
pushd build
call tester.exe --test_data:%test_data_folder%
popd
-654
View File
@@ -1,654 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Layer Initialization
internal void
ac_init(void)
{
Arena *arena = arena_alloc();
ac_shared = push_array(arena, AC_Shared, 1);
ac_shared->arena = arena;
ac_shared->cache_slots_count = 256;
ac_shared->cache_slots = push_array(arena, AC_Cache *, ac_shared->cache_slots_count);
ac_shared->cache_stripes = stripe_array_alloc(arena);
for EachElement(idx, ac_shared->req_batches)
{
ac_shared->req_batches[idx].mutex = mutex_alloc();
ac_shared->req_batches[idx].arena = arena_alloc();
}
ac_shared->cancel_thread = thread_launch(ac_cancel_thread_entry_point, 0);
ac_shared->cancel_thread_mutex = mutex_alloc();
mutex_take(ac_shared->cancel_thread_mutex);
}
////////////////////////////////
//~ rjf: Cache Lookups
internal AC_Artifact
ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U64 endt_us)
{
ProfBeginFunction();
AC_RequestBatch *req_batch = &ac_shared->req_batches[params->flags & AC_Flag_HighPriority ? 0 : 1];
//- rjf: create function -> cache
AC_Cache *cache = 0;
{
U64 cache_hash = u64_hash_from_str8(str8_struct(&params->create));
U64 cache_slot_idx = cache_hash%ac_shared->cache_slots_count;
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
{
RWMutexScope(cache_stripe->rw_mutex, write_mode)
{
for(AC_Cache *c = ac_shared->cache_slots[cache_slot_idx]; c != 0; c = c->next)
{
if(c->create == params->create)
{
cache = c;
break;
}
}
if(write_mode && cache == 0)
{
cache = push_array(cache_stripe->arena, AC_Cache, 1);
SLLStackPush(ac_shared->cache_slots[cache_slot_idx], cache);
cache->create = params->create;
cache->destroy = params->destroy;
cache->slots_count = Max(256, params->slots_count);
cache->slots = push_array(cache_stripe->arena, AC_Slot, cache->slots_count);
cache->stripes = stripe_array_alloc(cache_stripe->arena);
}
}
if(cache != 0)
{
break;
}
}
}
//- rjf: unpack key
U64 hash = u64_hash_from_str8(key);
U64 slot_idx = hash%cache->slots_count;
AC_Slot *slot = &cache->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
//- rjf: cache * key -> existing artifact
B32 artifact_is_stale = 1;
B32 got_artifact = 0;
B32 need_request = 0;
AC_Artifact artifact = {0};
RWMutexScope(stripe->rw_mutex, 0)
{
for(AC_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->key, key, 0))
{
ins_atomic_u64_eval_assign(&n->last_requested_gen, params->gen);
B32 is_stale = (n->last_completed_gen != params->gen);
if(ins_atomic_u64_eval(&n->completion_count) != 0 && (!is_stale || !(params->flags & AC_Flag_WaitForFresh)))
{
got_artifact = 1;
artifact_is_stale = is_stale;
artifact = n->val;
access_touch(access, &n->access_pt, stripe->cv);
}
if(is_stale)
{
B32 got_task = (ins_atomic_u64_eval_cond_assign(&n->working_count, 1, 0) == 0);
need_request = got_task;
}
break;
}
}
}
//- rjf: didn't get artifact we want? -> fall back to slow path
if(!got_artifact || need_request)
{
RWMutexScope(stripe->rw_mutex, 1) for(;;)
{
B32 out_of_time = (os_now_microseconds() >= endt_us);
// rjf: find node in cache
AC_Node *node = 0;
for(AC_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->key, key, 0))
{
node = n;
break;
}
}
// rjf: no node? -> create
if(node == 0)
{
need_request = 1;
node = stripe->free;
if(node)
{
stripe->free = node->next;
}
else
{
node = push_array_no_zero(stripe->arena, AC_Node, 1);
}
MemoryZeroStruct(node);
DLLPushBack(slot->first, slot->last, node);
// TODO(rjf): string allocator for keys
node->key = str8_copy(stripe->arena, key);
node->working_count = 1;
node->evict_threshold_us = params->evict_threshold_us;
}
node->access_pt.last_time_touched_us = os_now_microseconds();
node->access_pt.last_update_idx_touched = update_tick_idx();
// rjf: request
if(need_request)
{
need_request = 0;
MutexScope(req_batch->mutex)
{
AC_RequestNode *n = push_array(req_batch->arena, AC_RequestNode, 1);
if(params->flags & AC_Flag_Wide)
{
SLLQueuePush(req_batch->first_wide, req_batch->last_wide, n);
req_batch->wide_count += 1;
}
else
{
SLLQueuePush(req_batch->first_thin, req_batch->last_thin, n);
req_batch->thin_count += 1;
}
n->v.key = str8_copy(req_batch->arena, key);
n->v.gen = params->gen;
n->v.cancel_signal = &node->cancelled;
n->v.create = params->create;
}
cond_var_broadcast(async_tick_start_cond_var);
ins_atomic_u32_eval_assign(&async_loop_again, 1);
if(params->flags & AC_Flag_HighPriority)
{
ins_atomic_u32_eval_assign(&async_loop_again_high_priority, 1);
}
}
// rjf: get value from node, if possible
if(!got_artifact && ins_atomic_u64_eval(&node->completion_count) != 0 && ((node->last_completed_gen == params->gen) || !(params->flags & AC_Flag_WaitForFresh) || out_of_time))
{
got_artifact = 1;
artifact_is_stale = (node->last_completed_gen == params->gen);
artifact = node->val;
access_touch(access, &node->access_pt, stripe->cv);
}
// rjf: abort if needed
if(out_of_time || got_artifact || is_async_thread)
{
break;
}
// rjf: wait for results
cond_var_wait_rw(stripe->cv, stripe->rw_mutex, 1, endt_us);
}
}
//- rjf: report staleness
if(params->stale_out)
{
params->stale_out[0] = artifact_is_stale;
}
ProfEnd();
return artifact;
}
////////////////////////////////
//~ rjf: Asynchronous Tick
internal void
ac_async_tick(void)
{
Temp scratch = scratch_begin(0, 0);
//////////////////////////////
//- rjf: enable cancellation scanning
//
if(lane_idx() == 0)
{
mutex_drop(ac_shared->cancel_thread_mutex);
}
//////////////////////////////
//- rjf: do eviction pass across all caches
//
for EachIndex(cache_slot_idx, ac_shared->cache_slots_count)
{
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
RWMutexScope(cache_stripe->rw_mutex, 0)
{
for EachNode(cache, AC_Cache, ac_shared->cache_slots[cache_slot_idx])
{
Rng1U64 slot_range = lane_range(cache->slots_count);
for EachInRange(slot_idx, slot_range)
{
AC_Slot *slot = &cache->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
{
B32 slot_has_work = 0;
RWMutexScope(stripe->rw_mutex, write_mode)
{
for(AC_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
if(access_pt_is_expired(&n->access_pt, .time = n->evict_threshold_us) && ins_atomic_u64_eval(&n->working_count) == 0)
{
slot_has_work = 1;
if(!write_mode)
{
break;
}
else
{
DLLRemove(slot->first, slot->last, n);
n->next = (AC_Node *)stripe->free;
stripe->free = n;
if(cache->destroy)
{
cache->destroy(n->val);
}
}
}
}
}
if(!slot_has_work)
{
break;
}
}
}
}
}
}
//////////////////////////////
//- rjf: gather requests
//
typedef struct RequestBatchTask RequestBatchTask;
struct RequestBatchTask
{
AC_Request *wide;
U64 wide_count;
AC_Request *thin;
U64 thin_count;
};
RequestBatchTask *tasks = 0;
U64 tasks_count = 0;
if(lane_idx() == 0)
{
tasks_count = 2;
tasks = push_array(scratch.arena, RequestBatchTask, tasks_count);
for EachElement(task_idx, ac_shared->req_batches)
{
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
MutexScope(batch->mutex)
{
tasks[task_idx].wide_count = batch->wide_count;
tasks[task_idx].thin_count = batch->thin_count;
tasks[task_idx].wide = push_array(scratch.arena, AC_Request, tasks[task_idx].wide_count);
tasks[task_idx].thin = push_array(scratch.arena, AC_Request, tasks[task_idx].thin_count);
{
U64 idx = 0;
for EachNode(n, AC_RequestNode, batch->first_wide)
{
MemoryCopyStruct(&tasks[task_idx].wide[idx], &n->v);
tasks[task_idx].wide[idx].key = str8_copy(scratch.arena, tasks[task_idx].wide[idx].key);
idx += 1;
}
}
{
U64 idx = 0;
for EachNode(n, AC_RequestNode, batch->first_thin)
{
MemoryCopyStruct(&tasks[task_idx].thin[idx], &n->v);
tasks[task_idx].thin[idx].key = str8_copy(scratch.arena, tasks[task_idx].thin[idx].key);
idx += 1;
}
}
arena_clear(batch->arena);
batch->first_wide = batch->last_wide = batch->first_thin = batch->last_thin = 0;
batch->wide_count = batch->thin_count = 0;
}
}
}
lane_sync_u64(&tasks, 0);
lane_sync_u64(&tasks_count, 0);
lane_sync();
//////////////////////////////
//- rjf: do all requests
//
for EachIndex(task_idx, tasks_count)
{
lane_sync();
RequestBatchTask *task = &tasks[task_idx];
//- rjf: set up cancellation signal
U64 cancelled = 0;
U64 *cancelled_ptr = 0;
if(lane_idx() == 0)
{
cancelled_ptr = &cancelled;
}
lane_sync_u64(&cancelled_ptr, 0);
//- rjf: do all wide requests for this priority
U64 done_wide_count = 0;
ProfScope("wide requests (p%I64u)", task_idx)
{
for EachIndex(idx, task->wide_count)
{
lane_sync();
AC_Request *r = &task->wide[idx];
// rjf: any new higher priority tasks? -> cancel
if(lane_idx() == 0)
{
if(task_idx == 1 && idx != 0 && ins_atomic_u32_eval(&async_loop_again_high_priority))
{
ins_atomic_u64_eval_assign(cancelled_ptr, 1);
}
}
lane_sync();
// rjf: cancelled? -> exit
if(ins_atomic_u32_eval(cancelled_ptr))
{
break;
}
// rjf: compute val
B32 retry = 0;
U64 gen = r->gen;
AC_Artifact val = r->create(r->key, r->cancel_signal, &retry, &gen);
// rjf: retry? -> resubmit request
if(retry && lane_idx() == 0)
{
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
MutexScope(batch->mutex)
{
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
SLLQueuePush(batch->first_wide, batch->last_wide, n);
batch->wide_count += 1;
MemoryCopyStruct(&n->v, r);
n->v.key = str8_copy(batch->arena, n->v.key);
}
ins_atomic_u32_eval_assign(&async_loop_again, 1);
}
// rjf: create function -> cache
AC_Cache *cache = 0;
if(!retry && lane_idx() == 0)
{
U64 cache_hash = u64_hash_from_str8(str8_struct(&r->create));
U64 cache_slot_idx = cache_hash%ac_shared->cache_slots_count;
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
RWMutexScope(cache_stripe->rw_mutex, 0)
{
for(AC_Cache *c = ac_shared->cache_slots[cache_slot_idx]; c != 0; c = c->next)
{
if(c->create == r->create)
{
cache = c;
break;
}
}
}
}
// rjf: write value into cache
if(!retry && lane_idx() == 0)
{
U64 hash = u64_hash_from_str8(r->key);
U64 slot_idx = hash%cache->slots_count;
AC_Slot *slot = &cache->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
RWMutexScope(stripe->rw_mutex, 1)
{
for(AC_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->key, r->key, 0))
{
n->last_completed_gen = gen;
n->val = val;
ins_atomic_u64_dec_eval(&n->working_count);
ins_atomic_u64_inc_eval(&n->completion_count);
}
}
}
cond_var_broadcast(stripe->cv);
}
// rjf: increment count
lane_sync();
done_wide_count += 1;
}
}
lane_sync();
//- rjf: do all thin requests for this priority
U64 done_thin_count = 0;
ProfScope("thin requests (p%I64u)", task_idx)
{
U64 req_take_counter = 0;
U64 *req_take_counter_ptr = 0;
if(lane_idx() == 0)
{
req_take_counter_ptr = &req_take_counter;
}
lane_sync_u64(&req_take_counter_ptr, 0);
for(;;)
{
// rjf: any new higher priority tasks? -> cancel
if(task_idx == 1 && ins_atomic_u64_eval(req_take_counter_ptr) >= task->thin_count/2 &&
ins_atomic_u32_eval(&async_loop_again_high_priority))
{
ins_atomic_u64_eval_assign(cancelled_ptr, 1);
}
// rjf: cancelled? -> exit
if(ins_atomic_u64_eval(cancelled_ptr))
{
break;
}
// rjf: take next task
U64 req_idx = ins_atomic_u64_inc_eval(req_take_counter_ptr) - 1;
if(req_idx >= task->thin_count) { break; }
AC_Request *r = &task->thin[req_idx];
// rjf: push thin lane ctx
U64 thin_lane_ctx_broadcast_memory = 0;
LaneCtx thin_lane_ctx = {0, 1, {0}, &thin_lane_ctx_broadcast_memory};
LaneCtx lane_ctx_restore = lane_ctx(thin_lane_ctx);
// rjf: compute val
B32 retry = 0;
U64 gen = r->gen;
AC_Artifact val = r->create(r->key, r->cancel_signal, &retry, &gen);
// rjf: restore wide lane ctx
lane_ctx(lane_ctx_restore);
// rjf: retry? -> resubmit request
if(retry)
{
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
MutexScope(batch->mutex)
{
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
SLLQueuePush(batch->first_thin, batch->last_thin, n);
batch->thin_count += 1;
MemoryCopyStruct(&n->v, r);
n->v.key = str8_copy(batch->arena, n->v.key);
}
ins_atomic_u32_eval_assign(&async_loop_again, 1);
}
// rjf: create function -> cache
AC_Cache *cache = 0;
if(!retry)
{
U64 cache_hash = u64_hash_from_str8(str8_struct(&r->create));
U64 cache_slot_idx = cache_hash%ac_shared->cache_slots_count;
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
RWMutexScope(cache_stripe->rw_mutex, 0)
{
for(AC_Cache *c = ac_shared->cache_slots[cache_slot_idx]; c != 0; c = c->next)
{
if(c->create == r->create)
{
cache = c;
break;
}
}
}
}
// rjf: write value into cache
if(!retry)
{
U64 hash = u64_hash_from_str8(r->key);
U64 slot_idx = hash%cache->slots_count;
AC_Slot *slot = &cache->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
RWMutexScope(stripe->rw_mutex, 1)
{
for(AC_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->key, r->key, 0))
{
n->last_completed_gen = gen;
n->val = val;
ins_atomic_u64_dec_eval(&n->working_count);
ins_atomic_u64_inc_eval(&n->completion_count);
}
}
}
cond_var_broadcast(stripe->cv);
}
}
lane_sync();
done_thin_count = ins_atomic_u64_eval(req_take_counter_ptr);
lane_sync();
}
//- rjf: cancelled early, unfinished tasks? -> defer to next tick
if(lane_idx() == 0 && task_idx > 0)
{
B32 need_another_try = (done_wide_count < task->wide_count || done_thin_count < task->thin_count);
AC_RequestBatch *batch = &ac_shared->req_batches[task_idx];
MutexScope(batch->mutex)
{
// rjf: push leftover wide tasks
for(U64 idx = done_wide_count; idx < task->wide_count; idx += 1)
{
AC_Request *r = &task->wide[idx];
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
SLLQueuePush(batch->first_wide, batch->last_wide, n);
batch->wide_count += 1;
MemoryCopyStruct(&n->v, r);
n->v.key = str8_copy(batch->arena, n->v.key);
}
// rjf: push leftover thin tasks
for(U64 idx = done_thin_count; idx < task->thin_count; idx += 1)
{
AC_Request *r = &task->thin[idx];
AC_RequestNode *n = push_array(batch->arena, AC_RequestNode, 1);
SLLQueuePush(batch->first_thin, batch->last_thin, n);
batch->thin_count += 1;
MemoryCopyStruct(&n->v, r);
n->v.key = str8_copy(batch->arena, n->v.key);
}
}
if(need_another_try)
{
ins_atomic_u32_eval_assign(&async_loop_again, 1);
}
}
lane_sync();
}
lane_sync();
//////////////////////////////
//- rjf: disable cancellation scanning
//
if(lane_idx() == 0)
{
mutex_take(ac_shared->cancel_thread_mutex);
}
scratch_end(scratch);
}
////////////////////////////////
//~ rjf: Cancel Thread
internal void
ac_cancel_thread_entry_point(void *p)
{
for(;;)
{
os_sleep_milliseconds(50);
MutexScope(ac_shared->cancel_thread_mutex)
{
for EachIndex(cache_slot_idx, ac_shared->cache_slots_count)
{
Stripe *cache_stripe = stripe_from_slot_idx(&ac_shared->cache_stripes, cache_slot_idx);
RWMutexScope(cache_stripe->rw_mutex, 0)
{
for EachNode(cache, AC_Cache, ac_shared->cache_slots[cache_slot_idx])
{
Rng1U64 slot_range = lane_range(cache->slots_count);
for EachInRange(slot_idx, slot_range)
{
AC_Slot *slot = &cache->slots[slot_idx];
Stripe *stripe = stripe_from_slot_idx(&cache->stripes, slot_idx);
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
{
B32 slot_has_work = 0;
RWMutexScope(stripe->rw_mutex, write_mode)
{
for(AC_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
if(access_pt_is_expired(&n->access_pt, .time = n->evict_threshold_us) && ins_atomic_u64_eval(&n->working_count) > 0)
{
slot_has_work = 1;
if(!write_mode)
{
break;
}
else
{
n->cancelled = 1;
}
}
}
}
if(!slot_has_work)
{
break;
}
}
}
}
}
}
}
}
}
-161
View File
@@ -1,161 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef ARTIFACT_CACHE_H
#define ARTIFACT_CACHE_H
////////////////////////////////
//~ rjf: Artifact Handle Type
typedef struct AC_Artifact AC_Artifact;
struct AC_Artifact
{
U64 u64[4];
};
////////////////////////////////
//~ rjf: Artifact Computation Function Types
typedef AC_Artifact AC_CreateFunctionType(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out);
typedef void AC_DestroyFunctionType(AC_Artifact artifact);
typedef U32 AC_Flags;
typedef enum AC_FlagsEnum
{
AC_Flag_WaitForFresh = (1<<0),
AC_Flag_HighPriority = (1<<1),
AC_Flag_Wide = (1<<2),
}
AC_FlagsEnum;
typedef struct AC_ArtifactParams AC_ArtifactParams;
struct AC_ArtifactParams
{
AC_CreateFunctionType *create;
AC_DestroyFunctionType *destroy;
U64 slots_count;
U64 gen;
U64 evict_threshold_us;
B32 *stale_out;
AC_Flags flags;
};
////////////////////////////////
//~ rjf: Cache Types
typedef struct AC_Request AC_Request;
struct AC_Request
{
String8 key;
U64 gen;
B32 *cancel_signal;
AC_CreateFunctionType *create;
};
typedef struct AC_RequestNode AC_RequestNode;
struct AC_RequestNode
{
AC_RequestNode *next;
AC_Request v;
};
typedef struct AC_Node AC_Node;
struct AC_Node
{
AC_Node *next;
AC_Node *prev;
// rjf: key/gen/value
String8 key;
U64 last_requested_gen;
U64 last_completed_gen;
AC_Artifact val;
// rjf: metadata
AccessPt access_pt;
U64 working_count;
U64 completion_count;
U64 evict_threshold_us;
B32 cancelled;
U64 _unused_;
};
typedef struct AC_Slot AC_Slot;
struct AC_Slot
{
AC_Node *first;
AC_Node *last;
};
typedef struct AC_Cache AC_Cache;
struct AC_Cache
{
// rjf: link / key for cache-cache
AC_Cache *next;
AC_CreateFunctionType *create;
AC_DestroyFunctionType *destroy;
// rjf: artifact cache
U64 slots_count;
AC_Slot *slots;
StripeArray stripes;
};
typedef struct AC_RequestBatch AC_RequestBatch;
struct AC_RequestBatch
{
Mutex mutex;
Arena *arena;
AC_RequestNode *first_wide;
AC_RequestNode *last_wide;
AC_RequestNode *first_thin;
AC_RequestNode *last_thin;
U64 wide_count;
U64 thin_count;
};
typedef struct AC_Shared AC_Shared;
struct AC_Shared
{
Arena *arena;
// rjf: cache cache
U64 cache_slots_count;
AC_Cache **cache_slots;
StripeArray cache_stripes;
// rjf: requests
AC_RequestBatch req_batches[2]; // 0: high priority, 1: low priority
// rjf: cancel thread
Thread cancel_thread;
Mutex cancel_thread_mutex;
};
////////////////////////////////
//~ rjf: Globals
global AC_Shared *ac_shared = 0;
////////////////////////////////
//~ rjf: Layer Initialization
internal void ac_init(void);
////////////////////////////////
//~ rjf: Cache Lookups
internal AC_Artifact ac_artifact_from_key_(Access *access, String8 key, AC_ArtifactParams *params, U64 endt_us);
#define ac_artifact_from_key(access, key, create_fn, destroy_fn, endt_us, ...) ac_artifact_from_key_((access), (key), &(AC_ArtifactParams){.create = (create_fn), .destroy = (destroy_fn), .evict_threshold_us = (2000000), __VA_ARGS__}, (endt_us))
////////////////////////////////
//~ rjf: Asynchronous Tick
internal void ac_async_tick(void);
////////////////////////////////
//~ rjf: Cancel Thread
internal void ac_cancel_thread_entry_point(void *p);
#endif // ARTIFACT_CACHE_H
+214 -174
View File
@@ -1,192 +1,173 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//////////////////////////////// ////////////////////////////////
//~ rjf: Arena Functions // Implementation
//- rjf: arena creation/destruction
internal Arena * internal Arena *
arena_alloc_(ArenaParams *params) arena_alloc__sized(U64 init_res, U64 init_cmt)
{ {
// rjf: round up reserve/commit sizes ProfBeginFunction();
U64 reserve_size = params->reserve_size; Assert(ARENA_HEADER_SIZE < init_cmt && init_cmt <= init_res);
U64 commit_size = params->commit_size;
if(params->flags & ArenaFlag_LargePages) void *memory = 0;
U64 res = 0;
U64 cmt = 0;
B32 large_pages = os_large_pages_enabled();
if(large_pages)
{ {
reserve_size = AlignPow2(reserve_size, os_get_system_info()->large_page_size); U64 page_size = os_large_page_size();
commit_size = AlignPow2(commit_size, os_get_system_info()->large_page_size); res = AlignPow2(init_res, page_size);
#if OS_WINDOWS
cmt = res;
#else
cmt = AlignPow2(init_cmt, page_size);
#endif
memory = os_reserve_large(res);
if(!os_commit_large(memory, cmt))
{
memory = 0;
os_release(memory, res);
}
} }
else else
{ {
reserve_size = AlignPow2(reserve_size, os_get_system_info()->page_size); U64 page_size = os_page_size();
commit_size = AlignPow2(commit_size, os_get_system_info()->page_size); res = AlignPow2(init_res, page_size);
} cmt = AlignPow2(init_cmt, page_size);
memory = os_reserve(res);
// rjf: reserve/commit initial block if(!os_commit(memory, cmt))
void *base = params->optional_backing_buffer;
if(base == 0)
{
if(params->flags & ArenaFlag_LargePages)
{ {
base = os_reserve_large(reserve_size); memory = 0;
os_commit_large(base, commit_size); os_release(memory, res);
} }
else
{
base = os_reserve(reserve_size);
os_commit(base, commit_size);
}
raddbg_annotate_vaddr_range(base, reserve_size, "arena %s:%i", params->allocation_site_file, params->allocation_site_line);
} }
// rjf: panic on arena creation failure Arena *arena = (Arena*)memory;
#if OS_FEATURE_GRAPHICAL if(arena)
if(Unlikely(base == 0))
{ {
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure.")); AsanPoisonMemoryRegion(memory, cmt);
os_abort(1); AsanUnpoisonMemoryRegion(memory, ARENA_HEADER_SIZE);
arena->prev = 0;
arena->current = arena;
arena->base_pos = 0;
arena->pos = ARENA_HEADER_SIZE;
arena->cmt = cmt;
arena->res = res;
arena->align = 8;
arena->grow = 1;
arena->large_pages = large_pages;
} }
#endif
// rjf: extract arena header & fill ProfEnd();
Arena *arena = (Arena *)base; return arena;
arena->current = arena; }
arena->flags = params->flags;
arena->cmt_size = params->commit_size; internal Arena *
arena->res_size = params->reserve_size; arena_alloc(void)
arena->base_pos = 0; {
arena->pos = ARENA_HEADER_SIZE; ProfBeginFunction();
arena->cmt = commit_size;
arena->res = reserve_size; U64 init_res, init_cmt;
arena->allocation_site_file = params->allocation_site_file; if (os_large_pages_enabled()) {
arena->allocation_site_line = params->allocation_site_line; init_res = ARENA_RESERVE_SIZE_LARGE_PAGES;
#if ARENA_FREE_LIST init_cmt = ARENA_COMMIT_SIZE_LARGE_PAGES;
arena->free_last = 0; } else {
#endif init_res = ARENA_RESERVE_SIZE;
AsanPoisonMemoryRegion(base, commit_size); init_cmt = ARENA_COMMIT_SIZE;
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE); }
Arena *arena = arena_alloc__sized(init_res, init_cmt);
ProfEnd();
return arena; return arena;
} }
internal void internal void
arena_release(Arena *arena) arena_release(Arena *arena)
{ {
for(Arena *n = arena->current, *prev = 0; n != 0; n = prev) for (Arena *node = arena->current, *prev = 0; node != 0; node = prev) {
{ prev = node->prev;
prev = n->prev; os_release(node, node->res);
os_release(n, n->res);
} }
} }
//- rjf: arena push/pop core functions internal U64
arena_huge_push_threshold(void)
{
U64 reserve_size = os_large_pages_enabled() ? ARENA_RESERVE_SIZE_LARGE_PAGES : ARENA_RESERVE_SIZE;
U64 threshold = (reserve_size - ARENA_HEADER_SIZE) / 2 + 1;
return threshold;
}
internal void * internal void *
arena_push(Arena *arena, U64 size, U64 align, B32 zero) arena_push__impl(Arena *arena, U64 size)
{ {
Arena *current = arena->current; Arena *current = arena->current;
U64 pos_pre = AlignPow2(current->pos, align); U64 pos_mem = AlignPow2(current->pos, arena->align);
U64 pos_pst = pos_pre + size; U64 pos_new = pos_mem + size;
// rjf: chain, if needed if (current->res < pos_new && arena->grow) {
if(current->res < pos_pst && !(arena->flags & ArenaFlag_NoChain)) Arena *new_block;
{
Arena *new_block = 0;
#if ARENA_FREE_LIST // normal growth path
{ if (size < arena_huge_push_threshold()) {
Arena *prev_block; new_block = arena_alloc();
for(new_block = arena->free_last, prev_block = 0; new_block != 0; prev_block = new_block, new_block = new_block->prev)
{
if(new_block->res >= AlignPow2(new_block->pos, align) + size)
{
if(prev_block)
{
prev_block->prev = new_block->prev;
}
else
{
arena->free_last = new_block->prev;
}
break;
}
}
} }
#endif // huge growth path
else {
if(new_block == 0) U64 new_block_size = size + ARENA_HEADER_SIZE;
{ new_block = arena_alloc__sized(new_block_size, new_block_size);
U64 res_size = current->res_size;
U64 cmt_size = current->cmt_size;
if(size + ARENA_HEADER_SIZE > res_size)
{
res_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
cmt_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
}
new_block = arena_alloc(.reserve_size = res_size,
.commit_size = cmt_size,
.flags = current->flags,
.allocation_site_file = current->allocation_site_file,
.allocation_site_line = current->allocation_site_line);
} }
new_block->base_pos = current->base_pos + current->res; if (new_block) {
SLLStackPush_N(arena->current, new_block, prev); new_block->base_pos = current->base_pos + current->res;
SLLStackPush_N(arena->current, new_block, prev);
current = new_block; current = new_block;
pos_pre = AlignPow2(current->pos, align); pos_mem = AlignPow2(current->pos, current->align);
pos_pst = pos_pre + size; pos_new = pos_mem + size;
}
// rjf: compute the size we need to zero
U64 size_to_zero = 0;
if(zero)
{
size_to_zero = Min(current->cmt, pos_pst) - pos_pre;
}
// rjf: commit new pages, if needed
if(current->cmt < pos_pst)
{
U64 cmt_pst_aligned = pos_pst + current->cmt_size-1;
cmt_pst_aligned -= cmt_pst_aligned%current->cmt_size;
U64 cmt_pst_clamped = ClampTop(cmt_pst_aligned, current->res);
U64 cmt_size = cmt_pst_clamped - current->cmt;
U8 *cmt_ptr = (U8 *)current + current->cmt;
if(current->flags & ArenaFlag_LargePages)
{
os_commit_large(cmt_ptr, cmt_size);
}
else
{
os_commit(cmt_ptr, cmt_size);
}
current->cmt = cmt_pst_clamped;
}
// rjf: push onto current block
void *result = 0;
if(current->cmt >= pos_pst)
{
result = (U8 *)current+pos_pre;
current->pos = pos_pst;
AsanUnpoisonMemoryRegion(result, size);
if(size_to_zero != 0)
{
MemoryZero(result, size_to_zero);
} }
} }
// rjf: panic on failure if (current->cmt < pos_new) {
U64 cmt_new_aligned, cmt_new_clamped, cmt_new_size;
B32 is_cmt_ok;
if (current->large_pages) {
cmt_new_aligned = AlignPow2(pos_new, ARENA_COMMIT_SIZE_LARGE_PAGES);
cmt_new_clamped = ClampTop(cmt_new_aligned, current->res);
cmt_new_size = cmt_new_clamped - current->cmt;
is_cmt_ok = os_commit_large((U8*)current + current->cmt, cmt_new_size);
} else {
cmt_new_aligned = AlignPow2(pos_new, ARENA_COMMIT_SIZE);
cmt_new_clamped = ClampTop(cmt_new_aligned, current->res);
cmt_new_size = cmt_new_clamped - current->cmt;
is_cmt_ok = os_commit((U8*)current + current->cmt, cmt_new_size);
}
if (is_cmt_ok) {
current->cmt = cmt_new_clamped;
}
}
void *memory = 0;
if (current->cmt >= pos_new) {
memory = (U8*)current + pos_mem;
current->pos = pos_new;
AsanUnpoisonMemoryRegion(memory, size);
}
#if OS_FEATURE_GRAPHICAL #if OS_FEATURE_GRAPHICAL
if(Unlikely(result == 0)) if(Unlikely(memory == 0))
{ {
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure.")); os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1); os_exit_process(1);
} }
#endif #endif
return result; return memory;
} }
internal U64 internal U64
@@ -198,34 +179,85 @@ arena_pos(Arena *arena)
} }
internal void internal void
arena_pop_to(Arena *arena, U64 pos) arena_pop_to(Arena *arena, U64 big_pos_unclamped)
{ {
U64 big_pos = ClampBot(ARENA_HEADER_SIZE, pos); U64 big_pos = ClampBot(ARENA_HEADER_SIZE, big_pos_unclamped);
Arena *current = arena->current;
#if ARENA_FREE_LIST // unroll the chain
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev) Arena *current = arena->current;
{ for (Arena *prev = 0; current->base_pos >= big_pos; current = prev) {
prev = current->prev;
current->pos = ARENA_HEADER_SIZE;
SLLStackPush_N(arena->free_last, current, prev);
AsanPoisonMemoryRegion((U8*)current + ARENA_HEADER_SIZE, current->res - ARENA_HEADER_SIZE);
}
#else
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
{
prev = current->prev; prev = current->prev;
os_release(current, current->res); os_release(current, current->res);
} }
#endif AssertAlways(current);
arena->current = current; arena->current = current;
// compute arena-relative position
U64 new_pos = big_pos - current->base_pos; U64 new_pos = big_pos - current->base_pos;
AssertAlways(new_pos <= current->pos); AssertAlways(new_pos <= current->pos);
// poison popped memory block
AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos)); AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos));
// update position
current->pos = new_pos; current->pos = new_pos;
} }
//- rjf: arena push/pop helpers internal void
arena_absorb(Arena *arena, Arena *sub)
{
// base adjustment
Arena *current = arena->current;
U64 base_adjust = current->base_pos + current->res;
for (Arena *node = sub->current; node != 0; node = node->prev) {
node->base_pos += base_adjust;
}
// attach sub to arena
sub->prev = arena->current;
arena->current = sub->current;
sub->current = sub;
}
////////////////////////////////
// Wrappers
internal void *
arena_push(Arena *arena, U64 size)
{
void *memory = arena_push__impl(arena, size);
return memory;
}
internal void *
arena_push_contiguous(Arena *arena, U64 size)
{
B32 restore = arena->grow;
arena->grow = 0;
void *memory = arena_push(arena, size);
arena->grow = restore;
return memory;
}
internal void
arena_push_align(Arena *arena, U64 align)
{
Assert(IsPow2(align));
U64 amt = AlignPadPow2(arena->pos, align);
void *ptr = arena_push(arena, amt);
MemoryZero(ptr, amt);
}
internal void
arena_put_back(Arena *arena, U64 amt)
{
U64 pos_old = arena_pos(arena);
U64 pos_new = pos_old;
if (amt < pos_old) {
pos_new = pos_old - amt;
}
arena_pop_to(arena, pos_new);
}
internal void internal void
arena_clear(Arena *arena) arena_clear(Arena *arena)
@@ -233,20 +265,6 @@ arena_clear(Arena *arena)
arena_pop_to(arena, 0); arena_pop_to(arena, 0);
} }
internal void
arena_pop(Arena *arena, U64 amt)
{
U64 pos_old = arena_pos(arena);
U64 pos_new = pos_old;
if(amt < pos_old)
{
pos_new = pos_old - amt;
}
arena_pop_to(arena, pos_new);
}
//- rjf: temporary arena scopes
internal Temp internal Temp
temp_begin(Arena *arena) temp_begin(Arena *arena)
{ {
@@ -260,3 +278,25 @@ temp_end(Temp temp)
{ {
arena_pop_to(temp.arena, temp.pos); arena_pop_to(temp.arena, temp.pos);
} }
////////////////////////////////
//~ NOTE(allen): "Mini-Arena" Helper
internal B32
ensure_commit(void **cmtptr, void *pos, U64 cmt_block_size){
B32 result = 0;
U8 *cmt = (U8*)*cmtptr;
if (cmt < (U8*)pos){
U64 cmt_size_raw = (U8*)pos - cmt;
U64 cmt_size = AlignPow2(cmt_size_raw, cmt_block_size);
if (os_commit(cmt, cmt_size)){
*cmtptr = cmt + cmt_size;
result = 1;
}
}
else{
result = 1;
}
return(result);
}
+51 -52
View File
@@ -1,51 +1,44 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_ARENA_H #ifndef BASE_ARENA_H
#define BASE_ARENA_H #define BASE_ARENA_H
//////////////////////////////// ////////////////////////////////
//~ rjf: Arena Types //~ rjf: Constants
#define ARENA_HEADER_SIZE 128 #define ARENA_HEADER_SIZE 128
typedef U64 ArenaFlags; #ifndef ARENA_RESERVE_SIZE
enum # define ARENA_RESERVE_SIZE MB(64)
{ #endif
ArenaFlag_NoChain = (1<<0), #ifndef ARENA_COMMIT_SIZE
ArenaFlag_LargePages = (1<<1), # define ARENA_COMMIT_SIZE KB(64)
}; #endif
typedef struct ArenaParams ArenaParams; #ifndef ARENA_RESERVE_SIZE_LARGE_PAGES
struct ArenaParams # define ARENA_RESERVE_SIZE_LARGE_PAGES MB(8)
{ #endif
ArenaFlags flags; #ifndef ARENA_COMMIT_SIZE_LARGE_PAGES
U64 reserve_size; # define ARENA_COMMIT_SIZE_LARGE_PAGES MB(2)
U64 commit_size; #endif
void *optional_backing_buffer;
char *allocation_site_file; ////////////////////////////////
int allocation_site_line; //~ rjf: Arena Types
};
typedef struct Arena Arena; typedef struct Arena Arena;
struct Arena struct Arena
{ {
Arena *prev; // previous arena in chain struct Arena *prev;
Arena *current; // current arena in chain struct Arena *current;
ArenaFlags flags;
U64 cmt_size;
U64 res_size;
U64 base_pos; U64 base_pos;
U64 pos; U64 pos;
U64 cmt; U64 cmt;
U64 res; U64 res;
char *allocation_site_file; U64 align;
int allocation_site_line; B8 grow;
#if ARENA_FREE_LIST B8 large_pages;
Arena *free_last;
#endif
}; };
StaticAssert(sizeof(Arena) <= ARENA_HEADER_SIZE, arena_header_size_check);
typedef struct Temp Temp; typedef struct Temp Temp;
struct Temp struct Temp
@@ -55,34 +48,40 @@ struct Temp
}; };
//////////////////////////////// ////////////////////////////////
//~ rjf: Arena Functions // Implementation
global U64 arena_default_reserve_size = MB(64); internal Arena* arena_alloc__sized(U64 init_res, U64 init_cmt);
global U64 arena_default_commit_size = KB(64);
global ArenaFlags arena_default_flags = 0;
//- rjf: arena creation/destruction internal Arena* arena_alloc(void);
internal Arena *arena_alloc_(ArenaParams *params); internal void arena_release(Arena *arena);
#define arena_alloc(...) arena_alloc_(&(ArenaParams){.reserve_size = arena_default_reserve_size, .commit_size = arena_default_commit_size, .flags = arena_default_flags, .allocation_site_file = __FILE__, .allocation_site_line = __LINE__, __VA_ARGS__})
internal void arena_release(Arena *arena);
//- rjf: arena push/pop/pos core functions internal void* arena_push__impl(Arena *arena, U64 size);
internal void *arena_push(Arena *arena, U64 size, U64 align, B32 zero); internal U64 arena_pos(Arena *arena);
internal U64 arena_pos(Arena *arena); internal void arena_pop_to(Arena *arena, U64 pos);
internal void arena_pop_to(Arena *arena, U64 pos);
//- rjf: arena push/pop helpers internal void arena_absorb(Arena *arena, Arena *sub);
internal void arena_clear(Arena *arena);
internal void arena_pop(Arena *arena, U64 amt);
//- rjf: temporary arena scopes ////////////////////////////////
internal Temp temp_begin(Arena *arena); // Wrappers
internal void temp_end(Temp temp);
//- rjf: push helper macros internal void* arena_push(Arena *arena, U64 size);
#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (0)) internal void* arena_push_contiguous(Arena *arena, U64 size);
#define push_array_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (1)) internal void arena_clear(Arena *arena);
#define push_array_no_zero(a, T, c) push_array_no_zero_aligned(a, T, c, Max(8, AlignOf(T))) internal void arena_push_align(Arena *arena, U64 align);
#define push_array(a, T, c) push_array_aligned(a, T, c, Max(8, AlignOf(T))) internal void arena_put_back(Arena *arena, U64 amt);
internal Temp temp_begin(Arena *arena);
internal void temp_end(Temp temp);
////////////////////////////////
//~ NOTE(allen): "Mini-Arena" Helper
internal B32 ensure_commit(void **cmt, void *pos, U64 cmt_block_size);
////////////////////////////////
//~ NOTE(allen): Main API Macros
#define push_array_no_zero(a,T,c) (T*)arena_push((a), sizeof(T)*(c))
#define push_array(a,T,c) (T*)MemoryZero(push_array_no_zero(a,T,c), sizeof(T)*(c))
#endif // BASE_ARENA_H #endif // BASE_ARENA_H
+59 -55
View File
@@ -1,8 +1,19 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//////////////////////////////// ////////////////////////////////
//~ rjf: Command Line Parsing Functions //~ NOTE(rjf): Command Line Option Parsing
internal U64
cmd_line_hash_from_string(String8 string)
{
U64 result = 5381;
for(U64 i = 0; i < string.size; i += 1)
{
result = ((result << 5) + result) + string.str[i];
}
return result;
}
internal CmdLineOpt ** internal CmdLineOpt **
cmd_line_slot_from_string(CmdLine *cmd_line, String8 string) cmd_line_slot_from_string(CmdLine *cmd_line, String8 string)
@@ -10,7 +21,7 @@ cmd_line_slot_from_string(CmdLine *cmd_line, String8 string)
CmdLineOpt **slot = 0; CmdLineOpt **slot = 0;
if(cmd_line->option_table_size != 0) if(cmd_line->option_table_size != 0)
{ {
U64 hash = u64_hash_from_str8(string); U64 hash = cmd_line_hash_from_string(string);
U64 bucket = hash % cmd_line->option_table_size; U64 bucket = hash % cmd_line->option_table_size;
slot = &cmd_line->option_table[bucket]; slot = &cmd_line->option_table[bucket];
} }
@@ -53,7 +64,7 @@ cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List
{ {
var = push_array(arena, CmdLineOpt, 1); var = push_array(arena, CmdLineOpt, 1);
var->hash_next = *slot; var->hash_next = *slot;
var->hash = u64_hash_from_str8(string); var->hash = cmd_line_hash_from_string(string);
var->string = push_str8_copy(arena, string); var->string = push_str8_copy(arena, string);
var->value_strings = values; var->value_strings = values;
StringJoin join = {0}; StringJoin join = {0};
@@ -72,25 +83,27 @@ cmd_line_from_string_list(Arena *arena, String8List command_line)
{ {
CmdLine parsed = {0}; CmdLine parsed = {0};
parsed.exe_name = command_line.first->string; parsed.exe_name = command_line.first->string;
parsed.option_table_size = 64;
parsed.option_table = push_array(arena, CmdLineOpt *, parsed.option_table_size);
//- rjf: parse options / inputs // NOTE(rjf): Set up config option table.
{
parsed.option_table_size = 4096;
parsed.option_table = push_array(arena, CmdLineOpt *, parsed.option_table_size);
}
// NOTE(rjf): Parse command line.
B32 after_passthrough_option = 0; B32 after_passthrough_option = 0;
B32 first_passthrough = 1; B32 first_passthrough = 1;
for(String8Node *node = command_line.first->next, *next = 0; node != 0; node = next) for(String8Node *node = command_line.first->next, *next = 0; node != 0; node = next)
{ {
next = node->next; next = node->next;
//- rjf: look at --, -, or / (only on Windows) at the start of an
// argument to determine if it's a flag option. all arguments after a
// single "--" (with no trailing string on the command line will be
// considered as passthrough input strings.
B32 is_option = 0;
String8 option_name = node->string; String8 option_name = node->string;
if(!after_passthrough_option)
// NOTE(rjf): Look at -- or - at the start of an argument to determine if it's
// a flag option. All arguments after a single "--" (with no trailing string
// on the command line will be considered as input files.
B32 is_option = 1;
if(after_passthrough_option == 0)
{ {
is_option = 1;
if(str8_match(node->string, str8_lit("--"), 0)) if(str8_match(node->string, str8_lit("--"), 0))
{ {
after_passthrough_option = 1; after_passthrough_option = 1;
@@ -104,82 +117,73 @@ cmd_line_from_string_list(Arena *arena, String8List command_line)
{ {
option_name = str8_skip(option_name, 1); option_name = str8_skip(option_name, 1);
} }
else if(OperatingSystem_CURRENT == OperatingSystem_Windows &&
str8_match(str8_prefix(node->string, 1), str8_lit("/"), 0))
{
option_name = str8_skip(option_name, 1);
}
else else
{ {
is_option = 0; is_option = 0;
} }
} }
else
{
is_option = 0;
}
//- rjf: string is an option // NOTE(rjf): This string is an option.
if(is_option) if(is_option)
{ {
// rjf: unpack option prefix B32 has_arguments = 0;
B32 has_values = 0; U64 arg_signifier_position1 = str8_find_needle(option_name, 0, str8_lit(":"), 0);
U64 value_signifier_position1 = str8_find_needle(option_name, 0, str8_lit(":"), 0); U64 arg_signifier_position2 = str8_find_needle(option_name, 0, str8_lit("="), 0);
U64 value_signifier_position2 = str8_find_needle(option_name, 0, str8_lit("="), 0); U64 arg_signifier_position = Min(arg_signifier_position1, arg_signifier_position2);
U64 value_signifier_position = Min(value_signifier_position1, value_signifier_position2); String8 arg_portion_this_string = str8_skip(option_name, arg_signifier_position+1);
String8 value_portion_this_string = str8_skip(option_name, value_signifier_position+1); if(arg_signifier_position < option_name.size)
if(value_signifier_position < option_name.size)
{ {
has_values = 1; has_arguments = 1;
} }
option_name = str8_prefix(option_name, value_signifier_position); option_name = str8_prefix(option_name, arg_signifier_position);
// rjf: parse option's values String8List arguments = {0};
String8List values = {0};
if(has_values) // NOTE(rjf): Parse arguments.
if(has_arguments)
{ {
for(String8Node *n = node; n; n = n->next) for(String8Node *n = node; n; n = n->next)
{ {
next = n->next; next = n->next;
String8 string = n->string; String8 string = n->string;
if(n == node) if(n == node)
{ {
string = value_portion_this_string; string = arg_portion_this_string;
} }
U8 splits[] = { ',' }; U8 splits[] = { ',' };
String8List values_in_this_string = str8_split(arena, string, splits, ArrayCount(splits), 0); String8List args_in_this_string = str8_split(arena, string, splits, ArrayCount(splits), 0);
for(String8Node *sub_val = values_in_this_string.first; sub_val; sub_val = sub_val->next) for(String8Node *sub_arg = args_in_this_string.first; sub_arg; sub_arg = sub_arg->next)
{ {
str8_list_push(arena, &values, sub_val->string); str8_list_push(arena, &arguments, sub_arg->string);
} }
if(!str8_match(str8_postfix(n->string, 1), str8_lit(","), 0) && if(!str8_match(str8_postfix(n->string, 1), str8_lit(","), 0) &&
(n != node || value_portion_this_string.size != 0)) (n != node || arg_portion_this_string.size != 0))
{ {
break; break;
} }
} }
} }
// rjf: store // NOTE(rjf): Register config variable.
cmd_line_insert_opt(arena, &parsed, option_name, values); cmd_line_insert_opt(arena, &parsed, option_name, arguments);
} }
//- rjf: default path - treat as a passthrough input // NOTE(rjf): Default path, treat as a passthrough config option to be
// handled by tool-specific code.
else if(!str8_match(node->string, str8_lit("--"), 0) || !first_passthrough) else if(!str8_match(node->string, str8_lit("--"), 0) || !first_passthrough)
{ {
str8_list_push(arena, &parsed.inputs, node->string); str8_list_push(arena, &parsed.inputs, node->string);
after_passthrough_option = 1;
first_passthrough = 0; first_passthrough = 0;
} }
} }
//- rjf: fill argc/argv
parsed.argc = command_line.node_count;
parsed.argv = push_array(arena, char *, parsed.argc);
{
U64 idx = 0;
for(String8Node *n = command_line.first; n != 0; n = n->next)
{
parsed.argv[idx] = (char *)push_str8_copy(arena, n->string).str;
idx += 1;
}
}
return parsed; return parsed;
} }
@@ -217,12 +221,12 @@ internal B32
cmd_line_has_flag(CmdLine *cmd_line, String8 name) cmd_line_has_flag(CmdLine *cmd_line, String8 name)
{ {
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
return (var != 0); return(var != 0);
} }
internal B32 internal B32
cmd_line_has_argument(CmdLine *cmd_line, String8 name) cmd_line_has_argument(CmdLine *cmd_line, String8 name)
{ {
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
return (var != 0 && var->value_strings.node_count > 0); return(var != 0 && var->value_strings.node_count > 0);
} }
+3 -4
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_COMMAND_LINE_H #ifndef BASE_COMMAND_LINE_H
@@ -34,13 +34,12 @@ struct CmdLine
String8List inputs; String8List inputs;
U64 option_table_size; U64 option_table_size;
CmdLineOpt **option_table; CmdLineOpt **option_table;
U64 argc;
char **argv;
}; };
//////////////////////////////// ////////////////////////////////
//~ rjf: Command Line Parsing Functions //~ NOTE(rjf): Command Line Option Parsing
internal U64 cmd_line_hash_from_string(String8 string);
internal CmdLineOpt** cmd_line_slot_from_string(CmdLine *cmd_line, String8 string); internal CmdLineOpt** cmd_line_slot_from_string(CmdLine *cmd_line, String8 string);
internal CmdLineOpt* cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string); internal CmdLineOpt* cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string);
internal void cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var); internal void cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var);
+4 -4
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_CONTEXT_CRACKING_H #ifndef BASE_CONTEXT_CRACKING_H
@@ -155,11 +155,11 @@
#endif #endif
#if !defined(BUILD_VERSION_MINOR) #if !defined(BUILD_VERSION_MINOR)
# define BUILD_VERSION_MINOR 9 # define BUILD_VERSION_MINOR 0
#endif #endif
#if !defined(BUILD_VERSION_PATCH) #if !defined(BUILD_VERSION_PATCH)
# define BUILD_VERSION_PATCH 24 # define BUILD_VERSION_PATCH 0
#endif #endif
#define BUILD_VERSION_STRING_LITERAL Stringify(BUILD_VERSION_MAJOR) "." Stringify(BUILD_VERSION_MINOR) "." Stringify(BUILD_VERSION_PATCH) #define BUILD_VERSION_STRING_LITERAL Stringify(BUILD_VERSION_MAJOR) "." Stringify(BUILD_VERSION_MINOR) "." Stringify(BUILD_VERSION_PATCH)
@@ -183,7 +183,7 @@
#endif #endif
#if !defined(BUILD_ISSUES_LINK_STRING_LITERAL) #if !defined(BUILD_ISSUES_LINK_STRING_LITERAL)
# define BUILD_ISSUES_LINK_STRING_LITERAL "https://github.com/EpicGamesExt/raddebugger/issues" # define BUILD_ISSUES_LINK_STRING_LITERAL "https://github.com/EpicGames/raddebugger/issues"
#endif #endif
#define BUILD_TITLE_STRING_LITERAL BUILD_TITLE " (" BUILD_VERSION_STRING_LITERAL " " BUILD_RELEASE_PHASE_STRING_LITERAL ") - " __DATE__ "" BUILD_GIT_HASH_STRING_LITERAL_APPEND BUILD_MODE_STRING_LITERAL_APPEND #define BUILD_TITLE_STRING_LITERAL BUILD_TITLE " (" BUILD_VERSION_STRING_LITERAL " " BUILD_RELEASE_PHASE_STRING_LITERAL ") - " __DATE__ "" BUILD_GIT_HASH_STRING_LITERAL_APPEND BUILD_MODE_STRING_LITERAL_APPEND
+78 -212
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//////////////////////////////// ////////////////////////////////
@@ -41,7 +41,7 @@ u128_zero(void)
internal U128 internal U128
u128_make(U64 v0, U64 v1) u128_make(U64 v0, U64 v1)
{ {
U128 v = { .u64 = { v0, v1 }}; U128 v = {v0, v1};
return v; return v;
} }
@@ -143,6 +143,12 @@ bswap_u64(U64 x)
#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS) #if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS)
internal U64
count_bits_set16(U16 val)
{
return __popcnt16(val);
}
internal U64 internal U64
count_bits_set32(U32 val) count_bits_set32(U32 val)
{ {
@@ -189,40 +195,46 @@ clz64(U64 mask)
#elif COMPILER_CLANG || COMPILER_GCC #elif COMPILER_CLANG || COMPILER_GCC
internal U64
count_bits_set16(U16 val)
{
NotImplemented;
return 0;
}
internal U64 internal U64
count_bits_set32(U32 val) count_bits_set32(U32 val)
{ {
return __builtin_popcount(val); NotImplemented;
return 0;
} }
internal U64 internal U64
count_bits_set64(U64 val) count_bits_set64(U64 val)
{ {
return __builtin_popcountll(val); NotImplemented;
return 0;
} }
internal U64 internal U64
ctz32(U32 val) ctz32(U32 val)
{ {
return __builtin_ctz(val); NotImplemented;
return 0;
} }
internal U64 internal U64
clz32(U32 val) clz32(U32 val)
{ {
return __builtin_clz(val); NotImplemented;
} return 0;
internal U64
ctz64(U64 val)
{
return __builtin_ctzll(val);
} }
internal U64 internal U64
clz64(U64 val) clz64(U64 val)
{ {
return __builtin_clzll(val); NotImplemented;
return 0;
} }
#else #else
@@ -246,8 +258,7 @@ sign_from_side_F32(Side side){
//~ rjf: Memory Functions //~ rjf: Memory Functions
internal B32 internal B32
memory_is_zero(void *ptr, U64 size) memory_is_zero(void *ptr, U64 size){
{
B32 result = 1; B32 result = 1;
// break down size // break down size
@@ -258,10 +269,8 @@ memory_is_zero(void *ptr, U64 size)
U64 *p64 = (U64*)ptr; U64 *p64 = (U64*)ptr;
if(result) if(result)
{ {
for(U64 i = 0; i < count8; i += 1, p64 += 1) for (U64 i = 0; i < count8; i += 1, p64 += 1){
{ if (*p64 != 0){
if(*p64 != 0)
{
result = 0; result = 0;
goto done; goto done;
} }
@@ -272,10 +281,8 @@ memory_is_zero(void *ptr, U64 size)
if(result) if(result)
{ {
U8 *p8 = (U8*)p64; U8 *p8 = (U8*)p64;
for(U64 i = 0; i < extra; i += 1, p8 += 1) for (U64 i = 0; i < extra; i += 1, p8 += 1){
{ if (*p8 != 0){
if(*p8 != 0)
{
result = 0; result = 0;
goto done; goto done;
} }
@@ -283,7 +290,7 @@ memory_is_zero(void *ptr, U64 size)
} }
done:; done:;
return result; return(result);
} }
//////////////////////////////// ////////////////////////////////
@@ -392,34 +399,69 @@ txt_rng_contains(TxtRng r, TxtPt pt)
//~ rjf: Toolchain/Environment Enum Functions //~ rjf: Toolchain/Environment Enum Functions
internal U64 internal U64
bit_size_from_arch(Arch arch) bit_size_from_arch(Architecture arch)
{ {
// TODO(rjf): metacode // TODO(rjf): metacode
U64 arch_bitsize = 0; U64 arch_bitsize = 0;
switch(arch) switch(arch)
{ {
case Arch_x64: arch_bitsize = 64; break; case Architecture_x64: arch_bitsize = 64; break;
case Arch_x86: arch_bitsize = 32; break; case Architecture_x86: arch_bitsize = 32; break;
case Arch_arm64: arch_bitsize = 64; break; case Architecture_arm64: arch_bitsize = 64; break;
case Arch_arm32: arch_bitsize = 32; break; case Architecture_arm32: arch_bitsize = 32; break;
default: break; default: break;
} }
return arch_bitsize; return arch_bitsize;
} }
internal U64 internal U64
byte_size_from_arch(Arch arch) max_instruction_size_from_arch(Architecture arch)
{
return bit_size_from_arch(arch) / 8;
}
internal U64
max_instruction_size_from_arch(Arch arch)
{ {
// TODO(rjf): make this real // TODO(rjf): make this real
return 64; return 64;
} }
internal OperatingSystem
operating_system_from_context(void){
OperatingSystem os = OperatingSystem_Null;
#if OS_WINDOWS
os = OperatingSystem_Windows;
#elif OS_LINUX
os = OperatingSystem_Linux;
#elif OS_MAC
os = OperatingSystem_Mac;
#endif
return os;
}
internal Architecture
architecture_from_context(void){
Architecture arch = Architecture_Null;
#if ARCH_X64
arch = Architecture_x64;
#elif ARCH_X86
arch = Architecture_x86;
#elif ARCH_ARM64
arch = Architecture_arm64;
#elif ARCH_ARM32
arch = Architecture_arm32;
#endif
return arch;
}
internal Compiler
compiler_from_context(void){
Compiler compiler = Compiler_Null;
#if COMPILER_MSVC
compiler = Compiler_msvc;
#elif COMPILER_GCC
compiler = Compiler_gcc;
#elif COMPILER_CLANG
compiler = Compiler_clang;
#endif
return compiler;
}
//////////////////////////////// ////////////////////////////////
//~ rjf: Time Functions //~ rjf: Time Functions
@@ -484,60 +526,6 @@ date_time_from_micro_seconds(U64 time){
return(result); return(result);
} }
internal DateTime
date_time_from_unix_time(U64 unix_time)
{
DateTime date = {0};
date.year = 1970;
date.day = 1 + (unix_time / 86400);
date.sec = (U32)unix_time % 60;
date.min = (U32)(unix_time / 60) % 60;
date.hour = (U32)(unix_time / 3600) % 24;
for(;;)
{
for(date.month = 0; date.month < 12; ++date.month)
{
U64 c = 0;
switch(date.month)
{
case Month_Jan: c = 31; break;
case Month_Feb:
{
if((date.year % 4 == 0) && ((date.year % 100) != 0 || (date.year % 400) == 0))
{
c = 29;
}
else
{
c = 28;
}
} break;
case Month_Mar: c = 31; break;
case Month_Apr: c = 30; break;
case Month_May: c = 31; break;
case Month_Jun: c = 30; break;
case Month_Jul: c = 31; break;
case Month_Aug: c = 31; break;
case Month_Sep: c = 30; break;
case Month_Oct: c = 31; break;
case Month_Nov: c = 30; break;
case Month_Dec: c = 31; break;
default: InvalidPath;
}
if(date.day <= c)
{
goto exit;
}
date.day -= c;
}
++date.year;
}
exit:;
return date;
}
//////////////////////////////// ////////////////////////////////
//~ rjf: Non-Fancy Ring Buffer Reads/Writes //~ rjf: Non-Fancy Ring Buffer Reads/Writes
@@ -572,125 +560,3 @@ ring_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_data, U64 read_s
} }
return read_size; return read_size;
} }
////////////////////////////////
internal U64
u64_array_bsearch(U64 *arr, U64 count, U64 value)
{
if(count > 1 && arr[0] <= value && value < arr[count-1])
{
U64 l = 0;
U64 r = count - 1;
for(; l <= r; )
{
U64 m = l + (r - l) / 2;
if(arr[m] == value)
{
return m;
}
else if(arr[m] < value)
{
l = m + 1;
}
else
{
r = m - 1;
}
}
}
else if (count == 1 && arr[0] == value)
{
return 0;
}
return max_U64;
}
////////////////////////////////
internal U64
index_of_zero_u32(U32 *ptr, U64 count)
{
for (U64 i = 0; i < count; i += 1) {
if (ptr[i] == 0) {
return i;
}
}
return max_U64;
}
internal U64
index_of_zero_u64(U64 *ptr, U64 count)
{
for (U64 i = 0; i < count; i += 1) {
if (ptr[i] == 0) {
return i;
}
}
return max_U64;
}
////////////////////////////////
//~ rjf: Third Party Includes
#if !BUILD_SUPPLEMENTARY_UNIT
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wsign-conversion"
#elif defined(_MSC_VER)
#pragma warning (push, 0)
#endif
# define STB_SPRINTF_IMPLEMENTATION
# define STB_SPRINTF_STATIC
# include "third_party/stb/stb_sprintf.h"
#endif
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(_MSC_VER)
#pragma warning (pop, 0)
#endif
////////////////////////////////
//~ rjf: String <-> Integer Tables
read_only global U8 integer_symbols[16] =
{
'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F',
};
// NOTE(rjf): Includes reverses for uppercase and lowercase hex.
read_only global U8 integer_symbol_reverse[128] =
{
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
read_only global U8 base64[64] =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'_', '$',
};
read_only global U8 base64_reverse[128] =
{
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,
0xFF,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,
0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0xFF,0xFF,0xFF,0xFF,0x3E,
0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,
};
+58 -302
View File
@@ -1,8 +1,8 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_CORE_H #ifndef BASE_TYPES_H
#define BASE_CORE_H #define BASE_TYPES_H
//////////////////////////////// ////////////////////////////////
//~ rjf: Foreign Includes //~ rjf: Foreign Includes
@@ -13,13 +13,6 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
////////////////////////////////
//~ rjf: Third Party Includes
#define STB_SPRINTF_DECORATE(name) raddbg_##name
#define STB_SPRINTF_STATIC
#include "third_party/stb/stb_sprintf.h"
//////////////////////////////// ////////////////////////////////
//~ rjf: Codebase Keywords //~ rjf: Codebase Keywords
@@ -45,24 +38,6 @@
# define thread_static __declspec(thread) # define thread_static __declspec(thread)
#elif COMPILER_CLANG || COMPILER_GCC #elif COMPILER_CLANG || COMPILER_GCC
# define thread_static __thread # define thread_static __thread
#else
# error thread_static not defined for this compiler.
#endif
#if COMPILER_MSVC
# define force_inline __forceinline
#elif COMPILER_CLANG || COMPILER_GCC
# define force_inline __attribute__((always_inline))
#else
# error force_inline not defined for this compiler.
#endif
#if COMPILER_MSVC
# define no_inline __declspec(noinline)
#elif COMPILER_CLANG || COMPILER_GCC
# define no_inline __attribute__((noinline))
#else
# error no_inline not defined for this compiler.
#endif #endif
//////////////////////////////// ////////////////////////////////
@@ -84,45 +59,6 @@
# define C_LINKAGE # define C_LINKAGE
#endif #endif
////////////////////////////////
//~ rjf: Optimization Settings
#if COMPILER_MSVC
# define OPTIMIZE_BEGIN _Pragma("optimize(\"\", on)")
# define OPTIMIZE_END _Pragma("optimize(\"\", off)")
#elif COMPILER_CLANG
# define OPTIMIZE_BEGIN _Pragma("clang optimize on")
# define OPTIMIZE_END _Pragma("clang optimize off")
#elif COMPILER_GCC
# define OPTIMIZE_BEGIN _Pragma("GCC push_options") _Pragma("GCC optimize(\"O2\")")
# define OPTIMIZE_END _Pragma("GCC pop_options")
#else
# define OPTIMIZE_BEGIN
# define OPTIMIZE_END
#endif
#if COMPILER_MSVC && !BUILD_DEBUG
# define NO_OPTIMIZE_BEGIN _Pragma("optimize(\"\", off)")
# define NO_OPTIMIZE_END _Pragma("optimize(\"\", on)")
#elif COMPILER_CLANG && !BUILD_DEBUG
# define NO_OPTIMIZE_BEGIN _Pragma("clang optimize off")
# define NO_OPTIMIZE_END _Pragma("clang optimize on")
#elif COMPILER_GCC && !BUILD_DEBUG
# define NO_OPTIMIZE_BEGIN _Pragma("GCC push_options") _Pragma("GCC optimize(\"O0\")")
# define NO_OPTIMIZE_END _Pragma("GCC pop_options")
#else
# define NO_OPTIMIZE_BEGIN
# define NO_OPTIMIZE_END
#endif
////////////////////////////////
//~ rjf: Versions
#define Version(major, minor, patch) (U64)((((U64)(major) & 0xffff) << 32) | ((((U64)(minor) & 0xffff) << 16)) | ((((U64)(patch) & 0xffff) << 0)))
#define MajorFromVersion(version) (((version) & 0xffff00000000ull) >> 32)
#define MinorFromVersion(version) (((version) & 0x0000ffff0000ull) >> 16)
#define PatchFromVersion(version) (((version) & 0x00000000ffffull) >> 0)
//////////////////////////////// ////////////////////////////////
//~ rjf: Units //~ rjf: Units
@@ -155,27 +91,6 @@
#define ClampBot(X,B) Max(X,B) #define ClampBot(X,B) Max(X,B)
#define Clamp(A,X,B) (((X)<(A))?(A):((X)>(B))?(B):(X)) #define Clamp(A,X,B) (((X)<(A))?(A):((X)>(B))?(B):(X))
////////////////////////////////
//~ rjf: Type -> Alignment
#if COMPILER_MSVC
# define AlignOf(T) __alignof(T)
#elif COMPILER_CLANG
# define AlignOf(T) __alignof(T)
#elif COMPILER_GCC
# define AlignOf(T) __alignof__(T)
#else
# error AlignOf not defined for this compiler.
#endif
#if COMPILER_MSVC
# define AlignType(x) __declspec(align(x))
#elif COMPILER_CLANG || COMPILER_GCC
# define AlignType(x) __attribute__((aligned(x)))
#else
# error AlignType not defined for this compiler.
#endif
//////////////////////////////// ////////////////////////////////
//~ rjf: Member Offsets //~ rjf: Member Offsets
@@ -190,12 +105,8 @@
#define DeferLoop(begin, end) for(int _i_ = ((begin), 0); !_i_; _i_ += 1, (end)) #define DeferLoop(begin, end) for(int _i_ = ((begin), 0); !_i_; _i_ += 1, (end))
#define DeferLoopChecked(begin, end) for(int _i_ = 2 * !(begin); (_i_ == 2 ? ((end), 0) : !_i_); _i_ += 1, (end)) #define DeferLoopChecked(begin, end) for(int _i_ = 2 * !(begin); (_i_ == 2 ? ((end), 0) : !_i_); _i_ += 1, (end))
#define EachIndex(it, count) (U64 it = 0; it < (count); it += 1) #define EachEnumVal(type, it) type it = (type)0; it < type##_COUNT; it = (type)(it+1)
#define EachElement(it, array) (U64 it = 0; it < ArrayCount(array); it += 1) #define EachNonZeroEnumVal(type, it) type it = (type)1; it < type##_COUNT; it = (type)(it+1)
#define EachEnumVal(type, it) (type it = (type)0; it < type##_COUNT; it = (type)(it+1))
#define EachNonZeroEnumVal(type, it) (type it = (type)1; it < type##_COUNT; it = (type)(it+1))
#define EachInRange(it, range) (U64 it = (range).min; it < (range).max; it += 1)
#define EachNode(it, T, first) (T *it = first; it != 0; it = it->next)
//////////////////////////////// ////////////////////////////////
//~ rjf: Memory Operation Macros //~ rjf: Memory Operation Macros
@@ -208,7 +119,6 @@
#define MemoryCopyStruct(d,s) MemoryCopy((d),(s),sizeof(*(d))) #define MemoryCopyStruct(d,s) MemoryCopy((d),(s),sizeof(*(d)))
#define MemoryCopyArray(d,s) MemoryCopy((d),(s),sizeof(d)) #define MemoryCopyArray(d,s) MemoryCopy((d),(s),sizeof(d))
#define MemoryCopyTyped(d,s,c) MemoryCopy((d),(s),sizeof(*(d))*(c)) #define MemoryCopyTyped(d,s,c) MemoryCopy((d),(s),sizeof(*(d))*(c))
#define MemoryCopyStr8(dst, s) MemoryCopy(dst, (s).str, (s).size)
#define MemoryZero(s,z) memset((s),0,(z)) #define MemoryZero(s,z) memset((s),0,(z))
#define MemoryZeroStruct(s) MemoryZero((s),sizeof(*(s))) #define MemoryZeroStruct(s) MemoryZero((s),sizeof(*(s)))
@@ -219,8 +129,6 @@
#define MemoryMatchStruct(a,b) MemoryMatch((a),(b),sizeof(*(a))) #define MemoryMatchStruct(a,b) MemoryMatch((a),(b),sizeof(*(a)))
#define MemoryMatchArray(a,b) MemoryMatch((a),(b),sizeof(a)) #define MemoryMatchArray(a,b) MemoryMatch((a),(b),sizeof(a))
#define MemoryIsZeroStruct(ptr) memory_is_zero((ptr), sizeof(*(ptr)))
#define MemoryRead(T,p,e) ( ((p)+sizeof(T)<=(e))?(*(T*)(p)):(0) ) #define MemoryRead(T,p,e) ( ((p)+sizeof(T)<=(e))?(*(T*)(p)):(0) )
#define MemoryConsume(T,p,e) ( ((p)+sizeof(T)<=(e))?((p)+=sizeof(T),*(T*)((p)-sizeof(T))):((p)=(e),0) ) #define MemoryConsume(T,p,e) ( ((p)+sizeof(T)<=(e))?((p)+=sizeof(T),*(T*)((p)-sizeof(T))):((p)=(e),0) )
@@ -249,51 +157,33 @@
//////////////////////////////// ////////////////////////////////
//~ rjf: Atomic Operations //~ rjf: Atomic Operations
#if COMPILER_MSVC #if OS_WINDOWS
# include <windows.h>
# include <tmmintrin.h>
# include <wmmintrin.h>
# include <intrin.h> # include <intrin.h>
# if ARCH_X64 # if ARCH_X64
# define ins_atomic_u128_eval_cond_assign(x,k,c) (B32)InterlockedCompareExchange128((__int64 *)(x), ((__int64 *)&(k))[1], ((__int64 *)&(k))[0], (__int64 *)c) # define ins_atomic_u64_eval(x) InterlockedAdd64((volatile __int64 *)(x), 0)
# define ins_atomic_u64_eval(x) *((volatile U64 *)(x)) # define ins_atomic_u64_inc_eval(x) InterlockedIncrement64((volatile __int64 *)(x))
# define ins_atomic_u64_inc_eval(x) InterlockedIncrement64((__int64 *)(x)) # define ins_atomic_u64_dec_eval(x) InterlockedDecrement64((volatile __int64 *)(x))
# define ins_atomic_u64_dec_eval(x) InterlockedDecrement64((__int64 *)(x)) # define ins_atomic_u64_eval_assign(x,c) InterlockedExchange64((volatile __int64 *)(x),(c))
# define ins_atomic_u64_eval_assign(x,c) InterlockedExchange64((__int64 *)(x),(c)) # define ins_atomic_u64_add_eval(x,c) InterlockedAdd64((volatile __int64 *)(x), c)
# define ins_atomic_u64_add_eval(x,c) InterlockedAdd64((__int64 *)(x), c) # define ins_atomic_u64_eval_cond_assign(x,k,c) InterlockedCompareExchange64((volatile __int64 *)(x),(k),(c))
# define ins_atomic_u64_eval_cond_assign(x,k,c) InterlockedCompareExchange64((__int64 *)(x),(k),(c)) # define ins_atomic_u32_eval(x,c) InterlockedAdd((volatile LONG *)(x), 0)
# define ins_atomic_u32_eval(x) *((volatile U32 *)(x)) # define ins_atomic_u32_eval_assign(x,c) InterlockedExchange((volatile LONG *)(x),(c))
# define ins_atomic_u32_inc_eval(x) InterlockedIncrement((LONG *)(x)) # define ins_atomic_u32_eval_cond_assign(x,k,c) InterlockedCompareExchange((volatile LONG *)(x),(k),(c))
# define ins_atomic_u32_dec_eval(x) InterlockedDecrement((LONG *)(x)) # define ins_atomic_ptr_eval_assign(x,c) (void*)ins_atomic_u64_eval_assign((volatile __int64 *)(x), (__int64)(c))
# define ins_atomic_u32_eval_assign(x,c) InterlockedExchange((LONG *)(x),(c))
# define ins_atomic_u32_eval_cond_assign(x,k,c) InterlockedCompareExchange((LONG *)(x),(k),(c))
# define ins_atomic_u32_add_eval(x,c) InterlockedAdd((LONG *)(x), (c))
# define ins_atomic_u8_eval_assign(x,c) InterlockedExchange8((CHAR *)(x), (c))
# else # else
# error Atomic intrinsics not defined for this compiler / architecture combination. # error Atomic intrinsics not defined for this operating system / architecture combination.
# endif
#elif OS_LINUX
# if ARCH_X64
# define ins_atomic_u64_inc_eval(x) __sync_fetch_and_add((volatile U64 *)(x), 1)
# else
# error Atomic intrinsics not defined for this operating system / architecture combination.
# endif # endif
#elif COMPILER_CLANG || COMPILER_GCC
# define ins_atomic_u128_eval_cond_assign(x,k,c) (B32)__atomic_compare_exchange_n((__int128 *)(x),(__int128 *)(c),*(__int128 *)(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST)
# define ins_atomic_u64_eval(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
# define ins_atomic_u64_inc_eval(x) (__atomic_fetch_add((U64 *)(x), 1, __ATOMIC_SEQ_CST) + 1)
# define ins_atomic_u64_dec_eval(x) (__atomic_fetch_sub((U64 *)(x), 1, __ATOMIC_SEQ_CST) - 1)
# define ins_atomic_u64_eval_assign(x,c) __atomic_exchange_n(x, c, __ATOMIC_SEQ_CST)
# define ins_atomic_u64_add_eval(x,c) (__atomic_fetch_add((U64 *)(x), c, __ATOMIC_SEQ_CST) + (c))
# define ins_atomic_u64_eval_cond_assign(x,k,c) ({ U64 _new = (c); __atomic_compare_exchange_n((U64 *)(x),&_new,(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST); _new; })
# define ins_atomic_u32_eval(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
# define ins_atomic_u32_inc_eval(x) (__atomic_fetch_add((U32 *)(x), 1, __ATOMIC_SEQ_CST) + 1)
# define ins_atomic_u32_dec_eval(x) (__atomic_fetch_sub((U32 *)(x), 1, __ATOMIC_SEQ_CST) - 1)
# define ins_atomic_u32_add_eval(x,c) (__atomic_fetch_add((U32 *)(x), c, __ATOMIC_SEQ_CST) + (c))
# define ins_atomic_u32_eval_assign(x,c) __atomic_exchange_n((x), (c), __ATOMIC_SEQ_CST)
# define ins_atomic_u32_eval_cond_assign(x,k,c) ({ U32 _new = (c); __atomic_compare_exchange_n((U32 *)(x),&_new,(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST); _new; })
# define ins_atomic_u8_eval_assign(x,c) __atomic_exchange_n((x), (c), __ATOMIC_SEQ_CST)
#else #else
# error Atomic intrinsics not defined for this compiler / architecture. # error Atomic intrinsics not defined for this operating system.
#endif
#if ARCH_64BIT
# define ins_atomic_ptr_eval_cond_assign(x,k,c) (void *)ins_atomic_u64_eval_cond_assign((U64 *)(x), (U64)(k), (U64)(c))
# define ins_atomic_ptr_eval_assign(x,c) (void *)ins_atomic_u64_eval_assign((U64 *)(x), (U64)(c))
# define ins_atomic_ptr_eval(x) (void *)ins_atomic_u64_eval((U64 *)x)
#else
# error Atomic intrinsics for pointers not defined for this architecture.
#endif #endif
//////////////////////////////// ////////////////////////////////
@@ -375,10 +265,11 @@ CheckNil(nil,p) ? \
# endif # endif
# define NO_ASAN __attribute__((no_sanitize("address"))) # define NO_ASAN __attribute__((no_sanitize("address")))
#else #else
# define NO_ASAN # error "NO_ASAN is not defined for this compiler."
#endif #endif
#if ASAN_ENABLED #if ASAN_ENABLED
#pragma comment(lib, "clang_rt.asan-x86_64.lib")
C_LINKAGE void __asan_poison_memory_region(void const volatile *addr, size_t size); C_LINKAGE void __asan_poison_memory_region(void const volatile *addr, size_t size);
C_LINKAGE void __asan_unpoison_memory_region(void const volatile *addr, size_t size); C_LINKAGE void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
# define AsanPoisonMemoryRegion(addr, size) __asan_poison_memory_region((addr), (size)) # define AsanPoisonMemoryRegion(addr, size) __asan_poison_memory_region((addr), (size))
@@ -410,10 +301,9 @@ C_LINKAGE void __asan_unpoison_memory_region(void const volatile *addr, size_t s
#else #else
# error Missing pointer-to-integer cast for this architecture. # error Missing pointer-to-integer cast for this architecture.
#endif #endif
#define PtrFromInt(i) (void*)(i) #define PtrFromInt(i) (void*)((U8*)0 + (i))
#define Compose64Bit(a,b) ((((U64)a) << 32) | ((U64)b)) #define Compose64Bit(a,b) ((((U64)a) << 32) | ((U64)b));
#define Compose32Bit(a,b) ((((U32)a) << 16) | ((U32)b))
#define AlignPow2(x,b) (((x) + (b) - 1)&(~((b) - 1))) #define AlignPow2(x,b) (((x) + (b) - 1)&(~((b) - 1)))
#define AlignDownPow2(x,b) ((x)&(~((b) - 1))) #define AlignDownPow2(x,b) ((x)&(~((b) - 1)))
#define AlignPadPow2(x,b) ((0-(x)) & ((b) - 1)) #define AlignPadPow2(x,b) ((0-(x)) & ((b) - 1))
@@ -421,9 +311,6 @@ C_LINKAGE void __asan_unpoison_memory_region(void const volatile *addr, size_t s
#define IsPow2OrZero(x) ((((x) - 1)&(x)) == 0) #define IsPow2OrZero(x) ((((x) - 1)&(x)) == 0)
#define ExtractBit(word, idx) (((word) >> (idx)) & 1) #define ExtractBit(word, idx) (((word) >> (idx)) & 1)
#define Extract8(word, pos) (((word) >> ((pos)*8)) & max_U8)
#define Extract16(word, pos) (((word) >> ((pos)*16)) & max_U16)
#define Extract32(word, pos) (((word) >> ((pos)*32)) & max_U32)
#if LANG_CPP #if LANG_CPP
# define zero_struct {} # define zero_struct {}
@@ -455,64 +342,11 @@ typedef S64 B64;
typedef float F32; typedef float F32;
typedef double F64; typedef double F64;
typedef void VoidProc(void); typedef void VoidProc(void);
typedef union U128 U128; typedef struct U128 U128;
union U128 struct U128
{ {
U8 u8[16];
U16 u16[8];
U32 u32[4];
U64 u64[2]; U64 u64[2];
}; };
typedef union U256 U256;
union U256
{
U8 u8[32];
U16 u16[16];
U32 u32[8];
U64 u64[4];
U128 u128[2];
};
typedef union U512 U512;
union U512
{
U8 u8[64];
U16 u16[32];
U32 u32[16];
U64 u64[8];
U128 u128[4];
U256 u256[2];
};
////////////////////////////////
//~ rjf: Basic Type Structures
typedef struct U16Array U16Array;
struct U16Array
{
U64 count;
U16 *v;
};
typedef struct U32Array U32Array;
struct U32Array
{
U64 count;
U32 *v;
};
typedef struct U64Array U64Array;
struct U64Array
{
U64 count;
U64 *v;
};
typedef struct U128Array U128Array;
struct U128Array
{
U64 count;
U128 *v;
};
//////////////////////////////// ////////////////////////////////
//~ rjf: Basic Types & Spaces //~ rjf: Basic Types & Spaces
@@ -580,50 +414,19 @@ typedef enum OperatingSystem
OperatingSystem_Linux, OperatingSystem_Linux,
OperatingSystem_Mac, OperatingSystem_Mac,
OperatingSystem_COUNT, OperatingSystem_COUNT,
#if OS_WINDOWS
OperatingSystem_CURRENT = OperatingSystem_Windows,
#elif OS_LINUX
OperatingSystem_CURRENT = OperatingSystem_Linux,
#elif OS_MAC
OperatingSystem_CURRENT = OperatingSystem_Mac,
#else
OperatingSystem_CURRENT = OperatingSystem_Null,
#endif
} }
OperatingSystem; OperatingSystem;
typedef enum ExecutableImageKind typedef enum Architecture
{ {
ExecutableImageKind_Null, Architecture_Null,
ExecutableImageKind_CoffPe, Architecture_x64,
ExecutableImageKind_Elf32, Architecture_x86,
ExecutableImageKind_Elf64, Architecture_arm64,
ExecutableImageKind_Macho, Architecture_arm32,
ExecutableImageKind_COUNT Architecture_COUNT,
} }
ExecutableImageKind; Architecture;
typedef enum Arch
{
Arch_Null,
Arch_x64,
Arch_x86,
Arch_arm64,
Arch_arm32,
Arch_COUNT,
#if ARCH_X64
Arch_CURRENT = Arch_x64,
#elif ARCH_X86
Arch_CURRENT = Arch_x86,
#elif ARCH_ARM64
Arch_CURRENT = Arch_arm64,
#elif ARCH_ARM32
Arch_CURRENT = Arch_arm32,
#else
Arch_CURRENT = Arch_Null,
#endif
}
Arch;
typedef enum Compiler typedef enum Compiler
{ {
@@ -632,15 +435,6 @@ typedef enum Compiler
Compiler_gcc, Compiler_gcc,
Compiler_clang, Compiler_clang,
Compiler_COUNT, Compiler_COUNT,
#if COMPILER_MSVC
Compiler_CURRENT = Compiler_msvc,
#elif COMPILER_GCC
Compiler_CURRENT = Compiler_gcc,
#elif COMPILER_CLANG
Compiler_CURRENT = Compiler_clang,
#else
Compiler_CURRENT = Compiler_Null,
#endif
} }
Compiler; Compiler;
@@ -662,24 +456,7 @@ struct TxtRng
}; };
//////////////////////////////// ////////////////////////////////
//~ rjf: Globally Unique Ids //~ NOTE(allen): Constants
typedef union Guid Guid;
union Guid
{
struct
{
U32 data1;
U16 data2;
U16 data3;
U8 data4[8];
};
U8 v[16];
};
StaticAssert(sizeof(Guid) == 16, g_guid_size_check);
////////////////////////////////
//~ rjf: Basic Constants
global U32 sign32 = 0x80000000; global U32 sign32 = 0x80000000;
global U32 exponent32 = 0x7F800000; global U32 exponent32 = 0x7F800000;
@@ -697,15 +474,15 @@ global U32 max_U32 = 0xffffffff;
global U16 max_U16 = 0xffff; global U16 max_U16 = 0xffff;
global U8 max_U8 = 0xff; global U8 max_U8 = 0xff;
global S64 max_S64 = (S64)0x7fffffffffffffffll; global S64 max_S64 = (S64)0x7fffffffffffffffull;
global S32 max_S32 = (S32)0x7fffffff; global S32 max_S32 = (S32)0x7fffffff;
global S16 max_S16 = (S16)0x7fff; global S16 max_S16 = (S16)0x7fff;
global S8 max_S8 = (S8)0x7f; global S8 max_S8 = (S8)0x7f;
global S64 min_S64 = (S64)0x8000000000000000ll; global S64 min_S64 = (S64)0xffffffffffffffffull;
global S32 min_S32 = (S32)0x80000000; global S32 min_S32 = (S32)0xffffffff;
global S16 min_S16 = (S16)0x8000; global S16 min_S16 = (S16)0xffff;
global S8 min_S8 = (S8)0x80; global S8 min_S8 = (S8)0xff;
global const U32 bitmask1 = 0x00000001; global const U32 bitmask1 = 0x00000001;
global const U32 bitmask2 = 0x00000003; global const U32 bitmask2 = 0x00000003;
@@ -840,7 +617,7 @@ global const U64 bit63 = (1ull<<62);
global const U64 bit64 = (1ull<<63); global const U64 bit64 = (1ull<<63);
//////////////////////////////// ////////////////////////////////
//~ rjf: Time Types //~ allen: Time
typedef enum WeekDay typedef enum WeekDay
{ {
@@ -898,7 +675,7 @@ struct DateTime
typedef U64 DenseTime; typedef U64 DenseTime;
//////////////////////////////// ////////////////////////////////
//~ rjf: File Types //~ allen: Files
typedef U32 FilePropertyFlags; typedef U32 FilePropertyFlags;
enum enum
@@ -944,16 +721,7 @@ internal U16 bswap_u16(U16 x);
internal U32 bswap_u32(U32 x); internal U32 bswap_u32(U32 x);
internal U64 bswap_u64(U64 x); internal U64 bswap_u64(U64 x);
#if ARCH_LITTLE_ENDIAN internal U64 count_bits_set16(U16 val);
# define from_be_u16(x) bswap_u16(x)
# define from_be_u32(x) bswap_u32(x)
# define from_be_u64(x) bswap_u64(x)
#else
# define from_be_u16(x) (x)
# define from_be_u32(x) (x)
# define from_be_u64(x) (x)
#endif
internal U64 count_bits_set32(U32 val); internal U64 count_bits_set32(U32 val);
internal U64 count_bits_set64(U64 val); internal U64 count_bits_set64(U64 val);
@@ -989,17 +757,19 @@ internal B32 txt_rng_contains(TxtRng r, TxtPt pt);
//////////////////////////////// ////////////////////////////////
//~ rjf: Toolchain/Environment Enum Functions //~ rjf: Toolchain/Environment Enum Functions
internal U64 bit_size_from_arch(Arch arch); internal U64 bit_size_from_arch(Architecture arch);
internal U64 byte_size_from_arch(Arch arch); internal U64 max_instruction_size_from_arch(Architecture arch);
internal U64 max_instruction_size_from_arch(Arch arch);
internal OperatingSystem operating_system_from_context(void);
internal Architecture architecture_from_context(void);
internal Compiler compiler_from_context(void);
//////////////////////////////// ////////////////////////////////
//~ rjf: Time Functions //~ rjf: Time Functions
internal DenseTime dense_time_from_date_time(DateTime date_time); internal DenseTime dense_time_from_date_time(DateTime date_time);
internal DateTime date_time_from_dense_time(DenseTime time); internal DateTime date_time_from_dense_time(DenseTime time);
internal DateTime date_time_from_micro_seconds(U64 time); internal DateTime date_time_from_micro_seconds(U64 time);
internal DateTime date_time_from_unix_time(U64 unix_time);
//////////////////////////////// ////////////////////////////////
//~ rjf: Non-Fancy Ring Buffer Reads/Writes //~ rjf: Non-Fancy Ring Buffer Reads/Writes
@@ -1009,18 +779,4 @@ internal U64 ring_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_dat
#define ring_write_struct(ring_base, ring_size, ring_pos, ptr) ring_write((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr))) #define ring_write_struct(ring_base, ring_size, ring_pos, ptr) ring_write((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr)))
#define ring_read_struct(ring_base, ring_size, ring_pos, ptr) ring_read((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr))) #define ring_read_struct(ring_base, ring_size, ring_pos, ptr) ring_read((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr)))
////////////////////////////////
//~ rjf: Sorts
#define quick_sort(ptr, count, element_size, cmp_function) qsort((ptr), (count), (element_size), (int (*)(const void *, const void *))(cmp_function))
////////////////////////////////
internal U64 u64_array_bsearch(U64 *arr, U64 count, U64 value);
////////////////////////////////
internal U64 index_of_zero_u32(U32 *ptr, U64 count);
internal U64 index_of_zero_u64(U64 *ptr, U64 count);
#endif // BASE_CORE_H #endif // BASE_CORE_H
+45 -184
View File
@@ -1,76 +1,49 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
global U64 global_update_tick_idx = 0;
global U64 async_threads_count = 0;
global CondVar async_tick_start_cond_var = {0};
global Mutex async_tick_start_mutex = {0};
global Mutex async_tick_stop_mutex = {0};
global B32 async_loop_again = 0;
global B32 async_loop_again_high_priority = 0;
global B32 global_async_exit = 0;
thread_static B32 is_async_thread = 0;
internal void internal void
main_thread_base_entry_point(int arguments_count, char **arguments) main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **arguments, U64 arguments_count)
{ {
ThreadNameF("main_thread");
Temp scratch = scratch_begin(0, 0);
//- rjf: set up async thread group info
async_tick_start_cond_var = cond_var_alloc();
async_tick_start_mutex = mutex_alloc();
async_tick_stop_mutex = mutex_alloc();
//- rjf: set up telemetry
#if PROFILE_TELEMETRY #if PROFILE_TELEMETRY
local_persist char tm_data[MB(64)]; local_persist U8 tm_data[MB(64)];
tmLoadLibrary(TM_RELEASE); tmLoadLibrary(TM_RELEASE);
tmSetMaxThreadCount(256); tmSetMaxThreadCount(256);
tmInitialize(sizeof(tm_data), tm_data); tmInitialize(sizeof(tm_data), (char *)tm_data);
#endif #endif
TCTX tctx;
//- rjf: set up spall tctx_init_and_equip(&tctx);
#if PROFILE_SPALL ThreadNameF("[main thread]");
spall_profile = spall_init_file_ex("spall_capture", 1, 0); Temp scratch = scratch_begin(0, 0);
#endif String8List command_line_argument_strings = os_string_list_from_argcv(scratch.arena, (int)arguments_count, arguments);
//- rjf: parse command line
String8List command_line_argument_strings = {0};
{
for EachIndex(idx, arguments_count)
{
str8_list_push(scratch.arena, &command_line_argument_strings, str8_cstring(arguments[idx]));
}
}
CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings); CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings);
//- rjf: begin captures
B32 capture = cmd_line_has_flag(&cmdline, str8_lit("capture")); B32 capture = cmd_line_has_flag(&cmdline, str8_lit("capture"));
if(capture) if(capture)
{ {
ProfBeginCapture(arguments[0]); ProfBeginCapture(arguments[0]);
ProfMsg(BUILD_TITLE);
} }
#if defined(OS_CORE_H) && !defined(OS_INIT_MANUAL)
//- rjf: initialize all included layers os_init();
#if defined(ARTIFACT_CACHE_H) && !defined(AC_INIT_MANUAL)
ac_init();
#endif #endif
#if defined(ASYNC_H) && !defined(ASYNC_INIT_MANUAL) #if defined(TASK_SYSTEM_H) && !defined(TS_INIT_MANUAL)
async_init(&cmdline); ts_init();
#endif #endif
#if defined(CONTENT_H) && !defined(C_INIT_MANUAL) #if defined(HASH_STORE_H) && !defined(HS_INIT_MANUAL)
c_init(); hs_init();
#endif #endif
#if defined(FILE_STREAM_H) && !defined(FS_INIT_MANUAL) #if defined(FILE_STREAM_H) && !defined(FS_INIT_MANUAL)
fs_init(); fs_init();
#endif #endif
#if defined(MUTABLE_TEXT_H) && !defined(MTX_INIT_MANUAL) #if defined(TEXT_CACHE_H) && !defined(TXT_INIT_MANUAL)
mtx_init(); txt_init();
#endif #endif
#if defined(DBG_INFO_H) && !defined(DI_INIT_MANUAL) #if defined(DASM_CACHE_H) && !defined(DASM_INIT_MANUAL)
di_init(&cmdline); dasm_init();
#endif
#if defined(DI_H) && !defined(DI_INIT_MANUAL)
di_init();
#endif
#if defined(FUZZY_SEARCH_H) && !defined(FZY_INIT_MANUAL)
fzy_init();
#endif
#if defined(TXTI_H) && !defined(TXTI_INIT_MANUAL)
txti_init();
#endif #endif
#if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL) #if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL)
dmn_init(); dmn_init();
@@ -78,8 +51,8 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
#if defined(CTRL_CORE_H) && !defined(CTRL_INIT_MANUAL) #if defined(CTRL_CORE_H) && !defined(CTRL_INIT_MANUAL)
ctrl_init(); ctrl_init();
#endif #endif
#if defined(OS_GFX_H) && !defined(OS_GFX_INIT_MANUAL) #if defined(OS_GRAPHICAL_H) && !defined(OS_GFX_INIT_MANUAL)
os_gfx_init(); os_graphical_init();
#endif #endif
#if defined(FONT_PROVIDER_H) && !defined(FP_INIT_MANUAL) #if defined(FONT_PROVIDER_H) && !defined(FP_INIT_MANUAL)
fp_init(); fp_init();
@@ -87,148 +60,36 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
#if defined(RENDER_CORE_H) && !defined(R_INIT_MANUAL) #if defined(RENDER_CORE_H) && !defined(R_INIT_MANUAL)
r_init(&cmdline); r_init(&cmdline);
#endif #endif
#if defined(FONT_CACHE_H) && !defined(FNT_INIT_MANUAL) #if defined(TEXTURE_CACHE_H) && !defined(TEX_INIT_MANUAL)
fnt_init(); tex_init();
#endif #endif
#if defined(DBG_ENGINE_CORE_H) && !defined(D_INIT_MANUAL) #if defined(GEO_CACHE_H) && !defined(GEO_INIT_MANUAL)
d_init(); geo_init();
#endif #endif
#if defined(RADDBG_CORE_H) && !defined(RD_INIT_MANUAL) #if defined(FONT_CACHE_H) && !defined(F_INIT_MANUAL)
rd_init(&cmdline); f_init();
#endif #endif
#if defined(DF_CORE_H) && !defined(DF_INIT_MANUAL)
//- rjf: launch async threads DF_StateDeltaHistory *hist = df_state_delta_history_alloc();
Thread *async_threads = 0; df_core_init(&cmdline, hist);
U64 lane_broadcast_val = 0; #endif
{ #if defined(DF_GFX_H) && !defined(DF_GFX_INIT_MANUAL)
U64 num_main_threads = 1; df_gfx_init(update_and_render, df_state_delta_history());
#if defined(CTRL_CORE_H)
num_main_threads += 1;
#endif #endif
U64 num_async_threads = os_get_system_info()->logical_processor_count;
U64 num_main_threads_clamped = Min(num_async_threads, num_main_threads);
num_async_threads -= num_main_threads_clamped;
String8 num_async_threads_string = cmd_line_string(&cmdline, str8_lit("async_thread_count"));
if(num_async_threads_string.size != 0)
{
try_u64_from_str8_c_rules(num_async_threads_string, &num_async_threads);
}
num_async_threads = Max(1, num_async_threads);
Barrier barrier = barrier_alloc(num_async_threads);
LaneCtx *lane_ctxs = push_array(scratch.arena, LaneCtx, num_async_threads);
async_threads_count = num_async_threads;
async_threads = push_array(scratch.arena, Thread, async_threads_count);
for EachIndex(idx, num_async_threads)
{
lane_ctxs[idx].lane_idx = idx;
lane_ctxs[idx].lane_count = async_threads_count;
lane_ctxs[idx].barrier = barrier;
lane_ctxs[idx].broadcast_memory = &lane_broadcast_val;
async_threads[idx] = thread_launch(async_thread_entry_point, &lane_ctxs[idx]);
}
}
//- rjf: call into entry point
entry_point(&cmdline); entry_point(&cmdline);
//- rjf: join async threads
ins_atomic_u32_inc_eval(&global_async_exit);
cond_var_broadcast(async_tick_start_cond_var);
for EachIndex(idx, async_threads_count)
{
thread_join(async_threads[idx], max_U64);
}
//- rjf: end captures
if(capture) if(capture)
{ {
ProfEndCapture(); ProfEndCapture();
} }
scratch_end(scratch); scratch_end(scratch);
tctx_release();
} }
internal void internal void
supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params) supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params)
{ {
TCTX *tctx = tctx_alloc(); TCTX tctx;
tctx_select(tctx); tctx_init_and_equip(&tctx);
entry_point(params); entry_point(params);
tctx_release(tctx); tctx_release();
}
internal U64
update_tick_idx(void)
{
U64 result = ins_atomic_u64_eval(&global_update_tick_idx);
return result;
}
internal B32
update(void)
{
ProfTick(0);
ins_atomic_u64_inc_eval(&global_update_tick_idx);
#if defined(FONT_CACHE_H)
fnt_frame();
#endif
#if OS_FEATURE_GRAPHICAL
B32 result = frame();
#else
B32 result = 0;
#endif
return result;
}
internal void
async_thread_entry_point(void *params)
{
LaneCtx lctx = *(LaneCtx *)params;
lane_ctx(lctx);
is_async_thread = 1;
ThreadNameF("async_thread_%I64u", lane_idx());
for(;;)
{
// rjf: wait for signal if we need, otherwise reset loop signal & continue
if(lane_idx() == 0)
{
if(!ins_atomic_u32_eval(&async_loop_again))
{
MutexScope(async_tick_start_mutex) cond_var_wait(async_tick_start_cond_var, async_tick_start_mutex, os_now_microseconds()+1000000);
}
ins_atomic_u32_eval_assign(&async_loop_again, 0);
ins_atomic_u32_eval_assign(&async_loop_again_high_priority, 0);
}
lane_sync();
// rjf: do all ticks for all layers
ProfScope("async tick")
{
#if defined(ARTIFACT_CACHE_H)
ac_async_tick();
#endif
#if defined(CONTENT_H)
c_async_tick();
#endif
#if defined(FILE_STREAM_H)
fs_async_tick();
#endif
#if defined(DBG_INFO_H)
di_async_tick();
#endif
}
// rjf: take exit signal; break if set
lane_sync();
B32 need_exit = 0;
if(lane_idx() == 0)
{
need_exit = ins_atomic_u32_eval(&global_async_exit);
}
lane_sync_u64(&need_exit, 0);
if(need_exit)
{
break;
}
}
} }
+2 -5
View File
@@ -1,13 +1,10 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_ENTRY_POINT_H #ifndef BASE_ENTRY_POINT_H
#define BASE_ENTRY_POINT_H #define BASE_ENTRY_POINT_H
internal void main_thread_base_entry_point(int argc, char **argv); internal void main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **arguments, U64 arguments_count);
internal void supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params); internal void supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params);
internal U64 update_tick_idx(void);
internal B32 update(void);
internal void async_thread_entry_point(void *params);
#endif // BASE_ENTRY_POINT_H #endif // BASE_ENTRY_POINT_H
-50
View File
@@ -1,50 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: MD5
#include "third_party/martins_hash/md5.h"
internal MD5
md5_from_data(String8 data)
{
md5_ctx ctx = {0};
md5_init(&ctx);
md5_update(&ctx, (void*)data.str, data.size);
MD5 result = {0};
md5_finish(&ctx, result.u8);
return result;
}
////////////////////////////////
//~ rjf: SHA
#include "third_party/martins_hash/sha1.h"
#include "third_party/martins_hash/sha256.h"
internal SHA1
sha1_from_data(String8 data)
{
SHA1 result = {0};
{
sha1_ctx ctx = {0};
sha1_init(&ctx);
sha1_update(&ctx, data.str, data.size);
sha1_finish(&ctx, result.u8);
}
return result;
}
internal SHA256
sha256_from_data(String8 data)
{
SHA256 result = {0};
{
sha256_ctx ctx = {0};
sha256_init(&ctx);
sha256_update(&ctx, data.str, data.size);
sha256_finish(&ctx, result.u8);
}
return result;
}
-48
View File
@@ -1,48 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_HASH_H
#define BASE_HASH_H
////////////////////////////////
//~ rjf: Hash Result Types
typedef union MD5 MD5;
union MD5
{
U8 u8[16];
U16 u16[8];
U32 u32[4];
U64 u64[2];
U128 u128;
};
typedef union SHA1 SHA1;
union SHA1
{
U8 u8[20];
};
typedef union SHA256 SHA256;
union SHA256
{
U8 u8[32];
U16 u16[16];
U32 u32[8];
U64 u64[4];
U128 u128[2];
U256 u256;
};
////////////////////////////////
//~ rjf: MD5
internal MD5 md5_from_data(String8 data);
////////////////////////////////
//~ rjf: SHA
internal SHA1 sha1_from_data(String8 data);
internal SHA256 sha256_from_data(String8 data);
#endif // BASE_HASH_H
+3 -6
View File
@@ -1,22 +1,19 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//////////////////////////////// ////////////////////////////////
//~ rjf: Base Includes //~ rjf: Base Includes
#undef LAYER_COLOR #undef RADDBG_LAYER_COLOR
#define LAYER_COLOR 0x3399ccff #define RADDBG_LAYER_COLOR 0.20f, 0.60f, 0.80f
#include "base_core.c" #include "base_core.c"
#include "base_profile.c" #include "base_profile.c"
#include "base_arena.c" #include "base_arena.c"
#include "base_math.c" #include "base_math.c"
#include "base_strings.c" #include "base_strings.c"
#include "base_hash.c"
#include "base_threads.c"
#include "base_thread_context.c" #include "base_thread_context.c"
#include "base_command_line.c" #include "base_command_line.c"
#include "base_markup.c" #include "base_markup.c"
#include "base_meta.c"
#include "base_log.c" #include "base_log.c"
#include "base_entry_point.c" #include "base_entry_point.c"
+1 -4
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_INC_H #ifndef BASE_INC_H
@@ -14,12 +14,9 @@
#include "base_arena.h" #include "base_arena.h"
#include "base_math.h" #include "base_math.h"
#include "base_strings.h" #include "base_strings.h"
#include "base_hash.h"
#include "base_threads.h"
#include "base_thread_context.h" #include "base_thread_context.h"
#include "base_command_line.h" #include "base_command_line.h"
#include "base_markup.h" #include "base_markup.h"
#include "base_meta.h"
#include "base_log.h" #include "base_log.h"
#include "base_entry_point.h" #include "base_entry_point.h"
+3 -6
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//////////////////////////////// ////////////////////////////////
@@ -88,12 +88,9 @@ log_scope_end(Arena *arena)
SLLStackPop(log_active->top_scope); SLLStackPop(log_active->top_scope);
if(arena != 0) if(arena != 0)
{ {
for EachEnumVal(LogMsgKind, kind) for(EachEnumVal(LogMsgKind, kind))
{ {
Temp scratch = scratch_begin(&arena, 1); result.strings[kind] = str8_list_join(arena, &scope->strings[kind], 0);
String8 result_unindented = str8_list_join(scratch.arena, &scope->strings[kind], 0);
result.strings[kind] = indented_from_string(arena, result_unindented);
scratch_end(scratch);
} }
} }
arena_pop_to(log_active->arena, scope->pos); arena_pop_to(log_active->arena, scope->pos);
+5 -8
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_LOG_H #ifndef BASE_LOG_H
@@ -48,13 +48,10 @@ internal void log_select(Log *log);
internal void log_msg(LogMsgKind kind, String8 string); internal void log_msg(LogMsgKind kind, String8 string);
internal void log_msgf(LogMsgKind kind, char *fmt, ...); internal void log_msgf(LogMsgKind kind, char *fmt, ...);
#define log_info(s) log_msg(LogMsgKind_Info, (s)) #define log_info(s) log_msg(LogMsgKind_Info, (s))
#define log_infof(...) log_msgf(LogMsgKind_Info, __VA_ARGS__) #define log_infof(fmt, ...) log_msgf(LogMsgKind_Info, (fmt), __VA_ARGS__)
#define log_user_error(s) log_msg(LogMsgKind_UserError, (s)) #define log_user_error(s) log_msg(LogMsgKind_UserError, (s))
#define log_user_errorf(...) log_msgf(LogMsgKind_UserError, __VA_ARGS__) #define log_user_errorf(fmt, ...) log_msgf(LogMsgKind_UserError, (fmt), __VA_ARGS__)
#define LogInfoNamedBlock(s) DeferLoop(log_infof("%S:\n{\n", (s)), log_infof("}\n"))
#define LogInfoNamedBlockF(...) DeferLoop((log_infof(__VA_ARGS__), log_infof(":\n{\n")), log_infof("}\n"))
//////////////////////////////// ////////////////////////////////
//~ rjf: Log Scopes //~ rjf: Log Scopes
+4 -4
View File
@@ -1,21 +1,21 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
internal void internal void
set_thread_name(String8 string) thread_name(String8 string)
{ {
ProfThreadName("%.*s", str8_varg(string)); ProfThreadName("%.*s", str8_varg(string));
os_set_thread_name(string); os_set_thread_name(string);
} }
internal void internal void
set_thread_namef(char *fmt, ...) thread_namef(char *fmt, ...)
{ {
Temp scratch = scratch_begin(0, 0); Temp scratch = scratch_begin(0, 0);
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
String8 string = push_str8fv(scratch.arena, fmt, args); String8 string = push_str8fv(scratch.arena, fmt, args);
set_thread_name(string); thread_name(string);
va_end(args); va_end(args);
scratch_end(scratch); scratch_end(scratch);
} }
+5 -15
View File
@@ -1,22 +1,12 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_MARKUP_H #ifndef BASE_MARKUP_H
#define BASE_MARKUP_H #define BASE_MARKUP_H
#define RADDBG_MARKUP_IMPLEMENTATION internal void thread_name(String8 string);
#define RADDBG_MARKUP_VSNPRINTF raddbg_vsnprintf internal void thread_namef(char *fmt, ...);
#if OS_LINUX #define ThreadNameF(...) (ProfThreadName(__VA_ARGS__), thread_namef(__VA_ARGS__))
# define RADDBG_MARKUP_STUBS #define ThreadName(str) (ProfThreadName("%.*s", str8_varg(str)), thread_name(str))
#endif
#include "lib_raddbg_markup/raddbg_markup.h"
#if !defined(LAYER_COLOR)
# define LAYER_COLOR 0x404040ff
#endif
internal void set_thread_name(String8 string);
internal void set_thread_namef(char *fmt, ...);
#define ThreadNameF(...) (set_thread_namef(__VA_ARGS__), raddbg_thread_color_u32(LAYER_COLOR))
#endif // BASE_MARKUP_H #endif // BASE_MARKUP_H
+22 -236
View File
@@ -1,8 +1,8 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//////////////////////////////// ////////////////////////////////
//~ rjf: Scalar Math Ops //~ rjf: Scalar Ops
internal F32 internal F32
mix_1f32(F32 a, F32 b, F32 t) mix_1f32(F32 a, F32 b, F32 t)
@@ -42,6 +42,7 @@ internal Vec2S64 scale_2s64(Vec2S64 v, S64 s) {Vec2S64 c = {v.
internal S64 dot_2s64(Vec2S64 a, Vec2S64 b) {S64 c = a.x*b.x + a.y*b.y; return c;} internal S64 dot_2s64(Vec2S64 a, Vec2S64 b) {S64 c = a.x*b.x + a.y*b.y; return c;}
internal S64 length_squared_2s64(Vec2S64 v) {S64 c = v.x*v.x + v.y*v.y; return c;} internal S64 length_squared_2s64(Vec2S64 v) {S64 c = v.x*v.x + v.y*v.y; return c;}
internal S64 length_2s64(Vec2S64 v) {S64 c = (S64)sqrt_f64((F64)(v.x*v.x + v.y*v.y)); return c;} internal S64 length_2s64(Vec2S64 v) {S64 c = (S64)sqrt_f64((F64)(v.x*v.x + v.y*v.y)); return c;}
internal Vec2S64 normalize_2s64(Vec2S64 v) {v = scale_2s64(v, (S64)(1.f/length_2s64(v))); return v;}
internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t) {Vec2S64 c = {(S64)mix_1f32((F32)a.x, (F32)b.x, t), (S64)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;} internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t) {Vec2S64 c = {(S64)mix_1f32((F32)a.x, (F32)b.x, t), (S64)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;}
internal Vec2S32 vec_2s32(S32 x, S32 y) {Vec2S32 v = {x, y}; return v;} internal Vec2S32 vec_2s32(S32 x, S32 y) {Vec2S32 v = {x, y}; return v;}
@@ -53,6 +54,7 @@ internal Vec2S32 scale_2s32(Vec2S32 v, S32 s) {Vec2S32 c = {v.
internal S32 dot_2s32(Vec2S32 a, Vec2S32 b) {S32 c = a.x*b.x + a.y*b.y; return c;} internal S32 dot_2s32(Vec2S32 a, Vec2S32 b) {S32 c = a.x*b.x + a.y*b.y; return c;}
internal S32 length_squared_2s32(Vec2S32 v) {S32 c = v.x*v.x + v.y*v.y; return c;} internal S32 length_squared_2s32(Vec2S32 v) {S32 c = v.x*v.x + v.y*v.y; return c;}
internal S32 length_2s32(Vec2S32 v) {S32 c = (S32)sqrt_f32((F32)v.x*(F32)v.x + (F32)v.y*(F32)v.y); return c;} internal S32 length_2s32(Vec2S32 v) {S32 c = (S32)sqrt_f32((F32)v.x*(F32)v.x + (F32)v.y*(F32)v.y); return c;}
internal Vec2S32 normalize_2s32(Vec2S32 v) {v = scale_2s32(v, (S32)(1.f/length_2s32(v))); return v;}
internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t) {Vec2S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;} internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t) {Vec2S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;}
internal Vec2S16 vec_2s16(S16 x, S16 y) {Vec2S16 v = {x, y}; return v;} internal Vec2S16 vec_2s16(S16 x, S16 y) {Vec2S16 v = {x, y}; return v;}
@@ -64,6 +66,7 @@ internal Vec2S16 scale_2s16(Vec2S16 v, S16 s) {Vec2S16 c = {(S
internal S16 dot_2s16(Vec2S16 a, Vec2S16 b) {S16 c = a.x*b.x + a.y*b.y; return c;} internal S16 dot_2s16(Vec2S16 a, Vec2S16 b) {S16 c = a.x*b.x + a.y*b.y; return c;}
internal S16 length_squared_2s16(Vec2S16 v) {S16 c = v.x*v.x + v.y*v.y; return c;} internal S16 length_squared_2s16(Vec2S16 v) {S16 c = v.x*v.x + v.y*v.y; return c;}
internal S16 length_2s16(Vec2S16 v) {S16 c = (S16)sqrt_f32((F32)(v.x*v.x + v.y*v.y)); return c;} internal S16 length_2s16(Vec2S16 v) {S16 c = (S16)sqrt_f32((F32)(v.x*v.x + v.y*v.y)); return c;}
internal Vec2S16 normalize_2s16(Vec2S16 v) {v = scale_2s16(v, (S16)(1.f/length_2s16(v))); return v;}
internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t) {Vec2S16 c = {(S16)mix_1f32((F32)a.x, (F32)b.x, t), (S16)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;} internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t) {Vec2S16 c = {(S16)mix_1f32((F32)a.x, (F32)b.x, t), (S16)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;}
internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z) {Vec3F32 v = {x, y, z}; return v;} internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z) {Vec3F32 v = {x, y, z}; return v;}
@@ -78,14 +81,6 @@ internal F32 length_3f32(Vec3F32 v) {F32 c = sqrt_f3
internal Vec3F32 normalize_3f32(Vec3F32 v) {v = scale_3f32(v, 1.f/length_3f32(v)); return v;} internal Vec3F32 normalize_3f32(Vec3F32 v) {v = scale_3f32(v, 1.f/length_3f32(v)); return v;}
internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t) {Vec3F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t), mix_1f32(a.z, b.z, t)}; return c;} internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t) {Vec3F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t), mix_1f32(a.z, b.z, t)}; return c;}
internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;} internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;}
internal Vec3F32 xform_3f32(Vec3F32 v, Mat3x3F32 m)
{
Vec3F32 result;
result.x = v.x*m.v[0][0] + v.y*m.v[1][0] + v.z*m.v[2][0];
result.y = v.x*m.v[0][1] + v.y*m.v[1][1] + v.z*m.v[2][1];
result.z = v.x*m.v[0][2] + v.y*m.v[1][2] + v.z*m.v[2][2];
return result;
}
internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z) {Vec3S32 v = {x, y, z}; return v;} internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z) {Vec3S32 v = {x, y, z}; return v;}
internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;} internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;}
@@ -96,6 +91,7 @@ internal Vec3S32 scale_3s32(Vec3S32 v, S32 s) {Vec3S32 c = {v.
internal S32 dot_3s32(Vec3S32 a, Vec3S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;} internal S32 dot_3s32(Vec3S32 a, Vec3S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;}
internal S32 length_squared_3s32(Vec3S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;} internal S32 length_squared_3s32(Vec3S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;}
internal S32 length_3s32(Vec3S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z)); return c;} internal S32 length_3s32(Vec3S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z)); return c;}
internal Vec3S32 normalize_3s32(Vec3S32 v) {v = scale_3s32(v, (S32)(1.f/length_3s32(v))); return v;}
internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t) {Vec3S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t)}; return c;} internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t) {Vec3S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t)}; return c;}
internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;} internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;}
@@ -120,6 +116,7 @@ internal Vec4S32 scale_4s32(Vec4S32 v, S32 s) {Vec4S32 c = {v.
internal S32 dot_4s32(Vec4S32 a, Vec4S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;} internal S32 dot_4s32(Vec4S32 a, Vec4S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;}
internal S32 length_squared_4s32(Vec4S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;} internal S32 length_squared_4s32(Vec4S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;}
internal S32 length_4s32(Vec4S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w)); return c;} internal S32 length_4s32(Vec4S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w)); return c;}
internal Vec4S32 normalize_4s32(Vec4S32 v) {v = scale_4s32(v, (S32)(1.f/length_4s32(v))); return v;}
internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t) {Vec4S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t), (S32)mix_1f32((F32)a.w, (F32)b.w, t)}; return c;} internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t) {Vec4S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t), (S32)mix_1f32((F32)a.w, (F32)b.w, t)}; return c;}
//////////////////////////////// ////////////////////////////////
@@ -389,21 +386,6 @@ derotate_4x4f32(Mat4x4F32 mat)
return mat; return mat;
} }
internal Mat4x4F32
transpose_4x4f32(Mat4x4F32 mat)
{
Mat4x4F32 result =
{
{
mat.v[0][0], mat.v[1][0], mat.v[2][0], mat.v[3][0],
mat.v[0][1], mat.v[1][1], mat.v[2][1], mat.v[3][1],
mat.v[0][2], mat.v[1][2], mat.v[2][2], mat.v[3][2],
mat.v[0][3], mat.v[1][3], mat.v[2][3], mat.v[3][3],
}
};
return result;
}
//////////////////////////////// ////////////////////////////////
//~ rjf: Range Ops //~ rjf: Range Ops
@@ -412,7 +394,7 @@ internal Rng1U32 shift_1u32(Rng1U32 r, U32 x) {r.min += x; r.m
internal Rng1U32 pad_1u32(Rng1U32 r, U32 x) {r.min -= x; r.max += x; return r;} internal Rng1U32 pad_1u32(Rng1U32 r, U32 x) {r.min -= x; r.max += x; return r;}
internal U32 center_1u32(Rng1U32 r) {U32 c = (r.min+r.max)/2; return c;} internal U32 center_1u32(Rng1U32 r) {U32 c = (r.min+r.max)/2; return c;}
internal B32 contains_1u32(Rng1U32 r, U32 x) {B32 c = (r.min <= x && x < r.max); return c;} internal B32 contains_1u32(Rng1U32 r, U32 x) {B32 c = (r.min <= x && x < r.max); return c;}
internal U32 dim_1u32(Rng1U32 r) {U32 c = ((r.max > r.min) ? (r.max - r.min) : 0); return c;} internal U32 dim_1u32(Rng1U32 r) {U32 c = r.max-r.min; return c;}
internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b) {Rng1U32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b) {Rng1U32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;}
internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b) {Rng1U32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b) {Rng1U32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;}
internal U32 clamp_1u32(Rng1U32 r, U32 v) {v = Clamp(r.min, v, r.max); return v;} internal U32 clamp_1u32(Rng1U32 r, U32 v) {v = Clamp(r.min, v, r.max); return v;}
@@ -422,7 +404,7 @@ internal Rng1S32 shift_1s32(Rng1S32 r, S32 x) {r.min += x; r.m
internal Rng1S32 pad_1s32(Rng1S32 r, S32 x) {r.min -= x; r.max += x; return r;} internal Rng1S32 pad_1s32(Rng1S32 r, S32 x) {r.min -= x; r.max += x; return r;}
internal S32 center_1s32(Rng1S32 r) {S32 c = (r.min+r.max)/2; return c;} internal S32 center_1s32(Rng1S32 r) {S32 c = (r.min+r.max)/2; return c;}
internal B32 contains_1s32(Rng1S32 r, S32 x) {B32 c = (r.min <= x && x < r.max); return c;} internal B32 contains_1s32(Rng1S32 r, S32 x) {B32 c = (r.min <= x && x < r.max); return c;}
internal S32 dim_1s32(Rng1S32 r) {S32 c = ((r.max > r.min) ? (r.max - r.min) : 0); return c;} internal S32 dim_1s32(Rng1S32 r) {S32 c = r.max-r.min; return c;}
internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b) {Rng1S32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b) {Rng1S32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;}
internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b) {Rng1S32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b) {Rng1S32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;}
internal S32 clamp_1s32(Rng1S32 r, S32 v) {v = Clamp(r.min, v, r.max); return v;} internal S32 clamp_1s32(Rng1S32 r, S32 v) {v = Clamp(r.min, v, r.max); return v;}
@@ -432,7 +414,7 @@ internal Rng1U64 shift_1u64(Rng1U64 r, U64 x) {r.min += x; r.m
internal Rng1U64 pad_1u64(Rng1U64 r, U64 x) {r.min -= x; r.max += x; return r;} internal Rng1U64 pad_1u64(Rng1U64 r, U64 x) {r.min -= x; r.max += x; return r;}
internal U64 center_1u64(Rng1U64 r) {U64 c = (r.min+r.max)/2; return c;} internal U64 center_1u64(Rng1U64 r) {U64 c = (r.min+r.max)/2; return c;}
internal B32 contains_1u64(Rng1U64 r, U64 x) {B32 c = (r.min <= x && x < r.max); return c;} internal B32 contains_1u64(Rng1U64 r, U64 x) {B32 c = (r.min <= x && x < r.max); return c;}
internal U64 dim_1u64(Rng1U64 r) {U64 c = ((r.max > r.min) ? (r.max - r.min) : 0); return c;} internal U64 dim_1u64(Rng1U64 r) {U64 c = r.max-r.min; return c;}
internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b) {Rng1U64 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b) {Rng1U64 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;}
internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b) {Rng1U64 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b) {Rng1U64 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;}
internal U64 clamp_1u64(Rng1U64 r, U64 v) {v = Clamp(r.min, v, r.max); return v;} internal U64 clamp_1u64(Rng1U64 r, U64 v) {v = Clamp(r.min, v, r.max); return v;}
@@ -442,7 +424,7 @@ internal Rng1S64 shift_1s64(Rng1S64 r, S64 x) {r.min += x; r.m
internal Rng1S64 pad_1s64(Rng1S64 r, S64 x) {r.min -= x; r.max += x; return r;} internal Rng1S64 pad_1s64(Rng1S64 r, S64 x) {r.min -= x; r.max += x; return r;}
internal S64 center_1s64(Rng1S64 r) {S64 c = (r.min+r.max)/2; return c;} internal S64 center_1s64(Rng1S64 r) {S64 c = (r.min+r.max)/2; return c;}
internal B32 contains_1s64(Rng1S64 r, S64 x) {B32 c = (r.min <= x && x < r.max); return c;} internal B32 contains_1s64(Rng1S64 r, S64 x) {B32 c = (r.min <= x && x < r.max); return c;}
internal S64 dim_1s64(Rng1S64 r) {S64 c = ((r.max > r.min) ? (r.max - r.min) : 0); return c;} internal S64 dim_1s64(Rng1S64 r) {S64 c = r.max-r.min; return c;}
internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b) {Rng1S64 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b) {Rng1S64 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;}
internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b) {Rng1S64 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b) {Rng1S64 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;}
internal S64 clamp_1s64(Rng1S64 r, S64 v) {v = Clamp(r.min, v, r.max); return v;} internal S64 clamp_1s64(Rng1S64 r, S64 v) {v = Clamp(r.min, v, r.max); return v;}
@@ -452,7 +434,7 @@ internal Rng1F32 shift_1f32(Rng1F32 r, F32 x) {r.min += x; r.m
internal Rng1F32 pad_1f32(Rng1F32 r, F32 x) {r.min -= x; r.max += x; return r;} internal Rng1F32 pad_1f32(Rng1F32 r, F32 x) {r.min -= x; r.max += x; return r;}
internal F32 center_1f32(Rng1F32 r) {F32 c = (r.min+r.max)/2; return c;} internal F32 center_1f32(Rng1F32 r) {F32 c = (r.min+r.max)/2; return c;}
internal B32 contains_1f32(Rng1F32 r, F32 x) {B32 c = (r.min <= x && x < r.max); return c;} internal B32 contains_1f32(Rng1F32 r, F32 x) {B32 c = (r.min <= x && x < r.max); return c;}
internal F32 dim_1f32(Rng1F32 r) {F32 c = ((r.max > r.min) ? (r.max - r.min) : 0); return c;} internal F32 dim_1f32(Rng1F32 r) {F32 c = r.max-r.min; return c;}
internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b) {Rng1F32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;} internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b) {Rng1F32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;}
internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b) {Rng1F32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;} internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b) {Rng1F32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;}
internal F32 clamp_1f32(Rng1F32 r, F32 v) {v = Clamp(r.min, v, r.max); return v;} internal F32 clamp_1f32(Rng1F32 r, F32 v) {v = Clamp(r.min, v, r.max); return v;}
@@ -462,7 +444,7 @@ internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x) {r.min = add_2s1
internal Rng2S16 pad_2s16(Rng2S16 r, S16 x) {Vec2S16 xv = {x, x}; r.min = sub_2s16(r.min, xv); r.max = add_2s16(r.max, xv); return r;} internal Rng2S16 pad_2s16(Rng2S16 r, S16 x) {Vec2S16 xv = {x, x}; r.min = sub_2s16(r.min, xv); r.max = add_2s16(r.max, xv); return r;}
internal Vec2S16 center_2s16(Rng2S16 r) {Vec2S16 c = {(S16)((r.min.x+r.max.x)/2), (S16)((r.min.y+r.max.y)/2)}; return c;} internal Vec2S16 center_2s16(Rng2S16 r) {Vec2S16 c = {(S16)((r.min.x+r.max.x)/2), (S16)((r.min.y+r.max.y)/2)}; return c;}
internal B32 contains_2s16(Rng2S16 r, Vec2S16 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} internal B32 contains_2s16(Rng2S16 r, Vec2S16 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;}
internal Vec2S16 dim_2s16(Rng2S16 r) {Vec2S16 dim = {(S16)(((r.max.x > r.min.x) ? (r.max.x - r.min.x) : 0)), (S16)(((r.max.y > r.min.y) ? (r.max.y - r.min.y) : 0))}; return dim;} internal Vec2S16 dim_2s16(Rng2S16 r) {Vec2S16 dim = {(S16)(r.max.x-r.min.x), (S16)(r.max.y-r.min.y)}; return dim;}
internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b) {Rng2S16 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b) {Rng2S16 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;}
internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b) {Rng2S16 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b) {Rng2S16 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;}
internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;}
@@ -472,7 +454,7 @@ internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x) {r.min = add_2s3
internal Rng2S32 pad_2s32(Rng2S32 r, S32 x) {Vec2S32 xv = {x, x}; r.min = sub_2s32(r.min, xv); r.max = add_2s32(r.max, xv); return r;} internal Rng2S32 pad_2s32(Rng2S32 r, S32 x) {Vec2S32 xv = {x, x}; r.min = sub_2s32(r.min, xv); r.max = add_2s32(r.max, xv); return r;}
internal Vec2S32 center_2s32(Rng2S32 r) {Vec2S32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} internal Vec2S32 center_2s32(Rng2S32 r) {Vec2S32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;}
internal B32 contains_2s32(Rng2S32 r, Vec2S32 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} internal B32 contains_2s32(Rng2S32 r, Vec2S32 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;}
internal Vec2S32 dim_2s32(Rng2S32 r) {Vec2S32 dim = {((r.max.x > r.min.x) ? (r.max.x - r.min.x) : 0), ((r.max.y > r.min.y) ? (r.max.y - r.min.y) : 0)}; return dim;} internal Vec2S32 dim_2s32(Rng2S32 r) {Vec2S32 dim = {r.max.x-r.min.x, r.max.y-r.min.y}; return dim;}
internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b) {Rng2S32 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b) {Rng2S32 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;}
internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b) {Rng2S32 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b) {Rng2S32 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;}
internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;}
@@ -482,7 +464,7 @@ internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x) {r.min = add_2s6
internal Rng2S64 pad_2s64(Rng2S64 r, S64 x) {Vec2S64 xv = {x, x}; r.min = sub_2s64(r.min, xv); r.max = add_2s64(r.max, xv); return r;} internal Rng2S64 pad_2s64(Rng2S64 r, S64 x) {Vec2S64 xv = {x, x}; r.min = sub_2s64(r.min, xv); r.max = add_2s64(r.max, xv); return r;}
internal Vec2S64 center_2s64(Rng2S64 r) {Vec2S64 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} internal Vec2S64 center_2s64(Rng2S64 r) {Vec2S64 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;}
internal B32 contains_2s64(Rng2S64 r, Vec2S64 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} internal B32 contains_2s64(Rng2S64 r, Vec2S64 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;}
internal Vec2S64 dim_2s64(Rng2S64 r) {Vec2S64 dim = {((r.max.x > r.min.x) ? (r.max.x - r.min.x) : 0), ((r.max.y > r.min.y) ? (r.max.y - r.min.y) : 0)}; return dim;} internal Vec2S64 dim_2s64(Rng2S64 r) {Vec2S64 dim = {r.max.x-r.min.x, r.max.y-r.min.y}; return dim;}
internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b) {Rng2S64 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b) {Rng2S64 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;}
internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b) {Rng2S64 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b) {Rng2S64 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;}
internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;}
@@ -492,15 +474,13 @@ internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x) {r.min = add_2f3
internal Rng2F32 pad_2f32(Rng2F32 r, F32 x) {Vec2F32 xv = {x, x}; r.min = sub_2f32(r.min, xv); r.max = add_2f32(r.max, xv); return r;} internal Rng2F32 pad_2f32(Rng2F32 r, F32 x) {Vec2F32 xv = {x, x}; r.min = sub_2f32(r.min, xv); r.max = add_2f32(r.max, xv); return r;}
internal Vec2F32 center_2f32(Rng2F32 r) {Vec2F32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} internal Vec2F32 center_2f32(Rng2F32 r) {Vec2F32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;}
internal B32 contains_2f32(Rng2F32 r, Vec2F32 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;} internal B32 contains_2f32(Rng2F32 r, Vec2F32 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;}
internal Vec2F32 dim_2f32(Rng2F32 r) {Vec2F32 dim = {((r.max.x > r.min.x) ? (r.max.x - r.min.x) : 0), ((r.max.y > r.min.y) ? (r.max.y - r.min.y) : 0)}; return dim;} internal Vec2F32 dim_2f32(Rng2F32 r) {Vec2F32 dim = {r.max.x-r.min.x, r.max.y-r.min.y}; return dim;}
internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b) {Rng2F32 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;} internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b) {Rng2F32 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;}
internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b) {Rng2F32 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;} internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b) {Rng2F32 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;}
internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;} internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;}
//////////////////////////////// ////////////////////////////////
//~ rjf: Color Operations //~ rjf: Miscellaneous Ops
//- rjf: hsv <-> rgb
internal Vec3F32 internal Vec3F32
hsv_from_rgb(Vec3F32 rgb) hsv_from_rgb(Vec3F32 rgb)
@@ -587,102 +567,16 @@ rgba_from_hsva(Vec4F32 hsva)
return rgba; return rgba;
} }
//- rjf: srgb <-> linear
internal Vec3F32
linear_from_srgb(Vec3F32 srgb)
{
Vec3F32 result;
for EachElement(idx, srgb.v)
{
result.v[idx] = (srgb.v[idx] < 0.0404482362771082f ? srgb.v[idx] / 12.92f : pow_f32((srgb.v[idx] + 0.055f) / 1.055f, 2.4f));
}
return result;
}
internal Vec3F32
srgb_from_linear(Vec3F32 linear)
{
Vec3F32 result;
for EachElement(idx, linear.v)
{
result.v[idx] = (0 <= linear.v[idx] && linear.v[idx] < 0.00313066844250063) ? linear.v[idx]*12.92f : 1.05f * pow_f32(linear.v[idx], 1.f/2.4f) - 0.055f;
}
return result;
}
internal Vec4F32 internal Vec4F32
linear_from_srgba(Vec4F32 srgba) rgba_from_u32(U32 hex)
{ {
Vec4F32 result; Vec4F32 result = v4f32(((hex&0xff000000)>>24)/255.f,
result.xyz = linear_from_srgb(srgba.xyz); ((hex&0x00ff0000)>>16)/255.f,
result.w = srgba.w; ((hex&0x0000ff00)>> 8)/255.f,
((hex&0x000000ff)>> 0)/255.f);
return result; return result;
} }
internal Vec4F32
srgba_from_linear(Vec4F32 linear)
{
Vec4F32 result;
result.xyz = srgb_from_linear(linear.xyz);
result.w = linear.w;
return result;
}
//- rjf: oklab <-> linear
internal Vec3F32
oklab_from_linear(Vec3F32 linear)
{
F32 l = (0.4122214708f * linear.x + 0.5363325363f * linear.y + 0.0514459929f * linear.z);
F32 m = (0.2119034982f * linear.x + 0.6806995451f * linear.y + 0.1073969566f * linear.z);
F32 s = (0.0883024619f * linear.x + 0.2817188376f * linear.y + 0.6299787005f * linear.z);
F32 l_ = cbrt_f32(l);
F32 m_ = cbrt_f32(m);
F32 s_ = cbrt_f32(s);
Vec3F32 result;
result.x = 0.2104542553f*l_ + 0.7936177850f*m_ - 0.0040720468f*s_;
result.y = 1.9779984951f*l_ - 2.4285922050f*m_ + 0.4505937099f*s_;
result.z = 0.0259040371f*l_ + 0.7827717662f*m_ - 0.8086757660f*s_;
return result;
}
internal Vec3F32
linear_from_oklab(Vec3F32 oklab)
{
F32 l_ = oklab.x + 0.3963377774f * oklab.y + 0.2158037573f * oklab.z;
F32 m_ = oklab.x - 0.1055613458f * oklab.y - 0.0638541728f * oklab.z;
F32 s_ = oklab.x - 0.0894841775f * oklab.y - 1.2914855480f * oklab.z;
F32 l = l_*l_*l_;
F32 m = m_*m_*m_;
F32 s = s_*s_*s_;
Vec3F32 result;
result.x = +4.0767416621f * l - 3.3077115913f * m + 0.2309699292f * s;
result.y = -1.2684380046f * l + 2.6097574011f * m - 0.3413193965f * s;
result.z = -0.0041960863f * l - 0.7034186147f * m + 1.7076147010f * s;
return result;
}
internal Vec4F32
oklab_from_lineara(Vec4F32 lineara)
{
Vec4F32 result;
result.xyz = oklab_from_linear(lineara.xyz);
result.w = lineara.w;
return result;
}
internal Vec4F32
lineara_from_oklab(Vec4F32 oklab)
{
Vec4F32 result;
result.xyz = linear_from_oklab(oklab.xyz);
result.w = oklab.w;
return result;
}
//- rjf: rgba <-> u32
internal U32 internal U32
u32_from_rgba(Vec4F32 rgba) u32_from_rgba(Vec4F32 rgba)
{ {
@@ -694,100 +588,9 @@ u32_from_rgba(Vec4F32 rgba)
return result; return result;
} }
internal Vec4F32
rgba_from_u32(U32 hex)
{
Vec4F32 result = v4f32(((hex&0xff000000)>>24)/255.f,
((hex&0x00ff0000)>>16)/255.f,
((hex&0x0000ff00)>> 8)/255.f,
((hex&0x000000ff)>> 0)/255.f);
return result;
}
//////////////////////////////// ////////////////////////////////
//~ rjf: List Type Functions //~ rjf: List Type Functions
internal void
rng1u64_list_push_node(Rng1U64List *list, Rng1U64Node *n)
{
SLLQueuePush(list->first, list->last, n);
list->count += 1;
}
internal Rng1U64Node *
rng1u64_list_push(Arena *arena, Rng1U64List *list, Rng1U64 rng)
{
Rng1U64Node *n = push_array(arena, Rng1U64Node, 1);
MemoryCopyStruct(&n->v, &rng);
rng1u64_list_push_node(list, n);
return n;
}
internal void
rng1u64_list_concat(Rng1U64List *list, Rng1U64List *to_concat)
{
if(to_concat->first)
{
if(list->first)
{
list->last->next = to_concat->first;
list->last = to_concat->last;
}
else
{
list->first = to_concat->first;
list->last = to_concat->last;
}
MemoryZeroStruct(to_concat);
}
}
internal Rng1U64Array
rng1u64_array_from_list(Arena *arena, Rng1U64List *list)
{
Rng1U64Array arr = {0};
arr.count = list->count;
arr.v = push_array_no_zero(arena, Rng1U64, arr.count);
U64 idx = 0;
for(Rng1U64Node *n = list->first; n != 0; n = n->next)
{
arr.v[idx] = n->v;
idx += 1;
}
return arr;
}
internal U64
rng_1u64_array_bsearch(Rng1U64Array arr, U64 value)
{
if(arr.count > 0 && arr.v[0].min < value && value < arr.v[arr.count-1].max)
{
U64 l = 0;
U64 r = arr.count - 1;
for(; l <= r; )
{
U64 m = l + (r - l) / 2;
if(contains_1u64(arr.v[m], value))
{
return m;
}
else if(arr.v[m].min < value)
{
l = m + 1;
}
else
{
r = m - 1;
}
}
}
else if(arr.count == 1 && contains_1u64(arr.v[0], value))
{
return 0;
}
return max_U64;
}
internal void internal void
rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng) rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng)
{ {
@@ -811,20 +614,3 @@ rng1s64_array_from_list(Arena *arena, Rng1S64List *list)
} }
return arr; return arr;
} }
////////////////////////////////
//~ rjf: N -> M Element Subdivision
internal Rng1U64
m_range_from_n_idx_m_count(U64 n_idx, U64 n_count, U64 m_count)
{
U64 main_idxes_per_lane = m_count/n_count;
U64 leftover_idxes_count = m_count - main_idxes_per_lane*n_count;
U64 leftover_idxes_before_this_lane_count = Min(n_idx, leftover_idxes_count);
U64 lane_base_idx = n_idx*main_idxes_per_lane + leftover_idxes_before_this_lane_count;
U64 lane_base_idx__clamped = Min(lane_base_idx, m_count);
U64 lane_opl_idx = lane_base_idx__clamped + main_idxes_per_lane + ((n_idx < leftover_idxes_count) ? 1 : 0);
U64 lane_opl_idx__clamped = Min(lane_opl_idx, m_count);
Rng1U64 result = r1u64(lane_base_idx__clamped, lane_opl_idx__clamped);
return result;
}
+16 -62
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_MATH_H #ifndef BASE_MATH_H
@@ -327,29 +327,7 @@ union Rng2S64
}; };
//////////////////////////////// ////////////////////////////////
//~ rjf: Range List Types //~ rjf: List Types
typedef struct Rng1U64Node Rng1U64Node;
struct Rng1U64Node
{
Rng1U64Node *next;
Rng1U64 v;
};
typedef struct Rng1U64List Rng1U64List;
struct Rng1U64List
{
U64 count;
Rng1U64Node *first;
Rng1U64Node *last;
};
typedef struct Rng1U64Array Rng1U64Array;
struct Rng1U64Array
{
Rng1U64 *v;
U64 count;
};
typedef struct Rng1S64Node Rng1S64Node; typedef struct Rng1S64Node Rng1S64Node;
struct Rng1S64Node struct Rng1S64Node
@@ -374,20 +352,19 @@ struct Rng1S64Array
}; };
//////////////////////////////// ////////////////////////////////
//~ rjf: Scalar Math Ops //~ rjf: Scalar Ops
#define abs_s64(v) (S64)llabs(v) #define abs_s64(v) (S64)llabs(v)
#define sqrt_f32(v) sqrtf(v) #define sqrt_f32(v) sqrtf(v)
#define cbrt_f32(v) cbrtf(v)
#define mod_f32(a, b) fmodf((a), (b)) #define mod_f32(a, b) fmodf((a), (b))
#define pow_f32(b, e) powf((b), (e)) #define pow_f32(b, e) powf((b), (e))
#define ceil_f32(v) ceilf(v) #define ceil_f32(v) ceilf(v)
#define floor_f32(v) floorf(v) #define floor_f32(v) floorf(v)
#define round_f32(v) roundf(v) #define round_f32(v) roundf(v)
#define abs_f32(v) fabsf(v) #define abs_f32(v) fabsf(v)
#define radians_from_turns_f32(v) ((v)*(2*3.1415926535897f)) #define radians_from_turns_f32(v) ((v)*2*3.1415926535897f)
#define turns_from_radians_f32(v) ((v)/(2*3.1415926535897f)) #define turns_from_radians_f32(v) ((v)/2*3.1415926535897f)
#define degrees_from_turns_f32(v) ((v)*360.f) #define degrees_from_turns_f32(v) ((v)*360.f)
#define turns_from_degrees_f32(v) ((v)/360.f) #define turns_from_degrees_f32(v) ((v)/360.f)
#define degrees_from_radians_f32(v) (degrees_from_turns_f32(turns_from_radians_f32(v))) #define degrees_from_radians_f32(v) (degrees_from_turns_f32(turns_from_radians_f32(v)))
@@ -397,15 +374,14 @@ struct Rng1S64Array
#define tan_f32(v) tanf(radians_from_turns_f32(v)) #define tan_f32(v) tanf(radians_from_turns_f32(v))
#define sqrt_f64(v) sqrt(v) #define sqrt_f64(v) sqrt(v)
#define cbrt_f64(v) cbrt(v)
#define mod_f64(a, b) fmod((a), (b)) #define mod_f64(a, b) fmod((a), (b))
#define pow_f64(b, e) pow((b), (e)) #define pow_f64(b, e) pow((b), (e))
#define ceil_f64(v) ceil(v) #define ceil_f64(v) ceil(v)
#define floor_f64(v) floor(v) #define floor_f64(v) floor(v)
#define round_f64(v) round(v) #define round_f64(v) round(v)
#define abs_f64(v) fabs(v) #define abs_f64(v) fabs(v)
#define radians_from_turns_f64(v) ((v)*(2*3.1415926535897)) #define radians_from_turns_f64(v) ((v)*2*3.1415926535897)
#define turns_from_radians_f64(v) ((v)/(2*3.1415926535897)) #define turns_from_radians_f64(v) ((v)/2*3.1415926535897)
#define degrees_from_turns_f64(v) ((v)*360.0) #define degrees_from_turns_f64(v) ((v)*360.0)
#define turns_from_degrees_f64(v) ((v)/360.0) #define turns_from_degrees_f64(v) ((v)/360.0)
#define degrees_from_radians_f64(v) (degrees_from_turns_f64(turns_from_radians_f64(v))) #define degrees_from_radians_f64(v) (degrees_from_turns_f64(turns_from_radians_f64(v)))
@@ -443,6 +419,7 @@ internal Vec2S64 scale_2s64(Vec2S64 v, S64 s);
internal S64 dot_2s64(Vec2S64 a, Vec2S64 b); internal S64 dot_2s64(Vec2S64 a, Vec2S64 b);
internal S64 length_squared_2s64(Vec2S64 v); internal S64 length_squared_2s64(Vec2S64 v);
internal S64 length_2s64(Vec2S64 v); internal S64 length_2s64(Vec2S64 v);
internal Vec2S64 normalize_2s64(Vec2S64 v);
internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t); internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t);
#define v2s32(x, y) vec_2s32((x), (y)) #define v2s32(x, y) vec_2s32((x), (y))
@@ -455,6 +432,7 @@ internal Vec2S32 scale_2s32(Vec2S32 v, S32 s);
internal S32 dot_2s32(Vec2S32 a, Vec2S32 b); internal S32 dot_2s32(Vec2S32 a, Vec2S32 b);
internal S32 length_squared_2s32(Vec2S32 v); internal S32 length_squared_2s32(Vec2S32 v);
internal S32 length_2s32(Vec2S32 v); internal S32 length_2s32(Vec2S32 v);
internal Vec2S32 normalize_2s32(Vec2S32 v);
internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t); internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t);
#define v2s16(x, y) vec_2s16((x), (y)) #define v2s16(x, y) vec_2s16((x), (y))
@@ -467,6 +445,7 @@ internal Vec2S16 scale_2s16(Vec2S16 v, S16 s);
internal S16 dot_2s16(Vec2S16 a, Vec2S16 b); internal S16 dot_2s16(Vec2S16 a, Vec2S16 b);
internal S16 length_squared_2s16(Vec2S16 v); internal S16 length_squared_2s16(Vec2S16 v);
internal S16 length_2s16(Vec2S16 v); internal S16 length_2s16(Vec2S16 v);
internal Vec2S16 normalize_2s16(Vec2S16 v);
internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t); internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t);
#define v3f32(x, y, z) vec_3f32((x), (y), (z)) #define v3f32(x, y, z) vec_3f32((x), (y), (z))
@@ -482,7 +461,6 @@ internal F32 length_3f32(Vec3F32 v);
internal Vec3F32 normalize_3f32(Vec3F32 v); internal Vec3F32 normalize_3f32(Vec3F32 v);
internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t); internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t);
internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b); internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b);
internal Vec3F32 xform_3f32(Vec3F32 v, Mat3x3F32 m);
#define v3s32(x, y, z) vec_3s32((x), (y), (z)) #define v3s32(x, y, z) vec_3s32((x), (y), (z))
internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z); internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z);
@@ -494,6 +472,7 @@ internal Vec3S32 scale_3s32(Vec3S32 v, S32 s);
internal S32 dot_3s32(Vec3S32 a, Vec3S32 b); internal S32 dot_3s32(Vec3S32 a, Vec3S32 b);
internal S32 length_squared_3s32(Vec3S32 v); internal S32 length_squared_3s32(Vec3S32 v);
internal S32 length_3s32(Vec3S32 v); internal S32 length_3s32(Vec3S32 v);
internal Vec3S32 normalize_3s32(Vec3S32 v);
internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t); internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t);
internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b); internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b);
@@ -520,6 +499,7 @@ internal Vec4S32 scale_4s32(Vec4S32 v, S32 s);
internal S32 dot_4s32(Vec4S32 a, Vec4S32 b); internal S32 dot_4s32(Vec4S32 a, Vec4S32 b);
internal S32 length_squared_4s32(Vec4S32 v); internal S32 length_squared_4s32(Vec4S32 v);
internal S32 length_4s32(Vec4S32 v); internal S32 length_4s32(Vec4S32 v);
internal Vec4S32 normalize_4s32(Vec4S32 v);
internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t); internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t);
//////////////////////////////// ////////////////////////////////
@@ -541,7 +521,6 @@ internal Mat4x4F32 mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b);
internal Mat4x4F32 scale_4x4f32(Mat4x4F32 m, F32 scale); internal Mat4x4F32 scale_4x4f32(Mat4x4F32 m, F32 scale);
internal Mat4x4F32 inverse_4x4f32(Mat4x4F32 m); internal Mat4x4F32 inverse_4x4f32(Mat4x4F32 m);
internal Mat4x4F32 derotate_4x4f32(Mat4x4F32 mat); internal Mat4x4F32 derotate_4x4f32(Mat4x4F32 mat);
internal Mat4x4F32 transpose_4x4f32(Mat4x4F32 mat);
//////////////////////////////// ////////////////////////////////
//~ rjf: Range Ops //~ rjf: Range Ops
@@ -650,46 +629,21 @@ internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b);
internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v); internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v);
//////////////////////////////// ////////////////////////////////
//~ rjf: Color Operations //~ rjf: Miscellaneous Ops
//- rjf: hsv <-> rgb
internal Vec3F32 hsv_from_rgb(Vec3F32 rgb); internal Vec3F32 hsv_from_rgb(Vec3F32 rgb);
internal Vec3F32 rgb_from_hsv(Vec3F32 hsv); internal Vec3F32 rgb_from_hsv(Vec3F32 hsv);
internal Vec4F32 hsva_from_rgba(Vec4F32 rgba); internal Vec4F32 hsva_from_rgba(Vec4F32 rgba);
internal Vec4F32 rgba_from_hsva(Vec4F32 hsva); internal Vec4F32 rgba_from_hsva(Vec4F32 hsva);
//- rjf: srgb <-> linear
internal Vec3F32 linear_from_srgb(Vec3F32 srgb);
internal Vec3F32 srgb_from_linear(Vec3F32 linear);
internal Vec4F32 linear_from_srgba(Vec4F32 srgba);
internal Vec4F32 srgba_from_linear(Vec4F32 linear);
//- rjf: oklab <-> linear
internal Vec3F32 oklab_from_linear(Vec3F32 linear);
internal Vec3F32 linear_from_oklab(Vec3F32 oklab);
internal Vec4F32 oklab_from_lineara(Vec4F32 lineara);
internal Vec4F32 lineara_from_oklab(Vec4F32 oklab);
//- rjf: rgba <-> u32
internal U32 u32_from_rgba(Vec4F32 rgba);
internal Vec4F32 rgba_from_u32(U32 hex); internal Vec4F32 rgba_from_u32(U32 hex);
internal U32 u32_from_rgba(Vec4F32 rgba);
#define rgba_from_u32_lit_comp(h) { (((h)&0xff000000)>>24)/255.f, (((h)&0x00ff0000)>>16)/255.f, (((h)&0x0000ff00)>> 8)/255.f, (((h)&0x000000ff)>> 0)/255.f } #define rgba_from_u32_lit_comp(h) { (((h)&0xff000000)>>24)/255.f, (((h)&0x00ff0000)>>16)/255.f, (((h)&0x0000ff00)>> 8)/255.f, (((h)&0x000000ff)>> 0)/255.f }
//////////////////////////////// ////////////////////////////////
//~ rjf: List Type Functions //~ rjf: List Type Functions
internal void rng1u64_list_push_node(Rng1U64List *list, Rng1U64Node *n);
internal Rng1U64Node * rng1u64_list_push(Arena *arena, Rng1U64List *list, Rng1U64 rng);
internal void rng1u64_list_concat(Rng1U64List *list, Rng1U64List *to_concat);
internal Rng1U64Array rng1u64_array_from_list(Arena *arena, Rng1U64List *list);
internal U64 rng_1u64_array_bsearch(Rng1U64Array arr, U64 value);
internal void rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng); internal void rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng);
internal Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list); internal Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list);
//////////////////////////////// #endif // BASE_MATH_H
//~ rjf: N -> M Element Subdivision
internal Rng1U64 m_range_from_n_idx_m_count(U64 n_idx, U64 n_count, U64 m_count);
#endif //BASE_MATH_H
-421
View File
@@ -1,421 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Type Info Lookups
internal Member *
member_from_name(Type *type, String8 name)
{
Member *member = &member_nil;
if(type->members != 0 && name.size != 0)
{
for(U64 idx = 0; idx < type->count; idx += 1)
{
if(str8_match(type->members[idx].name, name, 0))
{
member = &type->members[idx];
break;
}
}
}
return member;
}
////////////////////////////////
//~ rjf: Type Info * Instance Operations
internal void
typed_data_rebase_ptrs(Type *type, String8 data, void *base_ptr)
{
Temp scratch = scratch_begin(0, 0);
typedef struct RebaseTypeTask RebaseTypeTask;
struct RebaseTypeTask
{
RebaseTypeTask *next;
Type *type;
U8 *ptr;
};
RebaseTypeTask start_task = {0, type, data.str};
RebaseTypeTask *first_task = &start_task;
RebaseTypeTask *last_task = first_task;
for(RebaseTypeTask *t = first_task; t != 0; t = t->next)
{
switch(t->type->kind)
{
default:{}break;
case TypeKind_Ptr:
{
*(U64 *)t->ptr = ((U64)(*(U8 **)t->ptr - (U8 *)base_ptr));
}break;
case TypeKind_Array:
{
for(U64 idx = 0; idx < t->type->count; idx += 1)
{
RebaseTypeTask *task = push_array(scratch.arena, RebaseTypeTask, 1);
task->type = t->type->direct;
task->ptr = t->ptr + t->type->direct->size * idx;
SLLQueuePush(first_task, last_task, task);
}
}break;
case TypeKind_Struct:
{
for(U64 idx = 0; idx < t->type->count; idx += 1)
{
Member *member = &t->type->members[idx];
RebaseTypeTask *task = push_array(scratch.arena, RebaseTypeTask, 1);
task->type = member->type;
task->ptr = t->ptr + member->value;
SLLQueuePush(first_task, last_task, task);
}
}break;
}
}
scratch_end(scratch);
}
internal String8
serialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params)
{
Temp scratch = scratch_begin(&arena, 1);
String8List strings = {0};
str8_serial_begin(scratch.arena, &strings);
{
typedef struct SerializeTypeTask SerializeTypeTask;
struct SerializeTypeTask
{
SerializeTypeTask *next;
Type *type;
U64 count;
U8 *src;
Type *containing_type;
U8 *containing_ptr;
B32 is_post_header;
};
SerializeTypeTask start_task = {0, type, 1, data.str};
SerializeTypeTask *first_task = &start_task;
SerializeTypeTask *last_task = first_task;
for(SerializeTypeTask *t = first_task; t != 0; t = t->next)
{
switch(t->type->kind)
{
//- rjf: leaf -> just copy the data directly
default:
if(TypeKind_FirstLeaf <= t->type->kind && t->type->kind <= TypeKind_LastLeaf)
{
str8_serial_push_string(scratch.arena, &strings, str8(t->src, t->type->size*t->count));
}break;
//- rjf: pointers -> try to interpret/understand pointer & read/write, otherwise just write as plain data
case TypeKind_Ptr:
{
// rjf: unpack info about this pointer
TypeSerializePtrRefInfo *ptr_ref_info = 0;
for(U64 idx = 0; idx < params->ptr_ref_infos_count; idx += 1)
{
if(params->ptr_ref_infos[idx].type == t->type->direct)
{
ptr_ref_info = &params->ptr_ref_infos[idx];
break;
}
}
// rjf: indexification -> subtract base, divide direct size, write index
if(ptr_ref_info != 0 && ptr_ref_info->indexify_base != 0)
{
U64 ptr_value = 0;
MemoryCopy(&ptr_value, t->src, sizeof(ptr_value));
U64 ptr_write_value = ((U64)((U8 *)ptr_value - (U8 *)ptr_ref_info->indexify_base)/t->type->direct->size);
str8_serial_push_struct(scratch.arena, &strings, &ptr_write_value);
}
// rjf: offsetification -> subtract base, write offsets
else if(ptr_ref_info != 0 && ptr_ref_info->offsetify_base != 0)
{
U64 ptr_value = 0;
MemoryCopy(&ptr_value, t->src, sizeof(ptr_value));
U64 ptr_write_value = (U64)((U8 *)ptr_value - (U8 *)ptr_ref_info->offsetify_base);
str8_serial_push_struct(scratch.arena, &strings, &ptr_write_value);
}
// rjf: size-by-member (pre-header): still potentially dependent on other members which
// delimit our size, so push a new post-header task for pointer.
else if(t->type->count_delimiter_name.size != 0 && !t->is_post_header)
{
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
task->type = t->type;
task->count = t->count;
task->src = t->src;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
task->is_post_header = 1;
SLLQueuePush(first_task, last_task, task);
}
// rjf: size-by-member (post-header): all flat parts of containing struct have been
// iterated, so now we can read the size, & descend to new task to read pointer
// destination contents
else if(t->type->count_delimiter_name.size != 0 && t->is_post_header)
{
// rjf: determine count of this pointer
U64 count = 0;
{
Member *count_member = member_from_name(t->containing_type, t->type->count_delimiter_name);
MemoryCopy(&count, t->containing_ptr + count_member->value, count_member->type->size);
}
// rjf: push task
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
task->type = t->type->direct;
task->count = count;
task->src = *(void **)t->src;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}
// rjf: catch-all: write pointer value
else
{
str8_serial_push_string(scratch.arena, &strings, str8(t->src, t->type->size*t->count));
}
}break;
//- rjf: arrays -> descend to underlying type, + count
case TypeKind_Array:
{
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
task->type = t->type->direct;
task->count = t->type->count;
task->src = t->src;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}break;
//- rjf: struct -> descend to members
case TypeKind_Struct:
{
U64 off = 0;
for(U64 idx = 0; idx < t->count; idx += 1)
{
for(U64 member_idx = 0; member_idx < t->type->count; member_idx += 1)
{
if(t->type->members[member_idx].flags & MemberFlag_DoNotSerialize)
{
continue;
}
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
task->type = t->type->members[member_idx].type;
task->count = 1;
task->src = t->src + idx*t->type->size + t->type->members[member_idx].value;
task->containing_type = t->type;
task->containing_ptr = t->src;
SLLQueuePush(first_task, last_task, task);
}
}
}break;
//- rjf: enum -> descend to basic type interpretation
case TypeKind_Enum:
{
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
task->type = t->type->direct;
task->count = t->count;
task->src = t->src;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}break;
}
}
}
String8 result = str8_serial_end(arena, &strings);
scratch_end(scratch);
return result;
}
internal String8
deserialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params)
{
String8 result = {0};
result.size = type->size;
result.str = push_array(arena, U8, result.size);
{
Temp scratch = scratch_begin(&arena, 1);
typedef struct DeserializeTypeTask DeserializeTypeTask;
struct DeserializeTypeTask
{
DeserializeTypeTask *next;
Type *type;
U64 count;
U8 *dst;
Type *containing_type;
U8 *containing_ptr;
B32 is_post_header;
};
U64 read_off = 0;
DeserializeTypeTask start_task = {0, type, 1, result.str};
DeserializeTypeTask *first_task = &start_task;
DeserializeTypeTask *last_task = first_task;
for(DeserializeTypeTask *t = first_task; t != 0; t = t->next)
{
U8 *t_src = data.str + read_off;
switch(t->type->kind)
{
//- rjf: leaf -> copy the data directly
default:
if(TypeKind_FirstLeaf <= t->type->kind && t->type->kind <= TypeKind_LastLeaf)
{
MemoryCopy(t->dst, t_src, t->type->size*t->count);
read_off += t->type->size*t->count;
}break;
//- rjf: pointers -> try to interpret/understand pointer & read/write, otherwise skip
case TypeKind_Ptr:
{
// rjf: unpack info about this pointer
TypeSerializePtrRefInfo *ptr_ref_info = 0;
for(U64 idx = 0; idx < params->ptr_ref_infos_count; idx += 1)
{
if(params->ptr_ref_infos[idx].type == t->type->direct)
{
ptr_ref_info = &params->ptr_ref_infos[idx];
break;
}
}
// rjf: indexification -> add base, multiply direct size
if(ptr_ref_info != 0 && ptr_ref_info->indexify_base != 0)
{
U64 ptr_value = 0;
MemoryCopy(&ptr_value, t_src, sizeof(ptr_value));
U64 ptr_write_value = (ptr_value + (U64)ptr_ref_info->indexify_base) * t->type->direct->size;
MemoryCopy(t->dst, &ptr_write_value, sizeof(ptr_write_value));
read_off += sizeof(ptr_value);
}
// rjf: offsetification -> subtract base, write offsets
else if(ptr_ref_info != 0 && ptr_ref_info->offsetify_base != 0)
{
U64 ptr_value = 0;
MemoryCopy(&ptr_value, t_src, sizeof(ptr_value));
U64 ptr_write_value = ptr_value + (U64)ptr_ref_info->offsetify_base;
MemoryCopy(t->dst, &ptr_write_value, sizeof(ptr_write_value));
read_off += sizeof(ptr_value);
}
// rjf: size-by-member (pre-header): still potentially dependent on other members which
// delimit our size, so push a new post-header task for pointer.
else if(t->type->count_delimiter_name.size != 0 && !t->is_post_header)
{
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
task->type = t->type;
task->count = t->count;
task->dst = t->dst;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
task->is_post_header = 1;
SLLQueuePush(first_task, last_task, task);
}
// rjf: size-by-member (post-header): all flat parts of containing struct have been
// iterated, so now we can read the size, & descend to new task to read pointer
// destination contents
else if(t->type->count_delimiter_name.size != 0 && t->is_post_header)
{
// rjf: determine count of this pointer
U64 count = 0;
{
Member *count_member = member_from_name(t->containing_type, t->type->count_delimiter_name);
MemoryCopy(&count, t->containing_ptr + count_member->value, count_member->type->size);
}
// rjf: allocate buffer for pointer destination; write address into pointer value slot
U64 ptr_dest_buffer_size = (count+1)*t->type->direct->size;
U8 *ptr_dest_buffer = push_array(arena, U8, ptr_dest_buffer_size);
MemoryCopy(t->dst, &ptr_dest_buffer, sizeof(ptr_dest_buffer));
// rjf: push task
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
task->type = t->type->direct;
task->count = count;
task->dst = ptr_dest_buffer;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}
// rjf: catch-all: read pointer value
else
{
MemoryCopy(t->dst, t_src, t->type->size*t->count);
read_off += t->type->size*t->count;
}
}break;
//- rjf: arrays -> descend to underlying type, + count
case TypeKind_Array:
{
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
task->type = t->type->direct;
task->count = t->type->count;
task->dst = t->dst;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}break;
//- rjf: struct -> descend to members
case TypeKind_Struct:
{
for(U64 idx = 0; idx < t->count; idx += 1)
{
for(U64 member_idx = 0; member_idx < t->type->count; member_idx += 1)
{
if(t->type->members[member_idx].flags & MemberFlag_DoNotSerialize)
{
continue;
}
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
task->type = t->type->members[member_idx].type;
task->count = 1;
task->dst = t->dst + idx*t->type->size + t->type->members[member_idx].value;
task->containing_type = t->type;
task->containing_ptr = t->dst;
SLLQueuePush(first_task, last_task, task);
}
}
}break;
//- rjf: enum -> descend to basic type interpretation
case TypeKind_Enum:
{
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
task->type = t->type->direct;
task->count = t->count;
task->dst = t->dst;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}break;
}
}
if(params->advance_out != 0)
{
params->advance_out[0] = read_off;
}
scratch_end(scratch);
}
return result;
}
internal String8
deep_copy_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params)
{
Temp scratch = scratch_begin(&arena, 1);
String8 data_srlz = serialized_from_typed_data(scratch.arena, type, data, params);
String8 data_copy = deserialized_from_typed_data(arena, type, data_srlz, params);
scratch_end(scratch);
return data_copy;
}
-297
View File
@@ -1,297 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_META_H
#define BASE_META_H
////////////////////////////////
//~ rjf: Meta Markup Features
#define EmbedFile(name, path)
#define TweakB32(name, default) (TWEAK_##name)
#define TweakF32(name, default, min, max) (TWEAK_##name)
////////////////////////////////
//~ rjf: Tweak Info Tables
typedef struct TweakB32Info TweakB32Info;
struct TweakB32Info
{
String8 name;
B32 default_value;
B32 *value_ptr;
};
typedef struct TweakF32Info TweakF32Info;
struct TweakF32Info
{
String8 name;
F32 default_value;
Rng1F32 value_range;
F32 *value_ptr;
};
typedef struct TweakB32InfoTable TweakB32InfoTable;
struct TweakB32InfoTable
{
TweakB32Info *v;
U64 count;
};
typedef struct TweakF32InfoTable TweakF32InfoTable;
struct TweakF32InfoTable
{
TweakF32Info *v;
U64 count;
};
typedef struct EmbedInfo EmbedInfo;
struct EmbedInfo
{
String8 name;
String8 *data;
U128 *hash;
};
typedef struct EmbedInfoTable EmbedInfoTable;
struct EmbedInfoTable
{
EmbedInfo *v;
U64 count;
};
////////////////////////////////
//~ rjf: Type Info Types
typedef enum TypeKind
{
TypeKind_Null,
// rjf: leaves
TypeKind_Void, TypeKind_FirstLeaf = TypeKind_Void,
TypeKind_U8,
TypeKind_U16,
TypeKind_U32,
TypeKind_U64,
TypeKind_S8,
TypeKind_S16,
TypeKind_S32,
TypeKind_S64,
TypeKind_B8,
TypeKind_B16,
TypeKind_B32,
TypeKind_B64,
TypeKind_F32,
TypeKind_F64, TypeKind_LastLeaf = TypeKind_F64,
// rjf: operators
TypeKind_Ptr,
TypeKind_Array,
// rjf: user-defined-types
TypeKind_Struct,
TypeKind_Union,
TypeKind_Enum,
TypeKind_COUNT
}
TypeKind;
typedef U32 TypeFlags;
enum
{
TypeFlag_IsPlainText = (1<<0),
TypeFlag_IsCodeText = (1<<1),
TypeFlag_IsPathText = (1<<2),
};
typedef U32 MemberFlags;
enum
{
MemberFlag_DoNotSerialize = (1<<0),
};
typedef struct Type Type;
typedef struct Member Member;
struct Member
{
String8 name;
String8 pretty_name;
Type *type;
U64 value;
MemberFlags flags;
};
typedef struct Type Type;
struct Type
{
TypeKind kind;
TypeFlags flags;
U64 size;
Type *direct;
String8 name;
String8 count_delimiter_name; // gathered from surrounding members, turns *->[1] into *->[N]
U64 count;
Member *members;
};
////////////////////////////////
//~ rjf: Type Serialization Parameters
typedef struct TypeSerializePtrRefInfo TypeSerializePtrRefInfo;
struct TypeSerializePtrRefInfo
{
Type *type; // pointers to this
void *indexify_base; // can be indexified using this
void *offsetify_base; // can be offsetified using this
void *nil_ptr; // is terminal if matching 0 or this
};
typedef struct TypeSerializeParams TypeSerializeParams;
struct TypeSerializeParams
{
U64 *advance_out;
TypeSerializePtrRefInfo *ptr_ref_infos;
U64 ptr_ref_infos_count;
};
////////////////////////////////
//~ rjf: Type Name -> Type Info
#define type(T) (&T##__type)
////////////////////////////////
//~ rjf: Type Info Table Initializer Helpers
#define member_lit_comp(S, ti, m, ...) {str8_lit_comp(#m), {0}, (ti), OffsetOf(S, m), __VA_ARGS__}
#define struct_members(S) read_only global Member S##__members[] =
#define struct_type(S, ...) read_only global Type S##__type = {TypeKind_Struct, 0, sizeof(S), &type_nil, str8_lit_comp(#S), {0}, ArrayCount(S##__members), S##__members, __VA_ARGS__}
#define named_struct_type(name, S, ...) read_only global Type name##__type = {TypeKind_Struct, 0, sizeof(S), &type_nil, str8_lit_comp(#name), {0}, ArrayCount(name##__members), name##__members, __VA_ARGS__}
#define ptr_type(name, ti, ...) read_only global Type name = {TypeKind_Ptr, 0, sizeof(void *), (ti), __VA_ARGS__}
////////////////////////////////
//~ rjf: Globals
read_only global Type type_nil = {TypeKind_Null, 0, 0, &type_nil};
read_only global Member member_nil = {{0}, {0}, &type_nil};
////////////////////////////////
//~ rjf: Built-In Types
//- rjf: leaves
read_only global Type void__type = {TypeKind_Void, 0, 0, &type_nil, str8_lit_comp("void")};
read_only global Type U8__type = {TypeKind_U8, 0, sizeof(U8), &type_nil, str8_lit_comp("U8")};
read_only global Type U16__type = {TypeKind_U16, 0, sizeof(U16), &type_nil, str8_lit_comp("U16")};
read_only global Type U32__type = {TypeKind_U32, 0, sizeof(U32), &type_nil, str8_lit_comp("U32")};
read_only global Type U64__type = {TypeKind_U64, 0, sizeof(U64), &type_nil, str8_lit_comp("U64")};
read_only global Type S8__type = {TypeKind_S8, 0, sizeof(S8), &type_nil, str8_lit_comp("S8")};
read_only global Type S16__type = {TypeKind_S16, 0, sizeof(S16), &type_nil, str8_lit_comp("S16")};
read_only global Type S32__type = {TypeKind_S32, 0, sizeof(S32), &type_nil, str8_lit_comp("S32")};
read_only global Type S64__type = {TypeKind_S64, 0, sizeof(S64), &type_nil, str8_lit_comp("S64")};
read_only global Type B8__type = {TypeKind_B8, 0, sizeof(B8), &type_nil, str8_lit_comp("B8")};
read_only global Type B16__type = {TypeKind_B16, 0, sizeof(B16), &type_nil, str8_lit_comp("B16")};
read_only global Type B32__type = {TypeKind_B32, 0, sizeof(B32), &type_nil, str8_lit_comp("B32")};
read_only global Type B64__type = {TypeKind_B64, 0, sizeof(B64), &type_nil, str8_lit_comp("B64")};
read_only global Type F32__type = {TypeKind_F32, 0, sizeof(F32), &type_nil, str8_lit_comp("F32")};
read_only global Type F64__type = {TypeKind_F64, 0, sizeof(F64), &type_nil, str8_lit_comp("F64")};
read_only global Type *type_kind_type_table[] =
{
&type_nil,
type(void),
type(U8),
type(U16),
type(U32),
type(U64),
type(S8),
type(S16),
type(S32),
type(S64),
type(B8),
type(B16),
type(B32),
type(B64),
type(F32),
type(F64),
&type_nil,
&type_nil,
&type_nil,
&type_nil,
&type_nil,
};
//- rjf: Rng1U64
struct_members(Rng1U64)
{
member_lit_comp(Rng1U64, type(U64), min),
member_lit_comp(Rng1U64, type(U64), max),
};
struct_type(Rng1U64);
//- rjf: String8
ptr_type(String8__str_ptr_type, type(U8), str8_lit_comp("size"));
struct_members(String8)
{
member_lit_comp(String8, &String8__str_ptr_type, str),
member_lit_comp(String8, type(U64), size),
};
struct_type(String8);
//- rjf: String8Node
extern Type String8Node__type;
Type String8Node__ptr_type = {TypeKind_Ptr, 0, sizeof(void *), &String8Node__type};
Member String8Node__members[] =
{
{str8_lit_comp("next"), {0}, &String8Node__ptr_type, OffsetOf(String8Node, next)},
{str8_lit_comp("string"), {0}, type(String8), OffsetOf(String8Node, string)},
};
Type String8Node__type =
{
TypeKind_Struct,
0,
sizeof(String8Node),
&type_nil,
str8_lit_comp("String8Node"),
{0},
ArrayCount(String8Node__members),
String8Node__members,
};
//- rjf: String8List
Member String8List__members[] =
{
{str8_lit_comp("first"), {0}, &String8Node__ptr_type, OffsetOf(String8List, first)},
{str8_lit_comp("last"), {0}, &String8Node__ptr_type, OffsetOf(String8List, last), MemberFlag_DoNotSerialize},
{str8_lit_comp("node_count"), {0}, type(U64), OffsetOf(String8List, node_count)},
{str8_lit_comp("total_size"), {0}, type(U64), OffsetOf(String8List, total_size)},
};
Type String8List__type =
{
TypeKind_Struct,
0,
sizeof(String8List),
&type_nil,
str8_lit_comp("String8List"),
{0},
ArrayCount(String8List__members),
String8List__members,
};
////////////////////////////////
//~ rjf: Type Info Lookups
internal Member *member_from_name(Type *type, String8 name);
#define EachMember(T, it) (Member *it = (type(T))->members; it != 0 && it < (type(T))->members + (type(T))->count; it += 1)
////////////////////////////////
//~ rjf: Type Info * Instance Operations
internal void typed_data_rebase_ptrs(Type *type, String8 data, void *base_ptr);
internal String8 serialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params);
internal String8 deserialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params);
internal String8 deep_copy_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params);
#define struct_rebase_ptrs(T, ptr, base) typed_data_rebase_ptrs(type(T), str8_struct(ptr), (base))
#define serialized_from_struct(arena, T, ptr, ...) serialized_from_typed_data((arena), type(T), str8_struct(ptr), &(TypeSerializeParams){.ptr_ref_infos = 0, __VA_ARGS__})
#define struct_from_serialized(arena, T, string, ...) (T *)deserialized_from_typed_data((arena), type(T), (string), &(TypeSerializeParams){.ptr_ref_infos = 0, __VA_ARGS__}).str
#define deep_copy_from_struct(arena, T, ptr, ...) (T *)deep_copy_from_typed_data((arena), type(T), str8_struct(ptr), &(TypeSerializeParams){.ptr_ref_infos = 0, __VA_ARGS__}).str
#endif // BASE_META_H
+1 -30
View File
@@ -1,31 +1,2 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#if PROFILE_SPALL
internal inline void
spall_begin(char *fmt, ...)
{
if(spall_buffer.data == 0)
{
spall_buffer.length = MB(1);
spall_buffer.data = os_reserve(spall_buffer.length);
os_commit(spall_buffer.data, spall_buffer.length);
spall_buffer_init(&spall_profile, &spall_buffer);
}
if(spall_pid == 0)
{
spall_pid = os_get_process_info()->pid;
}
if(spall_tid == 0)
{
spall_tid = os_tid();
}
Temp scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 string = push_str8fv(scratch.arena, fmt, args);
spall_buffer_begin_ex(&spall_profile, &spall_buffer, string.str, string.size, os_now_microseconds(), spall_tid, spall_pid);
va_end(args);
scratch_end(scratch);
}
#endif
+19 -71
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_PROFILE_H #ifndef BASE_PROFILE_H
@@ -10,8 +10,9 @@
#if !defined(PROFILE_TELEMETRY) #if !defined(PROFILE_TELEMETRY)
# define PROFILE_TELEMETRY 0 # define PROFILE_TELEMETRY 0
#endif #endif
#if !defined(PROFILE_SPALL)
# define PROFILE_SPALL 0 #if !defined(MARKUP_LAYER_COLOR)
# define MARKUP_LAYER_COLOR 1.00f, 0.00f, 1.00f
#endif #endif
//////////////////////////////// ////////////////////////////////
@@ -20,11 +21,8 @@
#if PROFILE_TELEMETRY #if PROFILE_TELEMETRY
# include "rad_tm.h" # include "rad_tm.h"
# if OS_WINDOWS # if OS_WINDOWS
# pragma comment(lib, "ws2_32.lib")
# pragma comment(lib, "rad_tm_win64.lib") # pragma comment(lib, "rad_tm_win64.lib")
# endif # endif
#elif PROFILE_SPALL
# include "spall.h"
#endif #endif
//////////////////////////////// ////////////////////////////////
@@ -44,77 +42,27 @@
# define ProfEndLockWait(...) tmEndWaitForLock(0) # define ProfEndLockWait(...) tmEndWaitForLock(0)
# define ProfLockTake(...) tmAcquiredLock(0, 0, __VA_ARGS__) # define ProfLockTake(...) tmAcquiredLock(0, 0, __VA_ARGS__)
# define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__) # define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__)
# define ProfColor(color) tmZoneColor((((color) & 0xff000000) >> 24) / 255.f, (((color) & 0x00ff0000) >> 16) / 255.f, (((color) & 0x0000ff00) >> 8) / 255.f) # define ProfColor(color) tmZoneColorSticky(color)
# define ProfBeginV(...) \
if (TM_API_PTR) { \
static tm_uint64 file_id = 0; TM_API_PTR->_tmStaticString(&file_id, __FILE__); \
Temp scratch = scratch_begin(0,0); \
String8 string = push_str8f(scratch.arena, __VA_ARGS__); \
tm_uint64 hash = TM_API_PTR->_tmHash((char*)string.str, string.size); \
hash = TM_API_PTR->_tmSendDynamicString(hash, (char*)string.str); \
TM_API_PTR->_tmEnterZoneFast_Core(0, 0, file_id, __LINE__, hash); \
scratch_end(scratch); \
}
# define ProfNoteV(...) \
if (TM_API_PTR) { \
static tm_uint64 file_id = 0; TM_API_PTR->_tmStaticString(&file_id, __FILE__); \
Temp scratch = scratch_begin(0,0); \
String8 string = push_str8f(scratch.arena, __VA_ARGS__); \
tm_uint64 hash = TM_API_PTR->_tmHash((char*)string.str, string.size); \
hash = TM_API_PTR->_tmSendDynamicString(hash, (char*)string.str); \
TM_API_PTR->_tmMessageFast_Core(0, TMMF_ICON_NOTE, file_id, __LINE__, hash); \
scratch_end(scratch); \
}
#endif
////////////////////////////////
//~ rjf: Spall Profile Defines
#if PROFILE_SPALL
global U64 spall_capturing = 0;
global SpallProfile spall_profile = {0};
thread_static SpallBuffer spall_buffer = {0};
thread_static U32 spall_tid = 0;
thread_static U32 spall_pid = 0;
internal inline void spall_begin(char *fmt, ...);
# define ProfBegin(...) (spall_capturing ? (spall_begin(__VA_ARGS__), 0) : 0)
# define ProfBeginDynamic(...) (spall_capturing ? (spall_begin(__VA_ARGS__), 0) : 0)
# define ProfEnd(...) (spall_capturing ? (spall_buffer_end_ex(&spall_profile, &spall_buffer, os_now_microseconds(), spall_tid, spall_pid)), 0 : 0)
# define ProfTick(...)
# define ProfIsCapturing(...) (!!spall_capturing)
# define ProfBeginCapture(...) (spall_capturing = 1)
# define ProfEndCapture(...) (spall_capturing = 0, spall_flush(&spall_profile))
# define ProfThreadName(...)
# define ProfMsg(...)
# define ProfBeginLockWait(...)
# define ProfEndLockWait(...)
# define ProfLockTake(...)
# define ProfLockDrop(...)
# define ProfColor(color)
# define ProfBeginV(...)
# define ProfNoteV(...)
#endif #endif
//////////////////////////////// ////////////////////////////////
//~ rjf: Zeroify Undefined Defines //~ rjf: Zeroify Undefined Defines
#if !defined(ProfBegin) #if !defined(ProfBegin)
# define ProfBegin(...) (0) # define ProfBegin(...) (0)
# define ProfBeginDynamic(...) (0) # define ProfBeginDynamic(...) (0)
# define ProfEnd(...) (0) # define ProfEnd(...) (0)
# define ProfTick(...) (0) # define ProfTick(...) (0)
# define ProfIsCapturing(...) (0) # define ProfIsCapturing(...) (0)
# define ProfBeginCapture(...) (0) # define ProfBeginCapture(...) (0)
# define ProfEndCapture(...) (0) # define ProfEndCapture(...) (0)
# define ProfThreadName(...) (0) # define ProfThreadName(...) (0)
# define ProfMsg(...) (0) # define ProfMsg(...) (0)
# define ProfBeginLockWait(...) (0) # define ProfBeginLockWait(...) (0)
# define ProfEndLockWait(...) (0) # define ProfEndLockWait(...) (0)
# define ProfLockTake(...) (0) # define ProfLockTake(...) (0)
# define ProfLockDrop(...) (0) # define ProfLockDrop(...) (0)
# define ProfColor(...) (0) # define ProfColor(...) (0)
# define ProfBeginV(...) (0)
# define ProfNoteV(...) (0)
#endif #endif
//////////////////////////////// ////////////////////////////////
+501 -1488
View File
File diff suppressed because it is too large Load Diff
+45 -124
View File
@@ -1,9 +1,15 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_STRINGS_H #ifndef BASE_STRINGS_H
#define BASE_STRINGS_H #define BASE_STRINGS_H
////////////////////////////////
//~ rjf: Third Party Includes
#define STB_SPRINTF_DECORATE(name) raddbg_##name
#include "third_party/stb/stb_sprintf.h"
//////////////////////////////// ////////////////////////////////
//~ rjf: String Types //~ rjf: String Types
@@ -38,6 +44,13 @@ struct String8Node
String8 string; String8 string;
}; };
typedef struct String8MetaNode String8MetaNode;
struct String8MetaNode
{
String8MetaNode *next;
String8Node *node;
};
typedef struct String8List String8List; typedef struct String8List String8List;
struct String8List struct String8List
{ {
@@ -50,9 +63,8 @@ struct String8List
typedef struct String8Array String8Array; typedef struct String8Array String8Array;
struct String8Array struct String8Array
{ {
String8 *v; String8 *strings;
U64 count; U64 count;
U64 total_size;
}; };
//////////////////////////////// ////////////////////////////////
@@ -74,7 +86,6 @@ enum
typedef enum PathStyle typedef enum PathStyle
{ {
PathStyle_Null,
PathStyle_Relative, PathStyle_Relative,
PathStyle_WindowsAbsolute, PathStyle_WindowsAbsolute,
PathStyle_UnixAbsolute, PathStyle_UnixAbsolute,
@@ -84,7 +95,7 @@ typedef enum PathStyle
#elif OS_LINUX #elif OS_LINUX
PathStyle_SystemAbsolute = PathStyle_UnixAbsolute PathStyle_SystemAbsolute = PathStyle_UnixAbsolute
#else #else
# error Absolute path style is undefined for this OS. # error "absolute path style is undefined for this OS"
#endif #endif
} }
PathStyle; PathStyle;
@@ -146,9 +157,9 @@ internal B32 char_is_lower(U8 c);
internal B32 char_is_alpha(U8 c); internal B32 char_is_alpha(U8 c);
internal B32 char_is_slash(U8 c); internal B32 char_is_slash(U8 c);
internal B32 char_is_digit(U8 c, U32 base); internal B32 char_is_digit(U8 c, U32 base);
internal U8 lower_from_char(U8 c); internal U8 char_to_lower(U8 c);
internal U8 upper_from_char(U8 c); internal U8 char_to_upper(U8 c);
internal U8 correct_slash_from_char(U8 c); internal U8 char_to_correct_slash(U8 c);
//////////////////////////////// ////////////////////////////////
//~ rjf: C-String Measurement //~ rjf: C-String Measurement
@@ -181,8 +192,6 @@ internal String8 str8_cstring(char *c);
internal String16 str16_cstring(U16 *c); internal String16 str16_cstring(U16 *c);
internal String32 str32_cstring(U32 *c); internal String32 str32_cstring(U32 *c);
internal String8 str8_cstring_capped(void *cstr, void *cap); internal String8 str8_cstring_capped(void *cstr, void *cap);
internal String16 str16_cstring_capped(void *cstr, void *cap);
internal String8 str8_cstring_capped_reverse(void *raw_start, void *raw_cap);
//////////////////////////////// ////////////////////////////////
//~ rjf: String Stylization //~ rjf: String Stylization
@@ -194,13 +203,9 @@ internal String8 backslashed_from_str8(Arena *arena, String8 string);
//////////////////////////////// ////////////////////////////////
//~ rjf: String Matching //~ rjf: String Matching
#define str8_match_lit(a_lit, b, flags) str8_match(str8_lit(a_lit), (b), (flags))
#define str8_match_cstr(a_cstr, b, flags) str8_match(str8_cstring(a_cstr), (b), (flags))
internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags); internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags);
internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags); internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
internal U64 str8_find_needle_reverse(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags); internal B32 str8_ends_with(String8 string, String8 end, StringMatchFlags flags);
internal B32 str8_is_before(String8 a, String8 b);
#define str8_ends_with(string, end, flags) str8_match(str8_postfix((string), (end).size), (end), (flags))
//////////////////////////////// ////////////////////////////////
//~ rjf: String Slicing //~ rjf: String Slicing
@@ -211,21 +216,14 @@ internal String8 str8_skip(String8 str, U64 amt);
internal String8 str8_postfix(String8 str, U64 size); internal String8 str8_postfix(String8 str, U64 size);
internal String8 str8_chop(String8 str, U64 amt); internal String8 str8_chop(String8 str, U64 amt);
internal String8 str8_skip_chop_whitespace(String8 string); internal String8 str8_skip_chop_whitespace(String8 string);
internal String8 str8_skip_chop_slashes(String8 string);
//////////////////////////////// ////////////////////////////////
//~ rjf: String Formatting & Copying //~ rjf: String Formatting & Copying
internal String8 str8_cat(Arena *arena, String8 s1, String8 s2); internal String8 push_str8_cat(Arena *arena, String8 s1, String8 s2);
internal String8 str8_copy(Arena *arena, String8 s); internal String8 push_str8_copy(Arena *arena, String8 s);
internal String8 str8fv(Arena *arena, char *fmt, va_list args); internal String8 push_str8fv(Arena *arena, char *fmt, va_list args);
internal String8 str8f(Arena *arena, char *fmt, ...); internal String8 push_str8f(Arena *arena, char *fmt, ...);
// TODO(rjf): remove these once we're ready to convert all usages:
#define push_str8_cat(arena, s1, s2) str8_cat((arena), (s1), (s2))
#define push_str8_copy(arena, s) str8_copy((arena), (s))
#define push_str8fv(arena, fmt, args) str8fv((arena), (fmt), (args))
#define push_str8f(arena, ...) str8f((arena), __VA_ARGS__)
internal String8 push_cstr(Arena *arena, String8 str); // TODO(rjf): this is unnecessary - this is implied by `push_str8_copy`. need to remove.
//////////////////////////////// ////////////////////////////////
//~ rjf: String <=> Integer Conversions //~ rjf: String <=> Integer Conversions
@@ -235,16 +233,11 @@ internal S64 sign_from_str8(String8 string, String8 *string_tail);
internal B32 str8_is_integer(String8 string, U32 radix); internal B32 str8_is_integer(String8 string, U32 radix);
internal U64 u64_from_str8(String8 string, U32 radix); internal U64 u64_from_str8(String8 string, U32 radix);
internal S64 s64_from_str8(String8 string, U32 radix); internal S64 s64_from_str8(String8 string, U32 radix);
internal U32 u32_from_str8(String8 string, U32 radix);
internal S32 s32_from_str8(String8 string, U32 radix);
internal B32 try_u64_from_str8_c_rules(String8 string, U64 *x); internal B32 try_u64_from_str8_c_rules(String8 string, U64 *x);
internal B32 try_s64_from_str8_c_rules(String8 string, S64 *x); internal B32 try_s64_from_str8_c_rules(String8 string, S64 *x);
//- rjf: integer -> string //- rjf: integer -> string
internal String8 str8_from_memory_size(Arena *arena, U64 size); internal String8 str8_from_memory_size(Arena *arena, U64 z);
internal String8 str8_from_count(Arena *arena, U64 count);
internal String8 str8_from_bits_u32(Arena *arena, U32 x);
internal String8 str8_from_bits_u64(Arena *arena, U64 x);
internal String8 str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator); internal String8 str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator);
internal String8 str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator); internal String8 str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator);
@@ -256,12 +249,12 @@ internal F64 f64_from_str8(String8 string);
//////////////////////////////// ////////////////////////////////
//~ rjf: String List Construction Functions //~ rjf: String List Construction Functions
internal String8Node *str8_list_push_node(String8List *list, String8Node *node); internal String8Node* str8_list_push_node(String8List *list, String8Node *node);
internal String8Node *str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string); internal String8Node* str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string);
internal String8Node *str8_list_push_node_front(String8List *list, String8Node *node); internal String8Node* str8_list_push_node_front(String8List *list, String8Node *node);
internal String8Node *str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string); internal String8Node* str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string);
internal String8Node *str8_list_push(Arena *arena, String8List *list, String8 string); internal String8Node* str8_list_push(Arena *arena, String8List *list, String8 string);
internal String8Node *str8_list_push_front(Arena *arena, String8List *list, String8 string); internal String8Node* str8_list_push_front(Arena *arena, String8List *list, String8 string);
internal void str8_list_concat_in_place(String8List *list, String8List *to_push); internal void str8_list_concat_in_place(String8List *list, String8List *to_push);
internal String8Node* str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align); internal String8Node* str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align);
internal String8Node* str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...); internal String8Node* str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...);
@@ -274,44 +267,19 @@ internal String8List str8_list_copy(Arena *arena, String8List *list);
internal String8List str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags); internal String8List str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags);
internal String8List str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags); internal String8List str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags);
internal String8List str8_list_split_by_string_chars(Arena *arena, String8List list, String8 split_chars, StringSplitFlags flags);
internal String8 str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params); internal String8 str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params);
internal void str8_list_from_flags(Arena *arena, String8List *list, U32 flags, String8 *flag_string_table, U32 flag_string_count);
////////////////////////////////
//~ rjf: Basic Data Stringification Helpers
internal String8List numeric_str8_list_from_data(Arena *arena, U32 radix, String8 data, U64 stride);
//////////////////////////////// ////////////////////////////////
//~ rjf; String Arrays //~ rjf; String Arrays
internal String8Array str8_array_zero(void);
internal String8Array str8_array_from_list(Arena *arena, String8List *list); internal String8Array str8_array_from_list(Arena *arena, String8List *list);
internal String8Array str8_array_reserve(Arena *arena, U64 count); internal String8Array str8_array_reserve(Arena *arena, U64 count);
internal String8Array str8_array_copy(Arena *arena, String8Array array);
////////////////////////////////
//~ rjf: String <-> Version Helpers
internal U64 version_from_str8(String8 string);
internal String8 str8_from_version(Arena *arena, U64 version);
//////////////////////////////// ////////////////////////////////
//~ rjf: String Path Helpers //~ rjf: String Path Helpers
global read_only struct
{
String8 string;
PathStyle path_style;
}
g_path_style_map[] =
{
{ str8_lit_comp(""), PathStyle_Null },
{ str8_lit_comp("relative"), PathStyle_Relative },
{ str8_lit_comp("windows"), PathStyle_WindowsAbsolute },
{ str8_lit_comp("unix"), PathStyle_UnixAbsolute },
{ str8_lit_comp("system"), PathStyle_SystemAbsolute },
};
internal String8 str8_chop_last_slash(String8 string); internal String8 str8_chop_last_slash(String8 string);
internal String8 str8_skip_last_slash(String8 string); internal String8 str8_skip_last_slash(String8 string);
internal String8 str8_chop_last_dot(String8 string); internal String8 str8_chop_last_dot(String8 string);
@@ -324,29 +292,6 @@ internal String8 str8_path_list_join_by_style(Arena *arena, String8List *pat
internal String8TxtPtPair str8_txt_pt_pair_from_string(String8 string); internal String8TxtPtPair str8_txt_pt_pair_from_string(String8 string);
////////////////////////////////
//~ rjf: Relative <-> Absolute Path
internal String8 path_relative_dst_from_absolute_dst_src(Arena *arena, String8 dst, String8 src);
internal String8 path_absolute_dst_from_relative_dst_src(Arena *arena, String8 dst, String8 src);
////////////////////////////////
//~ rjf: Path Normalization
internal String8List path_normalized_list_from_string(Arena *arena, String8 path, PathStyle *style_out);
internal String8 path_normalized_from_string(Arena *arena, String8 path);
internal B32 path_match_normalized(String8 left, String8 right);
////////////////////////////////
//~ rjf: Misc. Path Helpers
internal PathStyle path_style_from_string(String8 string);
internal String8 string_from_path_style(PathStyle style);
internal String8 path_separator_string_from_style(PathStyle style);
internal StringMatchFlags path_match_flags_from_os(OperatingSystem os);
internal String8 path_convert_slashes(Arena *arena, String8 path, PathStyle path_style);
internal String8 path_replace_file_extension(Arena *arena, String8 file_name, String8 ext);
//////////////////////////////// ////////////////////////////////
//~ rjf: UTF-8 & UTF-16 Decoding/Encoding //~ rjf: UTF-8 & UTF-16 Decoding/Encoding
@@ -354,6 +299,7 @@ internal UnicodeDecode utf8_decode(U8 *str, U64 max);
internal UnicodeDecode utf16_decode(U16 *str, U64 max); internal UnicodeDecode utf16_decode(U16 *str, U64 max);
internal U32 utf8_encode(U8 *str, U32 codepoint); internal U32 utf8_encode(U8 *str, U32 codepoint);
internal U32 utf16_encode(U16 *str, U32 codepoint); internal U32 utf16_encode(U16 *str, U32 codepoint);
internal U32 utf8_from_utf32_single(U8 *buffer, U32 character);
//////////////////////////////// ////////////////////////////////
//~ rjf: Unicode String Conversions //~ rjf: Unicode String Conversions
@@ -364,39 +310,24 @@ internal String8 str8_from_32(Arena *arena, String32 in);
internal String32 str32_from_8(Arena *arena, String8 in); internal String32 str32_from_8(Arena *arena, String8 in);
//////////////////////////////// ////////////////////////////////
//~ rjf: Basic Types & Space Enum <-> String Conversions //~ rjf: Basic Types & Space Enum -> String Conversions
internal OperatingSystem operating_system_from_string(String8 string);
internal String8 string_from_dimension(Dimension dimension); internal String8 string_from_dimension(Dimension dimension);
internal String8 string_from_side(Side side); internal String8 string_from_side(Side side);
internal String8 string_from_operating_system(OperatingSystem os); internal String8 string_from_operating_system(OperatingSystem os);
internal String8 string_from_arch(Arch arch); internal String8 string_from_architecture(Architecture arch);
////////////////////////////////
//~ rjf: Time Types -> String
internal String8 string_from_week_day(WeekDay week_day); internal String8 string_from_week_day(WeekDay week_day);
internal String8 string_from_month(Month month); internal String8 string_from_month(Month month);
internal String8 string_from_date_time(Arena *arena, DateTime *date_time); internal String8 push_date_time_string(Arena *arena, DateTime *date_time);
internal String8 string_from_date_time__file_name(Arena *arena, DateTime *date_time); internal String8 push_file_name_date_time_string(Arena *arena, DateTime *date_time);
internal String8 string_from_elapsed_time(Arena *arena, DateTime dt); internal String8 string_from_elapsed_time(Arena *arena, DateTime dt);
//////////////////////////////// ////////////////////////////////
//~ rjf: String <-> Globally Unique IDs //~ rjf: Textual String Wrapping
internal String8 string_from_guid(Arena *arena, Guid guid);
internal B32 try_guid_from_string(String8 string, Guid *guid_out);
internal Guid guid_from_string(String8 string);
////////////////////////////////
//~ rjf: Basic Text Indentation
internal String8 indented_from_string(Arena *arena, String8 string);
////////////////////////////////
//~ rjf: Text Escaping
internal String8 escaped_from_raw_str8(Arena *arena, String8 string);
internal String8 raw_from_escaped_str8(Arena *arena, String8 string);
////////////////////////////////
//~ rjf: Text Wrapping
internal String8List wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent); internal String8List wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent);
@@ -428,7 +359,6 @@ internal void str8_serial_push_u16(Arena *arena, String8List *srl, U16 x);
internal void str8_serial_push_u8(Arena *arena, String8List *srl, U8 x); internal void str8_serial_push_u8(Arena *arena, String8List *srl, U8 x);
internal void str8_serial_push_cstr(Arena *arena, String8List *srl, String8 str); internal void str8_serial_push_cstr(Arena *arena, String8List *srl, String8 str);
internal void str8_serial_push_string(Arena *arena, String8List *srl, String8 str); internal void str8_serial_push_string(Arena *arena, String8List *srl, String8 str);
internal void str8_serial_push_cstr(Arena *arena, String8List *srl, String8 str);
#define str8_serial_push_array(arena, srl, ptr, count) str8_serial_push_data(arena, srl, ptr, sizeof(*(ptr)) * (count)) #define str8_serial_push_array(arena, srl, ptr, count) str8_serial_push_data(arena, srl, ptr, sizeof(*(ptr)) * (count))
#define str8_serial_push_struct(arena, srl, ptr) str8_serial_push_array(arena, srl, ptr, 1) #define str8_serial_push_struct(arena, srl, ptr) str8_serial_push_array(arena, srl, ptr, 1)
@@ -437,19 +367,10 @@ internal void str8_serial_push_cstr(Arena *arena, String8List *srl, String8 s
internal U64 str8_deserial_read(String8 string, U64 off, void *read_dst, U64 read_size, U64 granularity); internal U64 str8_deserial_read(String8 string, U64 off, void *read_dst, U64 read_size, U64 granularity);
internal U64 str8_deserial_find_first_match(String8 string, U64 off, U16 scan_val); internal U64 str8_deserial_find_first_match(String8 string, U64 off, U16 scan_val);
internal void * str8_deserial_get_raw_ptr(String8 string, U64 off, U64 size); internal void * str8_deserial_get_raw_ptr(String8 string, U64 off, U64 size);internal U64 str8_deserial_read_cstr(String8 string, U64 off, String8 *cstr_out);
internal U64 str8_deserial_read_cstr(String8 string, U64 off, String8 *cstr_out);
internal U64 str8_deserial_read_windows_utf16_string16(String8 string, U64 off, String16 *str_out); internal U64 str8_deserial_read_windows_utf16_string16(String8 string, U64 off, String16 *str_out);
internal U64 str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out); internal U64 str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out);
#define str8_deserial_read_array(string, off, ptr, count) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr))*(count), sizeof(*(ptr))) #define str8_deserial_read_array(string, off, ptr, count) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr))*(count), sizeof(*(ptr)))
#define str8_deserial_read_struct(string, off, ptr) str8_deserial_read_array(string, off, ptr, 1) #define str8_deserial_read_struct(string, off, ptr) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr)), sizeof(*(ptr)))
////////////////////////////////
//~ rjf: Basic String Hashes
internal U64 u64_hash_from_seed_str8(U64 seed, String8 string);
internal U64 u64_hash_from_str8(String8 string);
internal U128 u128_hash_from_seed_str8(U64 seed, String8 string);
internal U128 u128_hash_from_str8(String8 string);
#endif // BASE_STRINGS_H #endif // BASE_STRINGS_H
+38 -176
View File
@@ -1,225 +1,87 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//////////////////////////////// ////////////////////////////////
//~ rjf: Globals // NOTE(allen): Thread Context Functions
C_LINKAGE thread_static TCTX *tctx_thread_local; C_LINKAGE thread_static TCTX* tctx_thread_local;
#if !BUILD_SUPPLEMENTARY_UNIT #if !BUILD_SUPPLEMENTARY_UNIT
C_LINKAGE thread_static TCTX *tctx_thread_local = 0; C_LINKAGE thread_static TCTX* tctx_thread_local = 0;
#endif #endif
////////////////////////////////
//~ rjf: Thread Context Functions
//- rjf: thread-context allocation & selection
internal TCTX *
tctx_alloc(void)
{
Arena *arena = arena_alloc();
TCTX *tctx = push_array(arena, TCTX, 1);
tctx->arenas[0] = arena;
tctx->arenas[1] = arena_alloc();
tctx->lane_ctx.lane_count = 1;
return tctx;
}
internal void internal void
tctx_release(TCTX *tctx) tctx_init_and_equip(TCTX *tctx){
{ MemoryZeroStruct(tctx);
arena_release(tctx->arenas[1]); Arena **arena_ptr = tctx->arenas;
arena_release(tctx->arenas[0]); for (U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1){
} *arena_ptr = arena_alloc();
}
internal void
tctx_select(TCTX *tctx)
{
tctx_thread_local = tctx; tctx_thread_local = tctx;
} }
internal TCTX * internal void
tctx_selected(void) tctx_release(void)
{ {
return tctx_thread_local; for(U64 i = 0; i < ArrayCount(tctx_thread_local->arenas); i += 1)
{
arena_release(tctx_thread_local->arenas[i]);
}
} }
//- rjf: scratch arenas internal TCTX*
tctx_get_equipped(void){
return(tctx_thread_local);
}
internal Arena*
tctx_get_scratch(Arena **conflicts, U64 count){
TCTX *tctx = tctx_get_equipped();
internal Arena *
tctx_get_scratch(Arena **conflicts, U64 count)
{
TCTX *tctx = tctx_selected();
Arena *result = 0; Arena *result = 0;
Arena **arena_ptr = tctx->arenas; Arena **arena_ptr = tctx->arenas;
for(U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1) for (U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1){
{
Arena **conflict_ptr = conflicts; Arena **conflict_ptr = conflicts;
B32 has_conflict = 0; B32 has_conflict = 0;
for(U64 j = 0; j < count; j += 1, conflict_ptr += 1) for (U64 j = 0; j < count; j += 1, conflict_ptr += 1){
{ if (*arena_ptr == *conflict_ptr){
if(*arena_ptr == *conflict_ptr)
{
has_conflict = 1; has_conflict = 1;
break; break;
} }
} }
if(!has_conflict) if (!has_conflict){
{
result = *arena_ptr; result = *arena_ptr;
break; break;
} }
} }
return result;
}
//- rjf: lane metadata return(result);
internal LaneCtx
tctx_set_lane_ctx(LaneCtx lane_ctx)
{
TCTX *tctx = tctx_selected();
LaneCtx restore = tctx->lane_ctx;
tctx->lane_ctx = lane_ctx;
return restore;
} }
internal void internal void
tctx_lane_barrier_wait(void *broadcast_ptr, U64 broadcast_size, U64 broadcast_src_lane_idx) tctx_set_thread_name(String8 string){
{ TCTX *tctx = tctx_get_equipped();
ProfBeginFunction();
ProfColor(0x00000ff);
TCTX *tctx = tctx_selected();
// rjf: doing broadcast -> copy to broadcast memory on source lane
U64 broadcast_size_clamped = ClampTop(broadcast_size, sizeof(tctx->lane_ctx.broadcast_memory[0]));
if(broadcast_ptr != 0 && lane_idx() == broadcast_src_lane_idx)
{
MemoryCopy(tctx->lane_ctx.broadcast_memory, broadcast_ptr, broadcast_size_clamped);
}
// rjf: all cases: barrier
os_barrier_wait(tctx->lane_ctx.barrier);
// rjf: doing broadcast -> copy from broadcast memory on destination lanes
if(broadcast_ptr != 0 && lane_idx() != broadcast_src_lane_idx)
{
MemoryCopy(broadcast_ptr, tctx->lane_ctx.broadcast_memory, broadcast_size_clamped);
}
// rjf: doing broadcast -> barrier on all lanes
if(broadcast_ptr != 0)
{
os_barrier_wait(tctx->lane_ctx.barrier);
}
ProfEnd();
}
//- rjf: thread names
internal void
tctx_set_thread_name(String8 string)
{
TCTX *tctx = tctx_selected();
U64 size = ClampTop(string.size, sizeof(tctx->thread_name)); U64 size = ClampTop(string.size, sizeof(tctx->thread_name));
MemoryCopy(tctx->thread_name, string.str, size); MemoryCopy(tctx->thread_name, string.str, size);
tctx->thread_name_size = size; tctx->thread_name_size = size;
} }
internal String8 internal String8
tctx_get_thread_name(void) tctx_get_thread_name(void){
{ TCTX *tctx = tctx_get_equipped();
TCTX *tctx = tctx_selected();
String8 result = str8(tctx->thread_name, tctx->thread_name_size); String8 result = str8(tctx->thread_name, tctx->thread_name_size);
return result; return(result);
} }
//- rjf: thread source-locations
internal void internal void
tctx_write_srcloc(char *file_name, U64 line_number) tctx_write_srcloc(char *file_name, U64 line_number){
{ TCTX *tctx = tctx_get_equipped();
TCTX *tctx = tctx_selected();
tctx->file_name = file_name; tctx->file_name = file_name;
tctx->line_number = line_number; tctx->line_number = line_number;
} }
internal void internal void
tctx_read_srcloc(char **file_name, U64 *line_number) tctx_read_srcloc(char **file_name, U64 *line_number){
{ TCTX *tctx = tctx_get_equipped();
TCTX *tctx = tctx_selected();
*file_name = tctx->file_name; *file_name = tctx->file_name;
*line_number = tctx->line_number; *line_number = tctx->line_number;
} }
////////////////////////////////
//~ rjf: Touch Scope Functions
internal Access *
access_open(void)
{
if(tctx_thread_local->access_arena == 0)
{
tctx_thread_local->access_arena = arena_alloc();
}
Access *access = tctx_thread_local->free_access;
if(access != 0)
{
SLLStackPop(tctx_thread_local->free_access);
}
else
{
access = push_array_no_zero(tctx_thread_local->access_arena, Access, 1);
}
MemoryZeroStruct(access);
return access;
}
internal void
access_close(Access *access)
{
for(Touch *touch = access->top_touch, *next = 0; touch != 0; touch = next)
{
next = touch->next;
ins_atomic_u64_dec_eval(&touch->pt->access_refcount);
if(touch->cv.u64[0] != 0) { cond_var_broadcast(touch->cv); }
SLLStackPush(tctx_thread_local->free_touch, touch);
}
SLLStackPush(tctx_thread_local->free_access, access);
}
internal void
access_touch(Access *access, AccessPt *pt, CondVar cv)
{
ins_atomic_u64_inc_eval(&pt->access_refcount);
ins_atomic_u64_eval_assign(&pt->last_time_touched_us, os_now_microseconds());
ins_atomic_u64_eval_assign(&pt->last_update_idx_touched, update_tick_idx());
Touch *touch = tctx_thread_local->free_touch;
if(touch != 0)
{
SLLStackPop(tctx_thread_local->free_touch);
}
else
{
touch = push_array_no_zero(tctx_thread_local->access_arena, Touch, 1);
}
MemoryZeroStruct(touch);
SLLStackPush(access->top_touch, touch);
touch->cv = cv;
touch->pt = pt;
}
//- rjf: access points
internal B32
access_pt_is_expired_(AccessPt *pt, AccessPtExpireParams *params)
{
U64 access_refcount = ins_atomic_u64_eval(&pt->access_refcount);
U64 last_time_touched_us = ins_atomic_u64_eval(&pt->last_time_touched_us);
U64 last_update_idx_touched = ins_atomic_u64_eval(&pt->last_update_idx_touched);
B32 result = (access_refcount == 0 &&
last_time_touched_us + params->time <= os_now_microseconds() &&
last_update_idx_touched + params->update_idxs <= update_tick_idx());
return result;
}
+16 -107
View File
@@ -1,132 +1,41 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_THREAD_CONTEXT_H #ifndef BASE_THREAD_CONTEXT_H
#define BASE_THREAD_CONTEXT_H #define BASE_THREAD_CONTEXT_H
//////////////////////////////// ////////////////////////////////
//~ rjf: Lane Context // NOTE(allen): Thread Context
typedef struct LaneCtx LaneCtx;
struct LaneCtx
{
U64 lane_idx;
U64 lane_count;
Barrier barrier;
U64 *broadcast_memory;
};
////////////////////////////////
//~ rjf: Access Scopes
typedef struct AccessPt AccessPt;
struct AccessPt
{
U64 access_refcount;
U64 last_time_touched_us;
U64 last_update_idx_touched;
};
typedef struct AccessPtExpireParams AccessPtExpireParams;
struct AccessPtExpireParams
{
U64 time;
U64 update_idxs;
};
typedef struct Touch Touch;
struct Touch
{
Touch *next;
AccessPt *pt;
CondVar cv;
};
typedef struct Access Access;
struct Access
{
Access *next;
Touch *top_touch;
};
////////////////////////////////
//~ rjf: Base Per-Thread State Bundle
typedef struct TCTX TCTX; typedef struct TCTX TCTX;
struct TCTX struct TCTX
{ {
// rjf: scratch arenas
Arena *arenas[2]; Arena *arenas[2];
// rjf: thread name
U8 thread_name[32]; U8 thread_name[32];
U64 thread_name_size; U64 thread_name_size;
// rjf: lane context
LaneCtx lane_ctx;
// rjf: source location info
char *file_name; char *file_name;
U64 line_number; U64 line_number;
// rjf: accesses
Arena *access_arena;
Access *free_access;
Touch *free_touch;
// rjf: progress
U64 *progress_counter_ptr;
U64 *progress_target_ptr;
}; };
//////////////////////////////// ////////////////////////////////
//~ rjf: Thread Context Functions // NOTE(allen): Thread Context Functions
//- rjf: thread-context allocation & selection internal void tctx_init_and_equip(TCTX *tctx);
internal TCTX *tctx_alloc(void); internal void tctx_release(void);
internal void tctx_release(TCTX *tctx); internal TCTX* tctx_get_equipped(void);
internal void tctx_select(TCTX *tctx);
internal TCTX *tctx_selected(void); internal Arena* tctx_get_scratch(Arena **conflicts, U64 count);
internal void tctx_set_thread_name(String8 name);
internal String8 tctx_get_thread_name(void);
internal void tctx_write_srcloc(char *file_name, U64 line_number);
internal void tctx_read_srcloc(char **file_name, U64 *line_number);
#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__)
//- rjf: scratch arenas
internal Arena *tctx_get_scratch(Arena **conflicts, U64 count);
#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count))) #define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count)))
#define scratch_end(scratch) temp_end(scratch) #define scratch_end(scratch) temp_end(scratch)
//- rjf: lane metadata #endif //BASE_THREAD_CONTEXT_H
internal LaneCtx tctx_set_lane_ctx(LaneCtx lane_ctx);
internal void tctx_lane_barrier_wait(void *broadcast_ptr, U64 broadcast_size, U64 broadcast_src_lane_idx);
#define lane_idx() (tctx_selected()->lane_ctx.lane_idx)
#define lane_count() (tctx_selected()->lane_ctx.lane_count)
#define lane_from_task_idx(idx) ((idx)%lane_count())
#define lane_ctx(ctx) tctx_set_lane_ctx((ctx))
#define lane_sync() tctx_lane_barrier_wait(0, 0, 0)
#define lane_sync_u64(ptr, src_lane_idx) tctx_lane_barrier_wait((ptr), sizeof(*(ptr)), (src_lane_idx))
#define lane_range(count) m_range_from_n_idx_m_count(lane_idx(), lane_count(), (count))
//- rjf: thread names
internal void tctx_set_thread_name(String8 name);
internal String8 tctx_get_thread_name(void);
//- rjf: thread source-locations
internal void tctx_write_srcloc(char *file_name, U64 line_number);
internal void tctx_read_srcloc(char **file_name, U64 *line_number);
#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__)
//- rjf: access scopes
internal Access *access_open(void);
internal void access_close(Access *access);
internal void access_touch(Access *access, AccessPt *pt, CondVar cv);
//- rjf: access points
internal B32 access_pt_is_expired_(AccessPt *pt, AccessPtExpireParams *params);
#define access_pt_is_expired(pt, ...) access_pt_is_expired_((pt), &(AccessPtExpireParams){.time = 2000000, .update_idxs = 2, __VA_ARGS__})
//- rjf: progress counters
#define set_progress_ptr(ptr) (tctx_selected()->progress_counter_ptr = (ptr))
#define set_progress_target_ptr(ptr) (tctx_selected()->progress_target_ptr = (ptr))
#define set_progress(val) (tctx_selected()->progress_counter_ptr ? ins_atomic_u64_eval_assign(tctx_selected()->progress_counter_ptr, (val)) : (void)0)
#define add_progress(val) (tctx_selected()->progress_counter_ptr ? ins_atomic_u64_add_eval(tctx_selected()->progress_counter_ptr, (val)) : (void)0)
#define set_progress_target(val) (tctx_selected()->progress_target_ptr ? ins_atomic_u64_eval_assign(tctx_selected()->progress_target_ptr, (val)) : (void)0)
#endif // BASE_THREAD_CONTEXT_H
-102
View File
@@ -1,102 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Thread Functions
internal Thread
thread_launch(ThreadEntryPointFunctionType *f, void *p)
{
Thread thread = os_thread_launch(f, p);
return thread;
}
internal B32
thread_join(Thread thread, U64 endt_us)
{
B32 result = os_thread_join(thread, endt_us);
return result;
}
internal void
thread_detach(Thread thread)
{
os_thread_detach(thread);
}
////////////////////////////////
//~ rjf: Synchronization Primitive Functions
//- rjf: recursive mutexes
internal Mutex mutex_alloc(void) {return os_mutex_alloc();}
internal void mutex_release(Mutex mutex) {os_mutex_release(mutex);}
internal void mutex_take(Mutex mutex) {os_mutex_take(mutex);}
internal void mutex_drop(Mutex mutex) {os_mutex_drop(mutex);}
//- rjf: reader/writer mutexes
internal RWMutex rw_mutex_alloc(void) {return os_rw_mutex_alloc();}
internal void rw_mutex_release(RWMutex mutex) {os_rw_mutex_release(mutex);}
internal void rw_mutex_take(RWMutex mutex, B32 write_mode) {os_rw_mutex_take(mutex, write_mode);}
internal void rw_mutex_drop(RWMutex mutex, B32 write_mode) {os_rw_mutex_drop(mutex, write_mode);}
//- rjf: condition variables
internal CondVar cond_var_alloc(void) {return os_cond_var_alloc();}
internal void cond_var_release(CondVar cv) {os_cond_var_release(cv);}
internal B32 cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us) {return os_cond_var_wait(cv, mutex, endt_us);}
internal B32 cond_var_wait_rw(CondVar cv, RWMutex mutex_rw, B32 write_mode, U64 endt_us) {return os_cond_var_wait_rw(cv, mutex_rw, write_mode, endt_us);}
internal void cond_var_signal(CondVar cv) {os_cond_var_signal(cv);}
internal void cond_var_broadcast(CondVar cv) {os_cond_var_broadcast(cv);}
//- rjf: cross-process semaphores
internal Semaphore semaphore_alloc(U32 initial_count, U32 max_count, String8 name) {return os_semaphore_alloc(initial_count, max_count, name);}
internal void semaphore_release(Semaphore semaphore) {os_semaphore_release(semaphore);}
internal Semaphore semaphore_open(String8 name) {return os_semaphore_open(name);}
internal void semaphore_close(Semaphore semaphore) {os_semaphore_close(semaphore);}
internal B32 semaphore_take(Semaphore semaphore, U64 endt_us) {return os_semaphore_take(semaphore, endt_us);}
internal void semaphore_drop(Semaphore semaphore) {os_semaphore_drop(semaphore);}
//- rjf: barriers
internal Barrier barrier_alloc(U64 count) {return os_barrier_alloc(count);}
internal void barrier_release(Barrier barrier) {os_barrier_release(barrier);}
internal void barrier_wait(Barrier barrier) {os_barrier_wait(barrier);}
////////////////////////////////
//~ rjf: Table Stripe Functions
internal StripeArray
stripe_array_alloc(Arena *arena)
{
StripeArray array = {0};
array.count = os_get_system_info()->logical_processor_count;
array.v = push_array(arena, Stripe, array.count);
for EachIndex(idx, array.count)
{
array.v[idx].arena = arena_alloc();
array.v[idx].rw_mutex = rw_mutex_alloc();
array.v[idx].cv = cond_var_alloc();
}
return array;
}
internal void
stripe_array_release(StripeArray *stripes)
{
for EachIndex(idx, stripes->count)
{
arena_release(stripes->v[idx].arena);
rw_mutex_release(stripes->v[idx].rw_mutex);
cond_var_release(stripes->v[idx].cv);
}
}
internal Stripe *
stripe_from_slot_idx(StripeArray *stripes, U64 slot_idx)
{
Stripe *stripe = &stripes->v[slot_idx%stripes->count];
return stripe;
}
-133
View File
@@ -1,133 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_THREADS_H
#define BASE_THREADS_H
////////////////////////////////
//~ rjf: Thread Types
typedef struct Thread Thread;
struct Thread
{
U64 u64[1];
};
typedef void ThreadEntryPointFunctionType(void *p);
////////////////////////////////
//~ rjf: Synchronization Primitive Types
typedef struct Mutex Mutex;
struct Mutex
{
U64 u64[1];
};
typedef struct RWMutex RWMutex;
struct RWMutex
{
U64 u64[1];
};
typedef struct CondVar CondVar;
struct CondVar
{
U64 u64[1];
};
typedef struct Semaphore Semaphore;
struct Semaphore
{
U64 u64[1];
};
typedef struct Barrier Barrier;
struct Barrier
{
U64 u64[1];
};
////////////////////////////////
//~ rjf: Table Stripes
typedef struct Stripe Stripe;
struct Stripe
{
Arena *arena;
RWMutex rw_mutex;
CondVar cv;
void *free;
};
typedef struct StripeArray StripeArray;
struct StripeArray
{
Stripe *v;
U64 count;
};
////////////////////////////////
//~ rjf: Thread Functions
internal Thread thread_launch(ThreadEntryPointFunctionType *f, void *p);
internal B32 thread_join(Thread thread, U64 endt_us);
internal void thread_detach(Thread thread);
////////////////////////////////
//~ rjf: Synchronization Primitive Functions
//- rjf: recursive mutexes
internal Mutex mutex_alloc(void);
internal void mutex_release(Mutex mutex);
internal void mutex_take(Mutex mutex);
internal void mutex_drop(Mutex mutex);
//- rjf: reader/writer mutexes
internal RWMutex rw_mutex_alloc(void);
internal void rw_mutex_release(RWMutex mutex);
internal void rw_mutex_take(RWMutex mutex, B32 write_mode);
internal void rw_mutex_drop(RWMutex mutex, B32 write_mode);
#define rw_mutex_take_r(m) rw_mutex_take((m), (0))
#define rw_mutex_take_w(m) rw_mutex_take((m), (1))
#define rw_mutex_drop_r(m) rw_mutex_drop((m), (0))
#define rw_mutex_drop_w(m) rw_mutex_drop((m), (1))
//- rjf: condition variables
internal CondVar cond_var_alloc(void);
internal void cond_var_release(CondVar cv);
// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout
internal B32 cond_var_wait(CondVar cv, Mutex mutex, U64 endt_us);
internal B32 cond_var_wait_rw(CondVar cv, RWMutex mutex_rw, B32 write_mode, U64 endt_us);
#define cond_var_wait_rw_r(cv, m, endt) cond_var_wait_rw((cv), (m), (0), (endt))
#define cond_var_wait_rw_w(cv, m, endt) cond_var_wait_rw((cv), (m), (1), (endt))
internal void cond_var_signal(CondVar cv);
internal void cond_var_broadcast(CondVar cv);
//- rjf: cross-process semaphores
internal Semaphore semaphore_alloc(U32 initial_count, U32 max_count, String8 name);
internal void semaphore_release(Semaphore semaphore);
internal Semaphore semaphore_open(String8 name);
internal void semaphore_close(Semaphore semaphore);
internal B32 semaphore_take(Semaphore semaphore, U64 endt_us);
internal void semaphore_drop(Semaphore semaphore);
//- rjf: barriers
internal Barrier barrier_alloc(U64 count);
internal void barrier_release(Barrier barrier);
internal void barrier_wait(Barrier barrier);
//- rjf: scope macros
#define MutexScope(mutex) DeferLoop(mutex_take(mutex), mutex_drop(mutex))
#define RWMutexScope(mutex, write_mode) DeferLoop(rw_mutex_take((mutex), (write_mode)), rw_mutex_drop((mutex), (write_mode)))
#define MutexScopeR(mutex) DeferLoop(rw_mutex_take_r(mutex), rw_mutex_drop_r(mutex))
#define MutexScopeW(mutex) DeferLoop(rw_mutex_take_w(mutex), rw_mutex_drop_w(mutex))
#define MutexScopeRWPromote(mutex) DeferLoop((rw_mutex_drop_r(mutex), rw_mutex_take_w(mutex)), (rw_mutex_drop_w(mutex), rw_mutex_take_r(mutex)))
////////////////////////////////
//~ rjf: Table Stripe Functions
internal StripeArray stripe_array_alloc(Arena *arena);
internal void stripe_array_release(StripeArray *stripes);
internal Stripe *stripe_from_slot_idx(StripeArray *stripes, U64 slot_idx);
#endif // BASE_THREADS_H
+443 -1048
View File
File diff suppressed because it is too large Load Diff
+1448 -1531
View File
File diff suppressed because it is too large Load Diff
+34 -42
View File
@@ -246,7 +246,7 @@ cv_string_from_arch:
{MANTYPREF - 0x1028} {MANTYPREF - 0x1028}
{UNAMESPACE_ST - 0x1029} {UNAMESPACE_ST - 0x1029}
{ST_MAX - 0x1100} {ST_MAX - 0x1100}
{OBJNAME ObjName 0x1101} {OBJNAME Objname 0x1101}
{THUNK32 Thunk32 0x1102} {THUNK32 Thunk32 0x1102}
{BLOCK32 Block32 0x1103} {BLOCK32 Block32 0x1103}
{WITH32 - 0x1104} {WITH32 - 0x1104}
@@ -395,48 +395,48 @@ CV_BasicTypeTable:
{FBASICSTR 0x06 "" } {FBASICSTR 0x06 "" }
{NOTTRANS 0x07 "" } {NOTTRANS 0x07 "" }
{HRESULT 0x08 "HRESULT" } {HRESULT 0x08 "HRESULT" }
{CHAR 0x10 "CHAR" } {CHAR 0x10 "char" }
{SHORT 0x11 "SHORT" } {SHORT 0x11 "S16" }
{LONG 0x12 "LONG" } {LONG 0x12 "S32" }
{QUAD 0x13 "QUAD" } {QUAD 0x13 "S64" }
{OCT 0x14 "OCT" } {OCT 0x14 "S128" }
{UCHAR 0x20 "UCHAR" } {UCHAR 0x20 "UCHAR" }
{USHORT 0x21 "USHORT" } {USHORT 0x21 "U16" }
{ULONG 0x22 "ULONG" } {ULONG 0x22 "U32" }
{UQUAD 0x23 "UQUAD" } {UQUAD 0x23 "U64" }
{UOCT 0x24 "UOCT" } {UOCT 0x24 "U128" }
{BOOL8 0x30 "BOOL8" } {BOOL8 0x30 "B8" }
{BOOL16 0x31 "BOOL16" } {BOOL16 0x31 "B16" }
{BOOL32 0x32 "BOOL32" } {BOOL32 0x32 "B32" }
{BOOL64 0x33 "BOOL64" } {BOOL64 0x33 "B64" }
{FLOAT32 0x40 "FLOAT32" } {FLOAT32 0x40 "F32" }
{FLOAT64 0x41 "FLOAT64" } {FLOAT64 0x41 "F64" }
{FLOAT80 0x42 "FLOAT80" } {FLOAT80 0x42 "F80" }
{FLOAT128 0x43 "FLOAT128" } {FLOAT128 0x43 "F128" }
{FLOAT48 0x44 "FLOAT48" } {FLOAT48 0x44 "F48" }
{FLOAT32PP 0x45 "FLOAT32PP" } {FLOAT32PP 0x45 "F32PP" }
{FLOAT16 0x46 "FLOAT16" } {FLOAT16 0x46 "F16" }
{COMPLEX32 0x50 "ComplexF32" } {COMPLEX32 0x50 "ComplexF32" }
{COMPLEX64 0x51 "ComplexF64" } {COMPLEX64 0x51 "ComplexF64" }
{COMPLEX80 0x52 "ComplexF80" } {COMPLEX80 0x52 "ComplexF80" }
{COMPLEX128 0x53 "ComplexF128" } {COMPLEX128 0x53 "ComplexF128" }
{BIT 0x60 "" } {BIT 0x60 "" }
{PASCHAR 0x61 "" } {PASCHAR 0x61 "" }
{BOOL32FF 0x62 "BOOL32FF" } {BOOL32FF 0x62 "B32FF" }
{INT8 0x68 "int8" } {INT8 0x68 "S8" }
{UINT8 0x69 "uint8" } {UINT8 0x69 "U8" }
{RCHAR 0x70 "char" } {RCHAR 0x70 "char" }
{WCHAR 0x71 "WCHAR" } {WCHAR 0x71 "WCHAR" }
{INT16 0x72 "int16" } {INT16 0x72 "S16" }
{UINT16 0x73 "uint16" } {UINT16 0x73 "U16" }
{INT32 0x74 "int32" } {INT32 0x74 "S32" }
{UINT32 0x75 "uint32" } {UINT32 0x75 "U32" }
{INT64 0x76 "int64" } {INT64 0x76 "S64" }
{UINT64 0x77 "uint64" } {UINT64 0x77 "U64" }
{INT128 0x78 "int128" } {INT128 0x78 "S128" }
{UINT128 0x79 "uint128" } {UINT128 0x79 "U128" }
{CHAR16 0x7a "char16" } {CHAR16 0x7a "CHAR16" }
{CHAR32 0x7b "char32" } {CHAR32 0x7b "CHAR32" }
{CHAR8 0x7c "char" } {CHAR8 0x7c "char" }
{PTR 0xf0 "PTR" } {PTR 0xf0 "PTR" }
} }
@@ -462,7 +462,6 @@ CV_BasicTypeTable:
@table(name header_type_name val) @table(name header_type_name val)
CV_LeafKindTable: CV_LeafKindTable:
{ {
{NOTYPE - 0x0000}
{MODIFIER_16t - 0x0001} {MODIFIER_16t - 0x0001}
{POINTER_16t - 0x0002} {POINTER_16t - 0x0002}
{ARRAY_16t - 0x0003} {ARRAY_16t - 0x0003}
@@ -587,13 +586,6 @@ CV_LeafKindTable:
{VECTOR - 0x151b} {VECTOR - 0x151b}
{MATRIX - 0x151c} {MATRIX - 0x151c}
{VFTABLE - 0x151d} {VFTABLE - 0x151d}
{FUNC_ID FuncId 0x1601}
{MFUNC_ID MFuncId 0x1602}
{BUILDINFO BuildInfo 0x1603}
{SUBSTR_LIST SubstrList 0x1604}
{STRING_ID StringId 0x1605}
{UDT_SRC_LINE UDTSrcLine 0x1606}
{UDT_MOD_SRC_LINE UDTModSrcLine 0x1607}
{CLASS2 Struct2 0x1608} {CLASS2 Struct2 0x1608}
{STRUCT2 Struct2 0x1609} {STRUCT2 Struct2 0x1609}
} }
-24
View File
@@ -1,24 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal String8
cv_string_from_numeric(Arena *arena, CV_NumericParsed num)
{
String8 result = str8_zero();
switch (num.kind) {
case CV_NumericKind_FLOAT16: NotImplemented; break; // TODO: format float16
case CV_NumericKind_FLOAT32: result = push_str8f(arena, "%f", (F64)(*(F32*)num.val)); break;
case CV_NumericKind_FLOAT48: NotImplemented; break; // TODO: format float48
case CV_NumericKind_FLOAT64: result = push_str8f(arena, "%f", *(F64*)num.val); break;
case CV_NumericKind_FLOAT80: NotImplemented; break; // TODO: format float80
case CV_NumericKind_FLOAT128: NotImplemented; break; // TODO: format float128
case CV_NumericKind_CHAR: result = push_str8f(arena, "%d", *(S8 *)num.val); break;
case CV_NumericKind_SHORT: result = push_str8f(arena, "%d", *(S16*)num.val); break;
case CV_NumericKind_LONG: result = push_str8f(arena, "%d", *(S32*)num.val); break;
case CV_NumericKind_QUADWORD: result = push_str8f(arena, "%lld", *(S64*)num.val); break;
case CV_NumericKind_USHORT: result = push_str8f(arena, "%u", *(U16*)num.val); break;
case CV_NumericKind_ULONG: result = push_str8f(arena, "%u", *(U32*)num.val); break;
case CV_NumericKind_UQUADWORD: result = push_str8f(arena, "%llu", *(U64*)num.val); break;
}
return result;
}
-9
View File
@@ -1,9 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CODEVIEW_DUMP_H
#define CODEVIEW_DUMP_H
internal String8 cv_string_from_numeric(Arena *arena, CV_NumericParsed num);
#endif // CODEVIEW_DUMP_H
File diff suppressed because it is too large Load Diff
-305
View File
@@ -1,305 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CODEVIEW_PARSE_H
#define CODEVIEW_PARSE_H
////////////////////////////////
//~ CodeView Common Parser Types
// CV_Numeric layout
// x: U16
// buf: U8[]
// case (x < 0x8000): kind=U16 val=x
// case (x >= 0x8000): kind=x val=buf
typedef struct CV_NumericParsed CV_NumericParsed;
struct CV_NumericParsed
{
CV_NumericKind kind;
U8 *val;
U64 encoded_size;
};
typedef struct CV_RecRange CV_RecRange;
struct CV_RecRange
{
U32 off;
CV_RecHeader hdr;
};
#define CV_REC_RANGE_CHUNK_SIZE 511
typedef struct CV_RecRangeChunk CV_RecRangeChunk;
struct CV_RecRangeChunk
{
struct CV_RecRangeChunk *next;
CV_RecRange ranges[CV_REC_RANGE_CHUNK_SIZE];
};
typedef struct CV_RecRangeStream CV_RecRangeStream;
struct CV_RecRangeStream
{
CV_RecRangeChunk *first_chunk;
CV_RecRangeChunk *last_chunk;
U64 total_count;
};
typedef struct CV_RecRangeArray CV_RecRangeArray;
struct CV_RecRangeArray
{
CV_RecRange *ranges;
U64 count;
};
////////////////////////////////
//~ CodeView Sym Parser Types
typedef struct CV_SymTopLevelInfo CV_SymTopLevelInfo;
struct CV_SymTopLevelInfo
{
CV_Arch arch;
CV_Language language;
String8 compiler_name;
};
typedef struct CV_SymParsed CV_SymParsed;
struct CV_SymParsed
{
// source information
String8 data;
U64 sym_align;
// sym index derived from source
CV_RecRangeArray sym_ranges;
// top-level info derived from the syms
CV_SymTopLevelInfo info;
};
////////////////////////////////
//~ CodeView Leaf Parser Types
typedef struct CV_LeafParsed CV_LeafParsed;
struct CV_LeafParsed
{
// source information
String8 data;
CV_TypeId itype_first;
CV_TypeId itype_opl;
// leaf index derived from source
CV_RecRangeArray leaf_ranges;
};
////////////////////////////////
//~ CodeView C13 Info Parser Types
typedef struct CV_C13InlineSiteDecoder CV_C13InlineSiteDecoder;
struct CV_C13InlineSiteDecoder
{
U64 cursor;
U64 parent_voff;
CV_InlineRangeKind range_kind;
U32 code_length;
U32 code_offset;
U32 file_off;
S32 ln;
S32 cn;
U64 code_offset_lo;
B32 code_offset_changed;
B32 code_offset_lo_changed;
B32 code_length_changed;
B32 ln_changed;
B32 file_off_changed;
Rng1U64 last_range;
U32 file_count;
Rng1U64 file_last_range;
U64 file_line_count;
U64 file_last_ln;
};
typedef U32 CV_C13InlineSiteDecoderStepFlags;
enum
{
CV_C13InlineSiteDecoderStepFlag_EmitRange = (1 << 0),
CV_C13InlineSiteDecoderStepFlag_ExtendLastRange = (1 << 1),
CV_C13InlineSiteDecoderStepFlag_EmitFile = (1 << 2),
CV_C13InlineSiteDecoderStepFlag_EmitLine = (1 << 3),
};
typedef struct CV_C13InlineSiteDecoderStep CV_C13InlineSiteDecoderStep;
struct CV_C13InlineSiteDecoderStep
{
CV_C13InlineSiteDecoderStepFlags flags;
Rng1U64 range;
U64 line_voff;
U64 line_voff_end;
U64 ln;
U64 cn;
U32 file_off;
};
typedef struct CV_C13LinesParsed CV_C13LinesParsed;
struct CV_C13LinesParsed
{
// raw info
U32 sec_idx;
U32 file_off;
U64 secrel_base_off;
// parsed info
String8 file_name;
CV_C13ChecksumKind checksum_kind;
String8 checksum;
U64 *voffs; // [line_count + 1]
U32 *line_nums; // [line_count]
U16 *col_nums; // [2*line_count]
U32 line_count;
};
typedef struct CV_C13LinesParsedNode CV_C13LinesParsedNode;
struct CV_C13LinesParsedNode
{
CV_C13LinesParsedNode *next;
CV_C13LinesParsed v;
};
typedef struct CV_C13InlineeLinesParsed CV_C13InlineeLinesParsed;
struct CV_C13InlineeLinesParsed
{
CV_ItemId inlinee;
U32 file_off;
String8 file_name;
CV_C13ChecksumKind checksum_kind;
String8 checksum;
U32 first_source_ln;
U32 extra_file_count;
U32 *extra_files;
};
typedef struct CV_C13InlineeLinesParsedNode CV_C13InlineeLinesParsedNode;
struct CV_C13InlineeLinesParsedNode
{
CV_C13InlineeLinesParsedNode *next;
CV_C13InlineeLinesParsedNode *hash_next;
CV_C13InlineeLinesParsed v;
};
typedef struct CV_C13SubSectionNode CV_C13SubSectionNode;
struct CV_C13SubSectionNode
{
struct CV_C13SubSectionNode *next;
CV_C13SubSectionKind kind;
U32 off;
U32 size;
CV_C13LinesParsedNode *lines_first;
CV_C13LinesParsedNode *lines_last;
CV_C13InlineeLinesParsedNode *inlinee_lines_first;
CV_C13InlineeLinesParsedNode *inlinee_lines_last;
};
typedef struct CV_C13Parsed CV_C13Parsed;
struct CV_C13Parsed
{
// rjf: source data
String8 data;
// rjf: full sub-section list
CV_C13SubSectionNode *first_sub_section;
CV_C13SubSectionNode *last_sub_section;
U64 sub_section_count;
// rjf: fastpath to file checksums section
CV_C13SubSectionNode *file_chksms_sub_section;
// rjf: fastpath to map inlinee CV_ItemId -> CV_InlineeLinesParsed quickly
CV_C13InlineeLinesParsedNode **inlinee_lines_parsed_slots;
U64 inlinee_lines_parsed_slots_count;
};
typedef struct CV_UDTInfo CV_UDTInfo;
struct CV_UDTInfo
{
String8 name;
String8 unique_name;
CV_TypeProps props;
};
////////////////////////////////
//~ CodeView Compound Types
typedef struct CV_TypeIdArray CV_TypeIdArray;
struct CV_TypeIdArray
{
CV_TypeId *itypes;
U64 count;
};
////////////////////////////////
//- Hasher
internal U64 cv_hash_from_string(String8 string);
internal U64 cv_hash_from_item_id(CV_ItemId item_id);
//- Numeric Decoder
internal CV_NumericParsed cv_numeric_from_data_range(U8 *first, U8 *opl);
internal U64 cv_read_numeric(String8 data, U64 offset, CV_NumericParsed *out);
internal B32 cv_numeric_fits_in_u64(CV_NumericParsed *num);
internal B32 cv_numeric_fits_in_s64(CV_NumericParsed *num);
internal B32 cv_numeric_fits_in_f64(CV_NumericParsed *num);
internal U64 cv_u64_from_numeric(CV_NumericParsed *num);
internal S64 cv_s64_from_numeric(CV_NumericParsed *num);
internal F64 cv_f64_from_numeric(CV_NumericParsed *num);
//- Inlinee Lines Binary Annot Decoder
internal U64 cv_decode_inline_annot_u32(String8 data, U64 offset, U32 *out_value);
internal U64 cv_decode_inline_annot_s32(String8 data, U64 offset, S32 *out_value);
internal S32 cv_inline_annot_signed_from_unsigned_operand(U32 value);
internal CV_C13InlineSiteDecoder cv_c13_inline_site_decoder_init(U32 file_off, U32 first_source_ln, U32 parent_voff);
internal CV_C13InlineSiteDecoderStep cv_c13_inline_site_decoder_step(CV_C13InlineSiteDecoder *decoder, String8 binary_annots);
//- Symbol/Leaf Helpers
internal B32 cv_is_udt_name_anon(String8 name);
internal B32 cv_is_global_symbol(CV_SymKind kind);
internal B32 cv_is_typedef(CV_SymKind kind);
internal B32 cv_is_scope_symbol(CV_SymKind kind);
internal B32 cv_is_end_symbol(CV_SymKind kind);
internal B32 cv_is_udt(CV_LeafKind kind);
internal B32 cv_is_leaf_type_server(CV_LeafKind kind);
internal B32 cv_is_leaf_pch(CV_LeafKind kind);
internal CV_TypeIndexSource cv_type_index_source_from_leaf_kind(CV_LeafKind leaf_kind);
internal CV_TypeIndexInfoList cv_get_symbol_type_index_offsets(Arena *arena, CV_SymKind kind, String8 data);
internal CV_TypeIndexInfoList cv_get_leaf_type_index_offsets(Arena *arena, CV_LeafKind leaf_kind, String8 data);
internal CV_TypeIndexInfoList cv_get_inlinee_type_index_offsets(Arena *arena, String8 raw_data);
internal String8Array cv_get_data_around_type_indices(Arena *arena, CV_TypeIndexInfoList ti_list, String8 data);
internal U64 cv_name_offset_from_symbol(CV_SymKind kind, String8 data);
internal String8 cv_name_from_symbol(CV_SymKind kind, String8 data);
internal CV_UDTInfo cv_get_udt_info(CV_LeafKind kind, String8 data);
internal String8 cv_name_from_udt_info(CV_UDTInfo udt_info);
//- rjf: record range stream parsing
internal CV_RecRangeStream * cv_rec_range_stream_from_data(Arena *arena, String8 data, U64 align);
internal CV_RecRangeArray cv_rec_range_array_from_stream(Arena *arena, CV_RecRangeStream *stream);
//- rjf: sym stream parsing
internal CV_SymParsed * cv_sym_from_data(Arena *arena, String8 sym_data, U64 sym_align);
//- rjf: leaf stream parsing
internal CV_LeafParsed * cv_leaf_from_data(Arena *arena, String8 leaf_data, CV_TypeId first);
internal CV_C13Parsed * cv_c13_parsed_from_data(Arena *arena, String8 c13_data, String8 strtbl, COFF_SectionHeaderArray sections);
#endif // CODEVIEW_PARSE_H
File diff suppressed because it is too large Load Diff
+82
View File
@@ -0,0 +1,82 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CODEVIEW_STRINGIZE_H
#define CODEVIEW_STRINGIZE_H
////////////////////////////////
//~ CodeView Stringize Helper Types
typedef struct CV_StringizeSymParams{
CV_Arch arch;
} CV_StringizeSymParams;
typedef struct CV_StringizeLeafParams{
U32 dummy;
} CV_StringizeLeafParams;
////////////////////////////////
//~ CodeView Common Stringize Functions
internal void cv_stringize_numeric(Arena *arena, String8List *out, CV_NumericParsed *num);
internal void cv_stringize_lvar_addr_range(Arena *arena, String8List *out,
CV_LvarAddrRange *range);
internal void cv_stringize_lvar_addr_gap(Arena *arena, String8List *out, CV_LvarAddrGap *gap);
internal void cv_stringize_lvar_addr_gap_list(Arena *arena, String8List *out,
void *first, void *opl);
internal String8 cv_string_from_basic_type(CV_BasicType basic_type);
internal String8 cv_string_from_c13_sub_section_kind(CV_C13_SubSectionKind kind);
internal String8 cv_string_from_reg(CV_Arch arch, CV_Reg reg);
internal String8 cv_string_from_pointer_kind(CV_PointerKind ptr_kind);
internal String8 cv_string_from_pointer_mode(CV_PointerMode ptr_mode);
internal String8 cv_string_from_hfa_kind(CV_HFAKind hfa_kind);
internal String8 cv_string_from_mo_com_udt_kind(CV_MoComUDTKind mo_com_udt_kind);
////////////////////////////////
//~ CodeView Flags Stringize Functions
internal void cv_stringize_modifier_flags(Arena *arena, String8List *out,
U32 indent, CV_ModifierFlags flags);
internal void cv_stringize_type_props(Arena *arena, String8List *out,
U32 indent, CV_TypeProps props);
internal void cv_stringize_pointer_attribs(Arena *arena, String8List *out,
U32 indent, CV_PointerAttribs attribs);
internal void cv_stringize_local_flags(Arena *arena, String8List *out,
U32 indent, CV_LocalFlags flags);
////////////////////////////////
//~ CodeView Sym Stringize Functions
internal void cv_stringize_sym_parsed(Arena *arena, String8List *out, CV_SymParsed *sym);
internal void cv_stringize_sym_range(Arena *arena, String8List *out,
CV_RecRange *range, String8 data,
CV_StringizeSymParams *p);
internal void cv_stringize_sym_array(Arena *arena, String8List *out,
CV_RecRangeArray *ranges, String8 data,
CV_StringizeSymParams *p);
////////////////////////////////
//~ CodeView Leaf Stringize Functions
internal void cv_stringize_leaf_parsed(Arena *arena, String8List *out, CV_LeafParsed *leaf);
internal void cv_stringize_leaf_range(Arena *arena, String8List *out,
CV_RecRange *range, CV_TypeId itype, String8 data,
CV_StringizeLeafParams *p);
internal void cv_stringize_leaf_array(Arena *arena, String8List *out,
CV_RecRangeArray *ranges, CV_TypeId itype_first,
String8 data,
CV_StringizeLeafParams *p);
////////////////////////////////
//~ CodeView C13 Stringize Functions
internal void cv_stringize_c13_parsed(Arena *arena, String8List *out, CV_C13Parsed *c13);
#endif // CODEVIEW_STRINGIZE_H
+38 -50
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//- GENERATED CODE //- GENERATED CODE
@@ -392,48 +392,48 @@ case CV_BasicType_NBASICSTR:{result = str8_lit("");}break;
case CV_BasicType_FBASICSTR:{result = str8_lit("");}break; case CV_BasicType_FBASICSTR:{result = str8_lit("");}break;
case CV_BasicType_NOTTRANS:{result = str8_lit("");}break; case CV_BasicType_NOTTRANS:{result = str8_lit("");}break;
case CV_BasicType_HRESULT:{result = str8_lit("HRESULT");}break; case CV_BasicType_HRESULT:{result = str8_lit("HRESULT");}break;
case CV_BasicType_CHAR:{result = str8_lit("CHAR");}break; case CV_BasicType_CHAR:{result = str8_lit("char");}break;
case CV_BasicType_SHORT:{result = str8_lit("SHORT");}break; case CV_BasicType_SHORT:{result = str8_lit("S16");}break;
case CV_BasicType_LONG:{result = str8_lit("LONG");}break; case CV_BasicType_LONG:{result = str8_lit("S32");}break;
case CV_BasicType_QUAD:{result = str8_lit("QUAD");}break; case CV_BasicType_QUAD:{result = str8_lit("S64");}break;
case CV_BasicType_OCT:{result = str8_lit("OCT");}break; case CV_BasicType_OCT:{result = str8_lit("S128");}break;
case CV_BasicType_UCHAR:{result = str8_lit("UCHAR");}break; case CV_BasicType_UCHAR:{result = str8_lit("UCHAR");}break;
case CV_BasicType_USHORT:{result = str8_lit("USHORT");}break; case CV_BasicType_USHORT:{result = str8_lit("U16");}break;
case CV_BasicType_ULONG:{result = str8_lit("ULONG");}break; case CV_BasicType_ULONG:{result = str8_lit("U32");}break;
case CV_BasicType_UQUAD:{result = str8_lit("UQUAD");}break; case CV_BasicType_UQUAD:{result = str8_lit("U64");}break;
case CV_BasicType_UOCT:{result = str8_lit("UOCT");}break; case CV_BasicType_UOCT:{result = str8_lit("U128");}break;
case CV_BasicType_BOOL8:{result = str8_lit("BOOL8");}break; case CV_BasicType_BOOL8:{result = str8_lit("B8");}break;
case CV_BasicType_BOOL16:{result = str8_lit("BOOL16");}break; case CV_BasicType_BOOL16:{result = str8_lit("B16");}break;
case CV_BasicType_BOOL32:{result = str8_lit("BOOL32");}break; case CV_BasicType_BOOL32:{result = str8_lit("B32");}break;
case CV_BasicType_BOOL64:{result = str8_lit("BOOL64");}break; case CV_BasicType_BOOL64:{result = str8_lit("B64");}break;
case CV_BasicType_FLOAT32:{result = str8_lit("FLOAT32");}break; case CV_BasicType_FLOAT32:{result = str8_lit("F32");}break;
case CV_BasicType_FLOAT64:{result = str8_lit("FLOAT64");}break; case CV_BasicType_FLOAT64:{result = str8_lit("F64");}break;
case CV_BasicType_FLOAT80:{result = str8_lit("FLOAT80");}break; case CV_BasicType_FLOAT80:{result = str8_lit("F80");}break;
case CV_BasicType_FLOAT128:{result = str8_lit("FLOAT128");}break; case CV_BasicType_FLOAT128:{result = str8_lit("F128");}break;
case CV_BasicType_FLOAT48:{result = str8_lit("FLOAT48");}break; case CV_BasicType_FLOAT48:{result = str8_lit("F48");}break;
case CV_BasicType_FLOAT32PP:{result = str8_lit("FLOAT32PP");}break; case CV_BasicType_FLOAT32PP:{result = str8_lit("F32PP");}break;
case CV_BasicType_FLOAT16:{result = str8_lit("FLOAT16");}break; case CV_BasicType_FLOAT16:{result = str8_lit("F16");}break;
case CV_BasicType_COMPLEX32:{result = str8_lit("ComplexF32");}break; case CV_BasicType_COMPLEX32:{result = str8_lit("ComplexF32");}break;
case CV_BasicType_COMPLEX64:{result = str8_lit("ComplexF64");}break; case CV_BasicType_COMPLEX64:{result = str8_lit("ComplexF64");}break;
case CV_BasicType_COMPLEX80:{result = str8_lit("ComplexF80");}break; case CV_BasicType_COMPLEX80:{result = str8_lit("ComplexF80");}break;
case CV_BasicType_COMPLEX128:{result = str8_lit("ComplexF128");}break; case CV_BasicType_COMPLEX128:{result = str8_lit("ComplexF128");}break;
case CV_BasicType_BIT:{result = str8_lit("");}break; case CV_BasicType_BIT:{result = str8_lit("");}break;
case CV_BasicType_PASCHAR:{result = str8_lit("");}break; case CV_BasicType_PASCHAR:{result = str8_lit("");}break;
case CV_BasicType_BOOL32FF:{result = str8_lit("BOOL32FF");}break; case CV_BasicType_BOOL32FF:{result = str8_lit("B32FF");}break;
case CV_BasicType_INT8:{result = str8_lit("int8");}break; case CV_BasicType_INT8:{result = str8_lit("S8");}break;
case CV_BasicType_UINT8:{result = str8_lit("uint8");}break; case CV_BasicType_UINT8:{result = str8_lit("U8");}break;
case CV_BasicType_RCHAR:{result = str8_lit("char");}break; case CV_BasicType_RCHAR:{result = str8_lit("char");}break;
case CV_BasicType_WCHAR:{result = str8_lit("WCHAR");}break; case CV_BasicType_WCHAR:{result = str8_lit("WCHAR");}break;
case CV_BasicType_INT16:{result = str8_lit("int16");}break; case CV_BasicType_INT16:{result = str8_lit("S16");}break;
case CV_BasicType_UINT16:{result = str8_lit("uint16");}break; case CV_BasicType_UINT16:{result = str8_lit("U16");}break;
case CV_BasicType_INT32:{result = str8_lit("int32");}break; case CV_BasicType_INT32:{result = str8_lit("S32");}break;
case CV_BasicType_UINT32:{result = str8_lit("uint32");}break; case CV_BasicType_UINT32:{result = str8_lit("U32");}break;
case CV_BasicType_INT64:{result = str8_lit("int64");}break; case CV_BasicType_INT64:{result = str8_lit("S64");}break;
case CV_BasicType_UINT64:{result = str8_lit("uint64");}break; case CV_BasicType_UINT64:{result = str8_lit("U64");}break;
case CV_BasicType_INT128:{result = str8_lit("int128");}break; case CV_BasicType_INT128:{result = str8_lit("S128");}break;
case CV_BasicType_UINT128:{result = str8_lit("uint128");}break; case CV_BasicType_UINT128:{result = str8_lit("U128");}break;
case CV_BasicType_CHAR16:{result = str8_lit("char16");}break; case CV_BasicType_CHAR16:{result = str8_lit("CHAR16");}break;
case CV_BasicType_CHAR32:{result = str8_lit("char32");}break; case CV_BasicType_CHAR32:{result = str8_lit("CHAR32");}break;
case CV_BasicType_CHAR8:{result = str8_lit("char");}break; case CV_BasicType_CHAR8:{result = str8_lit("char");}break;
case CV_BasicType_PTR:{result = str8_lit("PTR");}break; case CV_BasicType_PTR:{result = str8_lit("PTR");}break;
} }
@@ -447,7 +447,6 @@ String8 result = str8_lit("<Unknown CV_LeafKind>");
switch(v) switch(v)
{ {
default:{}break; default:{}break;
case CV_LeafKind_NOTYPE:{result = str8_lit("NOTYPE");}break;
case CV_LeafKind_MODIFIER_16t:{result = str8_lit("MODIFIER_16t");}break; case CV_LeafKind_MODIFIER_16t:{result = str8_lit("MODIFIER_16t");}break;
case CV_LeafKind_POINTER_16t:{result = str8_lit("POINTER_16t");}break; case CV_LeafKind_POINTER_16t:{result = str8_lit("POINTER_16t");}break;
case CV_LeafKind_ARRAY_16t:{result = str8_lit("ARRAY_16t");}break; case CV_LeafKind_ARRAY_16t:{result = str8_lit("ARRAY_16t");}break;
@@ -572,13 +571,6 @@ case CV_LeafKind_BINTERFACE:{result = str8_lit("BINTERFACE");}break;
case CV_LeafKind_VECTOR:{result = str8_lit("VECTOR");}break; case CV_LeafKind_VECTOR:{result = str8_lit("VECTOR");}break;
case CV_LeafKind_MATRIX:{result = str8_lit("MATRIX");}break; case CV_LeafKind_MATRIX:{result = str8_lit("MATRIX");}break;
case CV_LeafKind_VFTABLE:{result = str8_lit("VFTABLE");}break; case CV_LeafKind_VFTABLE:{result = str8_lit("VFTABLE");}break;
case CV_LeafKind_FUNC_ID:{result = str8_lit("FUNC_ID");}break;
case CV_LeafKind_MFUNC_ID:{result = str8_lit("MFUNC_ID");}break;
case CV_LeafKind_BUILDINFO:{result = str8_lit("BUILDINFO");}break;
case CV_LeafKind_SUBSTR_LIST:{result = str8_lit("SUBSTR_LIST");}break;
case CV_LeafKind_STRING_ID:{result = str8_lit("STRING_ID");}break;
case CV_LeafKind_UDT_SRC_LINE:{result = str8_lit("UDT_SRC_LINE");}break;
case CV_LeafKind_UDT_MOD_SRC_LINE:{result = str8_lit("UDT_MOD_SRC_LINE");}break;
case CV_LeafKind_CLASS2:{result = str8_lit("CLASS2");}break; case CV_LeafKind_CLASS2:{result = str8_lit("CLASS2");}break;
case CV_LeafKind_STRUCT2:{result = str8_lit("STRUCT2");}break; case CV_LeafKind_STRUCT2:{result = str8_lit("STRUCT2");}break;
} }
@@ -600,7 +592,7 @@ case CV_SymKind_OEM:{result = sizeof(CV_SymOEM);}break;
case CV_SymKind_VFTABLE32:{result = sizeof(CV_SymVPath32);}break; case CV_SymKind_VFTABLE32:{result = sizeof(CV_SymVPath32);}break;
case CV_SymKind_FRAMEPROC:{result = sizeof(CV_SymFrameproc);}break; case CV_SymKind_FRAMEPROC:{result = sizeof(CV_SymFrameproc);}break;
case CV_SymKind_ANNOTATION:{result = sizeof(CV_SymAnnotation);}break; case CV_SymKind_ANNOTATION:{result = sizeof(CV_SymAnnotation);}break;
case CV_SymKind_OBJNAME:{result = sizeof(CV_SymObjName);}break; case CV_SymKind_OBJNAME:{result = sizeof(CV_SymObjname);}break;
case CV_SymKind_THUNK32:{result = sizeof(CV_SymThunk32);}break; case CV_SymKind_THUNK32:{result = sizeof(CV_SymThunk32);}break;
case CV_SymKind_BLOCK32:{result = sizeof(CV_SymBlock32);}break; case CV_SymKind_BLOCK32:{result = sizeof(CV_SymBlock32);}break;
case CV_SymKind_LABEL32:{result = sizeof(CV_SymLabel32);}break; case CV_SymKind_LABEL32:{result = sizeof(CV_SymLabel32);}break;
@@ -704,15 +696,11 @@ case CV_LeafKind_ONEMETHOD:{result = sizeof(CV_LeafOneMethod);}break;
case CV_LeafKind_NESTTYPEEX:{result = sizeof(CV_LeafNestTypeEx);}break; case CV_LeafKind_NESTTYPEEX:{result = sizeof(CV_LeafNestTypeEx);}break;
case CV_LeafKind_TYPESERVER2:{result = sizeof(CV_LeafTypeServer2);}break; case CV_LeafKind_TYPESERVER2:{result = sizeof(CV_LeafTypeServer2);}break;
case CV_LeafKind_INTERFACE:{result = sizeof(CV_LeafStruct);}break; case CV_LeafKind_INTERFACE:{result = sizeof(CV_LeafStruct);}break;
case CV_LeafKind_FUNC_ID:{result = sizeof(CV_LeafFuncId);}break;
case CV_LeafKind_MFUNC_ID:{result = sizeof(CV_LeafMFuncId);}break;
case CV_LeafKind_BUILDINFO:{result = sizeof(CV_LeafBuildInfo);}break;
case CV_LeafKind_SUBSTR_LIST:{result = sizeof(CV_LeafSubstrList);}break;
case CV_LeafKind_STRING_ID:{result = sizeof(CV_LeafStringId);}break;
case CV_LeafKind_UDT_SRC_LINE:{result = sizeof(CV_LeafUDTSrcLine);}break;
case CV_LeafKind_UDT_MOD_SRC_LINE:{result = sizeof(CV_LeafUDTModSrcLine);}break;
case CV_LeafKind_CLASS2:{result = sizeof(CV_LeafStruct2);}break; case CV_LeafKind_CLASS2:{result = sizeof(CV_LeafStruct2);}break;
case CV_LeafKind_STRUCT2:{result = sizeof(CV_LeafStruct2);}break; case CV_LeafKind_STRUCT2:{result = sizeof(CV_LeafStruct2);}break;
} }
return result; return result;
} }
C_LINKAGE_BEGIN
C_LINKAGE_END
+4 -9
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//- GENERATED CODE //- GENERATED CODE
@@ -381,7 +381,6 @@ CV_BasicType_PTR = 0xf0,
typedef U16 CV_LeafKind; typedef U16 CV_LeafKind;
typedef enum CV_LeafKindEnum typedef enum CV_LeafKindEnum
{ {
CV_LeafKind_NOTYPE = 0x0000,
CV_LeafKind_MODIFIER_16t = 0x0001, CV_LeafKind_MODIFIER_16t = 0x0001,
CV_LeafKind_POINTER_16t = 0x0002, CV_LeafKind_POINTER_16t = 0x0002,
CV_LeafKind_ARRAY_16t = 0x0003, CV_LeafKind_ARRAY_16t = 0x0003,
@@ -506,13 +505,6 @@ CV_LeafKind_BINTERFACE = 0x151a,
CV_LeafKind_VECTOR = 0x151b, CV_LeafKind_VECTOR = 0x151b,
CV_LeafKind_MATRIX = 0x151c, CV_LeafKind_MATRIX = 0x151c,
CV_LeafKind_VFTABLE = 0x151d, CV_LeafKind_VFTABLE = 0x151d,
CV_LeafKind_FUNC_ID = 0x1601,
CV_LeafKind_MFUNC_ID = 0x1602,
CV_LeafKind_BUILDINFO = 0x1603,
CV_LeafKind_SUBSTR_LIST = 0x1604,
CV_LeafKind_STRING_ID = 0x1605,
CV_LeafKind_UDT_SRC_LINE = 0x1606,
CV_LeafKind_UDT_MOD_SRC_LINE = 0x1607,
CV_LeafKind_CLASS2 = 0x1608, CV_LeafKind_CLASS2 = 0x1608,
CV_LeafKind_STRUCT2 = 0x1609, CV_LeafKind_STRUCT2 = 0x1609,
} CV_LeafKindEnum; } CV_LeafKindEnum;
@@ -525,4 +517,7 @@ internal String8 cv_type_name_from_basic_type(CV_BasicType v);
internal String8 cv_string_from_leaf_kind(CV_LeafKind v); internal String8 cv_string_from_leaf_kind(CV_LeafKind v);
internal U64 cv_header_struct_size_from_sym_kind(CV_SymKind v); internal U64 cv_header_struct_size_from_sym_kind(CV_SymKind v);
internal U64 cv_header_struct_size_from_leaf_kind(CV_LeafKind v); internal U64 cv_header_struct_size_from_leaf_kind(CV_LeafKind v);
C_LINKAGE_BEGIN
C_LINKAGE_END
#endif // CODEVIEW_META_H #endif // CODEVIEW_META_H
+955 -790
View File
File diff suppressed because it is too large Load Diff
+716 -491
View File
File diff suppressed because it is too large Load Diff
-819
View File
@@ -1,819 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#if 0
internal void
coff_print_archive_member_header(Arena *arena, String8List *out, String8 indent, COFF_ParsedArchiveMemberHeader header, String8 long_names)
{
Temp scratch = scratch_begin(&arena, 1);
String8 time_stamp = coff_string_from_time_stamp(scratch.arena, header.time_stamp);
rd_printf("Name : %S" , header.name );
rd_printf("Time Stamp: (%#x) %S" , header.time_stamp, time_stamp );
rd_printf("User ID : %u" , header.user_id );
rd_printf("Group ID : %u" , header.group_id);
rd_printf("Mode : %S" , header.mode );
rd_printf("Data : [%#llx-%#llx)", header.data_range.min, header.data_range.max);
scratch_end(scratch);
}
internal void
coff_print_section_table(Arena *arena,
String8List *out,
String8 indent,
String8 string_table,
COFF_Symbol32Array symbol_table,
U64 section_count,
COFF_SectionHeader *section_table)
{
Temp scratch = scratch_begin(&arena, 1);
String8 *symlinks = push_array(scratch.arena, String8, section_count);
for (U64 i = 0; i < symbol_table.count; ++i) {
COFF_Symbol32 *symbol = symbol_table.v+i;
COFF_SymbolValueInterpType interp = coff_interp_symbol(symbol->section_number, symbol->value, symbol->storage_class);
if (interp == COFF_SymbolValueInterp_Regular &&
symbol->aux_symbol_count == 0 &&
(symbol->storage_class == COFF_SymStorageClass_External || symbol->storage_class == COFF_SymStorageClass_Static)) {
if (symbol->section_number > 0 && symbol->section_number <= symbol_table.count) {
COFF_SectionHeader *header = section_table+(symbol->section_number-1);
if (header->flags & COFF_SectionFlag_LnkCOMDAT) {
symlinks[symbol->section_number-1] = coff_read_symbol_name(string_table, &symbol->name);
}
}
}
i += symbol->aux_symbol_count;
}
if (section_count) {
rd_printf("# Section Table");
rd_indent();
rd_printf("%-4s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-8s %-5s %-10s %s",
"No.",
"Name",
"VirtSize",
"VirtOff",
"FileSize",
"FileOff",
"RelocOff",
"LinesOff",
"RelocCnt",
"LineCnt",
"Align",
"Flags",
"Symlink");
for (U64 i = 0; i < section_count; ++i) {
COFF_SectionHeader *header = section_table+i;
String8 name = str8_cstring_capped(header->name, header->name+sizeof(header->name));
String8 full_name = coff_name_from_section_header(string_table, header);
String8 align;
{
U64 align_size = coff_align_size_from_section_flags(header->flags);
align = push_str8f(scratch.arena, "%u", align_size);
}
String8 flags;
{
String8List mem_flags = {0};
if (header->flags & COFF_SectionFlag_MemRead) {
str8_list_pushf(scratch.arena, &mem_flags, "r");
}
if (header->flags & COFF_SectionFlag_MemWrite) {
str8_list_pushf(scratch.arena, &mem_flags, "w");
}
if (header->flags & COFF_SectionFlag_MemExecute) {
str8_list_pushf(scratch.arena, &mem_flags, "x");
}
String8List cnt_flags = {0};
if (header->flags & COFF_SectionFlag_CntCode) {
str8_list_pushf(scratch.arena, &cnt_flags, "c");
}
if (header->flags & COFF_SectionFlag_CntInitializedData) {
str8_list_pushf(scratch.arena, &cnt_flags, "d");
}
if (header->flags & COFF_SectionFlag_CntUninitializedData) {
str8_list_pushf(scratch.arena, &cnt_flags, "u");
}
String8List mem_extra_flags = {0};
if (header->flags & COFF_SectionFlag_MemShared) {
str8_list_pushf(scratch.arena, &mem_flags, "s");
}
if (header->flags & COFF_SectionFlag_Mem16Bit) {
str8_list_pushf(scratch.arena, &mem_extra_flags, "h");
}
if (header->flags & COFF_SectionFlag_MemLocked) {
str8_list_pushf(scratch.arena, &mem_extra_flags, "l");
}
if (header->flags & COFF_SectionFlag_MemDiscardable) {
str8_list_pushf(scratch.arena, &mem_extra_flags, "d");
}
if (header->flags & COFF_SectionFlag_MemNotCached) {
str8_list_pushf(scratch.arena, &mem_extra_flags, "c");
}
if (header->flags & COFF_SectionFlag_MemNotPaged) {
str8_list_pushf(scratch.arena, &mem_extra_flags, "p");
}
String8List lnk_flags = {0};
if (header->flags & COFF_SectionFlag_LnkRemove) {
str8_list_pushf(scratch.arena, &lnk_flags, "r");
}
if (header->flags & COFF_SectionFlag_LnkCOMDAT) {
str8_list_pushf(scratch.arena, &lnk_flags, "c");
}
if (header->flags & COFF_SectionFlag_LnkOther) {
str8_list_pushf(scratch.arena, &lnk_flags, "o");
}
if (header->flags & COFF_SectionFlag_LnkInfo) {
str8_list_pushf(scratch.arena, &lnk_flags, "i");
}
if (header->flags & COFF_SectionFlag_LnkNRelocOvfl) {
str8_list_pushf(scratch.arena, &lnk_flags, "f");
}
String8List other_flags = {0};
if (header->flags & COFF_SectionFlag_TypeNoPad) {
str8_list_pushf(scratch.arena, &other_flags, "n");
}
if (header->flags & COFF_SectionFlag_GpRel) {
str8_list_pushf(scratch.arena, &other_flags, "g");
}
String8 mem = str8_list_join(scratch.arena, &mem_flags, 0);
String8 cnt = str8_list_join(scratch.arena, &cnt_flags, 0);
String8 lnk = str8_list_join(scratch.arena, &lnk_flags, 0);
String8 ext = str8_list_join(scratch.arena, &mem_extra_flags, 0);
String8 oth = str8_list_join(scratch.arena, &other_flags, 0);
String8List f = {0};
str8_list_push(scratch.arena, &f, mem);
str8_list_push(scratch.arena, &f, cnt);
str8_list_push(scratch.arena, &f, ext);
str8_list_push(scratch.arena, &f, lnk);
str8_list_push(scratch.arena, &f, oth);
flags = str8_list_join(scratch.arena, &f, &(StringJoin){ .sep = str8_lit("-") });
if (!flags.size) {
flags = str8_lit("none");
}
}
String8List l = {0};
str8_list_pushf(scratch.arena, &l, "%-4x", i+1 );
str8_list_pushf(scratch.arena, &l, "%-8S", name );
str8_list_pushf(scratch.arena, &l, "%08x", header->vsize );
str8_list_pushf(scratch.arena, &l, "%08x", header->voff );
str8_list_pushf(scratch.arena, &l, "%08x", header->fsize );
str8_list_pushf(scratch.arena, &l, "%08x", header->foff );
str8_list_pushf(scratch.arena, &l, "%08x", header->relocs_foff);
str8_list_pushf(scratch.arena, &l, "%08x", header->lines_foff );
str8_list_pushf(scratch.arena, &l, "%08x", header->reloc_count);
str8_list_pushf(scratch.arena, &l, "%08x", header->line_count );
str8_list_pushf(scratch.arena, &l, "%-5S", align );
str8_list_pushf(scratch.arena, &l, "%-10S", flags );
if (symlinks[i].size > 0) {
str8_list_pushf(scratch.arena, &l, "%S", symlinks[i]);
} else {
str8_list_pushf(scratch.arena, &l, "[no symlink]");
}
String8 line = str8_list_join(scratch.arena, &l, &(StringJoin){ .sep = str8_lit(" "), });
rd_printf("%S", line);
if (full_name.size != name.size) {
rd_indent();
rd_printf("Full Name: %S", full_name);
rd_unindent();
}
}
rd_newline();
rd_printf("Flags:");
rd_indent();
rd_printf("r = MemRead w = MemWrite x = MemExecute");
rd_printf("c = CntCode d = InitializedData u = UninitializedData");
rd_printf("s = MemShared h = Mem16bit l = MemLocked d = MemDiscardable c = MemNotCached p = MemNotPaged");
rd_printf("r = LnkRemove c = LnkComdat o = LnkOther i = LnkInfo f = LnkNRelocOvfl");
rd_printf("g = GpRel n = TypeNoPad");
rd_unindent();
rd_unindent();
rd_newline();
}
scratch_end(scratch);
}
internal void
coff_disasm_sections(Arena *arena,
String8List *out,
String8 indent,
String8 raw_data,
COFF_MachineType machine,
U64 image_base,
B32 is_obj,
RD_MarkerArray *section_markers,
U64 section_count,
COFF_SectionHeader *sections)
{
if (section_count) {
for (U64 sect_idx = 0; sect_idx < section_count; ++sect_idx) {
COFF_SectionHeader *sect = sections+sect_idx;
if (sect->flags & COFF_SectionFlag_CntCode) {
U64 sect_off = is_obj ? sect->foff : sect->voff;
U64 sect_size = is_obj ? sect->fsize : sect->vsize;
String8 raw_code = str8_substr(raw_data, rng_1u64(sect->foff, sect->foff+sect_size));
U64 sect_number = sect_idx+1;
RD_MarkerArray markers = section_markers[sect_number];
rd_printf("# Disassembly [Section No. %#llx]", sect_number);
rd_indent();
rd_print_disasm(arena, out, indent, arch_from_coff_machine(machine), image_base, sect_off, markers.count, markers.v, raw_code);
rd_unindent();
}
}
}
}
internal void
coff_raw_data_sections(Arena *arena,
String8List *out,
String8 indent,
String8 raw_data,
B32 is_obj,
RD_MarkerArray *section_markers,
U64 section_count,
COFF_SectionHeader *section_table)
{
if (section_count) {
for (U64 sect_idx = 0; sect_idx < section_count; ++sect_idx) {
COFF_SectionHeader *sect = section_table+sect_idx;
if (sect->fsize > 0) {
U64 sect_size = is_obj ? sect->fsize : sect->vsize;
String8 raw_sect = str8_substr(raw_data, rng_1u64(sect->foff, sect->foff+sect_size));
RD_MarkerArray markers = section_markers[sect_idx];
rd_printf("# Raw Data [Section No. %#llx]", (sect_idx+1));
rd_indent();
rd_print_raw_data(arena, out, indent, 32, markers.count, markers.v, raw_sect);
rd_unindent();
rd_newline();
}
}
}
}
internal void
coff_print_relocs(Arena *arena,
String8List *out,
String8 indent,
String8 raw_data,
String8 string_table,
COFF_MachineType machine,
U64 sect_count,
COFF_SectionHeader *sect_headers,
COFF_Symbol32Array symbols)
{
Temp scratch = scratch_begin(&arena, 1);
B32 print_header = 1;
for (U64 sect_idx = 0; sect_idx < sect_count; ++sect_idx) {
COFF_SectionHeader *sect_header = sect_headers+sect_idx;
COFF_RelocInfo reloc_info = coff_reloc_info_from_section_header(raw_data, sect_header);
if (reloc_info.count) {
if (print_header) {
print_header = 0;
rd_printf("# Relocations");
rd_indent();
}
rd_printf("## Section %llx", sect_idx);
rd_indent();
rd_printf("%-4s %-8s %-16s %-16s %-8s %-7s", "No.", "Offset", "Type", "ApplyTo", "SymIdx", "SymName");
for (U64 reloc_idx = 0; reloc_idx < reloc_info.count; ++reloc_idx) {
COFF_Reloc *reloc = (COFF_Reloc*)(raw_data.str + reloc_info.array_off) + reloc_idx;
String8 type = coff_string_from_reloc(machine, reloc->type);
U64 apply_size = coff_apply_size_from_reloc(machine, reloc->type);
U64 apply_foff = sect_header->foff + reloc->apply_off;
if (apply_foff + apply_size > raw_data.size) {
rd_errorf("out of bounds apply file offset %#llx in relocation %#llx", apply_foff, reloc_idx);
break;
}
U64 raw_apply;
AssertAlways(apply_size <= sizeof(raw_apply));
MemoryCopy(&raw_apply, raw_data.str + apply_foff, apply_size);
S64 apply = extend_sign64(raw_apply, apply_size);
if (reloc->isymbol > symbols.count) {
rd_errorf("out of bounds symbol index %u in relocation %#llx", reloc->isymbol, reloc_idx);
break;
}
COFF_Symbol32 *symbol = symbols.v+reloc->isymbol;
String8 symbol_name = coff_read_symbol_name(string_table, &symbol->name);
String8List line = {0};
str8_list_pushf(scratch.arena, &line, "%-4x", reloc_idx );
str8_list_pushf(scratch.arena, &line, "%08x", reloc->apply_off);
str8_list_pushf(scratch.arena, &line, "%-16S", type );
str8_list_pushf(scratch.arena, &line, "%016x", apply );
str8_list_pushf(scratch.arena, &line, "%S", symbol_name );
String8 l = str8_list_join(scratch.arena, &line, &(StringJoin){.sep=str8_lit(" ")});
rd_printf("%S", l);
}
rd_unindent();
}
}
if (!print_header) {
rd_unindent();
}
rd_newline();
scratch_end(scratch);
}
internal void
coff_print_symbol_table(Arena *arena,
String8List *out,
String8 indent,
String8 raw_data,
B32 is_big_obj,
String8 string_table,
COFF_Symbol32Array symbols)
{
Temp scratch = scratch_begin(&arena, 1);
if (symbols.count) {
rd_printf("# Symbol Table");
rd_indent();
rd_printf("%-4s %-8s %-10s %-4s %-4s %-4s %-16s %-20s",
"No.", "Value", "SectNum", "Aux", "Msb", "Lsb", "Storage", "Name");
for (U64 i = 0; i < symbols.count; ++i) {
COFF_Symbol32 *symbol = &symbols.v[i];
String8 name = coff_read_symbol_name(string_table, &symbol->name);
String8 msb = coff_string_from_sym_dtype(symbol->type.u.msb);
String8 lsb = coff_string_from_sym_type(symbol->type.u.lsb);
String8 storage_class = coff_string_from_sym_storage_class(symbol->storage_class);
String8 section_number;
switch (symbol->section_number) {
case COFF_Symbol_UndefinedSection: section_number = str8_lit("Undef"); break;
case COFF_Symbol_AbsSection32: section_number = str8_lit("Abs"); break;
case COFF_Symbol_DebugSection32: section_number = str8_lit("Debug"); break;
default: section_number = push_str8f(scratch.arena, "%010x", symbol->section_number); break;
}
String8List line = {0};
str8_list_pushf(scratch.arena, &line, "%-4x", i );
str8_list_pushf(scratch.arena, &line, "%08x", symbol->value );
str8_list_pushf(scratch.arena, &line, "%-10S", section_number );
str8_list_pushf(scratch.arena, &line, "%-4u", symbol->aux_symbol_count);
str8_list_pushf(scratch.arena, &line, "%-4S", msb );
str8_list_pushf(scratch.arena, &line, "%-4S", lsb );
str8_list_pushf(scratch.arena, &line, "%-16S", storage_class );
str8_list_pushf(scratch.arena, &line, "%S", name );
String8 l = str8_list_join(scratch.arena, &line, &(StringJoin){.sep = str8_lit(" ")});
rd_printf("%S", l);
rd_indent();
for (U64 k=i+1, c = i+symbol->aux_symbol_count; k <= c; ++k) {
void *raw_aux = &symbols.v[k];
switch (symbol->storage_class) {
case COFF_SymStorageClass_External: {
COFF_SymbolFuncDef *func_def = (COFF_SymbolFuncDef*)&symbols.v[k];
rd_printf("Tag Index %#x, Total Size %#x, Line Numbers %#x, Next Function %#x",
func_def->tag_index, func_def->total_size, func_def->ptr_to_ln, func_def->ptr_to_next_func);
} break;
case COFF_SymStorageClass_Function: {
COFF_SymbolFunc *func = raw_aux;
rd_printf("Ordinal Line Number %#x, Next Function %#x", func->ln, func->ptr_to_next_func);
} break;
case COFF_SymStorageClass_WeakExternal: {
COFF_SymbolWeakExt *weak = raw_aux;
String8 type = coff_string_from_weak_ext_type(weak->characteristics);
rd_printf("Tag Index %#x, Characteristics %S", weak->tag_index, type);
} break;
case COFF_SymStorageClass_File: {
COFF_SymbolFile *file = raw_aux;
String8 name = str8_cstring_capped(file->name, file->name+sizeof(file->name));
rd_printf("Name %S", name);
} break;
case COFF_SymStorageClass_Static: {
COFF_SymbolSecDef *sd = raw_aux;
String8 selection = coff_string_from_comdat_select_type(sd->selection);
U32 number = sd->number_lo;
if (is_big_obj) {
number |= (U32)sd->number_hi << 16;
}
if (number) {
rd_printf("Length %x, Reloc Count %u, Line Count %u, Checksum %x, Section %x, Selection %S",
sd->length, sd->number_of_relocations, sd->number_of_ln, sd->check_sum, number, selection);
} else {
rd_printf("Length %x, Reloc Count %u, Line Count %u, Checksum %x",
sd->length, sd->number_of_relocations, sd->number_of_ln, sd->check_sum);
}
} break;
default: {
rd_printf("???");
} break;
}
}
i += symbol->aux_symbol_count;
rd_unindent();
}
rd_unindent();
rd_newline();
}
scratch_end(scratch);
}
internal void
coff_print_big_obj_header(Arena *arena, String8List *out, String8 indent, COFF_BigObjHeader *header)
{
Temp scratch = scratch_begin(&arena, 1);
String8 time_stamp = coff_string_from_time_stamp(scratch.arena, header->time_stamp);
String8 machine = coff_string_from_machine_type(header->machine);
rd_printf("# Big Obj");
rd_indent();
rd_printf("Time Stamp : %#x (%S)", header->time_stamp, time_stamp);
rd_printf("Machine : %#x (%S)", header->machine, machine );
rd_printf("Section Count: %u", header->section_count );
rd_printf("Symbol Table : %#x", header->symbol_table_foff);
rd_printf("Symbol Count : %u", header->symbol_count );
rd_unindent();
scratch_end(scratch);
}
internal void
coff_print_file_header(Arena *arena, String8List *out, String8 indent, COFF_FileHeader *header)
{
Temp scratch = scratch_begin(&arena, 1);
String8 time_stamp = coff_string_from_time_stamp(scratch.arena, header->time_stamp);
String8 machine = coff_string_from_machine_type(header->machine);
String8 flags = coff_string_from_flags(scratch.arena, header->flags);
rd_printf("# COFF File Header");
rd_indent();
rd_printf("Time Stamp : %#x (%S)", header->time_stamp, time_stamp );
rd_printf("Machine : %#x %S", header->machine, machine );
rd_printf("Section Count : %u", header->section_count );
rd_printf("Symbol Table : %#x", header->symbol_table_foff );
rd_printf("Symbol Count : %u", header->symbol_count );
rd_printf("Optional Header Size: %#x (%m)", header->optional_header_size, header->optional_header_size);
rd_printf("Flags : %#x (%S)", header->flags, flags );
rd_unindent();
scratch_end(scratch);
}
internal void
coff_print_import(Arena *arena, String8List *out, String8 indent, COFF_ParsedArchiveImportHeader *header)
{
Temp scratch = scratch_begin(&arena, 1);
String8 machine = coff_string_from_machine_type(header->machine);
String8 time_stamp = coff_string_from_time_stamp(scratch.arena, header->time_stamp);
rd_printf("# Import");
rd_indent();
rd_printf("Version : %u", header->version );
rd_printf("Machine : %S", machine );
rd_printf("Time Stamp: %#x (%S)", header->time_stamp, time_stamp );
rd_printf("Data Size : %#x (%m)", header->data_size, header->data_size);
rd_printf("Hint : %u", header->hint_or_ordinal);
rd_printf("Type : %u", header->type );
rd_printf("Import By : %u", header->import_by );
rd_printf("Function : %S", header->func_name );
rd_printf("DLL : %S", header->dll_name );
rd_unindent();
scratch_end(scratch);
}
internal void
coff_print_big_obj(Arena *arena, String8List *out, String8 indent, String8 raw_data, RD_Option opts)
{
Temp scratch = scratch_begin(&arena, 1);
COFF_FileHeaderInfo header_info = coff_file_header_info_from_data(raw_data);
String8 raw_header = str8_substr(raw_data, header_info.header_range);
String8 raw_section_table = str8_substr(raw_data, header_info.section_table_range);
String8 raw_string_table = str8_substr(raw_data, header_info.string_table_range);
COFF_BigObjHeader *big_obj = (COFF_BigObjHeader *)raw_header.str;
COFF_SectionHeader *section_table = (COFF_SectionHeader *)raw_section_table.str;
COFF_Symbol32Array symbol_table = coff_symbol_array_from_data_32(scratch.arena, raw_data, header_info.symbol_table_range.min, big_obj->symbol_count);
if (opts & RD_Option_Headers) {
coff_print_big_obj_header(arena, out, indent, big_obj);
rd_newline();
}
if (opts & RD_Option_Sections) {
Rng1U64 sect_headers_range = rng_1u64(sizeof(*big_obj), sizeof(*big_obj) + sizeof(COFF_SectionHeader)*big_obj->section_count);
Rng1U64 symbols_range = rng_1u64(big_obj->symbol_table_foff, big_obj->symbol_table_foff + sizeof(COFF_Symbol32)*big_obj->symbol_count);
if (sect_headers_range.max > raw_data.size) {
rd_errorf("not enough bytes to read big obj section headers");
goto exit;
}
if (big_obj->symbol_count) {
if (symbols_range.max > raw_data.size) {
rd_errorf("not enough bytes to read big obj symbol table");
goto exit;
}
if (contains_1u64(symbols_range, sect_headers_range.min) ||
contains_1u64(symbols_range, sect_headers_range.max)) {
rd_errorf("section headers and symbol table ranges overlap");
goto exit;
}
}
coff_print_section_table(arena, out, indent, raw_string_table, symbol_table, big_obj->section_count, section_table);
rd_newline();
}
if (opts & RD_Option_Relocs) {
coff_print_relocs(arena, out, indent, raw_data, raw_string_table, big_obj->machine, big_obj->section_count, section_table, symbol_table);
rd_newline();
}
if (opts & RD_Option_Symbols) {
coff_print_symbol_table(arena, out, indent, raw_data, 1, raw_string_table, symbol_table);
rd_newline();
}
exit:;
scratch_end(scratch);
}
internal void
coff_print_obj(Arena *arena, String8List *out, String8 indent, String8 raw_data, RD_Option opts)
{
Temp scratch = scratch_begin(&arena, 1);
COFF_FileHeaderInfo header_info = coff_file_header_info_from_data(raw_data);
String8 raw_header = str8_substr(raw_data, header_info.header_range);
String8 raw_section_table = str8_substr(raw_data, header_info.section_table_range);
String8 raw_string_table = str8_substr(raw_data, header_info.string_table_range);
COFF_FileHeader *header = (COFF_FileHeader *)raw_header.str;
COFF_SectionHeader *section_table = (COFF_SectionHeader *)raw_section_table.str;
COFF_Symbol32Array symbol_table = coff_symbol_array_from_data_16(scratch.arena, raw_data, header_info.symbol_table_range.min, header->symbol_count);
Arch arch = arch_from_coff_machine(header->machine);
if (opts & RD_Option_Headers) {
coff_print_file_header(arena, out, indent, header);
rd_newline();
}
if (opts & RD_Option_Sections) {
Rng1U64 sect_headers_range = rng_1u64(sizeof(*header), sizeof(*header) + sizeof(COFF_SectionHeader)*header->section_count);
Rng1U64 symbols_range = rng_1u64(header->symbol_table_foff, header->symbol_table_foff + sizeof(COFF_Symbol16)*header->symbol_count);
if (sect_headers_range.max > raw_data.size) {
rd_errorf("not enough bytes to read obj section headers");
goto exit;
}
if (header->symbol_count) {
if (symbols_range.max > raw_data.size) {
rd_errorf("not enough bytes to read obj symbol table");
goto exit;
}
if (contains_1u64(symbols_range, sect_headers_range.min) ||
contains_1u64(symbols_range, sect_headers_range.max)) {
rd_errorf("section headers and symbol table ranges overlap");
goto exit;
}
}
coff_print_section_table(arena, out, indent, raw_string_table, symbol_table, header->section_count, section_table);
rd_newline();
}
if (opts & RD_Option_Relocs) {
coff_print_relocs(arena, out, indent, raw_data, raw_string_table, header->machine, header->section_count, section_table, symbol_table);
rd_newline();
}
if (opts & RD_Option_Symbols) {
coff_print_symbol_table(arena, out, indent, raw_data, 0, raw_string_table, symbol_table);
rd_newline();
}
RD_MarkerArray *section_markers = 0;
if (opts & (RD_Option_Disasm|RD_Option_Rawdata)) {
section_markers = rd_section_markers_from_coff_symbol_table(scratch.arena, raw_string_table, header->section_count, symbol_table);
}
if (opts & RD_Option_Rawdata) {
coff_raw_data_sections(arena, out, indent, raw_data, 1, section_markers, header->section_count, section_table);
}
if (opts & RD_Option_Disasm) {
coff_disasm_sections(arena, out, indent, raw_data, header->machine, 0, 1, section_markers, header->section_count, section_table);
rd_newline();
}
if (opts & RD_Option_Codeview) {
cv_format_debug_sections(arena, out, indent, raw_data, raw_string_table, header->section_count, section_table);
}
if (opts & RD_Option_Dwarf) {
DW_Input dwarf_input = dw_input_from_coff_section_table(scratch.arena, raw_data, raw_string_table, header->section_count, section_table);
dw_format(arena, out, indent, opts, &dwarf_input, arch, ExecutableImageKind_CoffPe);
}
exit:;
scratch_end(scratch);
}
internal void
coff_print_archive(Arena *arena, String8List *out, String8 indent, String8 raw_archive, RD_Option opts)
{
Temp scratch = scratch_begin(&arena, 1);
COFF_ArchiveParse archive_parse = coff_archive_parse_from_data(raw_archive);
if (archive_parse.error.size) {
rd_errorf("%S", archive_parse.error);
return;
}
COFF_ArchiveFirstMember first_member = archive_parse.first_member;
{
rd_printf("# First Header");
rd_indent();
rd_printf("Symbol Count : %u", first_member.symbol_count);
rd_printf("String Table Size: %#llx (%M)", first_member.string_table.size, first_member.string_table.size);
rd_printf("Members:");
rd_indent();
String8List string_table = str8_split_by_string_chars(scratch.arena, first_member.string_table, str8_lit("\0"), 0);
if (string_table.node_count == first_member.member_offset_count) {
String8Node *string_n = string_table.first;
for (U64 i = 0; i < string_table.node_count; ++i, string_n = string_n->next) {
U32 offset = from_be_u32(first_member.member_offsets[i]);
rd_printf("[%4u] %#08x %S", i, offset, string_n->string);
}
} else {
rd_errorf("Member offset count (%llu) doesn't match string table count (%llu)", first_member.member_offset_count);
}
rd_unindent();
rd_unindent();
rd_newline();
}
if (archive_parse.has_second_header) {
COFF_ArchiveSecondMember second_member = archive_parse.second_member;
rd_printf("# Second Header");
rd_indent();
rd_printf("Member Count : %u", second_member.member_count);
rd_printf("Symbol Count : %u", second_member.symbol_count);
rd_printf("String Table Size: %#llx (%M)", second_member.string_table.size, second_member.string_table.size);
String8List string_table = str8_split_by_string_chars(scratch.arena, second_member.string_table, str8_lit("\0"), 0);
rd_printf("Members:");
rd_indent();
if (second_member.symbol_index_count == second_member.symbol_count) {
String8Node *string_n = string_table.first;
for (U64 i = 0; i < second_member.symbol_count; ++i, string_n = string_n->next) {
U16 symbol_number = second_member.symbol_indices[i];
if (symbol_number > 0 && symbol_number <= second_member.member_offset_count) {
U16 symbol_idx = symbol_number - 1;
U32 member_offset = second_member.member_offsets[i];
rd_printf("[%4u] %#08x %S", i, member_offset, string_n->string);
} else {
rd_errorf("[%4u] Out of bounds symbol number %u", i, symbol_number);
break;
}
}
} else {
rd_errorf("Symbol index count %u doesn't match symbol count %u",
second_member.symbol_index_count, second_member.symbol_count);
}
rd_unindent();
rd_unindent();
rd_newline();
}
if (archive_parse.has_long_names && opts & RD_Option_LongNames) {
rd_printf("# Long Names");
rd_indent();
String8List long_names = str8_split_by_string_chars(scratch.arena, archive_parse.long_names, str8_lit("\0"), 0);
U64 name_idx = 0;
for (String8Node *name_n = long_names.first; name_n != 0; name_n = name_n->next, ++name_idx) {
U64 offset = (U64)(name_n->string.str - archive_parse.long_names.str);
rd_printf("[%-4u] %#08x %S", name_idx, offset, name_n->string);
}
rd_unindent();
rd_newline();
}
U64 member_offset_count = 0;
U32 *member_offsets = 0;
if (archive_parse.has_second_header) {
member_offset_count = archive_parse.second_member.member_offset_count;
member_offsets = archive_parse.second_member.member_offsets;
} else {
HashTable *ht = hash_table_init(scratch.arena, 0x1000);
for (U64 i = 0; i < archive_parse.first_member.member_offset_count; ++i) {
U32 member_offset = from_be_u32(archive_parse.first_member.member_offsets[i]);
if (!hash_table_search_u32(ht, member_offset)) {
hash_table_push_u32_raw(scratch.arena, ht, member_offset, 0);
}
}
member_offset_count = ht->count;
member_offsets = keys_from_hash_table_u32(scratch.arena, ht);
radsort(member_offsets, member_offset_count, u32_is_before);
}
rd_printf("# Members");
rd_indent();
for (U64 i = 0; i < member_offset_count; ++i) {
U64 next_member_offset = i+1 < member_offset_count ? member_offsets[i+1] : raw_archive.size;
U64 member_offset = member_offsets[i];
String8 raw_member = str8_substr(raw_archive, rng_1u64(member_offset, next_member_offset));
COFF_ArchiveMember member = coff_archive_member_from_data(raw_member);
COFF_DataType member_type = coff_data_type_from_data(member.data);
rd_printf("Member @ %#llx", member_offset);
rd_indent();
if (opts & RD_Option_Headers) {
coff_print_archive_member_header(arena, out, indent, member.header, archive_parse.long_names);
rd_newline();
}
switch (member_type) {
case COFF_DataType_Obj: {
coff_print_obj(arena, out, indent, member.data, opts);
} break;
case COFF_DataType_BigObj: {
coff_print_big_obj(arena, out, indent, member.data, opts);
} break;
case COFF_DataType_Import: {
if (opts & RD_Option_Headers) {
COFF_ParsedArchiveImportHeader header = {0};
U64 parse_size = coff_parse_import(member.data, 0, &header);
if (parse_size) {
coff_print_import(arena, out, indent, &header);
} else {
rd_errorf("not enough bytes to parse import header");
}
}
} break;
case COFF_DataType_Null: {
rd_errorf("unknown member format", member_offset);
} break;
}
rd_unindent();
rd_newline();
}
rd_unindent();
scratch_end(scratch);
}
#endif
-22
View File
@@ -1,22 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef COFF_DUMP_H
#define COFF_DUMP_H
#if 0
internal void coff_print_archive_member_header(Arena *arena, String8List *out, String8 indent, COFF_ParsedArchiveMemberHeader header, String8 long_names);
internal void coff_print_section_table (Arena *arena, String8List *out, String8 indent, String8 string_table, COFF_Symbol32Array symbols, U64 sect_count, COFF_SectionHeader *sect_headers);
internal void coff_disasm_sections (Arena *arena, String8List *out, String8 indent, String8 raw_data, COFF_MachineType machine, U64 image_base, B32 is_obj, RD_MarkerArray *section_markers, U64 section_count, COFF_SectionHeader *sections);
internal void coff_raw_data_sections (Arena *arena, String8List *out, String8 indent, String8 raw_data, B32 is_obj, RD_MarkerArray *section_markers, U64 section_count, COFF_SectionHeader *sections);
internal void coff_print_relocs (Arena *arena, String8List *out, String8 indent, String8 raw_data, String8 string_table, COFF_MachineType machine, U64 sect_count, COFF_SectionHeader *sect_headers, COFF_Symbol32Array symbols);
internal void coff_print_symbol_table (Arena *arena, String8List *out, String8 indent, String8 raw_data, B32 is_big_obj, String8 string_table, COFF_Symbol32Array symbols);
internal void coff_print_big_obj_header (Arena *arena, String8List *out, String8 indent, COFF_BigObjHeader *header);
internal void coff_print_file_header (Arena *arena, String8List *out, String8 indent, COFF_FileHeader *header);
internal void coff_print_import (Arena *arena, String8List *out, String8 indent, COFF_ParsedArchiveImportHeader *header);
internal void coff_print_big_obj (Arena *arena, String8List *out, String8 indent, String8 raw_data, RD_Option opts);
internal void coff_print_obj (Arena *arena, String8List *out, String8 indent, String8 raw_data, RD_Option opts);
internal void coff_print_archive (Arena *arena, String8List *out, String8 indent, String8 raw_archive, RD_Option opts);
#endif
#endif // COFF_DUMP_H
-1
View File
@@ -1 +0,0 @@
-6
View File
@@ -1,6 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "coff/coff.c"
#include "coff/coff_parse.c"
#include "coff/coff_dump.c"
-11
View File
@@ -1,11 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef COFF_INC_H
#define COFF_INC_H
#include "coff/coff.h"
#include "coff/coff_parse.h"
#include "coff/coff_dump.h"
#endif // COFF_INC_H
-387
View File
@@ -1,387 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal COFF_LibWriterSymbolNode *
coff_lib_writer_symbol_list_push(Arena *arena, COFF_LibWriterSymbolList *list, COFF_LibWriterSymbol symbol)
{
COFF_LibWriterSymbolNode *node = push_array_no_zero(arena, COFF_LibWriterSymbolNode, 1);
node->next = 0;
node->data = symbol;
SLLQueuePush(list->first, list->last, node);
list->count += 1;
return node;
}
internal COFF_LibWriterMemberNode *
coff_lib_writer_member_list_push(Arena *arena, COFF_LibWriterMemberList *list, COFF_LibWriterMember member)
{
COFF_LibWriterMemberNode *node = push_array_no_zero(arena, COFF_LibWriterMemberNode, 1);
node->next = 0;
node->data = member;
SLLQueuePush(list->first, list->last, node);
list->count += 1;
return node;
}
internal COFF_LibWriterSymbol *
coff_lib_writer_symbol_array_from_list(Arena *arena, COFF_LibWriterSymbolList list)
{
COFF_LibWriterSymbol *arr = push_array_no_zero(arena, COFF_LibWriterSymbol, list.count + 2);
COFF_LibWriterSymbol *ptr = arr + 1;
for (COFF_LibWriterSymbolNode *i = list.first; i != 0; i = i->next, ptr += 1) {
*ptr = i->data;
}
MemoryZeroStruct(&arr[0]);
MemoryZeroStruct(&arr[list.count+1]);
return arr;
}
internal COFF_LibWriterMember *
coff_lib_writer_member_array_from_list(Arena *arena, COFF_LibWriterMemberList list)
{
COFF_LibWriterMember *arr = push_array_no_zero(arena, COFF_LibWriterMember, list.count);
COFF_LibWriterMember *ptr = arr;
for (COFF_LibWriterMemberNode *i = list.first; i != 0; i = i->next, ptr += 1) {
ptr->name = push_str8_copy(arena, i->data.name);
ptr->data = push_str8_copy(arena, i->data.data);
}
return arr;
}
internal int
coff_lib_writer_symbol_name_compar(const void *raw_a, const void *raw_b)
{
const COFF_LibWriterSymbol *sa = raw_a;
const COFF_LibWriterSymbol *sb = raw_b;
return str8_compar_case_sensitive(&sa->name, &sb->name);
}
internal int
coff_lib_writer_symbol_is_before_name(void *raw_a, void *raw_b)
{
return coff_lib_writer_symbol_name_compar(raw_a, raw_b) < 0;
}
internal int
coff_lib_writer_symbol_is_before_member_idx(void *raw_a, void *raw_b)
{
COFF_LibWriterSymbol *a = raw_a, *b = raw_b;
return a->member_idx < b->member_idx;
}
internal void
coff_lib_writer_symbol_array_sort_on_name(COFF_LibWriterSymbol *arr, U64 count)
{
Assert(count >= 2);
radsort(arr + 1, count - 2, coff_lib_writer_symbol_is_before_name);
}
internal void
coff_lib_writer_symbol_array_sort_on_member_idx(COFF_LibWriterSymbol *arr, U64 count)
{
Assert(count >= 2);
radsort(arr + 1, count - 2, coff_lib_writer_symbol_is_before_member_idx);
}
internal COFF_LibWriter *
coff_lib_writer_alloc(void)
{
Arena *arena = arena_alloc();
COFF_LibWriter *writer = push_array(arena, COFF_LibWriter, 1);
writer->arena = arena;
return writer;
}
internal void
coff_lib_writer_release(COFF_LibWriter **writer_ptr)
{
arena_release((*writer_ptr)->arena);
*writer_ptr = 0;
}
internal U64
coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_data)
{
U64 member_idx = writer->member_list.count;
// push obj member
COFF_LibWriterMember member = { .name = obj_path, .data = obj_data };
COFF_LibWriterMemberNode *member_node = coff_lib_writer_member_list_push(writer->arena, &writer->member_list, member);
// push external symbols
COFF_FileHeaderInfo obj_header = coff_file_header_info_from_data(obj_data);
String8 string_table = str8_substr(obj_data, obj_header.string_table_range);
String8 symbol_table = str8_substr(obj_data, obj_header.symbol_table_range);
COFF_ParsedSymbol symbol;
for (U64 symbol_idx = 0; symbol_idx < obj_header.symbol_count; symbol_idx += (1 + symbol.aux_symbol_count)) {
symbol = coff_parse_symbol(obj_header, string_table, symbol_table, symbol_idx);
COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(symbol);
if (interp == COFF_SymbolValueInterp_Regular || interp == COFF_SymbolValueInterp_Common || interp == COFF_SymbolValueInterp_Abs) {
if (symbol.storage_class == COFF_SymStorageClass_External) {
COFF_LibWriterSymbol lib_symbol = { .name = symbol.name, .member_idx = member_idx };
coff_lib_writer_symbol_list_push(writer->arena, &writer->symbol_list, lib_symbol);
}
}
}
return member_idx;
}
internal void
coff_lib_writer_push_import(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportByType import_by, String8 name, U16 hint_or_ordinal, COFF_ImportType import_type)
{
U64 member_idx = lib_writer->member_list.count;
String8 member_data = coff_make_import_header(lib_writer->arena, machine, time_stamp, dll_name, import_by, name, hint_or_ordinal, import_type);
COFF_LibWriterMember member = { .name = dll_name, .data = member_data };
COFF_LibWriterMemberNode *member_node = coff_lib_writer_member_list_push(lib_writer->arena, &lib_writer->member_list, member);
if (name.size) {
switch (import_type) {
case COFF_ImportHeader_Code: {
COFF_LibWriterSymbol thunk_symbol = {0};
thunk_symbol.name = push_str8_copy(lib_writer->arena, name);
thunk_symbol.member_idx = member_idx;
coff_lib_writer_symbol_list_push(lib_writer->arena, &lib_writer->symbol_list, thunk_symbol);
COFF_LibWriterSymbol imp_symbol = {0};
imp_symbol.name = push_str8f(lib_writer->arena, "__imp_%S", name);
imp_symbol.member_idx = member_idx;
coff_lib_writer_symbol_list_push(lib_writer->arena, &lib_writer->symbol_list, imp_symbol);
} break;
case COFF_ImportHeader_Data: {
COFF_LibWriterSymbol imp_symbol = {0};
imp_symbol.name = push_str8f(lib_writer->arena, "__imp_%S", name);
imp_symbol.member_idx = member_idx;
coff_lib_writer_symbol_list_push(lib_writer->arena, &lib_writer->symbol_list, imp_symbol);
} break;
case COFF_ImportHeader_Const: { NotImplemented; } break;
default: { InvalidPath; } break;
}
}
}
internal String8
coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeStamp time_stamp, U16 mode, B32 emit_second_member)
{
Temp scratch = scratch_begin(&arena, 1);
// member lists -> arrays
U64 member_count = lib_writer->member_list.count;
COFF_LibWriterMember *member_array = coff_lib_writer_member_array_from_list(scratch.arena, lib_writer->member_list);
// serialize members
U64 *member_offsets = push_array_no_zero(scratch.arena, U64, member_count);
String8List long_names_list = {0};
String8List member_data_list = {0};
{
HashTable *name_ht = hash_table_init(scratch.arena, 1024);
for EachIndex(member_idx, member_count) {
COFF_LibWriterMember *member = &member_array[member_idx];
// make member name
String8 name;
U64 name_with_slash_size = member->name.size + 1;
if (name_with_slash_size > COFF_Archive_MaxShortNameSize) {
// have we seen this member name before?
KeyValuePair *is_present = hash_table_search_string(name_ht, member->name);
if (is_present) {
name = is_present->value_string;
} else {
name = push_str8f(scratch.arena, "/%u", long_names_list.total_size);
str8_list_pushf(scratch.arena, &long_names_list, "%S/\n", member->name);
hash_table_push_string_string(scratch.arena, name_ht, member->name, name);
}
} else {
name = push_str8f(scratch.arena, "%S/", member->name);
}
member_offsets[member_idx] = member_data_list.total_size;
String8 member_data = member->data;
String8 member_header = coff_make_lib_member_header(scratch.arena, name, time_stamp, 0, 0, mode, member_data.size);
str8_list_push(scratch.arena, &member_data_list, member_header);
str8_list_push(scratch.arena, &member_data_list, member_data);
{
U64 pad_size = AlignPadPow2(member_data_list.total_size, COFF_Archive_MemberAlign);
U8 *pad = push_array(scratch.arena, U8, pad_size);
str8_list_push(scratch.arena, &member_data_list, str8(pad, pad_size));
}
}
}
// write long names member
if (long_names_list.total_size) {
String8 header = coff_make_lib_member_header(scratch.arena, str8_lit("//"), time_stamp, 0, 0, mode, long_names_list.total_size);
String8 data = str8_list_join(scratch.arena, &long_names_list, 0);
U64 member_offset = member_data_list.total_size + data.size + header.size;
{
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
U8 *pad = push_array(scratch.arena, U8, pad_size);
str8_list_push_front(scratch.arena, &member_data_list, str8(pad, pad_size));
}
str8_list_push_front(scratch.arena, &member_data_list, data);
str8_list_push_front(scratch.arena, &member_data_list, header);
}
// compute size for symbol string table
U32 name_buffer_size = 0;
for (COFF_LibWriterSymbolNode *node = lib_writer->symbol_list.first; node != 0; node = node->next) {
name_buffer_size += node->data.name.size;
name_buffer_size += 1; // null
}
// compute members base offset
U64 members_base_offset;
{
U64 sizeof_first_header = sizeof(COFF_ArchiveMemberHeader) + sizeof(U32) + sizeof(U32) * lib_writer->symbol_list.count + name_buffer_size;
U64 sizeof_second_header = sizeof(COFF_ArchiveMemberHeader) + sizeof(U32) + sizeof(U32) * member_count + sizeof(U32) + sizeof(U16) * lib_writer->symbol_list.count + name_buffer_size;
U64 sizeof_long_names = sizeof(COFF_ArchiveMemberHeader) + long_names_list.total_size;
sizeof_first_header = AlignPow2(sizeof_first_header, COFF_Archive_MemberAlign);
sizeof_second_header = AlignPow2(sizeof_second_header, COFF_Archive_MemberAlign);
sizeof_long_names = AlignPow2(sizeof_long_names, COFF_Archive_MemberAlign);
members_base_offset = sizeof(g_coff_archive_sig);
members_base_offset += sizeof_first_header;
if (emit_second_member) {
members_base_offset += sizeof_second_header;
}
if (long_names_list.total_size) {
members_base_offset += sizeof_long_names;
}
}
// write second linker member
if (emit_second_member) {
U64 symbols_count;
COFF_LibWriterSymbol *symbols;
{
U64 symbols_count_with_null = lib_writer->symbol_list.count + 2;
COFF_LibWriterSymbol *symbols_with_null = coff_lib_writer_symbol_array_from_list(scratch.arena, lib_writer->symbol_list);
coff_lib_writer_symbol_array_sort_on_name(symbols_with_null, symbols_count_with_null);
symbols_count = symbols_count_with_null - 2;
symbols = symbols_with_null + 1;
}
// write symbol name buffer
U8 *name_buffer = push_array_no_zero(scratch.arena, U8, name_buffer_size);
{
U64 name_cursor = 0;
for EachIndex(symbol_idx, symbols_count) {
COFF_LibWriterSymbol *symbol = &symbols[symbol_idx];
MemoryCopy(name_buffer + name_cursor, symbol->name.str, symbol->name.size);
name_buffer[name_cursor + symbol->name.size] = '\0';
name_cursor += symbol->name.size + 1;
}
}
U32 member_count32 = safe_cast_u32(member_count);
U32 symbol_count32 = safe_cast_u32(symbols_count);
U32 *member_off32_arr = push_array_no_zero(scratch.arena, U32, member_count);
U16 *member_idx16_arr = push_array_no_zero(scratch.arena, U16, symbols_count);
// write member offset array
for EachIndex(member_idx, member_count) {
U64 member_offset = members_base_offset + member_offsets[member_idx];
U32 member_off32 = safe_cast_u32(member_offset);
member_off32_arr[member_idx] = member_off32;
}
// write member offset indices for each symbol
for EachIndex(symbol_idx, symbols_count) {
// member offset indices are 1-based
U64 member_idx = symbols[symbol_idx].member_idx + 1;
U16 member_idx16 = safe_cast_u16(member_idx);
member_idx16_arr[symbol_idx] = member_idx16;
}
// layout second member data
String8List second_member_data_list = {0};
str8_list_push(scratch.arena, &second_member_data_list, str8_struct(&member_count32));
str8_list_push(scratch.arena, &second_member_data_list, str8_array(member_off32_arr, member_count));
str8_list_push(scratch.arena, &second_member_data_list, str8_struct(&symbol_count32));
str8_list_push(scratch.arena, &second_member_data_list, str8_array(member_idx16_arr, symbols_count));
str8_list_push(scratch.arena, &second_member_data_list, str8(name_buffer, name_buffer_size));
String8 member_data = str8_list_join(scratch.arena, &second_member_data_list, 0);
String8 member_header = coff_make_lib_member_header(scratch.arena, str8_lit("/"), time_stamp, 0, 0, mode, member_data.size);
U64 member_offset = member_data_list.total_size + member_data.size + member_header.size;
{
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
U8 *pad = push_array(scratch.arena, U8, pad_size);
str8_list_push_front(scratch.arena, &member_data_list, str8(pad, pad_size));
}
str8_list_push_front(scratch.arena, &member_data_list, member_data);
str8_list_push_front(scratch.arena, &member_data_list, member_header);
}
// write first linker member (obsolete, but kept for compatability reasons)
{
U64 symbols_count;
COFF_LibWriterSymbol *symbols;
{
U64 symbols_count_with_null = lib_writer->symbol_list.count + 2;
COFF_LibWriterSymbol *symbols_with_null = coff_lib_writer_symbol_array_from_list(scratch.arena, lib_writer->symbol_list);
coff_lib_writer_symbol_array_sort_on_member_idx(symbols_with_null, symbols_count_with_null);
symbols_count = symbols_count_with_null - 2;
symbols = symbols_with_null + 1;
}
// write symbol name buffer
U8 *name_buffer = push_array_no_zero(scratch.arena, U8, name_buffer_size);
{
U64 name_cursor = 0;
for EachIndex(symbol_idx, symbols_count) {
COFF_LibWriterSymbol *symbol = &symbols[symbol_idx];
MemoryCopy(name_buffer + name_cursor, symbol->name.str, symbol->name.size);
name_buffer[name_cursor + symbol->name.size] = '\0';
name_cursor += symbol->name.size + 1;
}
}
U32 symbol_count_be = from_be_u32(symbols_count);
U32 *member_off32_arr = push_array_no_zero(scratch.arena, U32, symbols_count);
for EachIndex(symbol_idx, symbols_count) {
COFF_LibWriterSymbol *symbol = &symbols[symbol_idx];
// write big endian member offset
U64 member_offset = members_base_offset + member_offsets[symbol->member_idx];
U32 member_off32 = from_be_u32(safe_cast_u32(member_offset));
member_off32_arr[symbol_idx] = member_off32;
}
// layout first member data
String8List first_member_data_list = {0};
str8_list_push(scratch.arena, &first_member_data_list, str8_struct(&symbol_count_be));
str8_list_push(scratch.arena, &first_member_data_list, str8_array(member_off32_arr, symbols_count));
str8_list_push(scratch.arena, &first_member_data_list, str8(name_buffer, name_buffer_size));
String8 member_data = str8_list_join(scratch.arena, &first_member_data_list, 0);
String8 member_header = coff_make_lib_member_header(scratch.arena, str8_lit("/"), time_stamp, 0, 0, mode, member_data.size);
U64 member_offset = sizeof(g_coff_archive_sig) + member_header.size + member_data.size;
{
U64 pad_size = AlignPadPow2(member_offset, COFF_Archive_MemberAlign);
U8 *pad = push_array(scratch.arena, U8, pad_size);
str8_list_push_front(scratch.arena, &member_data_list, str8(pad, pad_size));
}
str8_list_push_front(scratch.arena, &member_data_list, member_data);
str8_list_push_front(scratch.arena, &member_data_list, member_header);
}
// archive signature
str8_list_push_front(scratch.arena, &member_data_list, str8_struct(&g_coff_archive_sig));
String8 raw_lib = str8_list_join(arena, &member_data_list, 0);
scratch_end(scratch);
return raw_lib;
}
-70
View File
@@ -1,70 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef COFF_LIB_WRITER_H
#define COFF_LIB_WRITER_H
typedef struct COFF_LibWriterMember
{
String8 name;
String8 data;
} COFF_LibWriterMember;
typedef struct COFF_LibWriterMemberNode
{
COFF_LibWriterMember data;
struct COFF_LibWriterMemberNode *next;
} COFF_LibWriterMemberNode;
typedef struct COFF_LibWriterMemberList
{
U64 count;
COFF_LibWriterMemberNode *first;
COFF_LibWriterMemberNode *last;
} COFF_LibWriterMemberList;
typedef struct COFF_LibWriterSymbol
{
String8 name;
U64 member_idx;
} COFF_LibWriterSymbol;
typedef struct COFF_LibWriterSymbolNode
{
COFF_LibWriterSymbol data;
struct COFF_LibWriterSymbolNode *next;
} COFF_LibWriterSymbolNode;
typedef struct COFF_LibWriterSymbolList
{
U64 count;
COFF_LibWriterSymbolNode *first;
COFF_LibWriterSymbolNode *last;
} COFF_LibWriterSymbolList;
typedef struct COFF_LibWriter
{
Arena *arena;
COFF_LibWriterMemberList member_list;
COFF_LibWriterSymbolList symbol_list;
} COFF_LibWriter;
////////////////////////////////
internal COFF_LibWriterSymbolNode * coff_lib_writer_symbol_list_push(Arena *arena, COFF_LibWriterSymbolList *list, COFF_LibWriterSymbol symbol);
internal COFF_LibWriterMemberNode * coff_lib_writer_member_list_push(Arena *arena, COFF_LibWriterMemberList *list, COFF_LibWriterMember member);
internal COFF_LibWriterSymbol * coff_lib_writer_symbol_array_from_list(Arena *arena, COFF_LibWriterSymbolList list);
internal COFF_LibWriterMember * coff_lib_writer_member_array_from_list(Arena *arena, COFF_LibWriterMemberList list);
internal void coff_lib_writer_symbol_array_sort_on_name(COFF_LibWriterSymbol *arr, U64 count);
internal void coff_lib_writer_symbol_array_sort_on_member_idx(COFF_LibWriterSymbol *arr, U64 count);
internal COFF_LibWriter * coff_lib_writer_alloc(void);
internal void coff_lib_writer_release(COFF_LibWriter **writer_ptr);
internal U64 coff_lib_writer_push_obj(COFF_LibWriter *writer, String8 obj_path, String8 obj_data);
internal void coff_lib_writer_push_import(COFF_LibWriter *lib_writer, COFF_MachineType machine, COFF_TimeStamp time_stamp, String8 dll_name, COFF_ImportByType import_by, String8 name, U16 hint_or_ordinal, COFF_ImportType import_type);
internal String8 coff_lib_writer_serialize(Arena *arena, COFF_LibWriter *lib_writer, COFF_TimeStamp time_stamp, U16 mode, B32 emit_second_member);
#endif // COFF_LIB_WRITER_H
-500
View File
@@ -1,500 +0,0 @@
internal COFF_ObjWriter*
coff_obj_writer_alloc(COFF_TimeStamp time_stamp, COFF_MachineType machine)
{
Arena *arena = arena_alloc();
COFF_ObjWriter *obj_writer = push_array(arena, COFF_ObjWriter, 1);
obj_writer->arena = arena;
obj_writer->time_stamp = time_stamp;
obj_writer->machine = machine;
return obj_writer;
}
internal void
coff_obj_writer_release(COFF_ObjWriter **obj_writer)
{
arena_release((*obj_writer)->arena);
*obj_writer = 0;
}
internal String8
coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer)
{
Temp scratch = scratch_begin(&arena, 1);
String8List srl = {0};
String8List string_table = {0};
U32 *string_table_size = push_array(scratch.arena, U32, 1);
*string_table_size = sizeof(*string_table_size);
str8_list_push(scratch.arena, &string_table, str8_struct(string_table_size));
//
// assing section numbers
//
U64 obj_sections_count;
COFF_ObjSection **obj_sections;
{
obj_sections_count = obj_writer->sect_count;
obj_sections = push_array(scratch.arena, COFF_ObjSection *, obj_writer->sect_count);
U64 sect_idx = 0;
for (COFF_ObjSectionNode *sect_n = obj_writer->sect_first; sect_n != 0; sect_n = sect_n->next, sect_idx += 1) {
COFF_ObjSection *sect = &sect_n->v;
sect->section_number = sect_idx+1;
obj_sections[sect_idx] = sect;
}
}
AssertAlways(obj_sections_count <= max_U16);
//
// serialize symbol table
//
String8List symbol_table = {0};
{
{
U64 symbol_idx = 0;
for (COFF_ObjSymbolNode *symbol_n = obj_writer->symbol_first; symbol_n != 0; symbol_n = symbol_n->next) {
COFF_ObjSymbol *s = &symbol_n->v;
// assign symbol index
s->idx = symbol_idx++;
symbol_idx += s->aux_symbols.node_count;
}
}
U64 symbol_idx = 0;
for (COFF_ObjSymbolNode *symbol_n = obj_writer->symbol_first; symbol_n != 0; symbol_n = symbol_n->next) {
COFF_ObjSymbol *s = &symbol_n->v;
COFF_Symbol16 *d = push_array(scratch.arena, COFF_Symbol16, 1);
str8_list_push(scratch.arena, &symbol_table, str8_struct(d));
COFF_SymbolName name = {0};
// long name
if (s->name.size > sizeof(name.short_name)) {
U64 string_table_offset = string_table.total_size;
str8_list_push(scratch.arena, &string_table, s->name);
str8_list_push(scratch.arena, &string_table, str8_lit("\0"));
name.long_name.zeroes = 0;
name.long_name.string_table_offset = safe_cast_u32(string_table_offset);
}
// short name
else {
MemoryCopyStr8(name.short_name, s->name);
MemoryZeroTyped(name.short_name + s->name.size, sizeof(name.short_name) - s->name.size);
}
// symbol header
AssertAlways(s->aux_symbols.node_count <= max_U8);
d->name = name;
d->value = s->value;
switch (s->loc.type) {
case COFF_SymbolLocation_Null: break;
case COFF_SymbolLocation_Section: d->section_number = safe_cast_u16(s->loc.u.section->section_number); break;
case COFF_SymbolLocation_Abs: d->section_number = COFF_Symbol_AbsSection16; break;
case COFF_SymbolLocation_Undef: d->section_number = COFF_Symbol_UndefinedSection; break;
case COFF_SymbolLocation_Common: d->section_number = COFF_Symbol_UndefinedSection; break;
}
d->type = s->type;
d->storage_class = s->storage_class;
d->aux_symbol_count = 0;
U64 start_symbol_idx = symbol_idx;
if (s->storage_class == COFF_SymStorageClass_WeakExternal) {
if (s->aux_symbols.node_count > 0) {
COFF_ObjSymbolWeak *s_weak = (COFF_ObjSymbolWeak *)s->aux_symbols.first->string.str;
COFF_SymbolWeakExt *d_weak = push_array(scratch.arena, COFF_SymbolWeakExt, 1);
d_weak->tag_index = s_weak->tag ? s_weak->tag->idx : max_U32;
d_weak->characteristics = s_weak->characteristics;
str8_list_push(scratch.arena, &symbol_table, str8_struct(d_weak));
symbol_idx += 1;
}
} else if (s->storage_class == COFF_SymStorageClass_Static) {
if (s->aux_symbols.node_count > 0) {
Assert(s->loc.type == COFF_SymbolLocation_Section);
COFF_ObjSection *sect = s->loc.u.section;
COFF_ObjSymbolSecDef *s_sd = (COFF_ObjSymbolSecDef *)s->aux_symbols.first->string.str;
COFF_SymbolSecDef *d_sd = push_array(scratch.arena, COFF_SymbolSecDef, 1);
d_sd->length = safe_cast_u32(sect->data.total_size);
d_sd->number_of_relocations = (U16)sect->reloc_count;
d_sd->check_sum = 0;
d_sd->number_lo = s_sd->selection == COFF_ComdatSelect_Associative ? safe_cast_u16(s_sd->associate->section_number) : 0;
d_sd->selection = s_sd->selection;
str8_list_push(scratch.arena, &symbol_table, str8_struct(d_sd));
symbol_idx += 1;
}
}
U8 processed_aux_symbol_count = (U8)(symbol_idx - start_symbol_idx);
for (U64 aux_idx = processed_aux_symbol_count; aux_idx < s->aux_symbols.node_count; aux_idx += 1) {
COFF_Symbol16 *a = push_array(scratch.arena, COFF_Symbol16, 1);
str8_list_push(scratch.arena, &symbol_table, str8_struct(a));
}
d->aux_symbol_count = (U8)s->aux_symbols.node_count;
}
}
//
// file header
//
COFF_FileHeader *file_header = push_array(scratch.arena, COFF_FileHeader, 1);
file_header->machine = obj_writer->machine;
file_header->section_count = obj_sections_count;
file_header->time_stamp = obj_writer->time_stamp;
file_header->symbol_table_foff = 0;
file_header->symbol_count = safe_cast_u32(symbol_table.node_count);
file_header->optional_header_size = 0;
file_header->flags = 0;
str8_list_push(scratch.arena, &srl, str8_struct(file_header));
//
// section table
//
COFF_SectionHeader *sectab = push_array(scratch.arena, COFF_SectionHeader, obj_sections_count);
str8_list_push(scratch.arena, &srl, str8_array(sectab, obj_sections_count));
{
for (U64 sect_idx = 0; sect_idx < obj_sections_count; sect_idx += 1) {
COFF_ObjSection *s = obj_sections[sect_idx];
COFF_SectionHeader *d = &sectab[sect_idx];
// section name
String8 sect_name = s->name;
if (sect_name.size > sizeof(d->name)) {
U64 sect_name_off = string_table.total_size;
str8_list_push(scratch.arena, &string_table, push_cstr(scratch.arena, sect_name));
sect_name = push_str8f(scratch.arena, "/%u", sect_name_off);
AssertAlways(sect_name.size <= sizeof(d->name));
}
// alloc zero nodes
for (String8Node *data_n = s->data.first; data_n != 0; data_n = data_n->next) {
if (data_n->string.str == 0 && data_n->string.size > 0) {
data_n->string = str8(push_array(scratch.arena, U8, data_n->string.size), data_n->string.size);
}
}
// section data
U64 data_foff = 0;
U64 data_size = 0;
if (s->data.total_size > 0) {
data_foff = srl.total_size;
data_size = s->data.total_size;
str8_list_concat_in_place(&srl, &s->data);
}
// section relocs
U64 relocs_foff = 0;
if (s->reloc_count) {
AssertAlways(s->reloc_count <= max_U16);
COFF_Reloc *relocs = push_array(scratch.arena, COFF_Reloc, s->reloc_count);
U64 reloc_idx = 0;
for (COFF_ObjRelocNode *reloc_n = s->reloc_first; reloc_n != 0; reloc_n = reloc_n->next, reloc_idx += 1) {
COFF_ObjReloc *rs = &reloc_n->v;
COFF_Reloc *rd = &relocs[reloc_idx];
rd->apply_off = rs->apply_off;
rd->isymbol = rs->symbol->idx;
rd->type = rs->type;
}
relocs_foff = srl.total_size;
str8_list_push(scratch.arena, &srl, str8_array(relocs, s->reloc_count));
}
// section header
MemoryCopyStr8(d->name, sect_name);
MemoryZeroTyped(d->name + sect_name.size, sizeof(d->name) - sect_name.size);
d->vsize = 0;
d->voff = 0;
d->fsize = data_size;
d->foff = data_foff;
d->relocs_foff = relocs_foff;
d->lines_foff = 0;
d->reloc_count = safe_cast_u32(s->reloc_count);
d->line_count = 0;
d->flags = s->flags;
}
}
//
// symbol table
//
if (symbol_table.total_size || string_table.total_size > sizeof(*string_table_size)) {
file_header->symbol_table_foff = srl.total_size;
str8_list_concat_in_place(&srl, &symbol_table);
}
//
// string table
//
if (string_table.total_size) {
*string_table_size = safe_cast_u32(string_table.total_size);
str8_list_concat_in_place(&srl, &string_table);
}
//
// join
//
String8 obj = str8_list_join(arena, &srl, 0);
scratch_end(scratch);
return obj;
}
internal COFF_ObjSection *
coff_obj_writer_push_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags, String8 data)
{
COFF_ObjSectionNode *sect_n = push_array(obj_writer->arena, COFF_ObjSectionNode, 1);
SLLQueuePush(obj_writer->sect_first, obj_writer->sect_last, sect_n);
obj_writer->sect_count += 1;
COFF_ObjSection *sect = &sect_n->v;
sect->name = name;
sect->flags = flags;
if (data.size) {
str8_list_push(obj_writer->arena, &sect->data, data);
}
return sect;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymbolLocation loc, COFF_SymbolType type, COFF_SymStorageClass storage_class)
{
COFF_ObjSymbolNode *n = push_array(obj_writer->arena, COFF_ObjSymbolNode, 1);
SLLQueuePush(obj_writer->symbol_first, obj_writer->symbol_last, n);
obj_writer->symbol_count += 1;
COFF_ObjSymbol *s = &n->v;
s->name = name;
s->value = value;
s->loc = loc;
s->type = type;
s->storage_class = storage_class;
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_extern(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section)
{
COFF_SymbolLocation loc = {0};
loc.type = COFF_SymbolLocation_Section;
loc.u.section = section;
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, value, loc, (COFF_SymbolType){0}, COFF_SymStorageClass_External);
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_extern_func(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section)
{
COFF_SymbolType type = { .u.msb = COFF_SymDType_Func };
COFF_SymbolLocation loc = { .type = COFF_SymbolLocation_Section, .u.section = section };
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, value, loc, type, COFF_SymStorageClass_External);
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_static(COFF_ObjWriter *obj_writer, String8 name, U32 off, COFF_ObjSection *section)
{
COFF_SymbolLocation loc = {0};
loc.type = COFF_SymbolLocation_Section;
loc.u.section = section;
COFF_SymbolType symtype = {0};
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, off, loc, symtype, COFF_SymStorageClass_Static);
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_secdef(COFF_ObjWriter *obj_writer, COFF_ObjSection *section, COFF_ComdatSelectType selection)
{
COFF_ObjSymbol *s = coff_obj_writer_push_symbol_static(obj_writer, section->name, 0, section);
COFF_ObjSymbolSecDef *sd = push_array(obj_writer->arena, COFF_ObjSymbolSecDef, 1);
sd->selection = selection;
str8_list_push(obj_writer->arena, &s->aux_symbols, str8_struct(sd));
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_associative(COFF_ObjWriter *obj_writer, COFF_ObjSection *head, COFF_ObjSection *associate)
{
COFF_ObjSymbol *s = coff_obj_writer_push_symbol_static(obj_writer, head->name, 0, head);
COFF_ObjSymbolSecDef *sd = push_array(obj_writer->arena, COFF_ObjSymbolSecDef, 1);
sd->selection = COFF_ComdatSelect_Associative;
sd->associate = associate;
str8_list_push(obj_writer->arena, &s->aux_symbols, str8_struct(sd));
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_weak(COFF_ObjWriter *obj_writer, String8 name, COFF_WeakExtType characteristics, COFF_ObjSymbol *tag)
{
COFF_SymbolLocation loc = {0};
COFF_SymbolType symtype = {0};
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, COFF_Symbol_UndefinedSection, loc, symtype, COFF_SymStorageClass_WeakExternal);
COFF_ObjSymbolWeak *weak_ext = push_array(obj_writer->arena, COFF_ObjSymbolWeak, 1);
weak_ext->tag = tag;
weak_ext->characteristics = characteristics;
str8_list_push(obj_writer->arena, &s->aux_symbols, str8_struct(weak_ext));
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_abs(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymStorageClass storage_class)
{
COFF_SymbolLocation loc = {0};
loc.type = COFF_SymbolLocation_Abs;
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, value, loc, (COFF_SymbolType){0}, storage_class);
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_undef(COFF_ObjWriter *obj_writer, String8 name)
{
COFF_SymbolType type = {0};
COFF_SymbolLocation loc = {0};
loc.type = COFF_SymbolLocation_Undef;
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, 0, loc, type, COFF_SymStorageClass_External);
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_undef_func(COFF_ObjWriter *obj_writer, String8 name)
{
COFF_SymbolType type = {0};
type.u.msb = COFF_SymDType_Func;
COFF_SymbolLocation loc = {0};
loc.type = COFF_SymbolLocation_Undef;
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, 0, loc, type, COFF_SymStorageClass_External);
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_undef_sect(COFF_ObjWriter *obj_writer, String8 name, U32 value)
{
COFF_SymbolType type = {0};
COFF_SymbolLocation loc = {0};
loc.type = COFF_SymbolLocation_Undef;
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, value, loc, type, COFF_SymStorageClass_Section);
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_sect(COFF_ObjWriter *obj_writer, String8 name, COFF_ObjSection *sect)
{
COFF_SymbolType type = {0};
COFF_SymbolLocation loc = {0};
loc.type = COFF_SymbolLocation_Section;
loc.u.section = sect;
// strip align flags
COFF_SectionFlags expected_flags = sect->flags & ~(COFF_SectionFlag_AlignMask << COFF_SectionFlag_AlignShift);
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, expected_flags, loc, type, COFF_SymStorageClass_Section);
return s;
}
internal COFF_ObjSymbol *
coff_obj_writer_push_symbol_common(COFF_ObjWriter *obj_writer, String8 name, U32 size)
{
COFF_SymbolType type = {0};
COFF_SymbolLocation loc = {0};
loc.type = COFF_SymbolLocation_Common;
COFF_ObjSymbol *s = coff_obj_writer_push_symbol(obj_writer, name, size, loc, type, COFF_SymStorageClass_External);
return s;
}
internal void
coff_obj_writer_set_default_symbol(COFF_ObjSymbol *weak_symbol, COFF_ObjSymbol *default_symbol)
{
AssertAlways(weak_symbol->storage_class == COFF_SymStorageClass_WeakExternal);
COFF_ObjSymbolWeak *w = (COFF_ObjSymbolWeak *)weak_symbol->aux_symbols.first->string.str;
w->tag = default_symbol;
}
internal COFF_ObjReloc*
coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType type)
{
COFF_ObjRelocNode *reloc_n = push_array(obj_writer->arena, COFF_ObjRelocNode, 1);
SLLQueuePush(sect->reloc_first, sect->reloc_last, reloc_n);
sect->reloc_count += 1;
COFF_ObjReloc *reloc = &reloc_n->v;
reloc->apply_off = apply_off;
reloc->symbol = symbol;
reloc->type = type;
return reloc;
}
internal COFF_ObjReloc *
coff_obj_writer_section_push_reloc_addr32(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol)
{
COFF_RelocType reloc_type = 0;
switch (obj_writer->machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr32; break;
default: { NotImplemented; } break;
}
return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type);
}
internal COFF_ObjReloc *
coff_obj_writer_section_push_reloc_addr(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol)
{
COFF_RelocType reloc_type = 0;
switch (obj_writer->machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr64; break;
default: { NotImplemented; } break;
}
return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type);
}
internal COFF_ObjReloc *
coff_obj_writer_section_push_reloc_voff(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol)
{
COFF_RelocType reloc_type = 0;
switch (obj_writer->machine) {
case COFF_MachineType_Unknown: break;
case COFF_MachineType_X64: reloc_type = COFF_Reloc_X64_Addr32Nb; break;
default: { NotImplemented; } break;
}
return coff_obj_writer_section_push_reloc(obj_writer, sect, apply_off, symbol, reloc_type);
}
internal void
coff_obj_writer_push_directive(COFF_ObjWriter *obj_writer, String8 directive)
{
if (obj_writer->drectve_sect == 0) {
local_persist const U8 bom_sig[] = { ' ', ' ', ' ' };
obj_writer->drectve_sect = coff_obj_writer_push_section(obj_writer, str8_lit(".drectve"), COFF_SectionFlag_LnkInfo|COFF_SectionFlag_LnkRemove|COFF_SectionFlag_Align1Bytes, str8_array_fixed(bom_sig));
}
String8List *data = &obj_writer->drectve_sect->data;
str8_list_push(obj_writer->arena, data, directive);
str8_list_pushf(obj_writer->arena, data, " ");
}
internal int
coff_obj_section_is_before(void *raw_a, void *raw_b)
{
COFF_ObjSection **a = raw_a;
COFF_ObjSection **b = raw_b;
return (*a)->section_number < (*b)->section_number;
}
-128
View File
@@ -1,128 +0,0 @@
#ifndef COFF_OBJ_WRITER_H
#define COFF_OBJ_WRITER_H
typedef enum
{
COFF_SymbolLocation_Null,
COFF_SymbolLocation_Section,
COFF_SymbolLocation_Abs,
COFF_SymbolLocation_Undef,
COFF_SymbolLocation_Common,
} COFF_SymbolLocationType;
typedef struct COFF_SymbolLocation
{
COFF_SymbolLocationType type;
union {
struct COFF_ObjSection *section;
} u;
} COFF_SymbolLocation;
typedef struct COFF_ObjSymbolWeak
{
struct COFF_ObjSymbol *tag;
COFF_WeakExtType characteristics;
} COFF_ObjSymbolWeak;
typedef struct COFF_ObjSymbolSecDef
{
COFF_ComdatSelectType selection;
struct COFF_ObjSection *associate;
} COFF_ObjSymbolSecDef;
typedef struct COFF_ObjSymbol
{
String8 name;
U32 value;
COFF_SymbolLocation loc;
COFF_SymbolType type;
COFF_SymStorageClass storage_class;
String8List aux_symbols;
U32 idx;
} COFF_ObjSymbol;
typedef struct COFF_ObjSymbolNode
{
struct COFF_ObjSymbolNode *next;
COFF_ObjSymbol v;
} COFF_ObjSymbolNode;
typedef struct COFF_ObjReloc
{
U32 apply_off;
COFF_ObjSymbol *symbol;
COFF_RelocType type;
} COFF_ObjReloc;
typedef struct COFF_ObjRelocNode
{
struct COFF_ObjRelocNode *next;
COFF_ObjReloc v;
} COFF_ObjRelocNode;
typedef struct COFF_ObjSection
{
String8 name;
String8List data;
COFF_SectionFlags flags;
U64 reloc_count;
COFF_ObjRelocNode *reloc_first;
COFF_ObjRelocNode *reloc_last;
U32 section_number;
} COFF_ObjSection;
typedef struct COFF_ObjSectionNode
{
struct COFF_ObjSectionNode *next;
COFF_ObjSection v;
} COFF_ObjSectionNode;
typedef struct COFF_ObjWriter
{
Arena *arena;
COFF_TimeStamp time_stamp;
COFF_MachineType machine;
U64 symbol_count;
COFF_ObjSymbolNode *symbol_first;
COFF_ObjSymbolNode *symbol_last;
U64 sect_count;
COFF_ObjSectionNode *sect_first;
COFF_ObjSectionNode *sect_last;
COFF_ObjSection *drectve_sect;
} COFF_ObjWriter;
////////////////////////////////
internal COFF_ObjWriter * coff_obj_writer_alloc(COFF_TimeStamp time_stamp, COFF_MachineType machine);
internal void coff_obj_writer_release(COFF_ObjWriter **obj_writer);
internal String8 coff_obj_writer_serialize(Arena *arena, COFF_ObjWriter *obj_writer);
internal COFF_ObjSection * coff_obj_writer_push_section(COFF_ObjWriter *obj_writer, String8 name, COFF_SectionFlags flags, String8 data);
internal COFF_ObjSymbol* coff_obj_writer_push_symbol(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymbolLocation loc, COFF_SymbolType type, COFF_SymStorageClass storage_class);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_extern_func(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_extern(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_ObjSection *section);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_static(COFF_ObjWriter *obj_writer, String8 name, U32 off, COFF_ObjSection *section);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_secdef(COFF_ObjWriter *obj_writer, COFF_ObjSection *section, COFF_ComdatSelectType selection);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_associative(COFF_ObjWriter *obj_writer, COFF_ObjSection *head, COFF_ObjSection *associate);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_weak(COFF_ObjWriter *obj_writer, String8 name, COFF_WeakExtType characteristics, COFF_ObjSymbol *tag);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_abs(COFF_ObjWriter *obj_writer, String8 name, U32 value, COFF_SymStorageClass storage_class);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef(COFF_ObjWriter *obj_writer, String8 name);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_func(COFF_ObjWriter *obj_writer, String8 name);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_undef_sect(COFF_ObjWriter *obj_writer, String8 name, U32 value);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_sect(COFF_ObjWriter *obj_writer, String8 name, COFF_ObjSection *sect);
internal COFF_ObjSymbol * coff_obj_writer_push_symbol_common(COFF_ObjWriter *obj_writer, String8 name, U32 size);
internal void coff_obj_writer_set_default_symbol(COFF_ObjSymbol *weak_symbol, COFF_ObjSymbol *default_symbol);
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol, COFF_RelocType reloc_type);
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr32(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol);
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_addr(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol);
internal COFF_ObjReloc * coff_obj_writer_section_push_reloc_voff(COFF_ObjWriter *obj_writer, COFF_ObjSection *sect, U32 apply_off, COFF_ObjSymbol *symbol);
internal void coff_obj_writer_push_directive(COFF_ObjWriter *obj_writer, String8 directive);
#endif // COFF_OBJ_WRITER_H
-991
View File
@@ -1,991 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal B32
coff_is_big_obj(String8 raw_coff)
{
B32 is_big_obj = 0;
if (raw_coff.size >= sizeof(COFF_BigObjHeader)) {
COFF_BigObjHeader *file_header32 = (COFF_BigObjHeader*)(raw_coff.str);
is_big_obj = file_header32->sig1 == COFF_MachineType_Unknown &&
file_header32->sig2 == max_U16 &&
file_header32->version >= 2 &&
MemoryCompare(file_header32->magic, g_coff_big_header_magic, sizeof(file_header32->magic)) == 0;
}
return is_big_obj;
}
internal B32
coff_is_obj(String8 raw_coff)
{
B32 is_obj = 0;
if (raw_coff.size >= sizeof(COFF_FileHeader)) {
COFF_FileHeader *header = (COFF_FileHeader*)(raw_coff.str);
// validate machine
B32 is_machine_type_valid = 0;
switch (header->machine) {
case COFF_MachineType_Unknown:
case COFF_MachineType_X86: case COFF_MachineType_X64:
case COFF_MachineType_Am33: case COFF_MachineType_Arm:
case COFF_MachineType_Arm64: case COFF_MachineType_ArmNt:
case COFF_MachineType_Ebc: case COFF_MachineType_Ia64:
case COFF_MachineType_M32R: case COFF_MachineType_Mips16:
case COFF_MachineType_MipsFpu:case COFF_MachineType_MipsFpu16:
case COFF_MachineType_PowerPc:case COFF_MachineType_PowerPcFp:
case COFF_MachineType_R4000: case COFF_MachineType_RiscV32:
case COFF_MachineType_RiscV64:case COFF_MachineType_RiscV128:
case COFF_MachineType_Sh3: case COFF_MachineType_Sh3Dsp:
case COFF_MachineType_Sh4: case COFF_MachineType_Sh5:
case COFF_MachineType_Thumb: case COFF_MachineType_WceMipsV2:
{
is_machine_type_valid = 1;
}break;
}
if (is_machine_type_valid) {
// validate section count
U64 section_count = header->section_count;
U64 section_hdr_opl_off = sizeof(*header) + section_count*sizeof(COFF_SectionHeader);
if (raw_coff.size >= section_hdr_opl_off) {
COFF_SectionHeader *section_hdrs = (COFF_SectionHeader*)(raw_coff.str + sizeof(*header));
COFF_SectionHeader *section_hdr_opl = section_hdrs + section_count;
// validate section ranges
B32 is_sect_range_valid = 1;
for (COFF_SectionHeader *sec_hdr = section_hdrs;
sec_hdr < section_hdr_opl;
sec_hdr += 1) {
if (!(sec_hdr->flags & COFF_SectionFlag_CntUninitializedData)) {
U64 min = sec_hdr->foff;
U64 max = min + sec_hdr->fsize;
if (sec_hdr->fsize > 0 && !(section_hdr_opl_off <= min && min <= max && max <= raw_coff.size)) {
is_sect_range_valid = 0;
break;
}
}
}
if (is_sect_range_valid) {
// validate symbol table
U64 symbol_table_off = header->symbol_table_foff;
U64 symbol_table_size = sizeof(COFF_Symbol16)*header->symbol_count;
U64 symbol_table_opl_off = symbol_table_off + symbol_table_size;
// don't validate symbol table when there is none
if (symbol_table_off == 0 && symbol_table_size == 0) {
symbol_table_off = section_hdr_opl_off;
symbol_table_opl_off = section_hdr_opl_off;
}
is_obj = (section_hdr_opl_off <= symbol_table_off &&
symbol_table_off <= symbol_table_opl_off &&
symbol_table_opl_off <= raw_coff.size);
}
}
}
}
return is_obj;
}
internal COFF_FileHeaderInfo
coff_file_header_info_from_data(String8 raw_coff)
{
COFF_FileHeaderInfo info = {0};
if (coff_is_big_obj(raw_coff)) {
COFF_BigObjHeader *header32 = (COFF_BigObjHeader*)raw_coff.str;
info.is_big_obj = 1;
info.machine = header32->machine;
info.section_count_no_null = header32->section_count;
info.symbol_count = header32->symbol_count;
info.symbol_size = sizeof(COFF_Symbol32);
info.header_range = rng_1u64(0, sizeof(COFF_BigObjHeader));
info.section_table_range = rng_1u64(info.header_range.max, info.header_range.max + sizeof(COFF_SectionHeader) * header32->section_count);
info.symbol_table_range = rng_1u64(header32->symbol_table_foff, header32->symbol_table_foff + sizeof(COFF_Symbol32) * header32->symbol_count);
info.string_table_range = rng_1u64(info.symbol_table_range.max, raw_coff.size);
} else if (coff_is_obj(raw_coff)) {
COFF_FileHeader *header16 = (COFF_FileHeader*)raw_coff.str;
info.is_big_obj = 0;
info.machine = header16->machine;
info.section_count_no_null = header16->section_count;
info.symbol_count = header16->symbol_count;
info.symbol_size = sizeof(COFF_Symbol16);
info.header_range = rng_1u64(0, sizeof(COFF_FileHeader));
info.section_table_range = rng_1u64(info.header_range.max, info.header_range.max + sizeof(COFF_SectionHeader) * header16->section_count);
info.symbol_table_range = rng_1u64(header16->symbol_table_foff, header16->symbol_table_foff + sizeof(COFF_Symbol16) * header16->symbol_count);
info.string_table_range = rng_1u64(info.symbol_table_range.max, raw_coff.size);
}
return info;
}
internal COFF_SectionHeader **
coff_section_table_from_data(Arena *arena, String8 data, Rng1U64 section_table_range)
{
U64 section_count = dim_1u64(section_table_range) / sizeof(COFF_SectionHeader);
COFF_SectionHeader **section_table = push_array_no_zero(arena, COFF_SectionHeader *, section_count+1);
section_table[0] = push_array(arena, COFF_SectionHeader, 1);
for (U64 i = 0; i < section_count; ++i) {
section_table[i+1] = str8_deserial_get_raw_ptr(data, section_table_range.min + i*sizeof(COFF_SectionHeader), sizeof(COFF_SectionHeader));
}
return section_table;
}
internal COFF_ParsedSymbol
coff_parse_symbol32(String8 string_table, COFF_Symbol32 *sym32)
{
COFF_ParsedSymbol result = {0};
result.name = coff_read_symbol_name(string_table, &sym32->name);
result.value = sym32->value;
result.section_number = sym32->section_number;
result.type = sym32->type;
result.storage_class = sym32->storage_class;
result.aux_symbol_count = sym32->aux_symbol_count;
result.raw_symbol = sym32;
return result;
}
internal COFF_ParsedSymbol
coff_parse_symbol16(String8 string_table, COFF_Symbol16 *sym16)
{
COFF_ParsedSymbol result = {0};
result.name = coff_read_symbol_name(string_table, &sym16->name);
result.value = sym16->value;
if (sym16->section_number == COFF_Symbol_DebugSection16) {
result.section_number = COFF_Symbol_DebugSection32;
} else if (sym16->section_number == COFF_Symbol_AbsSection16) {
result.section_number = COFF_Symbol_AbsSection32;
} else {
result.section_number = (U32)sym16->section_number;
}
result.type = sym16->type;
result.storage_class = sym16->storage_class;
result.aux_symbol_count = sym16->aux_symbol_count;
result.raw_symbol = sym16;
return result;
}
internal COFF_ParsedSymbol
coff_parse_symbol(COFF_FileHeaderInfo header, String8 string_table, String8 symbol_table, U32 symbol_idx)
{
COFF_ParsedSymbol symbol;
if (header.is_big_obj) {
symbol = coff_parse_symbol32(string_table, (COFF_Symbol32 *)symbol_table.str + symbol_idx);
} else {
symbol = coff_parse_symbol16(string_table, (COFF_Symbol16 *)symbol_table.str + symbol_idx);
}
return symbol;
}
internal COFF_Symbol32Array
coff_symbol_array_from_data_16(Arena *arena, String8 raw_coff, U64 symbol_array_off, U64 symbol_count)
{
COFF_Symbol32Array result = {0};
result.count = symbol_count;
result.v = push_array_no_zero_aligned(arena, COFF_Symbol32, result.count, 8);
Rng1U64 sym16_arr_range = rng_1u64(symbol_array_off, symbol_array_off + sizeof(COFF_Symbol16) * symbol_count);
String8 raw_sym16_arr = str8_substr(raw_coff, sym16_arr_range);
COFF_Symbol16 *sym16_arr = (COFF_Symbol16 *)raw_sym16_arr.str;
for (U64 isymbol = 0, count = raw_sym16_arr.size / sizeof(COFF_Symbol16); isymbol < count; isymbol += 1) {
COFF_Symbol16 *sym16 = &sym16_arr[isymbol];
COFF_Symbol32 *sym32 = &result.v[isymbol];
sym32->name = sym16->name;
sym32->value = sym16->value;
if (sym16->section_number == COFF_Symbol_DebugSection16) {
sym32->section_number = COFF_Symbol_DebugSection32;
} else if (sym16->section_number == COFF_Symbol_AbsSection16) {
sym32->section_number = COFF_Symbol_AbsSection32;
} else {
sym32->section_number = (U32)sym16->section_number;
}
sym32->type.v = sym16->type.v;
sym32->storage_class = sym16->storage_class;
sym32->aux_symbol_count = sym16->aux_symbol_count;
// copy aux symbols
for (U64 iaux = isymbol+1, iaux_hi = Min(count, iaux+sym16->aux_symbol_count); iaux < iaux_hi; iaux += 1) {
COFF_Symbol16 *aux16 = sym16_arr + iaux;
COFF_Symbol32 *aux32 = result.v + iaux;
// 32bit COFF uses 16bit aux symbols
MemoryCopy(aux32, aux16, sizeof(COFF_Symbol16));
MemoryZero((U8 *)aux32 + sizeof(COFF_Symbol16), sizeof(COFF_Symbol32)-sizeof(COFF_Symbol16));
}
// take into account aux symbols
isymbol += sym32->aux_symbol_count;
}
return result;
}
internal COFF_Symbol32Array
coff_symbol_array_from_data_32(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count)
{
COFF_Symbol32Array result;
result.count = symbol_count;
result.v = (COFF_Symbol32 *)(data.str + symbol_array_off);
return result;
}
internal COFF_Symbol32Array
coff_symbol_array_from_data(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count, U64 symbol_size)
{
COFF_Symbol32Array result = {0};
switch (symbol_size) {
case sizeof(COFF_Symbol16): result = coff_symbol_array_from_data_16(arena, data, symbol_array_off, symbol_count); break;
case sizeof(COFF_Symbol32): result = coff_symbol_array_from_data_32(arena, data, symbol_array_off, symbol_count); break;
}
return result;
}
internal COFF_Symbol16Node *
coff_symbol16_list_push(Arena *arena, COFF_Symbol16List *list, COFF_Symbol16 symbol)
{
COFF_Symbol16Node *node = push_array(arena, COFF_Symbol16Node, 1);
node->next = 0;
node->data = symbol;
SLLQueuePush(list->first, list->last, node);
list->count += 1;
return node;
}
internal COFF_SymbolValueInterpType
coff_interp_symbol(U32 section_number, U32 value, COFF_SymStorageClass storage_class)
{
if (storage_class == COFF_SymStorageClass_Section && section_number == COFF_Symbol_UndefinedSection) {
return COFF_SymbolValueInterp_Undefined;
}
if (storage_class == COFF_SymStorageClass_External && value == 0 && section_number == COFF_Symbol_UndefinedSection) {
return COFF_SymbolValueInterp_Undefined;
}
if (storage_class == COFF_SymStorageClass_External && value != 0 && section_number == COFF_Symbol_UndefinedSection) {
return COFF_SymbolValueInterp_Common;
}
if (section_number == COFF_Symbol_AbsSection32) {
return COFF_SymbolValueInterp_Abs;
}
if (section_number == COFF_Symbol_DebugSection32) {
return COFF_SymbolValueInterp_Debug;
}
if (storage_class == COFF_SymStorageClass_WeakExternal) {
return COFF_SymbolValueInterp_Weak;
}
return COFF_SymbolValueInterp_Regular;
}
internal COFF_SymbolValueInterpType
coff_interp_from_parsed_symbol(COFF_ParsedSymbol symbol)
{
return coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class);
}
internal B32
coff_is_undefined_data_symbol(COFF_ParsedSymbol symbol)
{
COFF_SymbolValueInterpType interp = coff_interp_from_parsed_symbol(symbol);
return interp == COFF_SymbolValueInterp_Undefined && symbol.storage_class == COFF_SymStorageClass_External;
}
internal void
coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out)
{
Assert(coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class) == COFF_SymbolValueInterp_Regular);
Assert(symbol.aux_symbol_count > 0);
if (is_big_obj) {
COFF_SymbolSecDef *sd = (COFF_SymbolSecDef *)((COFF_Symbol32 *)symbol.raw_symbol + 1);
if (selection_out) *selection_out = sd->selection;
if (length_out) *length_out = sd->length;
if (check_sum_out) *check_sum_out = sd->check_sum;
if (number_out) *number_out = Compose32Bit(sd->number_hi, sd->number_lo);
} else {
COFF_SymbolSecDef *sd = (COFF_SymbolSecDef *)((COFF_Symbol16 *)symbol.raw_symbol + 1);
if (selection_out) *selection_out = sd->selection;
if (length_out) *length_out = sd->length;
if (check_sum_out) *check_sum_out = sd->check_sum;
if (number_out) *number_out = sd->number_lo;
}
}
internal COFF_SymbolWeakExt *
coff_parse_weak_tag(COFF_ParsedSymbol symbol, B32 is_big_obj)
{
Assert(coff_interp_symbol(symbol.section_number, symbol.value, symbol.storage_class) == COFF_SymbolValueInterp_Weak);
Assert(symbol.aux_symbol_count > 0);
void *tag;
if (is_big_obj) {
tag = (COFF_SymbolWeakExt *)((COFF_Symbol32 *)symbol.raw_symbol + 1);
} else {
tag = (COFF_SymbolWeakExt *)((COFF_Symbol16 *)symbol.raw_symbol + 1);
}
return tag;
}
internal COFF_RelocNode *
coff_reloc_list_push(Arena *arena, COFF_RelocList *list, COFF_Reloc reloc)
{
COFF_RelocNode *node = push_array(arena, COFF_RelocNode, 1);
node->data = reloc;
SLLQueuePush(list->first, list->last, node);
++list->count;
return node;
}
internal COFF_RelocInfo
coff_reloc_info_from_section_header(String8 data, COFF_SectionHeader *header)
{
COFF_RelocInfo result = {0};
if (header->flags & COFF_SectionFlag_LnkNRelocOvfl && header->reloc_count == max_U16) {
COFF_Reloc counter;
U64 read_size = str8_deserial_read_struct(data, header->relocs_foff, &counter);
if (read_size == sizeof(counter) && counter.apply_off > 0) {
result.array_off = header->relocs_foff + sizeof(COFF_Reloc);
result.count = counter.apply_off - 1; // exclude counter entry
}
} else {
result.array_off = header->relocs_foff;
result.count = header->reloc_count;
}
return result;
}
internal String8
coff_resource_string_from_str16(Arena *arena, String16 string)
{
AssertAlways(string.size <= max_U16);
U16 size16 = (U16)string.size;
U16 *buffer = push_array_no_zero(arena, U16, size16 + 1);
MemoryCopy(buffer + 0, &size16, sizeof(size16));
MemoryCopy(buffer + 1, string.str, size16 * sizeof(string.str[0]));
return str8_array(buffer, size16 + 1);
}
internal String8
coff_resource_string_from_str8(Arena *arena, String8 string)
{
Temp scratch = scratch_begin(&arena, 1);
String16 string16 = str16_from_8(scratch.arena, string);
String8 result = coff_resource_string_from_str16(arena, string16);
scratch_end(scratch);
return result;
}
internal String8
coff_resource_number_from_u16(Arena *arena, U16 number)
{
U16 *buffer = push_array_no_zero(arena, U16, 2);
buffer[0] = max_U16;
buffer[1] = number;
return str8_array(buffer, 2);
}
internal COFF_ResourceID
coff_utf8_resource_id_from_utf16(Arena *arena, COFF_ResourceID16 *id_16)
{
COFF_ResourceID id = {0};
id.type = id_16->type;
switch (id_16->type) {
case COFF_ResourceIDType_Null: break;
case COFF_ResourceIDType_Number: {
id.u.number = id_16->u.number;
} break;
case COFF_ResourceIDType_String: {
id.u.string = str8_from_16(arena, id_16->u.string);
} break;
default: InvalidPath;
}
return id;
}
internal U64
coff_read_resource_id_utf16(String8 raw_res, U64 off, COFF_ResourceID16 *id_out)
{
U64 cursor = off;
U16 flag = 0;
str8_deserial_read_struct(raw_res, cursor, &flag);
if (flag == max_U16) {
id_out->type = COFF_ResourceIDType_Number;
cursor += sizeof(flag);
cursor += str8_deserial_read_struct(raw_res, cursor, &id_out->u.number);
} else {
id_out->type = COFF_ResourceIDType_String;
cursor += str8_deserial_read_windows_utf16_string16(raw_res, cursor, &id_out->u.string);
}
U64 read_size = cursor - off;
read_size = AlignPow2(read_size, COFF_ResourceAlign);
return read_size;
}
internal U64
coff_read_resource(Arena *arena, String8 raw_res, U64 off, COFF_ParsedResource *res_out)
{
String8 raw_header = str8_skip(raw_res, off);
U64 header_cursor = 0;
// prefix
COFF_ResourceHeaderPrefix prefix = {0};
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &prefix);
Assert(prefix.header_size >= sizeof(COFF_ResourceHeaderPrefix));
raw_header = str8_prefix(raw_header, prefix.header_size);
// header
COFF_ResourceID16 type_16 = {0};
COFF_ResourceID16 name_16 = {0};
header_cursor += coff_read_resource_id_utf16(raw_header, header_cursor, &type_16);
header_cursor += coff_read_resource_id_utf16(raw_header, header_cursor, &name_16);
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &res_out->data_version);
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &res_out->memory_flags);
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &res_out->language_id);
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &res_out->version);
header_cursor += str8_deserial_read_struct(raw_header, header_cursor, &res_out->characteristics);
Assert(prefix.header_size == header_cursor);
// convert utf-16 resource ids to utf-8
res_out->type = coff_utf8_resource_id_from_utf16(arena, &type_16);
res_out->name = coff_utf8_resource_id_from_utf16(arena, &name_16);
// read data
U64 data_read_size = str8_deserial_read_block(raw_res, off + prefix.header_size, prefix.data_size, &res_out->data);
Assert(prefix.data_size == data_read_size);
// compute read size
U64 read_size = Max(prefix.header_size, sizeof(prefix)) + AlignPow2(prefix.data_size, COFF_ResourceAlign);
return read_size;
}
internal COFF_ParsedResourceList
coff_resource_list_from_data(Arena *arena, String8 data)
{
COFF_ParsedResourceList list = {0};
U64 cursor;
for (cursor = 0 ; cursor < data.size; ) {
COFF_ParsedResourceNode *node = push_array(arena, COFF_ParsedResourceNode, 1);
cursor += coff_read_resource(arena, data, cursor, &node->data);
SLLQueuePush(list.first, list.last, node);
++list.count;
}
Assert(cursor == data.size);
return list;
}
internal String8
coff_write_resource_id(Arena *arena, COFF_ResourceID id)
{
String8 result = str8_zero();
switch (id.type) {
case COFF_ResourceIDType_Null: break;
case COFF_ResourceIDType_Number: {
result = coff_resource_number_from_u16(arena, id.u.number);
} break;
case COFF_ResourceIDType_String: {
result = coff_resource_string_from_str8(arena, id.u.string);
} break;
default: InvalidPath;
}
return result;
}
internal String8
coff_write_resource(Arena *arena,
COFF_ResourceID type,
COFF_ResourceID name,
U32 data_version,
COFF_ResourceMemoryFlags memory_flags,
U16 language_id,
U32 version,
U32 characteristics,
String8 data)
{
Temp scratch = scratch_begin(&arena, 1);
String8List list = {0};
COFF_ResourceHeaderPrefix *prefix = push_array(scratch.arena, COFF_ResourceHeaderPrefix, 1);
String8 packed_type = coff_write_resource_id(scratch.arena, type);
String8 packed_name = coff_write_resource_id(scratch.arena, name);
// prefix + header
str8_list_push(scratch.arena, &list, str8_struct(prefix));
str8_list_push(scratch.arena, &list, packed_type);
str8_list_push(scratch.arena, &list, packed_name);
str8_list_push(scratch.arena, &list, str8_struct(&data_version));
str8_list_push(scratch.arena, &list, str8_struct(&memory_flags));
str8_list_push(scratch.arena, &list, str8_struct(&language_id));
str8_list_push(scratch.arena, &list, str8_struct(&version));
str8_list_push(scratch.arena, &list, str8_struct(&characteristics));
prefix->data_size = safe_cast_u32(data.size);
prefix->header_size = safe_cast_u32(list.total_size);
// data
str8_list_push(scratch.arena, &list, data);
// magic
str8_list_push_front(scratch.arena, &list, str8_array_fixed(g_coff_res_magic));
// align
U64 align_size = AlignPow2(list.total_size, COFF_ResourceAlign) - list.total_size;
U8 *align = push_array(scratch.arena, U8, align_size);
str8_list_push(scratch.arena, &list, str8(align, align_size));
// join
String8 res = str8_list_join(arena, &list, 0);
scratch_end(scratch);
return res;
}
internal int
coff_resource_id_compar(void *raw_a, void *raw_b)
{
int cmp;
COFF_ResourceID *a = raw_a;
COFF_ResourceID *b = raw_b;
if (a->type == b->type) {
switch (a->type) {
default:
case COFF_ResourceIDType_Null: cmp = 0; break;
case COFF_ResourceIDType_Number: cmp = MemoryCompare(&a->u.number, &b->u.number, sizeof(a->u.number)); break;
case COFF_ResourceIDType_String: cmp = strncmp((char *)a->u.string.str, (char *)b->u.string.str, Min(a->u.string.size, b->u.string.size)); break;
}
} else {
cmp = a->type < b->type ? -1 : a->type > b->type ? +1 : 0;
}
return cmp;
}
internal B32
coff_is_import(String8 raw_archive_member)
{
B32 is_import = 0;
if (raw_archive_member.size >= sizeof(U16)*2) {
U16 *sig1 = (U16*)raw_archive_member.str;
U16 *sig2 = sig1 + 1;
is_import = *sig1 == COFF_MachineType_Unknown && *sig2 == 0xffff;
}
return is_import;
}
internal COFF_DataType
coff_data_type_from_data(String8 raw_archive_member)
{
B32 is_big_obj = coff_is_big_obj(raw_archive_member);
if (is_big_obj) {
return COFF_DataType_BigObj;
}
B32 is_import = coff_is_import(raw_archive_member);
if (is_import) {
return COFF_DataType_Import;
}
return COFF_DataType_Obj;
}
internal B32
coff_is_regular_archive(String8 raw_archive)
{
B32 is_archive = 0;
U8 sig[sizeof(g_coff_archive_sig)];
if (str8_deserial_read_struct(raw_archive, 0, &sig) == sizeof(sig)) {
is_archive = MemoryCompare(&sig[0], &g_coff_archive_sig[0], sizeof(g_coff_archive_sig)) == 0;
}
return is_archive;
}
internal B32
coff_is_thin_archive(String8 raw_archive)
{
B32 is_archive = 0;
U8 sig[sizeof(g_coff_thin_archive_sig)];
if (str8_deserial_read_struct(raw_archive, 0, &sig) == sizeof(sig)) {
is_archive = MemoryCompare(&sig[0], &g_coff_thin_archive_sig[0], sizeof(g_coff_thin_archive_sig)) == 0;
}
return is_archive;
}
internal COFF_ArchiveType
coff_archive_type_from_data(String8 raw_archive)
{
if (coff_is_regular_archive(raw_archive)) {
return COFF_Archive_Regular;
} else if (coff_is_thin_archive(raw_archive)) {
return COFF_Archive_Thin;
}
return COFF_Archive_Null;
}
internal U64
coff_parse_archive_member_header(String8 raw_archive, U64 offset, COFF_ParsedArchiveMemberHeader *header_out)
{
COFF_ArchiveMemberHeader *header = str8_deserial_get_raw_ptr(raw_archive, offset, sizeof(*header));
if (header) {
String8 name = str8_skip_chop_whitespace(str8_cstring_capped(header->name, header->name + sizeof(header->name) ));
String8 date = str8_skip_chop_whitespace(str8_cstring_capped(header->date, header->date + sizeof(header->date) ));
String8 user_id = str8_skip_chop_whitespace(str8_cstring_capped(header->user_id, header->user_id + sizeof(header->user_id) ));
String8 group_id = str8_skip_chop_whitespace(str8_cstring_capped(header->group_id, header->group_id + sizeof(header->group_id)));
String8 mode = str8_skip_chop_whitespace(str8_cstring_capped(header->mode, header->mode + sizeof(header->mode) ));
String8 size = str8_skip_chop_whitespace(str8_cstring_capped(header->size, header->size + sizeof(header->size) ));
String8 end = str8_cstring_capped(header->end, header->end + sizeof(header->end));
U32 data_size = u32_from_str8(size, 10);
U64 data_off = offset + sizeof(COFF_ArchiveMemberHeader);
header_out->name = name;
header_out->time_stamp = u32_from_str8(date, 10);
header_out->user_id = u32_from_str8(user_id, 10);
header_out->group_id = u32_from_str8(group_id, 10);
header_out->mode = mode;
header_out->is_end_correct = str8_match_lit("`\n", end, 0);
header_out->data_range = rng_1u64(data_off, data_off + data_size);
return sizeof(*header);
}
return 0;
}
internal COFF_ArchiveFirstMember
coff_parse_first_archive_member(COFF_ArchiveMember *member)
{
Assert(str8_match_lit("/", member->header.name, 0));
U64 cursor = 0;
U32 symbol_count = 0;
cursor += str8_deserial_read_struct(member->data, cursor, &symbol_count);
symbol_count = from_be_u32(symbol_count);
Rng1U64 member_offsets_range = rng_1u64(cursor, cursor + symbol_count * sizeof(U32));
cursor += dim_1u64(member_offsets_range);
Rng1U64 string_table_range = rng_1u64(cursor, member->data.size);
cursor += dim_1u64(string_table_range);
String8 raw_member_offsets = str8_substr(member->data, member_offsets_range);
U32 *member_offsets = (U32 *)raw_member_offsets.str;
U64 member_offset_count = raw_member_offsets.size / sizeof(member_offsets[0]);
COFF_ArchiveFirstMember result = {0};
result.symbol_count = symbol_count;
result.member_offset_count = member_offset_count;
result.member_offsets = member_offsets;
result.string_table = str8_substr(member->data, string_table_range);
return result;
}
internal COFF_ArchiveSecondMember
coff_parse_second_archive_member(COFF_ArchiveMember *member)
{
COFF_ArchiveSecondMember result = {0};
if (str8_match_lit("/", member->header.name, 0)) {
U64 cursor = 0;
U32 member_count = 0;
cursor += str8_deserial_read_struct(member->data, cursor, &member_count);
Rng1U64 member_offsets_range = rng_1u64(cursor, cursor + member_count * sizeof(U32));
cursor += dim_1u64(member_offsets_range);
U32 symbol_count = 0;
cursor += str8_deserial_read_struct(member->data, cursor, &symbol_count);
Rng1U64 symbol_indices_range = rng_1u64(cursor, cursor + symbol_count * sizeof(U16));
cursor += dim_1u64(symbol_indices_range);
Rng1U64 string_table_range = rng_1u64(cursor, member->data.size);
String8 raw_member_offsets = str8_substr(member->data, member_offsets_range);
String8 raw_indices = str8_substr(member->data, symbol_indices_range);
U32 *member_offsets = (U32 *)raw_member_offsets.str;
U64 member_offset_count = raw_member_offsets.size / sizeof(member_offsets[0]);
U16 *symbol_indices = (U16 *)raw_indices.str;
U64 symbol_index_count = raw_indices.size / sizeof(symbol_indices[0]);
result.member_count = member_count;
result.symbol_count = symbol_count;
result.member_offsets = member_offsets;
result.member_offset_count = member_offset_count;
result.symbol_indices = symbol_indices;
result.symbol_index_count = symbol_index_count;
result.string_table = str8_substr(member->data, string_table_range);
}
return result;
}
internal String8
coff_decode_raw_member_name(String8 long_names, String8 name)
{
String8 result = name;
if (name.size > 0 && name.str[0] == '/') {
String8 offset_str = str8(name.str + 1, name.size - 1);
U64 offset = u64_from_str8(offset_str, 10);
if (offset < long_names.size) {
U8 *ptr = long_names.str + offset;
U8 *opl = long_names.str + long_names.size;
for (; ptr < opl; ++ptr) {
if (*ptr == '\0' || *ptr == '\n') {
break;
}
}
result = str8_range(long_names.str + offset, ptr);
}
}
return result;
}
internal String8
coff_decode_member_name(String8 long_names, String8 name)
{
String8 member_name = coff_decode_raw_member_name(long_names, name);
String8 slash = str8_lit("/");
if (str8_ends_with(member_name, slash, 0)) {
member_name = str8_chop(member_name, slash.size);
}
return member_name;
}
internal U64
coff_parse_import(String8 raw_archive_member, U64 offset, COFF_ParsedArchiveImportHeader *header_out)
{
COFF_ImportHeader *header = str8_deserial_get_raw_ptr(raw_archive_member, offset, sizeof(*header));
if (header) {
Rng1U64 data_range = rng_1u64(offset + sizeof(*header), offset + sizeof(*header) + header->data_size);
String8 raw_data = str8_substr(raw_archive_member, data_range);
U64 data_cursor = 0;
header_out->version = header->version;
header_out->machine = header->machine;
header_out->time_stamp = header->time_stamp;
header_out->data_size = header->data_size;
header_out->hint_or_ordinal = header->hint_or_ordinal;
header_out->type = COFF_ImportHeader_ExtractType(header->flags);
header_out->import_by = COFF_ImportHeader_ExtractImportBy(header->flags);
data_cursor += str8_deserial_read_cstr(raw_data, data_cursor, &header_out->func_name);
data_cursor += str8_deserial_read_cstr(raw_data, data_cursor, &header_out->dll_name);
Assert(header_out->func_name.size + header_out->dll_name.size + /* nulls */ 2 == header_out->data_size);
U64 read_size = sizeof(*header) + header->data_size;
return read_size;
}
return 0;
}
internal COFF_ArchiveMember
coff_archive_member_from_offset(String8 raw_archive, U64 offset)
{
COFF_ArchiveMember member = {0};
coff_regular_archive_member_iter_next(raw_archive, &offset, &member);
return member;
}
internal COFF_ArchiveMember
coff_archive_member_from_data(String8 raw_archive_member)
{
return coff_archive_member_from_offset(raw_archive_member, 0);
}
internal COFF_ParsedArchiveImportHeader
coff_archive_import_from_data(String8 raw_archive_member)
{
COFF_ParsedArchiveImportHeader header = {0};
coff_parse_import(raw_archive_member, 0, &header);
return header;
}
internal U64
coff_regular_archive_member_iter_init(String8 raw_archive)
{
U64 cursor = raw_archive.size;
if (coff_is_regular_archive(raw_archive)) {
cursor = sizeof(g_coff_archive_sig);
}
return cursor;
}
internal B32
coff_regular_archive_member_iter_next(String8 raw_archive, U64 *offset, COFF_ArchiveMember *member_out)
{
B32 is_parsed = 0;
member_out->header.is_end_correct = 0;
U64 header_size = coff_parse_archive_member_header(raw_archive, *offset, &member_out->header);
if (member_out->header.is_end_correct) {
member_out->offset = *offset;
member_out->data = str8_substr(raw_archive, member_out->header.data_range);
U64 read_size = AlignPow2(header_size + dim_1u64(member_out->header.data_range), COFF_Archive_MemberAlign);
*offset += read_size;
is_parsed = 1;
}
return is_parsed;
}
internal U64
coff_thin_archive_member_iter_init(String8 raw_archive)
{
U64 cursor = raw_archive.size;
if (coff_is_thin_archive(raw_archive)) {
cursor = sizeof(g_coff_thin_archive_sig);
}
return cursor;
}
internal B32
coff_thin_archive_member_iter_next(String8 raw_archive, U64 *offset, COFF_ArchiveMember *member_out)
{
B32 is_parsed = 0;
member_out->header.is_end_correct = 0;
U64 header_size = coff_parse_archive_member_header(raw_archive, *offset, &member_out->header);
if (member_out->header.is_end_correct) {
Rng1U64 data_in_archive_range = {0};
if (str8_match_lit("/", member_out->header.name, 0) || str8_match_lit("//", member_out->header.name, 0)) {
data_in_archive_range = member_out->header.data_range;
}
member_out->offset = *offset;
member_out->data = str8_substr(raw_archive, data_in_archive_range);
U64 read_size = AlignPow2(header_size + dim_1u64(data_in_archive_range), COFF_Archive_MemberAlign);
*offset += read_size;
is_parsed = 1;
}
return is_parsed;
}
internal void
coff_archive_member_list_push_node(COFF_ArchiveMemberList *list, COFF_ArchiveMemberNode *node)
{
SLLQueuePush(list->first, list->last, node);
list->count += 1;
}
internal COFF_ArchiveParse
coff_archive_parse_from_member_list(COFF_ArchiveMemberList member_list)
{
String8 error = str8_zero();
B32 has_second_header = 0;
B32 has_long_names = 0;
COFF_ArchiveMember first_header = {0};
COFF_ArchiveMember second_header = {0};
COFF_ArchiveMember long_names_member = {0};
COFF_ArchiveMemberNode *ptr = member_list.first;
if (ptr) {
if (str8_match_lit("/", ptr->data.header.name, 0)) {
if (ptr->data.header.is_end_correct) {
first_header = ptr->data;
ptr = ptr->next;
} else {
error = str8_lit("first header doesn't have correct end");
}
}
} else {
error = str8_lit("missing first header");
}
if (!error.size && ptr) {
if (str8_match_lit("/", ptr->data.header.name, 0)) {
if (ptr->data.header.is_end_correct) {
second_header = ptr->data;
ptr = ptr->next;
has_second_header = 1;
} else {
error = str8_lit("second header doesn't have correct end");
}
}
}
if (!error.size && ptr) {
if (str8_match_lit("//", ptr->data.header.name, 0)) {
if (ptr->data.header.is_end_correct) {
long_names_member = ptr->data;
ptr = ptr->next;
has_long_names;
} else {
error = str8_lit("long names header doesn't have correct end");
}
}
}
COFF_ArchiveParse parse = {0};
parse.has_second_header = has_second_header;
parse.has_long_names = has_long_names;
parse.first_member = coff_parse_first_archive_member(&first_header);
parse.second_member = coff_parse_second_archive_member(&second_header);
parse.long_names = long_names_member.data;
parse.error = error;
return parse;
}
internal COFF_ArchiveParse
coff_regular_archive_parse_from_data(String8 raw_archive)
{
COFF_ArchiveMemberList list = {0};
COFF_ArchiveMemberNode node_arr[3] = {0};
U64 cursor = coff_regular_archive_member_iter_init(raw_archive);
for (U64 i = 0; i < ArrayCount(node_arr); ++i) {
COFF_ArchiveMemberNode *node = &node_arr[i];
if (!coff_regular_archive_member_iter_next(raw_archive, &cursor, &node->data)) {
break;
}
coff_archive_member_list_push_node(&list, node);
}
return coff_archive_parse_from_member_list(list);
}
internal COFF_ArchiveParse
coff_thin_archive_parse_from_data(String8 raw_archive)
{
COFF_ArchiveMemberList list = {0};
COFF_ArchiveMemberNode node_arr[3] = {0};
U64 cursor = coff_thin_archive_member_iter_init(raw_archive);
for (U64 i = 0; i < ArrayCount(node_arr); i += 1) {
COFF_ArchiveMemberNode *node = &node_arr[i];
if (!coff_thin_archive_member_iter_next(raw_archive, &cursor, &node->data)) {
break;
}
coff_archive_member_list_push_node(&list, node);
}
return coff_archive_parse_from_member_list(list);
}
internal COFF_ArchiveParse
coff_archive_parse_from_data(String8 raw_archive)
{
COFF_ArchiveType type = coff_archive_type_from_data(raw_archive);
switch (type) {
case COFF_Archive_Null: break;
case COFF_Archive_Regular: return coff_regular_archive_parse_from_data(raw_archive);
case COFF_Archive_Thin: return coff_thin_archive_parse_from_data(raw_archive);
}
COFF_ArchiveParse null_parse = {0};
return null_parse;
}
-329
View File
@@ -1,329 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef COFF_PARSE_H
#define COFF_PARSE_H
typedef struct COFF_FileHeaderInfo
{
B32 is_big_obj;
COFF_MachineType machine;
U64 header_size;
U64 section_count_no_null;
U64 symbol_size;
U64 symbol_count;
Rng1U64 header_range;
Rng1U64 section_table_range;
Rng1U64 symbol_table_range;
Rng1U64 string_table_range;
} COFF_FileHeaderInfo;
////////////////////////////////
typedef struct COFF_SectionHeaderArray
{
U64 count;
COFF_SectionHeader *v;
} COFF_SectionHeaderArray;
////////////////////////////////
typedef struct COFF_Symbol16Node
{
struct COFF_Symbol16Node *next;
COFF_Symbol16 data;
} COFF_Symbol16Node;
typedef struct COFF_Symbol16List
{
U64 count;
COFF_Symbol16Node *first;
COFF_Symbol16Node *last;
} COFF_Symbol16List;
typedef struct COFF_Symbol32Array
{
U64 count;
COFF_Symbol32 *v;
} COFF_Symbol32Array;
typedef struct COFF_ParsedSymbol
{
String8 name;
U64 value;
U32 section_number;
COFF_SymbolType type;
COFF_SymStorageClass storage_class;
U8 aux_symbol_count;
void *raw_symbol;
} COFF_ParsedSymbol;
typedef U32 COFF_SymbolValueInterpType;
enum
{
COFF_SymbolValueInterp_Regular, // symbol has section and offset.
COFF_SymbolValueInterp_Weak, // symbol is overridable.
COFF_SymbolValueInterp_Undefined, // symbol doesn't have a reference section.
COFF_SymbolValueInterp_Common, // symbol has no section but still has size.
COFF_SymbolValueInterp_Abs, // symbol has an absolute (non-relocatable) value and is not an address.
COFF_SymbolValueInterp_Debug // symbol is used to provide general type of debugging information.
};
////////////////////////////////
typedef struct COFF_RelocNode
{
struct COFF_RelocNode *next;
COFF_Reloc data;
} COFF_RelocNode;
typedef struct COFF_RelocList
{
U64 count;
COFF_RelocNode *first;
COFF_RelocNode *last;
} COFF_RelocList;
typedef struct COFF_RelocArray
{
U64 count;
COFF_Reloc *v;
} COFF_RelocArray;
typedef struct COFF_RelocInfo
{
U64 array_off;
U64 count;
} COFF_RelocInfo;
////////////////////////////////
typedef U32 COFF_ResourceIDType;
enum COFF_ResourceIDTypeEnum
{
COFF_ResourceIDType_Null,
COFF_ResourceIDType_Number,
COFF_ResourceIDType_String,
COFF_ResourceIDType_Count
};
typedef struct COFF_ResourceID16
{
COFF_ResourceIDType type;
union {
U16 number;
String16 string;
} u;
} COFF_ResourceID16;
typedef struct COFF_ResourceID
{
COFF_ResourceIDType type;
union {
U16 number;
String8 string;
} u;
} COFF_ResourceID;
typedef struct COFF_ParsedResource
{
COFF_ResourceID type;
COFF_ResourceID name;
U32 data_version;
COFF_ResourceMemoryFlags memory_flags;
U16 language_id;
U32 version;
U32 characteristics;
String8 data;
} COFF_ParsedResource;
typedef struct COFF_ParsedResourceNode
{
struct COFF_ParsedResourceNode *next;
COFF_ParsedResource data;
} COFF_ParsedResourceNode;
typedef struct COFF_ParsedResourceList
{
U64 count;
COFF_ParsedResourceNode *first;
COFF_ParsedResourceNode *last;
} COFF_ParsedResourceList;
////////////////////////////////
typedef enum
{
COFF_DataType_Null,
COFF_DataType_Obj,
COFF_DataType_BigObj,
COFF_DataType_Import
} COFF_DataType;
typedef enum
{
COFF_Archive_Null,
COFF_Archive_Regular,
COFF_Archive_Thin
} COFF_ArchiveType;
typedef struct COFF_ParsedArchiveMemberHeader
{
String8 name; // padded to 16 bytes with spaces
COFF_TimeStamp time_stamp;
U32 user_id; // unix artifact that does not have meaning on windows
U32 group_id; // unix artifact that does not have meaning on windows
String8 mode; // octal representation the members file mode
B32 is_end_correct; // set to true if found correct signature after header
Rng1U64 data_range;
} COFF_ParsedArchiveMemberHeader;
typedef struct COFF_ParsedArchiveImportHeader
{
B32 is_sig_correct;
U16 version;
COFF_MachineType machine;
COFF_TimeStamp time_stamp;
U32 data_size;
U16 hint_or_ordinal;
COFF_ImportType type;
COFF_ImportByType import_by;
String8 func_name;
String8 dll_name;
} COFF_ParsedArchiveImportHeader;
typedef struct COFF_ArchiveMember
{
COFF_ParsedArchiveMemberHeader header;
U64 offset;
String8 data;
} COFF_ArchiveMember;
typedef struct COFF_ArchiveFirstMember
{
U32 symbol_count;
U64 member_offset_count;
U32 *member_offsets;
String8 string_table;
} COFF_ArchiveFirstMember;
typedef struct COFF_ArchiveSecondMember
{
U32 member_count;
U32 symbol_count;
U64 member_offset_count;
U32 *member_offsets;
U64 symbol_index_count;
U16 *symbol_indices;
String8 string_table;
} COFF_ArchiveSecondMember;
typedef struct COFF_ArchiveMemberNode
{
struct COFF_ArchiveMemberNode *next;
COFF_ArchiveMember data;
} COFF_ArchiveMemberNode;
typedef struct COFF_ArchiveMemberList
{
U64 count;
COFF_ArchiveMemberNode *first;
COFF_ArchiveMemberNode *last;
} COFF_ArchiveMemberList;
typedef struct COFF_ArchiveParse
{
B32 has_second_header;
B32 has_long_names;
COFF_ArchiveFirstMember first_member;
COFF_ArchiveSecondMember second_member;
String8 long_names;
String8 error;
} COFF_ArchiveParse;
////////////////////////////////
// Obj Header
internal B32 coff_is_big_obj(String8 raw_coff);
internal B32 coff_is_obj (String8 raw_coff);
internal COFF_FileHeaderInfo coff_file_header_info_from_data(String8 raw_coff);
////////////////////////////////
// Section
internal COFF_SectionHeader ** coff_section_table_from_data(Arena *arena, String8 data, Rng1U64 section_table_range);
////////////////////////////////
// Symbol
internal COFF_ParsedSymbol coff_parse_symbol32(String8 string_table, COFF_Symbol32 *sym32);
internal COFF_ParsedSymbol coff_parse_symbol16(String8 string_table, COFF_Symbol16 *sym16);
internal COFF_ParsedSymbol coff_parse_symbol(COFF_FileHeaderInfo header, String8 string_table, String8 symbol_table, U32 symbol_idx);
internal COFF_Symbol32Array coff_symbol_array_from_data_16(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count);
internal COFF_Symbol32Array coff_symbol_array_from_data_32(Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count);
internal COFF_Symbol32Array coff_symbol_array_from_data (Arena *arena, String8 data, U64 symbol_array_off, U64 symbol_count, U64 symbol_size);
internal COFF_Symbol16Node *coff_symbol16_list_push(Arena *arena, COFF_Symbol16List *list, COFF_Symbol16 symbol);
internal COFF_SymbolValueInterpType coff_interp_symbol(U32 section_number, U32 value, COFF_SymStorageClass storage_class);
internal COFF_SymbolValueInterpType coff_interp_from_parsed_symbol(COFF_ParsedSymbol symbol);
internal B32 coff_is_undefined_data_symbol(COFF_ParsedSymbol symbol);
internal void coff_parse_secdef(COFF_ParsedSymbol symbol, B32 is_big_obj, COFF_ComdatSelectType *selection_out, U32 *number_out, U32 *length_out, U32 *check_sum_out);
internal COFF_SymbolWeakExt * coff_parse_weak_tag(COFF_ParsedSymbol symbol, B32 is_big_obj);
////////////////////////////////
// Reloc
internal COFF_RelocInfo coff_reloc_info_from_section_header(String8 data, COFF_SectionHeader *header);
////////////////////////////////
// Resource
internal String8 coff_resource_string_from_str16 (Arena *arena, String16 string);
internal String8 coff_resource_string_from_str8 (Arena *arena, String8 string);
internal String8 coff_resource_number_from_u16 (Arena *arena, U16 number);
internal COFF_ResourceID coff_utf8_resource_id_from_utf16(Arena *arena, COFF_ResourceID16 *id_16);
internal U64 coff_read_resource_id_utf16 (String8 raw_res, U64 off, COFF_ResourceID16 *id_out);
internal U64 coff_read_resource (Arena *arena, String8 raw_res, U64 off, COFF_ParsedResource *res_out);
internal COFF_ParsedResourceList coff_resource_list_from_data(Arena *arena, String8 data);
internal String8 coff_write_resource_id(Arena *arena, COFF_ResourceID id);
internal String8 coff_write_resource (Arena *arena, COFF_ResourceID type, COFF_ResourceID name, U32 data_version, COFF_ResourceMemoryFlags memory_flags, U16 language_id, U32 version, U32 characteristics, String8 data);
internal int coff_resource_id_compar(void *raw_a, void *raw_b); // COFF_ResourceID
////////////////////////////////
// Archive
internal B32 coff_is_import (String8 raw_archive_member);
internal COFF_DataType coff_data_type_from_data (String8 raw_archive_member);
internal B32 coff_is_regular_archive (String8 raw_archive);
internal B32 coff_is_thin_archive (String8 raw_archive);
internal COFF_ArchiveType coff_archive_type_from_data(String8 raw_archive);
internal U64 coff_parse_archive_member_header(String8 raw_archive, U64 offset, COFF_ParsedArchiveMemberHeader *header_out);
internal COFF_ArchiveFirstMember coff_parse_first_archive_member (COFF_ArchiveMember *member);
internal COFF_ArchiveSecondMember coff_parse_second_archive_member(COFF_ArchiveMember *member);
internal String8 coff_decode_raw_member_name (String8 long_names, String8 name);
internal String8 coff_decode_member_name (String8 long_names, String8 name);
internal U64 coff_parse_import (String8 raw_archive_member, U64 offset, COFF_ParsedArchiveImportHeader *header_out);
internal COFF_ArchiveMember coff_archive_member_from_offset(String8 raw_archive, U64 offset);
internal COFF_ArchiveMember coff_archive_member_from_data (String8 raw_archive_member);
internal COFF_ParsedArchiveImportHeader coff_archive_import_from_data (String8 raw_archive_member);
internal U64 coff_regular_archive_member_iter_init(String8 raw_archive);
internal B32 coff_regular_archive_member_iter_next(String8 raw_archive, U64 *offset, COFF_ArchiveMember *member_out);
internal U64 coff_thin_archive_member_iter_init(String8 raw_archive);
internal B32 coff_thin_archive_member_iter_next(String8 raw_archive, U64 *offset, COFF_ArchiveMember *member_out);
internal COFF_ArchiveParse coff_regular_archive_parse_from_member_list(COFF_ArchiveMemberList list);
internal COFF_ArchiveParse coff_thin_archive_parse_from_data (String8 raw_archive);
internal COFF_ArchiveParse coff_regular_archive_parse_from_data (String8 raw_archive);
internal COFF_ArchiveParse coff_archive_parse_from_data (String8 raw_archive);
#endif // COFF_PARSE_H
-111
View File
@@ -1,111 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal CFG_KeyMap *
cfg_key_map_from_cfg(Arena *arena)
{
Temp scratch = scratch_begin(&arena, 1);
CFG_KeyMap *key_map = push_array(arena, CFG_KeyMap, 1);
{
key_map->name_slots_count = 4096;
key_map->name_slots = push_array(arena, CFG_KeyMapSlot, key_map->name_slots_count);
key_map->binding_slots_count = 4096;
key_map->binding_slots = push_array(arena, CFG_KeyMapSlot, key_map->binding_slots_count);
//- rjf: gather & parse all explicitly stored keybinding sets
CFG_NodePtrList keybindings_cfg_list = cfg_node_top_level_list_from_string(scratch.arena, str8_lit("keybindings"));
for(CFG_NodePtrNode *n = keybindings_cfg_list.first; n != 0; n = n->next)
{
CFG_Node *keybindings_root = n->v;
for(CFG_Node *keybinding = keybindings_root->first; keybinding != &cfg_nil_node; keybinding = keybinding->next)
{
String8 name = {0};
CFG_Binding binding = {0};
for(CFG_Node *child = keybinding->first; child != &cfg_nil_node; child = child->next)
{
if(0){}
else if(str8_match(child->string, str8_lit("ctrl"), 0)) { binding.modifiers |= OS_Modifier_Ctrl; }
else if(str8_match(child->string, str8_lit("alt"), 0)) { binding.modifiers |= OS_Modifier_Alt; }
else if(str8_match(child->string, str8_lit("shift"), 0)) { binding.modifiers |= OS_Modifier_Shift; }
else
{
OS_Key key = OS_Key_Null;
for EachEnumVal(OS_Key, k)
{
if(str8_match(child->string, os_g_key_cfg_string_table[k], StringMatchFlag_CaseInsensitive))
{
key = k;
break;
}
}
if(key != OS_Key_Null)
{
binding.key = key;
}
else
{
name = child->string;
}
}
}
if(name.size != 0)
{
U64 name_hash = d_hash_from_string(name);
U64 binding_hash = d_hash_from_string(str8_struct(&binding));
U64 name_slot_idx = name_hash%key_map->name_slots_count;
U64 binding_slot_idx = binding_hash%key_map->binding_slots_count;
CFG_KeyMapNode *n = push_array(arena, CFG_KeyMapNode, 1);
n->cfg_id = keybinding->id;
n->name = push_str8_copy(arena, name);
n->binding = binding;
SLLQueuePush_N(key_map->name_slots[name_slot_idx].first, key_map->name_slots[name_slot_idx].last, n, name_hash_next);
SLLQueuePush_N(key_map->binding_slots[binding_slot_idx].first, key_map->binding_slots[binding_slot_idx].last, n, binding_hash_next);
}
}
}
}
scratch_end(scratch);
return key_map;
}
internal CFG_KeyMapNodePtrList
cfg_key_map_node_ptr_list_from_name(Arena *arena, CFG_KeyMap *key_map, String8 string)
{
CFG_KeyMapNodePtrList list = {0};
{
U64 hash = d_hash_from_string(string);
U64 slot_idx = hash%key_map->name_slots_count;
for(CFG_KeyMapNode *n = key_map->name_slots[slot_idx].first; n != 0; n = n->name_hash_next)
{
if(str8_match(n->name, string, 0))
{
CFG_KeyMapNodePtr *ptr = push_array(arena, CFG_KeyMapNodePtr, 1);
ptr->v = n;
SLLQueuePush(list.first, list.last, ptr);
list.count += 1;
}
}
}
return list;
}
internal CFG_KeyMapNodePtrList
cfg_key_map_node_ptr_list_from_binding(Arena *arena, CFG_KeyMap *key_map, CFG_Binding binding)
{
CFG_KeyMapNodePtrList list = {0};
{
U64 hash = d_hash_from_string(str8_struct(&binding));
U64 slot_idx = hash%key_map->binding_slots_count;
for(CFG_KeyMapNode *n = key_map->binding_slots[slot_idx].first; n != 0; n = n->binding_hash_next)
{
if(MemoryMatchStruct(&binding, &n->binding))
{
CFG_KeyMapNodePtr *ptr = push_array(arena, CFG_KeyMapNodePtr, 1);
ptr->v = n;
SLLQueuePush(list.first, list.last, ptr);
list.count += 1;
}
}
}
return list;
}
-59
View File
@@ -1,59 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CONFIG_BINDINGS_H
#define CONFIG_BINDINGS_H
typedef struct CFG_Binding CFG_Binding;
struct CFG_Binding
{
OS_Key key;
OS_Modifiers modifiers;
};
typedef struct CFG_KeyMapNode CFG_KeyMapNode;
struct CFG_KeyMapNode
{
CFG_KeyMapNode *name_hash_next;
CFG_KeyMapNode *binding_hash_next;
CFG_ID cfg_id;
String8 name;
CFG_Binding binding;
};
typedef struct CFG_KeyMapNodePtr CFG_KeyMapNodePtr;
struct CFG_KeyMapNodePtr
{
CFG_KeyMapNodePtr *next;
CFG_KeyMapNode *v;
};
typedef struct CFG_KeyMapNodePtrList CFG_KeyMapNodePtrList;
struct CFG_KeyMapNodePtrList
{
CFG_KeyMapNodePtr *first;
CFG_KeyMapNodePtr *last;
U64 count;
};
typedef struct CFG_KeyMapSlot CFG_KeyMapSlot;
struct CFG_KeyMapSlot
{
CFG_KeyMapNode *first;
CFG_KeyMapNode *last;
};
typedef struct CFG_KeyMap CFG_KeyMap;
struct CFG_KeyMap
{
U64 name_slots_count;
CFG_KeyMapSlot *name_slots;
U64 binding_slots_count;
CFG_KeyMapSlot *binding_slots;
};
internal CFG_KeyMap *cfg_key_map_from_cfg(Arena *arena);
internal CFG_KeyMapNodePtrList cfg_key_map_node_ptr_list_from_name(Arena *arena, CFG_KeyMap *key_map, String8 string);
internal CFG_KeyMapNodePtrList cfg_key_map_node_ptr_list_from_binding(Arena *arena, CFG_KeyMap *key_map, CFG_Binding binding);
#endif // CONFIG_BINDINGS_H
-896
View File
@@ -1,896 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: ID Functions
internal void
cfg_id_list_push(Arena *arena, CFG_IDList *list, CFG_ID id)
{
CFG_IDNode *n = push_array(arena, CFG_IDNode, 1);
SLLQueuePush(list->first, list->last, n);
list->count += 1;
n->v = id;
}
internal CFG_IDList
cfg_id_list_copy(Arena *arena, CFG_IDList *src)
{
CFG_IDList dst = {0};
for EachNode(n, CFG_IDNode, src->first)
{
cfg_id_list_push(arena, &dst, n->v);
}
return dst;
}
////////////////////////////////
//~ rjf: Node Pointer Data Structure Functions
internal void
cfg_node_ptr_list_push(Arena *arena, CFG_NodePtrList *list, CFG_Node *node)
{
CFG_NodePtrNode *n = push_array(arena, CFG_NodePtrNode, 1);
DLLPushBack(list->first, list->last, n);
list->count += 1;
n->v = node;
}
internal void
cfg_node_ptr_list_push_front(Arena *arena, CFG_NodePtrList *list, CFG_Node *node)
{
CFG_NodePtrNode *n = push_array(arena, CFG_NodePtrNode, 1);
DLLPushFront(list->first, list->last, n);
list->count += 1;
n->v = node;
}
internal CFG_NodePtrArray
cfg_node_ptr_array_from_list(Arena *arena, CFG_NodePtrList *list)
{
CFG_NodePtrArray array = {0};
array.count = list->count;
array.v = push_array_no_zero(arena, CFG_Node *, array.count);
{
U64 idx = 0;
for EachNode(n, CFG_NodePtrNode, list->first)
{
array.v[idx] = n->v;
idx += 1;
}
}
return array;
}
////////////////////////////////
//~ rjf: Schema Data Structure Functions
internal void
cfg_schema_table_insert(Arena *arena, CFG_SchemaTable *table, String8 name, MD_Node *schema)
{
U64 hash = u64_hash_from_str8(name);
U64 slot_idx = hash%table->slots_count;
CFG_SchemaNode *node = 0;
for(CFG_SchemaNode *n = table->slots[slot_idx]; n != 0; n = n->next)
{
if(str8_match(n->name, name, 0))
{
node = n;
break;
}
}
if(node == 0)
{
node = push_array(arena, CFG_SchemaNode, 1);
node->name = str8_copy(arena, name);
node->schema = schema;
SLLStackPush(table->slots[slot_idx], node);
}
}
internal MD_Node *
cfg_schema_from_name(CFG_SchemaTable *table, String8 name)
{
MD_Node *result = &md_nil_node;
{
U64 hash = u64_hash_from_str8(name);
U64 slot_idx = hash%table->slots_count;
CFG_SchemaNode *node = 0;
for(CFG_SchemaNode *n = table->slots[slot_idx]; n != 0; n = n->next)
{
if(str8_match(n->name, name, 0))
{
result = n->schema;
break;
}
}
}
return result;
}
internal MD_NodePtrList
cfg_schemas_from_name(Arena *arena, CFG_SchemaTable *table, String8 name)
{
MD_NodePtrList result = {0};
{
Temp scratch = scratch_begin(&arena, 1);
String8List tasks = {0};
String8Node seed_task = {0, name};
str8_list_push_node(&tasks, &seed_task);
for EachNode(task, String8Node, tasks.first)
{
MD_Node *schema = cfg_schema_from_name(table, task->string);
if(!md_node_is_nil(schema))
{
md_node_ptr_list_push_front(arena, &result, schema);
for MD_EachNode(tag, schema->first_tag)
{
if(str8_match(tag->string, str8_lit("inherit"), 0))
{
str8_list_push(scratch.arena, &tasks, tag->first->string);
}
}
}
}
scratch_end(scratch);
}
return result;
}
////////////////////////////////
//~ rjf: Config Reading Functions
//- rjf: context selection
internal void
cfg_ctx_select(CFG_Ctx *ctx)
{
cfg_ctx = ctx;
}
//- rjf: tree navigations
internal U64
cfg_change_gen(void)
{
U64 result = 0;
if(cfg_ctx != 0)
{
result = cfg_ctx->change_gen;
}
return result;
}
internal CFG_Node *
cfg_node_root(void)
{
CFG_Node *result = &cfg_nil_node;
if(cfg_ctx != 0)
{
result = cfg_ctx->root;
}
return result;
}
internal CFG_Node *
cfg_node_from_id(CFG_ID id)
{
CFG_Node *result = &cfg_nil_node;
if(id != 0 &&
id == cfg_ctx->last_accessed_id &&
id == cfg_ctx->last_accessed->id)
{
result = cfg_ctx->last_accessed;
}
else
{
U64 hash = u64_hash_from_str8(str8_struct(&id));
U64 slot_idx = hash%cfg_ctx->id_slots_count;
for(CFG_NodePtrNode *n = cfg_ctx->id_slots[slot_idx].first; n != 0; n = n->next)
{
if(n->v->id == id)
{
result = n->v;
break;
}
}
}
cfg_ctx->last_accessed_id = id;
cfg_ctx->last_accessed = result;
return result;
}
internal CFG_Node *
cfg_node_child_from_string(CFG_Node *parent, String8 string)
{
CFG_Node *child = &cfg_nil_node;
if(string.size != 0)
{
for(CFG_Node *c = parent->first; c != &cfg_nil_node; c = c->next)
{
if(str8_match(c->string, string, 0))
{
child = c;
break;
}
}
}
return child;
}
internal CFG_Node *
cfg_node_child_from_string_or_parent(CFG_Node *parent, String8 string)
{
CFG_Node *result = cfg_node_child_from_string(parent, string);
if(result == &cfg_nil_node)
{
result = parent;
}
return result;
}
internal CFG_NodePtrList
cfg_node_child_list_from_string(Arena *arena, CFG_Node *parent, String8 string)
{
CFG_NodePtrList result = {0};
for(CFG_Node *child = parent->first; child != &cfg_nil_node; child = child->next)
{
if(str8_match(child->string, string, 0))
{
cfg_node_ptr_list_push(arena, &result, child);
}
}
return result;
}
internal CFG_NodePtrList
cfg_node_top_level_list_from_string(Arena *arena, String8 string)
{
CFG_NodePtrList result = {0};
for(CFG_Node *bucket = cfg_ctx->root->first; bucket != &cfg_nil_node; bucket = bucket->next)
{
for(CFG_Node *tln = bucket->first; tln != &cfg_nil_node; tln = tln->next)
{
if(str8_match(tln->string, string, 0))
{
cfg_node_ptr_list_push(arena, &result, tln);
}
}
}
return result;
}
internal CFG_NodeRec
cfg_node_rec__depth_first(CFG_Node *root, CFG_Node *node)
{
CFG_NodeRec rec = {&cfg_nil_node};
if(node->first != &cfg_nil_node)
{
rec.next = node->first;
rec.push_count = 1;
}
else for(CFG_Node *p = node; p != root; p = p->parent, rec.pop_count += 1)
{
if(p->next != &cfg_nil_node)
{
rec.next = p->next;
break;
}
}
return rec;
}
//- rjf: serialization
internal String8
cfg_string_from_tree(Arena *arena, CFG_SchemaTable *schema_table, String8 root_path, CFG_Node *root)
{
Temp scratch = scratch_begin(&arena, 1);
String8List strings = {0};
{
typedef struct NestTask NestTask;
struct NestTask
{
NestTask *next;
CFG_Node *cfg;
MD_Node *schema;
B32 is_simple;
};
NestTask *top_nest_task = 0;
CFG_NodeRec rec = {0};
for(CFG_Node *c = root; c != &cfg_nil_node; c = rec.next)
{
// rjf: look up parent's schemas
MD_NodePtrList schemas = {0};
if(top_nest_task != 0)
{
CFG_Node *parent = top_nest_task->cfg;
schemas = cfg_schemas_from_name(scratch.arena, schema_table, parent->string);
}
// rjf: look up child schema
MD_Node *c_schema = &md_nil_node;
for(MD_NodePtrNode *n = schemas.first; n != 0 && c_schema == &md_nil_node; n = n->next)
{
c_schema = md_child_from_string(n->v, c->string, 0);
}
// rjf: push name of this node
if(c->string.size != 0 || c->first == &cfg_nil_node)
{
// rjf: extract the textualized form for this string (we may need to escape / relativize)
String8 c_serialized_string = c->string;
{
MD_Node *c_schema = &md_nil_node;
if(top_nest_task != 0)
{
c_schema = top_nest_task->schema;
}
// rjf: paths -> relativize
if(!md_node_has_tag(c_schema->first, str8_lit("no_relativize"), 0))
{
if(str8_match(c_schema->first->string, str8_lit("path"), 0))
{
String8 path_absolute = c->string;
String8 path_relative = path_relative_dst_from_absolute_dst_src(arena, path_absolute, root_path);
c_serialized_string = path_relative;
}
else if(str8_match(c_schema->first->string, str8_lit("path_pt"), 0))
{
String8 value = c->string;
String8TxtPtPair parts = str8_txt_pt_pair_from_string(value);
String8 path_relative = path_relative_dst_from_absolute_dst_src(scratch.arena, parts.string, root_path);
c_serialized_string = push_str8f(arena, "%S:%I64d:%I64d", path_relative, parts.pt.line, parts.pt.column);
}
}
// rjf: all strings -> escape
c_serialized_string = escaped_from_raw_str8(arena, c_serialized_string);
}
// rjf: generate all strings for this node's string
String8List c_name_strings = {0};
{
B32 name_can_be_pushed_standalone = 0;
{
Temp temp = temp_begin(scratch.arena);
MD_TokenizeResult c_name_tokenize = md_tokenize_from_text(temp.arena, c_serialized_string);
name_can_be_pushed_standalone = (c_name_tokenize.tokens.count == 1 && c_name_tokenize.tokens.v[0].flags & (MD_TokenFlag_Identifier|
MD_TokenFlag_Numeric|
MD_TokenFlag_StringLiteral|
MD_TokenFlag_Symbol));
temp_end(temp);
}
if(name_can_be_pushed_standalone)
{
str8_list_push(scratch.arena, &c_name_strings, c_serialized_string);
}
else
{
str8_list_push(scratch.arena, &c_name_strings, str8_lit("\""));
str8_list_push(scratch.arena, &c_name_strings, c_serialized_string);
str8_list_push(scratch.arena, &c_name_strings, str8_lit("\""));
}
}
// rjf: if we're in a simple nesting task, then just break children by space
if(top_nest_task != 0 && top_nest_task->is_simple)
{
str8_list_push(scratch.arena, &strings, str8_lit(" "));
}
// rjf: join c's strings with main string list
str8_list_concat_in_place(&strings, &c_name_strings);
}
// rjf: grab next recursion
rec = cfg_node_rec__depth_first(root, c);
// rjf: push a new nesting task before descending to children
if(c->first != &cfg_nil_node)
{
B32 is_simple_children_list = 1;
for(CFG_Node *child = c->first; child != &cfg_nil_node; child = child->next)
{
if(child->first != &cfg_nil_node && child != c->last)
{
is_simple_children_list = 0;
break;
}
}
NestTask *task = push_array(scratch.arena, NestTask, 1);
task->cfg = c;
task->schema = c_schema;
task->is_simple = is_simple_children_list;
SLLStackPush(top_nest_task, task);
}
// rjf: tree navigations -> encode hierarchy
if(rec.push_count > 0)
{
if(top_nest_task->is_simple && c->string.size != 0)
{
str8_list_push(scratch.arena, &strings, str8_lit(":"));
}
else
{
if(c->string.size != 0)
{
str8_list_push(scratch.arena, &strings, str8_lit(":\n"));
}
str8_list_push(scratch.arena, &strings, str8_lit("{"));
}
}
else
{
for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1, SLLStackPop(top_nest_task))
{
if(top_nest_task->is_simple)
{
if(top_nest_task->cfg->string.size == 0)
{
str8_list_push(scratch.arena, &strings, str8_lit(" }"));
}
}
else
{
str8_list_push(scratch.arena, &strings, str8_lit("\n}"));
}
}
}
if(!top_nest_task || top_nest_task->is_simple == 0)
{
str8_list_push(scratch.arena, &strings, str8_lit("\n"));
}
}
}
String8 result_unindented = str8_list_join(scratch.arena, &strings, 0);
String8 result = indented_from_string(arena, result_unindented);
scratch_end(scratch);
return result;
}
////////////////////////////////
//~ rjf: Config Writing Functions
//- rjf: state creation / destroying
internal CFG_State *
cfg_state_alloc(void)
{
Arena *arena = arena_alloc();
CFG_State *state = push_array(arena, CFG_State, 1);
state->arena = arena;
state->ctx.id_slots_count = 4096;
state->ctx.id_slots = push_array(arena, CFG_NodePtrSlot, state->ctx.id_slots_count);
state->ctx.root = cfg_node_alloc(state);
return state;
}
internal void
cfg_state_release(CFG_State *state)
{
arena_release(state->arena);
}
//- rjf: state -> ctx
internal CFG_Ctx *
cfg_state_ctx(CFG_State *state)
{
CFG_Ctx *ctx = &state->ctx;
return ctx;
}
//- rjf: string allocations
internal U64
cfg_string_bucket_num_from_size(U64 size)
{
U64 bucket_num = 0;
if(size > 0)
{
for EachElement(idx, cfg_string_bucket_chunk_sizes)
{
if(size <= cfg_string_bucket_chunk_sizes[idx])
{
bucket_num = idx+1;
break;
}
}
}
return bucket_num;
}
internal String8
cfg_string_alloc(CFG_State *state, String8 string)
{
//- rjf: allocate node
CFG_StringChunkNode *node = 0;
{
U64 bucket_num = cfg_string_bucket_num_from_size(string.size);
if(bucket_num == ArrayCount(cfg_string_bucket_chunk_sizes))
{
CFG_StringChunkNode *best_node = 0;
CFG_StringChunkNode *best_node_prev = 0;
U64 best_node_size = max_U64;
{
for(CFG_StringChunkNode *n = state->free_string_chunks[bucket_num-1], *prev = 0; n != 0; (prev = n, n = n->next))
{
if(n->size >= string.size && n->size < best_node_size)
{
best_node = n;
best_node_prev = prev;
best_node_size = n->size;
}
}
}
if(best_node != 0)
{
node = best_node;
if(best_node_prev)
{
best_node_prev->next = best_node->next;
}
else
{
state->free_string_chunks[bucket_num-1] = best_node->next;
}
}
else
{
U64 chunk_size = u64_up_to_pow2(string.size);
node = (CFG_StringChunkNode *)push_array(state->arena, U8, chunk_size);
}
}
else if(bucket_num != 0)
{
node = state->free_string_chunks[bucket_num-1];
if(node != 0)
{
SLLStackPop(state->free_string_chunks[bucket_num-1]);
}
else
{
node = (CFG_StringChunkNode *)push_array(state->arena, U8, cfg_string_bucket_chunk_sizes[bucket_num-1]);
}
}
}
//- rjf: fill node
String8 result = {0};
if(node != 0)
{
result.str = (U8 *)node;
result.size = string.size;
MemoryCopy(result.str, string.str, result.size);
}
return result;
}
internal void
cfg_string_release(CFG_State *state, String8 string)
{
U64 bucket_num = cfg_string_bucket_num_from_size(string.size);
if(1 <= bucket_num && bucket_num <= ArrayCount(cfg_string_bucket_chunk_sizes))
{
U64 bucket_idx = bucket_num-1;
CFG_StringChunkNode *node = (CFG_StringChunkNode *)string.str;
SLLStackPush(state->free_string_chunks[bucket_idx], node);
node->size = u64_up_to_pow2(string.size);
}
}
//- rjf: tree building
internal CFG_Node *
cfg_node_alloc(CFG_State *state)
{
state->ctx.change_gen += 1;
// rjf: allocate
CFG_Node *result = state->free;
{
if(result)
{
SLLStackPop(state->free);
}
else
{
result = push_array_no_zero(state->arena, CFG_Node, 1);
}
}
// rjf: generate ID & fill
state->id_gen += 1;
MemoryZeroStruct(result);
result->first = result->last = result->next = result->prev = result->parent = &cfg_nil_node;
result->id = state->id_gen;
// rjf: store to ID -> cfg map
{
CFG_NodePtrNode *cfg_id_node = state->free_id_node;
if(cfg_id_node != 0)
{
SLLStackPop(state->free_id_node);
}
else
{
cfg_id_node = push_array(state->arena, CFG_NodePtrNode, 1);
}
U64 hash = u64_hash_from_str8(str8_struct(&result->id));
U64 slot_idx = hash%state->ctx.id_slots_count;
DLLPushBack(state->ctx.id_slots[slot_idx].first, state->ctx.id_slots[slot_idx].last, cfg_id_node);
cfg_id_node->v = result;
}
return result;
}
internal void
cfg_node_release(CFG_State *state, CFG_Node *node)
{
state->ctx.change_gen += 1;
Temp scratch = scratch_begin(0, 0);
// rjf: unhook from context
cfg_node_unhook(state, node->parent, node);
// rjf: gather root & all descendants
CFG_NodePtrList nodes = {0};
for(CFG_Node *c = node; c != &cfg_nil_node; c = cfg_node_rec__depth_first(node, c).next)
{
cfg_node_ptr_list_push(scratch.arena, &nodes, c);
}
// rjf: release all nodes
for(CFG_NodePtrNode *n = nodes.first; n != 0; n = n->next)
{
CFG_Node *c = n->v;
cfg_string_release(state, c->string);
SLLStackPush(state->free, c);
c->first = c->last = c->prev = c->parent = 0;
c->id = 0;
c->string = str8_zero();
U64 hash = u64_hash_from_str8(str8_struct(&c->id));
U64 slot_idx = hash%state->ctx.id_slots_count;
for(CFG_NodePtrNode *n = state->ctx.id_slots[slot_idx].first; n != 0; n = n->next)
{
if(n->v == c)
{
DLLRemove(state->ctx.id_slots[slot_idx].first, state->ctx.id_slots[slot_idx].last, n);
SLLStackPush(state->free_id_node, n);
break;
}
}
}
scratch_end(scratch);
}
internal void
cfg_node_release_all_children(CFG_State *state, CFG_Node *node)
{
for(CFG_Node *child = node->first, *next = &cfg_nil_node; child != &cfg_nil_node; child = next)
{
next = child->next;
cfg_node_release(state, child);
}
}
internal CFG_Node *
cfg_node_new(CFG_State *state, CFG_Node *parent, String8 string)
{
CFG_Node *node = cfg_node_alloc(state);
cfg_node_insert_child(state, parent, parent->last, node);
cfg_node_equip_string(state, node, string);
return node;
}
internal CFG_Node *
cfg_node_newf(CFG_State *state, CFG_Node *parent, char *fmt, ...)
{
Temp scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 string = push_str8fv(scratch.arena, fmt, args);
CFG_Node *result = cfg_node_new(state, parent, string);
va_end(args);
scratch_end(scratch);
return result;
}
internal CFG_Node *
cfg_node_new_replace(CFG_State *state, CFG_Node *parent, String8 string)
{
Temp scratch = scratch_begin(0, 0);
string = push_str8_copy(scratch.arena, string);
for(CFG_Node *child = parent->first->next, *next = &cfg_nil_node; child != &cfg_nil_node; child = next)
{
next = child->next;
cfg_node_release(state, child);
}
if(parent->first == &cfg_nil_node)
{
cfg_node_new(state, parent, str8_zero());
}
CFG_Node *child = parent->first;
cfg_node_equip_string(state, child, string);
scratch_end(scratch);
return child;
}
internal CFG_Node *
cfg_node_new_replacef(CFG_State *state, CFG_Node *parent, char *fmt, ...)
{
Temp scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 string = push_str8fv(scratch.arena, fmt, args);
CFG_Node *result = cfg_node_new_replace(state, parent, string);
va_end(args);
scratch_end(scratch);
return result;
}
internal CFG_Node *
cfg_node_deep_copy(CFG_State *state, CFG_Node *src_root)
{
CFG_NodeRec rec = {0};
CFG_Node *dst_root = &cfg_nil_node;
CFG_Node *dst_parent = &cfg_nil_node;
for(CFG_Node *src = src_root; src != &cfg_nil_node; src = rec.next)
{
CFG_Node *dst = cfg_node_new(state, dst_parent, src->string);
if(dst_root == &cfg_nil_node)
{
dst_root = dst;
}
rec = cfg_node_rec__depth_first(src_root, src);
if(rec.push_count > 0)
{
dst_parent = dst;
}
else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
{
dst_parent = dst_parent->parent;
}
}
return dst_root;
}
internal void
cfg_node_equip_string(CFG_State *state, CFG_Node *node, String8 string)
{
cfg_string_release(state, node->string);
node->string = cfg_string_alloc(state, string);
state->ctx.change_gen += 1;
}
internal void
cfg_node_equip_stringf(CFG_State *state, CFG_Node *node, char *fmt, ...)
{
Temp scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 string = push_str8fv(scratch.arena, fmt, args);
cfg_node_equip_string(state, node, string);
va_end(args);
scratch_end(scratch);
}
internal void
cfg_node_insert_child(CFG_State *state, CFG_Node *parent, CFG_Node *prev_child, CFG_Node *new_child)
{
if(parent != &cfg_nil_node)
{
if(new_child->parent != &cfg_nil_node)
{
cfg_node_unhook(state, new_child->parent, new_child);
}
DLLInsert_NPZ(&cfg_nil_node, parent->first, parent->last, prev_child, new_child, next, prev);
new_child->parent = parent;
}
}
internal void
cfg_node_unhook(CFG_State *state, CFG_Node *parent, CFG_Node *child)
{
if(child != &cfg_nil_node && parent == child->parent && parent != &cfg_nil_node)
{
DLLRemove_NPZ(&cfg_nil_node, parent->first, parent->last, child, next, prev);
child->parent = &cfg_nil_node;
}
}
internal CFG_Node *
cfg_node_child_from_string_or_alloc(CFG_State *state, CFG_Node *parent, String8 string)
{
CFG_Node *node = cfg_node_child_from_string(parent, string);
if(node == &cfg_nil_node)
{
node = cfg_node_new(state, parent, string);
}
return node;
}
//- rjf: deserialization
internal CFG_NodePtrList
cfg_node_ptr_list_from_string(Arena *arena, CFG_State *state, CFG_SchemaTable *schema_table, String8 root_path, String8 string)
{
CFG_NodePtrList result = {0};
Temp scratch = scratch_begin(&arena, 1);
//- rjf: parse the string as metadesk
MD_Node *root = md_tree_from_string(scratch.arena, string);
//- rjf: iterate the top-level metadesk trees, generate new cfg trees for each
for MD_EachNode(tln, root->first)
{
CFG_Node *dst_root_n = &cfg_nil_node;
CFG_Node *dst_active_parent_n = &cfg_nil_node;
MD_NodeRec rec = {0};
for(MD_Node *src_n = tln; !md_node_is_nil(src_n); src_n = rec.next)
{
// rjf: lookup schema for this string
MD_Node *schema = &md_nil_node;
{
MD_NodePtrList schemas = cfg_schemas_from_name(scratch.arena, schema_table, dst_active_parent_n->parent->string);
for(MD_NodePtrNode *n = schemas.first; n != 0 && schema == &md_nil_node; n = n->next)
{
schema = md_child_from_string(n->v, dst_active_parent_n->string, 0);
}
}
// rjf: extract & transform metadesk node's string (it is raw textual data, so we need to
// go escaped -> raw, and derelativize paths)
String8 dst_n_string = {0};
{
String8 src_n_string = src_n->string;
String8 src_n_string__raw = raw_from_escaped_str8(scratch.arena, src_n_string);
if(!md_node_has_tag(schema->first, str8_lit("no_relativize"), 0))
{
if(str8_match(schema->first->string, str8_lit("path"), 0))
{
src_n_string__raw = path_absolute_dst_from_relative_dst_src(scratch.arena, src_n_string__raw, root_path);
}
else if(str8_match(schema->first->string, str8_lit("path_pt"), 0))
{
String8TxtPtPair parts = str8_txt_pt_pair_from_string(src_n_string__raw);
src_n_string__raw = push_str8f(scratch.arena, "%S:%I64d:%I64d", path_absolute_dst_from_relative_dst_src(scratch.arena, parts.string, root_path), parts.pt.line, parts.pt.column);
}
}
dst_n_string = src_n_string__raw;
}
// rjf: allocate, fill, & insert new cfg for this metadesk node
CFG_Node *dst_n = cfg_node_alloc(state);
cfg_node_equip_string(state, dst_n, dst_n_string);
if(dst_active_parent_n != &cfg_nil_node)
{
cfg_node_insert_child(state, dst_active_parent_n, dst_active_parent_n->last, dst_n);
}
// rjf: recurse
rec = md_node_rec_depth_first_pre(src_n, tln);
if(dst_active_parent_n == &cfg_nil_node)
{
dst_root_n = dst_n;
}
if(rec.push_count > 0)
{
dst_active_parent_n = dst_n;
}
else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
{
dst_active_parent_n = dst_active_parent_n->parent;
}
}
cfg_node_ptr_list_push(arena, &result, dst_root_n);
}
scratch_end(scratch);
return result;
}
-233
View File
@@ -1,233 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CONFIG_CORE_H
#define CONFIG_CORE_H
////////////////////////////////
//~ rjf: IDs
typedef U64 CFG_ID;
typedef struct CFG_IDNode CFG_IDNode;
struct CFG_IDNode
{
CFG_IDNode *next;
CFG_ID v;
};
typedef struct CFG_IDList CFG_IDList;
struct CFG_IDList
{
CFG_IDNode *first;
CFG_IDNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Tree Types
typedef struct CFG_Node CFG_Node;
struct CFG_Node
{
CFG_Node *first;
CFG_Node *last;
CFG_Node *next;
CFG_Node *prev;
CFG_Node *parent;
CFG_ID id;
String8 string;
};
typedef struct CFG_NodePtrNode CFG_NodePtrNode;
struct CFG_NodePtrNode
{
CFG_NodePtrNode *next;
CFG_NodePtrNode *prev;
CFG_Node *v;
};
typedef struct CFG_NodePtrSlot CFG_NodePtrSlot;
struct CFG_NodePtrSlot
{
CFG_NodePtrNode *first;
CFG_NodePtrNode *last;
};
typedef struct CFG_NodePtrList CFG_NodePtrList;
struct CFG_NodePtrList
{
CFG_NodePtrNode *first;
CFG_NodePtrNode *last;
U64 count;
};
typedef struct CFG_NodePtrArray CFG_NodePtrArray;
struct CFG_NodePtrArray
{
CFG_Node **v;
U64 count;
};
typedef struct CFG_NodeRec CFG_NodeRec;
struct CFG_NodeRec
{
CFG_Node *next;
S32 push_count;
S32 pop_count;
};
////////////////////////////////
//~ rjf: String Allocator
read_only global U64 cfg_string_bucket_chunk_sizes[] =
{
16,
64,
256,
1024,
4096,
16384,
65536,
0xffffffffffffffffull,
};
typedef struct CFG_StringChunkNode CFG_StringChunkNode;
struct CFG_StringChunkNode
{
CFG_StringChunkNode *next;
U64 size;
};
////////////////////////////////
//~ rjf: Config State Bundles
typedef struct CFG_Ctx CFG_Ctx;
struct CFG_Ctx
{
CFG_Node *root;
U64 id_slots_count;
CFG_NodePtrSlot *id_slots;
U64 change_gen;
CFG_ID last_accessed_id;
CFG_Node *last_accessed;
};
typedef struct CFG_State CFG_State;
struct CFG_State
{
Arena *arena;
CFG_Node *free;
CFG_NodePtrNode *free_id_node;
CFG_StringChunkNode *free_string_chunks[ArrayCount(cfg_string_bucket_chunk_sizes)];
U64 id_gen;
CFG_Ctx ctx;
};
////////////////////////////////
//~ rjf: Schema Table
typedef struct CFG_SchemaNode CFG_SchemaNode;
struct CFG_SchemaNode
{
CFG_SchemaNode *next;
String8 name;
MD_Node *schema;
};
typedef struct CFG_SchemaTable CFG_SchemaTable;
struct CFG_SchemaTable
{
CFG_SchemaNode **slots;
U64 slots_count;
};
////////////////////////////////
//~ rjf: Globals
read_only global CFG_Node cfg_nil_node =
{
&cfg_nil_node,
&cfg_nil_node,
&cfg_nil_node,
&cfg_nil_node,
&cfg_nil_node,
};
thread_static CFG_Ctx *cfg_ctx = 0;
////////////////////////////////
//~ rjf: ID Functions
internal void cfg_id_list_push(Arena *arena, CFG_IDList *list, CFG_ID id);
internal CFG_IDList cfg_id_list_copy(Arena *arena, CFG_IDList *src);
////////////////////////////////
//~ rjf: Node Pointer Data Structure Functions
internal void cfg_node_ptr_list_push(Arena *arena, CFG_NodePtrList *list, CFG_Node *node);
internal void cfg_node_ptr_list_push_front(Arena *arena, CFG_NodePtrList *list, CFG_Node *node);
#define cfg_node_ptr_list_first(list) ((list)->count ? (list)->first->v : &cfg_nil_node)
#define cfg_node_ptr_list_last(list) ((list)->count ? (list)->last->v : &cfg_nil_node)
internal CFG_NodePtrArray cfg_node_ptr_array_from_list(Arena *arena, CFG_NodePtrList *list);
////////////////////////////////
//~ rjf: Schema Data Structure Functions
internal void cfg_schema_table_insert(Arena *arena, CFG_SchemaTable *table, String8 name, MD_Node *schema);
internal MD_Node *cfg_schema_from_name(CFG_SchemaTable *table, String8 name);
internal MD_NodePtrList cfg_schemas_from_name(Arena *arena, CFG_SchemaTable *table, String8 name);
////////////////////////////////
//~ rjf: Config Reading Functions
//- rjf: context selection
internal void cfg_ctx_select(CFG_Ctx *ctx);
//- rjf: tree navigations
internal U64 cfg_change_gen(void);
internal CFG_Node *cfg_node_root(void);
internal CFG_Node *cfg_node_from_id(CFG_ID id);
internal CFG_Node *cfg_node_child_from_string(CFG_Node *parent, String8 string);
internal CFG_Node *cfg_node_child_from_string_or_parent(CFG_Node *parent, String8 string);
internal CFG_NodePtrList cfg_node_child_list_from_string(Arena *arena, CFG_Node *parent, String8 string);
internal CFG_NodePtrList cfg_node_top_level_list_from_string(Arena *arena, String8 string);
internal CFG_NodeRec cfg_node_rec__depth_first(CFG_Node *root, CFG_Node *node);
//- rjf: serialization
internal String8 cfg_string_from_tree(Arena *arena, CFG_SchemaTable *schema_table, String8 root_path, CFG_Node *root);
////////////////////////////////
//~ rjf: Config Writing Functions
//- rjf: state creation / destroying
internal CFG_State *cfg_state_alloc(void);
internal void cfg_state_release(CFG_State *state);
//- rjf: state -> ctx
internal CFG_Ctx *cfg_state_ctx(CFG_State *state);
//- rjf: string allocations
internal U64 cfg_string_bucket_num_from_size(U64 size);
internal String8 cfg_string_alloc(CFG_State *state, String8 string);
internal void cfg_string_release(CFG_State *state, String8 string);
//- rjf: tree building
internal CFG_Node *cfg_node_alloc(CFG_State *state);
internal void cfg_node_release(CFG_State *state, CFG_Node *node);
internal void cfg_node_release_all_children(CFG_State *state, CFG_Node *node);
internal CFG_Node *cfg_node_new(CFG_State *state, CFG_Node *parent, String8 string);
internal CFG_Node *cfg_node_newf(CFG_State *state, CFG_Node *parent, char *fmt, ...);
internal CFG_Node *cfg_node_new_replace(CFG_State *state, CFG_Node *parent, String8 string);
internal CFG_Node *cfg_node_new_replacef(CFG_State *state, CFG_Node *parent, char *fmt, ...);
internal CFG_Node *cfg_node_deep_copy(CFG_State *state, CFG_Node *src_root);
internal void cfg_node_equip_string(CFG_State *state, CFG_Node *node, String8 string);
internal void cfg_node_equip_stringf(CFG_State *state, CFG_Node *node, char *fmt, ...);
internal void cfg_node_insert_child(CFG_State *state, CFG_Node *parent, CFG_Node *prev_child, CFG_Node *new_child);
internal void cfg_node_unhook(CFG_State *state, CFG_Node *parent, CFG_Node *child);
internal CFG_Node *cfg_node_child_from_string_or_alloc(CFG_State *state, CFG_Node *parent, String8 string);
//- rjf: deserialization
internal CFG_NodePtrList cfg_node_ptr_list_from_string(Arena *arena, CFG_State *state, CFG_SchemaTable *schema_table, String8 root_path, String8 string);
#endif // CONFIG_CORE_H
-8
View File
@@ -1,8 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "config_core.c"
#include "config_panels.c"
#if defined(OS_GFX_H)
# include "config_bindings.c"
#endif
-13
View File
@@ -1,13 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CONFIG_INC_H
#define CONFIG_INC_H
#include "config_core.h"
#include "config_panels.h"
#if defined(OS_GFX_H)
# include "config_bindings.h"
#endif
#endif // CONFIG_INC_H
-217
View File
@@ -1,217 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal CFG_Node *
cfg_window_from_cfg(CFG_Node *cfg)
{
CFG_Node *result = &cfg_nil_node;
for(CFG_Node *c = cfg; c != &cfg_nil_node; c = c->parent)
{
if(c->parent->parent == cfg_node_root() && str8_match(c->string, str8_lit("window"), 0))
{
result = c;
break;
}
}
return result;
}
internal CFG_PanelTree
cfg_panel_tree_from_cfg(Arena *arena, CFG_Node *cfg_root)
{
Temp scratch = scratch_begin(&arena, 1);
CFG_Node *wcfg = cfg_window_from_cfg(cfg_root);
CFG_Node *src_root = cfg_node_child_from_string(wcfg, str8_lit("panels"));
CFG_PanelNode *dst_root = &cfg_nil_panel_node;
CFG_PanelNode *dst_focused = &cfg_nil_panel_node;
{
Axis2 active_split_axis = cfg_node_child_from_string(wcfg, str8_lit("split_x")) != &cfg_nil_node ? Axis2_X : Axis2_Y;
CFG_NodeRec rec = {0};
CFG_PanelNode *dst_active_parent = &cfg_nil_panel_node;
for(CFG_Node *src = src_root; src != &cfg_nil_node; src = rec.next)
{
// rjf: build a panel node
CFG_PanelNode *dst = push_array(arena, CFG_PanelNode, 1);
MemoryCopyStruct(dst, &cfg_nil_panel_node);
dst->parent = dst_active_parent;
if(dst_active_parent != &cfg_nil_panel_node)
{
DLLPushBack_NPZ(&cfg_nil_panel_node, dst_active_parent->first, dst_active_parent->last, dst, next, prev);
dst_active_parent->child_count += 1;
}
if(dst_root == &cfg_nil_panel_node)
{
dst_root = dst;
}
// rjf: extract cfg info
B32 panel_has_children = 0;
dst->cfg = src;
dst->pct_of_parent = (src == src_root ? 1.f : (F32)f64_from_str8(src->string));
dst->tab_side = (cfg_node_child_from_string(src, str8_lit("tabs_on_bottom")) != &cfg_nil_node ? Side_Max : Side_Min);
dst->split_axis = active_split_axis;
for(CFG_Node *src_child = src->first; src_child != &cfg_nil_node; src_child = src_child->next)
{
MD_TokenizeResult tokenize = md_tokenize_from_text(scratch.arena, src_child->string);
if(tokenize.tokens.count == 1 && tokenize.tokens.v[0].flags & MD_TokenFlag_Numeric)
{
panel_has_children = 1;
}
else if(str8_match(src_child->string, str8_lit("tabs_on_bottom"), 0))
{
// NOTE(rjf): skip - this is a panel option.
}
else if(str8_match(src_child->string, str8_lit("selected"), 0))
{
dst_focused = dst;
}
else if(tokenize.tokens.count == 1 && tokenize.tokens.v[0].flags & MD_TokenFlag_Identifier)
{
cfg_node_ptr_list_push(arena, &dst->tabs, src_child);
if(cfg_node_child_from_string(src_child, str8_lit("selected")) != &cfg_nil_node)
{
dst->selected_tab = src_child;
}
}
}
// rjf: recurse
rec = cfg_node_rec__depth_first(src_root, src);
if(!panel_has_children)
{
MemoryZeroStruct(&rec);
rec.next = &cfg_nil_node;
for(CFG_Node *p = src; p != src_root && p != &cfg_nil_node; p = p->parent, rec.pop_count += 1)
{
if(p->next != &cfg_nil_node)
{
rec.next = p->next;
break;
}
}
}
if(rec.push_count > 0)
{
dst_active_parent = dst;
active_split_axis = axis2_flip(active_split_axis);
}
else for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1)
{
dst_active_parent = dst_active_parent->parent;
active_split_axis = axis2_flip(active_split_axis);
}
}
}
scratch_end(scratch);
CFG_PanelTree tree = {dst_root, dst_focused};
return tree;
}
internal CFG_PanelNodeRec
cfg_panel_node_rec__depth_first(CFG_PanelNode *root, CFG_PanelNode *panel, U64 sib_off, U64 child_off)
{
CFG_PanelNodeRec rec = {&cfg_nil_panel_node};
if(*MemberFromOffset(CFG_PanelNode **, panel, child_off) != &cfg_nil_panel_node)
{
rec.next = *MemberFromOffset(CFG_PanelNode **, panel, child_off);
rec.push_count += 1;
}
else for(CFG_PanelNode *p = panel; p != &cfg_nil_panel_node && p != root; p = p->parent, rec.pop_count += 1)
{
if(*MemberFromOffset(CFG_PanelNode **, p, sib_off) != &cfg_nil_panel_node)
{
rec.next = *MemberFromOffset(CFG_PanelNode **, p, sib_off);
break;
}
}
return rec;
}
internal CFG_PanelNode *
cfg_panel_node_from_tree_cfg(CFG_PanelNode *root, CFG_Node *cfg)
{
CFG_PanelNode *result = &cfg_nil_panel_node;
for(CFG_PanelNode *p = root;
p != &cfg_nil_panel_node;
p = cfg_panel_node_rec__depth_first_pre(root, p).next)
{
if(p->cfg == cfg)
{
result = p;
break;
}
}
return result;
}
internal Rng2F32
cfg_target_rect_from_panel_node_child(Rng2F32 parent_rect, CFG_PanelNode *parent, CFG_PanelNode *panel)
{
Rng2F32 rect = parent_rect;
if(parent != &cfg_nil_panel_node)
{
Vec2F32 parent_rect_size = dim_2f32(parent_rect);
Axis2 axis = parent->split_axis;
rect.p1.v[axis] = rect.p0.v[axis];
for(CFG_PanelNode *child = parent->first; child != &cfg_nil_panel_node; child = child->next)
{
rect.p1.v[axis] += parent_rect_size.v[axis] * child->pct_of_parent;
if(child == panel)
{
break;
}
rect.p0.v[axis] = rect.p1.v[axis];
}
//rect.p0.v[axis] += parent_rect_size.v[axis] * panel->off_pct_of_parent.v[axis];
//rect.p0.v[axis2_flip(axis)] += parent_rect_size.v[axis2_flip(axis)] * panel->off_pct_of_parent.v[axis2_flip(axis)];
}
rect.x0 = round_f32(rect.x0);
rect.x1 = round_f32(rect.x1);
rect.y0 = round_f32(rect.y0);
rect.y1 = round_f32(rect.y1);
return rect;
}
internal Rng2F32
cfg_target_rect_from_panel_node(Rng2F32 root_rect, CFG_PanelNode *root, CFG_PanelNode *panel)
{
Temp scratch = scratch_begin(0, 0);
// rjf: count ancestors
U64 ancestor_count = 0;
for(CFG_PanelNode *p = panel->parent; p != &cfg_nil_panel_node; p = p->parent)
{
ancestor_count += 1;
}
// rjf: gather ancestors
CFG_PanelNode **ancestors = push_array(scratch.arena, CFG_PanelNode *, ancestor_count);
{
U64 ancestor_idx = 0;
for(CFG_PanelNode *p = panel->parent; p != &cfg_nil_panel_node; p = p->parent)
{
ancestors[ancestor_idx] = p;
ancestor_idx += 1;
}
}
// rjf: go from highest ancestor => panel and calculate rect
Rng2F32 parent_rect = root_rect;
for(S64 ancestor_idx = (S64)ancestor_count-1;
0 <= ancestor_idx && ancestor_idx < ancestor_count;
ancestor_idx -= 1)
{
CFG_PanelNode *ancestor = ancestors[ancestor_idx];
CFG_PanelNode *parent = ancestor->parent;
if(parent != &cfg_nil_panel_node)
{
parent_rect = cfg_target_rect_from_panel_node_child(parent_rect, parent, ancestor);
}
}
// rjf: calculate final rect
Rng2F32 rect = cfg_target_rect_from_panel_node_child(parent_rect, panel->parent, panel);
scratch_end(scratch);
return rect;
}
-67
View File
@@ -1,67 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CONFIG_PANELS_H
#define CONFIG_PANELS_H
typedef struct CFG_PanelNode CFG_PanelNode;
struct CFG_PanelNode
{
// rjf: links data
CFG_PanelNode *first;
CFG_PanelNode *last;
CFG_PanelNode *next;
CFG_PanelNode *prev;
CFG_PanelNode *parent;
U64 child_count;
CFG_Node *cfg;
// rjf: split data
Axis2 split_axis;
F32 pct_of_parent;
// rjf: tab params
Side tab_side;
// rjf: which tabs are attached
CFG_NodePtrList tabs;
CFG_Node *selected_tab;
};
typedef struct CFG_PanelTree CFG_PanelTree;
struct CFG_PanelTree
{
CFG_PanelNode *root;
CFG_PanelNode *focused;
};
typedef struct CFG_PanelNodeRec CFG_PanelNodeRec;
struct CFG_PanelNodeRec
{
CFG_PanelNode *next;
S32 push_count;
S32 pop_count;
};
read_only global CFG_PanelNode cfg_nil_panel_node =
{
&cfg_nil_panel_node,
&cfg_nil_panel_node,
&cfg_nil_panel_node,
&cfg_nil_panel_node,
&cfg_nil_panel_node,
0,
&cfg_nil_node,
.selected_tab = &cfg_nil_node,
};
internal CFG_Node *cfg_window_from_cfg(CFG_Node *cfg);
internal CFG_PanelTree cfg_panel_tree_from_cfg(Arena *arena, CFG_Node *cfg_root);
internal CFG_PanelNodeRec cfg_panel_node_rec__depth_first(CFG_PanelNode *root, CFG_PanelNode *panel, U64 sib_off, U64 child_off);
#define cfg_panel_node_rec__depth_first_pre(root, p) cfg_panel_node_rec__depth_first((root), (p), OffsetOf(CFG_PanelNode, next), OffsetOf(CFG_PanelNode, first))
#define cfg_panel_node_rec__depth_first_pre_rev(root, p) cfg_panel_node_rec__depth_first((root), (p), OffsetOf(CFG_PanelNode, prev), OffsetOf(CFG_PanelNode, last))
internal CFG_PanelNode *cfg_panel_node_from_tree_cfg(CFG_PanelNode *root, CFG_Node *cfg);
internal Rng2F32 cfg_target_rect_from_panel_node_child(Rng2F32 parent_rect, CFG_PanelNode *parent, CFG_PanelNode *panel);
internal Rng2F32 cfg_target_rect_from_panel_node(Rng2F32 root_rect, CFG_PanelNode *root, CFG_PanelNode *panel);
#endif // CONFIG_PANELS_H
-522
View File
@@ -1,522 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#undef LAYER_COLOR
#define LAYER_COLOR 0x684123ff
////////////////////////////////
//~ rjf: Basic Helpers
internal C_ID
c_id_make(U64 u64_0, U64 u64_1)
{
C_ID id;
id.u128[0].u64[0] = u64_0;
id.u128[0].u64[1] = u64_1;
return id;
}
internal B32
c_id_match(C_ID a, C_ID b)
{
B32 result = MemoryMatchStruct(&a, &b);
return result;
}
internal C_Key
c_key_make(C_Root root, C_ID id)
{
C_Key key = {root, 0, id};
return key;
}
internal B32
c_key_match(C_Key a, C_Key b)
{
return (MemoryMatchStruct(&a.root, &b.root) && c_id_match(a.id, b.id));
}
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
c_init(void)
{
Arena *arena = arena_alloc();
c_shared = push_array(arena, C_Shared, 1);
c_shared->arena = arena;
c_shared->blob_slots_count = 16384;
c_shared->blob_stripes_count = Min(c_shared->blob_slots_count, os_get_system_info()->logical_processor_count);
c_shared->blob_slots = push_array(arena, C_BlobSlot, c_shared->blob_slots_count);
c_shared->blob_stripes = push_array(arena, C_Stripe, c_shared->blob_stripes_count);
c_shared->blob_stripes_free_nodes = push_array(arena, C_BlobNode *, c_shared->blob_stripes_count);
for(U64 idx = 0; idx < c_shared->blob_stripes_count; idx += 1)
{
C_Stripe *stripe = &c_shared->blob_stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = rw_mutex_alloc();
stripe->cv = cond_var_alloc();
}
c_shared->key_slots_count = 4096;
c_shared->key_stripes_count = Min(c_shared->key_slots_count, os_get_system_info()->logical_processor_count);
c_shared->key_slots = push_array(arena, C_KeySlot, c_shared->key_slots_count);
c_shared->key_stripes = push_array(arena, C_Stripe, c_shared->key_stripes_count);
c_shared->key_stripes_free_nodes = push_array(arena, C_KeyNode *, c_shared->key_stripes_count);
for(U64 idx = 0; idx < c_shared->key_stripes_count; idx += 1)
{
C_Stripe *stripe = &c_shared->key_stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = rw_mutex_alloc();
stripe->cv = cond_var_alloc();
}
c_shared->root_slots_count = 4096;
c_shared->root_stripes_count = Min(c_shared->root_slots_count, os_get_system_info()->logical_processor_count);
c_shared->root_slots = push_array(arena, C_RootSlot, c_shared->root_slots_count);
c_shared->root_stripes = push_array(arena, C_Stripe, c_shared->root_stripes_count);
c_shared->root_stripes_free_nodes = push_array(arena, C_RootNode *, c_shared->root_stripes_count);
for(U64 idx = 0; idx < c_shared->root_stripes_count; idx += 1)
{
C_Stripe *stripe = &c_shared->root_stripes[idx];
stripe->arena = arena_alloc();
stripe->rw_mutex = rw_mutex_alloc();
stripe->cv = cond_var_alloc();
}
}
////////////////////////////////
//~ rjf: Root Allocation/Deallocation
internal C_Root
c_root_alloc(void)
{
C_Root root = {0};
root.u64[0] = ins_atomic_u64_inc_eval(&c_shared->root_id_gen);
U64 slot_idx = root.u64[0]%c_shared->root_slots_count;
U64 stripe_idx = slot_idx%c_shared->root_stripes_count;
C_RootSlot *slot = &c_shared->root_slots[slot_idx];
C_Stripe *stripe = &c_shared->root_stripes[stripe_idx];
MutexScopeW(stripe->rw_mutex)
{
C_RootNode *node = c_shared->root_stripes_free_nodes[stripe_idx];
if(node != 0)
{
SLLStackPop(c_shared->root_stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array(stripe->arena, C_RootNode, 1);
}
DLLPushBack(slot->first, slot->last, node);
node->root = root;
node->arena = arena_alloc();
}
return root;
}
internal void
c_root_release(C_Root root)
{
//- rjf: unpack root
U64 slot_idx = root.u64[0]%c_shared->root_slots_count;
U64 stripe_idx = slot_idx%c_shared->root_stripes_count;
C_RootSlot *slot = &c_shared->root_slots[slot_idx];
C_Stripe *stripe = &c_shared->root_stripes[stripe_idx];
//- rjf: release root node, grab its arena / ID list
Arena *root_arena = 0;
C_RootIDChunkList root_ids = {0};
MutexScopeW(stripe->rw_mutex)
{
for(C_RootNode *n = slot->first; n != 0; n = n->next)
{
if(MemoryMatchStruct(&root, &n->root))
{
DLLRemove(slot->first, slot->last, n);
root_arena = n->arena;
root_ids = n->ids;
SLLStackPush(c_shared->root_stripes_free_nodes[stripe_idx], n);
break;
}
}
}
//- rjf: release all IDs
for(C_RootIDChunkNode *id_chunk_n = root_ids.first; id_chunk_n != 0; id_chunk_n = id_chunk_n->next)
{
for EachIndex(chunk_idx, id_chunk_n->count)
{
C_ID id = id_chunk_n->v[chunk_idx];
C_Key key = c_key_make(root, id);
c_close_key(key);
}
}
}
////////////////////////////////
//~ rjf: Cache Submission
internal U128
c_submit_data(C_Key key, Arena **data_arena, String8 data)
{
//- rjf: unpack key
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
U64 key_slot_idx = key_hash%c_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%c_shared->key_stripes_count;
C_KeySlot *key_slot = &c_shared->key_slots[key_slot_idx];
C_Stripe *key_stripe = &c_shared->key_stripes[key_stripe_idx];
//- rjf: hash data, unpack hash
U128 hash = u128_hash_from_str8(data);
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
//- rjf: commit to (hash -> data) cache
ProfScope("commit to (hash -> data) cache") RWMutexScope(stripe->rw_mutex, 1)
{
// rjf: find existing node
C_BlobNode *node = 0;
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
node = n;
break;
}
}
// rjf: release duplicate data if node already exists
if(node != 0 && data_arena != 0 && *data_arena != 0)
{
arena_release(*data_arena);
}
// rjf: allocate node if needed
if(node == 0)
{
node = c_shared->blob_stripes_free_nodes[stripe_idx];
if(node)
{
SLLStackPop(c_shared->blob_stripes_free_nodes[stripe_idx]);
}
else
{
node = push_array_no_zero(stripe->arena, C_BlobNode, 1);
}
MemoryZeroStruct(node);
node->hash = hash;
if(data_arena != 0)
{
node->arena = *data_arena;
}
node->data = data;
DLLPushBack(slot->first, slot->last, node);
}
// rjf: bump key ref count
node->key_ref_count += 1;
// rjf "steal" arena from caller
if(data_arena != 0)
{
*data_arena = 0;
}
}
//- rjf: commit to (key -> list(hash)) cache
U128 key_expired_hash = {0};
ProfScope("commit to (key -> list(hash)) cache") RWMutexScope(key_stripe->rw_mutex, 1)
{
// rjf: find existing key
C_KeyNode *key_node = 0;
for(C_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(c_key_match(n->key, key))
{
key_node = n;
break;
}
}
// rjf: create key node if it doesn't exist
B32 key_is_new = 0;
if(!key_node)
{
key_is_new = 1;
key_node = c_shared->key_stripes_free_nodes[key_stripe_idx];
if(key_node)
{
SLLStackPop(c_shared->key_stripes_free_nodes[key_stripe_idx]);
}
else
{
key_node = push_array_no_zero(key_stripe->arena, C_KeyNode, 1);
}
MemoryZeroStruct(key_node);
key_node->key = key;
DLLPushBack(key_slot->first, key_slot->last, key_node);
}
// rjf: push hash into key's history
if(key_node)
{
if(key_node->hash_history_gen >= C_KEY_HASH_HISTORY_STRONG_REF_COUNT)
{
key_expired_hash = key_node->hash_history[(key_node->hash_history_gen-C_KEY_HASH_HISTORY_STRONG_REF_COUNT)%ArrayCount(key_node->hash_history)];
}
key_node->hash_history[key_node->hash_history_gen%ArrayCount(key_node->hash_history)] = hash;
key_node->hash_history_gen += 1;
}
// rjf: key is new -> add this key to the associated root
if(key_is_new)
{
U64 root_hash = u64_hash_from_str8(str8_struct(&key.root));
U64 root_slot_idx = root_hash%c_shared->root_slots_count;
U64 root_stripe_idx = root_slot_idx%c_shared->root_stripes_count;
C_RootSlot *root_slot = &c_shared->root_slots[root_slot_idx];
C_Stripe *root_stripe = &c_shared->root_stripes[root_stripe_idx];
RWMutexScope(root_stripe->rw_mutex, 1)
{
for(C_RootNode *n = root_slot->first; n != 0; n = n->next)
{
if(MemoryMatchStruct(&n->root, &key.root))
{
C_RootIDChunkNode *chunk = n->ids.last;
if(chunk == 0 || chunk->count >= chunk->cap)
{
chunk = push_array(n->arena, C_RootIDChunkNode, 1);
SLLQueuePush(n->ids.first, n->ids.last, chunk);
n->ids.chunk_count += 1;
chunk->cap = 1024;
chunk->v = push_array_no_zero(n->arena, C_ID, chunk->cap);
}
chunk->v[chunk->count] = key.id;
chunk->count += 1;
n->ids.total_count += 1;
break;
}
}
}
}
}
//- rjf: decrement key ref count of expired hash
if(!u128_match(key_expired_hash, u128_zero())) ProfScope("decrement key ref count of expired hash")
{
U64 old_hash_slot_idx = key_expired_hash.u64[1]%c_shared->blob_slots_count;
U64 old_hash_stripe_idx = old_hash_slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *old_hash_slot = &c_shared->blob_slots[old_hash_slot_idx];
C_Stripe *old_hash_stripe = &c_shared->blob_stripes[old_hash_stripe_idx];
RWMutexScope(old_hash_stripe->rw_mutex, 0)
{
for(C_BlobNode *n = old_hash_slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, key_expired_hash))
{
ins_atomic_u64_dec_eval(&n->key_ref_count);
break;
}
}
}
}
return hash;
}
////////////////////////////////
//~ rjf: Key Closing
internal void
c_close_key(C_Key key)
{
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
U64 key_slot_idx = key_hash%c_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%c_shared->key_stripes_count;
C_KeySlot *key_slot = &c_shared->key_slots[key_slot_idx];
C_Stripe *key_stripe = &c_shared->key_stripes[key_stripe_idx];
RWMutexScope(key_stripe->rw_mutex, 1)
{
for(C_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(c_key_match(n->key, key))
{
for(U64 history_idx = 0;
history_idx < C_KEY_HASH_HISTORY_STRONG_REF_COUNT && history_idx < n->hash_history_gen;
history_idx += 1)
{
U128 hash = n->hash_history[(n->hash_history_gen-1-history_idx) % ArrayCount(n->hash_history)];
U64 hash_slot_idx = hash.u64[1]%c_shared->blob_slots_count;
U64 hash_stripe_idx = hash_slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *hash_slot = &c_shared->blob_slots[hash_slot_idx];
C_Stripe *hash_stripe = &c_shared->blob_stripes[hash_stripe_idx];
MutexScopeR(hash_stripe->rw_mutex)
{
for(C_BlobNode *n = hash_slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
ins_atomic_u64_dec_eval(&n->key_ref_count);
break;
}
}
}
}
DLLRemove(key_slot->first, key_slot->last, n);
SLLStackPush(c_shared->key_stripes_free_nodes[key_stripe_idx], n);
break;
}
}
}
}
////////////////////////////////
//~ rjf: Downstream Accesses
internal void
c_hash_downstream_inc(U128 hash)
{
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
MutexScopeR(stripe->rw_mutex)
{
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_inc_eval(&n->downstream_ref_count);
break;
}
}
}
}
internal void
c_hash_downstream_dec(U128 hash)
{
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
MutexScopeR(stripe->rw_mutex)
{
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash))
{
ins_atomic_u64_dec_eval(&n->downstream_ref_count);
break;
}
}
}
}
////////////////////////////////
//~ rjf: Cache Lookup
internal U128
c_hash_from_key(C_Key key, U64 rewind_count)
{
U128 result = {0};
U64 key_hash = u64_hash_from_str8(str8_struct(&key));
U64 key_slot_idx = key_hash%c_shared->key_slots_count;
U64 key_stripe_idx = key_slot_idx%c_shared->key_stripes_count;
C_KeySlot *key_slot = &c_shared->key_slots[key_slot_idx];
C_Stripe *key_stripe = &c_shared->key_stripes[key_stripe_idx];
RWMutexScope(key_stripe->rw_mutex, 0)
{
for(C_KeyNode *n = key_slot->first; n != 0; n = n->next)
{
if(c_key_match(n->key, key) && n->hash_history_gen > 0 && n->hash_history_gen-1 >= rewind_count)
{
result = n->hash_history[(n->hash_history_gen-1-rewind_count)%ArrayCount(n->hash_history)];
break;
}
}
}
return result;
}
internal String8
c_data_from_hash(Access *access, U128 hash)
{
ProfBeginFunction();
String8 result = {0};
U64 slot_idx = hash.u64[1]%c_shared->blob_slots_count;
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
MutexScopeR(stripe->rw_mutex)
{
for(C_BlobNode *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash))
{
result = n->data;
access_touch(access, &n->access_pt, stripe->cv);
break;
}
}
}
ProfEnd();
return result;
}
////////////////////////////////
//~ rjf: Asynchronous Tick
internal void
c_async_tick(void)
{
ProfBeginFunction();
//- rjf: garbage collect blobs
{
Rng1U64 range = lane_range(c_shared->blob_slots_count);
for EachInRange(slot_idx, range)
{
U64 stripe_idx = slot_idx%c_shared->blob_stripes_count;
C_BlobSlot *slot = &c_shared->blob_slots[slot_idx];
C_Stripe *stripe = &c_shared->blob_stripes[stripe_idx];
for(B32 write_mode = 0; write_mode <= 1; write_mode += 1)
{
B32 slot_has_work = 0;
RWMutexScope(stripe->rw_mutex, write_mode)
{
for(C_BlobNode *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
U64 key_ref_count = ins_atomic_u64_eval(&n->key_ref_count);
U64 downstream_ref_count = ins_atomic_u64_eval(&n->downstream_ref_count);
if(access_pt_is_expired(&n->access_pt, .time = 5000000) && key_ref_count == 0 && downstream_ref_count == 0)
{
slot_has_work = 1;
if(!write_mode)
{
break;
}
else
{
DLLRemove(slot->first, slot->last, n);
SLLStackPush(c_shared->blob_stripes_free_nodes[stripe_idx], n);
if(n->arena != 0)
{
arena_release(n->arena);
}
}
}
}
}
if(!slot_has_work)
{
break;
}
}
}
}
ProfEnd();
}
-243
View File
@@ -1,243 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CONTENT_H
#define CONTENT_H
////////////////////////////////
//~ NOTE(rjf): Hash Store Notes (2025/05/18)
//
// The hash store is a general-purpose data cache. It offers three layers of
// caching: (a) content (hash of data), (b) key (unique identity correllated
// with history of hashes), and (c) root (bucket for many keys, manually
// allocated / deallocated).
//
// (a) The "content" level of cache access is a simply hash(data) -> data
// mapping. This bypasses all identity/key/root mechanisms and provides a
// way to just talk about unique (and deduplicated) blobs of data.
//
// (b) The "key" level of cache access is used to encode a history of hashes
// for some unique "identity", where the "identity" is a concept managed
// by the user. One example of an identity would be a particular address
// range inside of some process to which the debugger is attached. Another
// might be a range inside of some file.
//
// (c) The "root" level is to provide a top-level allocation/deallocation
// mechanism for a large set of keys. It also provides an extra level of
// key uniqueness. For instance, each process to which the debugger is
// attached might have its own root, and each key might correspond to a
// particular address range within that process. This way, when the
// process ends, all of its keys can be easily destroyed using a single
// deallocation of the root.
//
// The way this might be generally used inside of the debugger would be that
// some evaluation - let's say it's some variable `x` - is mapped (via debug
// info) to some address range. If `x` is a `char[4096]`, then it might map
// to some address range [&x, &x + 4096). This, together with the process
// within which `x` is evaluated, forms both a `root` (for the process) and
// a `key` (for the address range). Some asynchronous memory streaming system
// can then, together with the root and key, read memory for that range, then
// submit that data to the hash store, correllating with the root and key
// combo.
////////////////////////////////
//~ rjf: Key Types
typedef struct C_Root C_Root;
struct C_Root
{
U64 u64[1];
};
typedef struct C_ID C_ID;
struct C_ID
{
U128 u128[1];
};
typedef struct C_Key C_Key;
struct C_Key
{
C_Root root;
U64 _padding_;
C_ID id;
};
////////////////////////////////
//~ rjf: Cache Stripe Type
typedef struct C_Stripe C_Stripe;
struct C_Stripe
{
Arena *arena;
RWMutex rw_mutex;
CondVar cv;
};
////////////////////////////////
//~ rjf: Root Cache Types
typedef struct C_RootIDChunkNode C_RootIDChunkNode;
struct C_RootIDChunkNode
{
C_RootIDChunkNode *next;
C_ID *v;
U64 count;
U64 cap;
};
typedef struct C_RootIDChunkList C_RootIDChunkList;
struct C_RootIDChunkList
{
C_RootIDChunkNode *first;
C_RootIDChunkNode *last;
U64 chunk_count;
U64 total_count;
};
typedef struct C_RootNode C_RootNode;
struct C_RootNode
{
C_RootNode *next;
C_RootNode *prev;
Arena *arena;
C_Root root;
C_RootIDChunkList ids;
};
typedef struct C_RootSlot C_RootSlot;
struct C_RootSlot
{
C_RootNode *first;
C_RootNode *last;
};
////////////////////////////////
//~ rjf: Key Cache Types
#define C_KEY_HASH_HISTORY_COUNT 64
#define C_KEY_HASH_HISTORY_STRONG_REF_COUNT 2
typedef struct C_KeyNode C_KeyNode;
struct C_KeyNode
{
C_KeyNode *next;
C_KeyNode *prev;
C_Key key;
U128 hash_history[C_KEY_HASH_HISTORY_COUNT];
U64 hash_history_gen;
};
typedef struct C_KeySlot C_KeySlot;
struct C_KeySlot
{
C_KeyNode *first;
C_KeyNode *last;
};
////////////////////////////////
//~ rjf: Content Blob Cache Types
typedef struct C_BlobNode C_BlobNode;
struct C_BlobNode
{
C_BlobNode *next;
C_BlobNode *prev;
U128 hash;
Arena *arena;
String8 data;
AccessPt access_pt;
U64 key_ref_count;
U64 downstream_ref_count;
};
typedef struct C_BlobSlot C_BlobSlot;
struct C_BlobSlot
{
C_BlobNode *first;
C_BlobNode *last;
};
////////////////////////////////
//~ rjf: Shared State
typedef struct C_Shared C_Shared;
struct C_Shared
{
Arena *arena;
// rjf: main data blob cache
U64 blob_slots_count;
U64 blob_stripes_count;
C_BlobSlot *blob_slots;
C_Stripe *blob_stripes;
C_BlobNode **blob_stripes_free_nodes;
// rjf: key cache
U64 key_slots_count;
U64 key_stripes_count;
C_KeySlot *key_slots;
C_Stripe *key_stripes;
C_KeyNode **key_stripes_free_nodes;
// rjf: root cache
U64 root_slots_count;
U64 root_stripes_count;
C_RootSlot *root_slots;
C_Stripe *root_stripes;
C_RootNode **root_stripes_free_nodes;
U64 root_id_gen;
};
////////////////////////////////
//~ rjf: Globals
global C_Shared *c_shared = 0;
////////////////////////////////
//~ rjf: Basic Helpers
internal C_ID c_id_make(U64 u64_0, U64 u64_1);
internal B32 c_id_match(C_ID a, C_ID b);
internal C_Key c_key_make(C_Root root, C_ID id);
internal B32 c_key_match(C_Key a, C_Key b);
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void c_init(void);
////////////////////////////////
//~ rjf: Root Allocation/Deallocation
internal C_Root c_root_alloc(void);
internal void c_root_release(C_Root root);
////////////////////////////////
//~ rjf: Cache Submission
internal U128 c_submit_data(C_Key key, Arena **data_arena, String8 data);
////////////////////////////////
//~ rjf: Key Closing
internal void c_close_key(C_Key key);
////////////////////////////////
//~ rjf: Downstream Accesses
internal void c_hash_downstream_inc(U128 hash);
internal void c_hash_downstream_dec(U128 hash);
////////////////////////////////
//~ rjf: Cache Lookups
internal U128 c_hash_from_key(C_Key key, U64 rewind_count);
internal String8 c_data_from_hash(Access *access, U128 hash);
////////////////////////////////
//~ rjf: Asynchronous Tick
internal void c_async_tick(void);
#endif // CONTENT_H
+5 -116
View File
@@ -1,46 +1,8 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//////////////////////////////// ////////////////////////////////
//~ rjf: Entity Kinds //~ rjf: Tables
@table(name code_name display_string)
CTRL_EntityKindTable:
{
{Root root "Root" }
{Machine machine "Machine" }
{Process process "Process" }
{Thread thread "Thread" }
{Module module "Module" }
{EntryPoint entry_point "Entry Point" }
{DebugInfoPath debug_info_path "Debug Info Path" }
{PendingThreadName pending_thread_name "Pending Thread Name" }
{PendingThreadColor pending_thread_color "Pending Thread Color" }
{Breakpoint breakpoint "Breakpoint" }
{AddressRangeAnnotation address_range_annotation "Address Range Annotation" }
}
@enum CTRL_EntityKind:
{
Null,
@expand(CTRL_EntityKindTable a) `$(a.name)`,
COUNT,
}
@data(String8) ctrl_entity_kind_code_name_table:
{
`{0}`,
@expand(CTRL_EntityKindTable a) `str8_lit_comp("$(a.code_name)")`
}
@data(String8) ctrl_entity_kind_display_string_table:
{
`{0}`,
@expand(CTRL_EntityKindTable a) `str8_lit_comp("$(a.display_name)")`
}
////////////////////////////////
//~ rjf: Exception Codes
@table(name lower_name code default display_string) @table(name lower_name code default display_string)
CTRL_ExceptionCodeKindTable: CTRL_ExceptionCodeKindTable:
@@ -84,6 +46,9 @@ CTRL_ExceptionCodeKindTable:
{Win32DirectXDebugLayer win32_directx_debug_layer 0x0000087a 1 "(Win32) DirectX Debug Layer" } {Win32DirectXDebugLayer win32_directx_debug_layer 0x0000087a 1 "(Win32) DirectX Debug Layer" }
} }
////////////////////////////////
//~ rjf: Generators
@enum CTRL_ExceptionCodeKind: @enum CTRL_ExceptionCodeKind:
{ {
Null, Null,
@@ -114,79 +79,3 @@ CTRL_ExceptionCodeKindTable:
`0`; `0`;
@expand(CTRL_ExceptionCodeKindTable a) `$(a.default)`; @expand(CTRL_ExceptionCodeKindTable a) `$(a.default)`;
} }
////////////////////////////////
//~ rjf: Exception Sub-Codes
@table(name, value)
CTRL_ExceptionSubCodeKindTable:
{
{ W32_FAST_FAIL_LEGACY_GS_VIOLATION 0 }
{ W32_FAST_FAIL_VTGUARD_CHECK_FAILURE 1 }
{ W32_FAST_FAIL_STACK_COOKIE_CHECK_FAILURE 2 }
{ W32_FAST_FAIL_CORRUPT_LIST_ENTRY 3 }
{ W32_FAST_FAIL_INCORRECT_STACK 4 }
{ W32_FAST_FAIL_INVALID_ARG 5 }
{ W32_FAST_FAIL_GS_COOKIE_INIT 6 }
{ W32_FAST_FAIL_FATAL_APP_EXIT 7 }
{ W32_FAST_FAIL_RANGE_CHECK_FAILURE 8 }
{ W32_FAST_FAIL_UNSAFE_REGISTRY_ACCESS 9 }
{ W32_FAST_FAIL_GUARD_ICALL_CHECK_FAILURE 10 }
{ W32_FAST_FAIL_GUARD_WRITE_CHECK_FAILURE 11 }
{ W32_FAST_FAIL_INVALID_FIBER_SWITCH 12 }
{ W32_FAST_FAIL_INVALID_SET_OF_CONTEXT 13 }
{ W32_FAST_FAIL_INVALID_REFERENCE_COUNT 14 }
{ W32_FAST_FAIL_INVALID_JUMP_BUFFER 18 }
{ W32_FAST_FAIL_MRDATA_MODIFIED 19 }
{ W32_FAST_FAIL_CERTIFICATION_FAILURE 20 }
{ W32_FAST_FAIL_INVALID_EXCEPTION_CHAIN 21 }
{ W32_FAST_FAIL_CRYPTO_LIBRARY 22 }
{ W32_FAST_FAIL_INVALID_CALL_IN_DLL_CALLOUT 23 }
{ W32_FAST_FAIL_INVALID_IMAGE_BASE 24 }
{ W32_FAST_FAIL_DLOAD_PROTECTION_FAILURE 25 }
{ W32_FAST_FAIL_UNSAFE_EXTENSION_CALL 26 }
{ W32_FAST_FAIL_DEPRECATED_SERVICE_INVOKED 27 }
{ W32_FAST_FAIL_INVALID_BUFFER_ACCESS 28 }
{ W32_FAST_FAIL_INVALID_BALANCED_TREE 29 }
{ W32_FAST_FAIL_INVALID_NEXT_THREAD 30 }
{ W32_FAST_FAIL_GUARD_ICALL_CHECK_SUPPRESSED 31 }
{ W32_FAST_FAIL_APCS_DISABLED 32 }
{ W32_FAST_FAIL_INVALID_IDLE_STATE 33 }
{ W32_FAST_FAIL_MRDATA_PROTECTION_FAILURE 34 }
{ W32_FAST_FAIL_UNEXPECTED_HEAP_EXCEPTION 35 }
{ W32_FAST_FAIL_INVALID_LOCK_STATE 36 }
{ W32_FAST_FAIL_GUARD_JUMPTABLE 37 }
{ W32_FAST_FAIL_INVALID_LONGJUMP_TARGET 38 }
{ W32_FAST_FAIL_INVALID_DISPATCH_CONTEXT 39 }
{ W32_FAST_FAIL_INVALID_THREAD 40 }
{ W32_FAST_FAIL_INVALID_SYSCALL_NUMBER 41 }
{ W32_FAST_FAIL_INVALID_FILE_OPERATION 42 }
{ W32_FAST_FAIL_LPAC_ACCESS_DENIED 43 }
{ W32_FAST_FAIL_GUARD_SS_FAILURE 44 }
{ W32_FAST_FAIL_LOADER_CONTINUITY_FAILURE 45 }
{ W32_FAST_FAIL_GUARD_EXPORT_SUPPRESSION_FAILURE 46 }
{ W32_FAST_FAIL_INVALID_CONTROL_STACK 47 }
{ W32_FAST_FAIL_SET_CONTEXT_DENIED 48 }
{ W32_FAST_FAIL_INVALID_IAT 49 }
{ W32_FAST_FAIL_HEAP_METADATA_CORRUPTION 50 }
{ W32_FAST_FAIL_PAYLOAD_RESTRICTION_VIOLATION 51 }
{ W32_FAST_FAIL_LOW_LABEL_ACCESS_DENIED 52 }
{ W32_FAST_FAIL_ENCLAVE_CALL_FAILURE 53 }
{ W32_FAST_FAIL_UNHANDLED_LSS_EXCEPTON 54 }
{ W32_FAST_FAIL_ADMINLESS_ACCESS_DENIED 55 }
{ W32_FAST_FAIL_UNEXPECTED_CALL 56 }
{ W32_FAST_FAIL_CONTROL_INVALID_RETURN_ADDRESS 57 }
{ W32_FAST_FAIL_UNEXPECTED_HOST_BEHAVIOR 58 }
{ W32_FAST_FAIL_FLAGS_CORRUPTION 59 }
{ W32_FAST_FAIL_VEH_CORRUPTION 60 }
{ W32_FAST_FAIL_ETW_CORRUPTION 61 }
{ W32_FAST_FAIL_RIO_ABORT 62 }
{ W32_FAST_FAIL_INVALID_PFN 63 }
{ W32_FAST_FAIL_GUARD_ICALL_CHECK_FAILURE_XFG 64 }
{ W32_FAST_FAIL_CAST_GUARD 65 }
{ W32_FAST_FAIL_HOST_VISIBILITY_CHANGE 66 }
{ W32_FAST_FAIL_KERNEL_CET_SHADOW_STACK_ASSIST 67 }
{ W32_FAST_FAIL_PATCH_CALLBACK_FAILED 68 }
{ W32_FAST_FAIL_NTDLL_PATCH_FAILED 69 }
{ W32_FAST_FAIL_INVALID_FLS_DATA 70 }
}
+1495 -3093
View File
File diff suppressed because it is too large Load Diff
+231 -423
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CTRL_CORE_H #ifndef CTRL_CORE_H
@@ -13,92 +13,47 @@ typedef U64 CTRL_MachineID;
#define CTRL_MachineID_Local (1) #define CTRL_MachineID_Local (1)
//////////////////////////////// ////////////////////////////////
//~ rjf: User Breakpoint Types //~ rjf: Machine/Handle Pair Types
typedef U32 CTRL_UserBreakpointFlags; typedef struct CTRL_MachineIDHandlePair CTRL_MachineIDHandlePair;
enum struct CTRL_MachineIDHandlePair
{
CTRL_UserBreakpointFlag_BreakOnWrite = (1<<0),
CTRL_UserBreakpointFlag_BreakOnRead = (1<<1),
CTRL_UserBreakpointFlag_BreakOnExecute = (1<<2),
};
typedef enum CTRL_UserBreakpointKind
{
CTRL_UserBreakpointKind_Null,
CTRL_UserBreakpointKind_FileNameAndLineColNumber,
CTRL_UserBreakpointKind_Expression,
CTRL_UserBreakpointKind_COUNT
}
CTRL_UserBreakpointKind;
typedef struct CTRL_UserBreakpoint CTRL_UserBreakpoint;
struct CTRL_UserBreakpoint
{
CTRL_UserBreakpointKind kind;
CTRL_UserBreakpointFlags flags;
U64 id;
String8 string;
TxtPt pt;
U64 size;
String8 condition;
};
typedef struct CTRL_UserBreakpointNode CTRL_UserBreakpointNode;
struct CTRL_UserBreakpointNode
{
CTRL_UserBreakpointNode *next;
CTRL_UserBreakpoint v;
};
typedef struct CTRL_UserBreakpointList CTRL_UserBreakpointList;
struct CTRL_UserBreakpointList
{
CTRL_UserBreakpointNode *first;
CTRL_UserBreakpointNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Entity Handle Types
typedef struct CTRL_Handle CTRL_Handle;
struct CTRL_Handle
{ {
CTRL_MachineID machine_id; CTRL_MachineID machine_id;
DMN_Handle dmn_handle; DMN_Handle handle;
}; };
typedef struct CTRL_HandleNode CTRL_HandleNode; typedef struct CTRL_MachineIDHandlePairNode CTRL_MachineIDHandlePairNode;
struct CTRL_HandleNode struct CTRL_MachineIDHandlePairNode
{ {
CTRL_HandleNode *next; CTRL_MachineIDHandlePairNode *next;
CTRL_Handle v; CTRL_MachineIDHandlePair v;
}; };
typedef struct CTRL_HandleList CTRL_HandleList; typedef struct CTRL_MachineIDHandlePairList CTRL_MachineIDHandlePairList;
struct CTRL_HandleList struct CTRL_MachineIDHandlePairList
{ {
CTRL_HandleNode *first; CTRL_MachineIDHandlePairNode *first;
CTRL_HandleNode *last; CTRL_MachineIDHandlePairNode *last;
U64 count; U64 count;
}; };
typedef struct CTRL_HandleArray CTRL_HandleArray;
struct CTRL_HandleArray
{
CTRL_Handle *v;
U64 count;
};
////////////////////////////////
//~ rjf: Generated Code
#include "generated/ctrl.meta.h"
//////////////////////////////// ////////////////////////////////
//~ rjf: Entity Types //~ rjf: Entity Types
typedef enum CTRL_EntityKind
{
CTRL_EntityKind_Null,
CTRL_EntityKind_Root,
CTRL_EntityKind_Machine,
CTRL_EntityKind_Process,
CTRL_EntityKind_Thread,
CTRL_EntityKind_Module,
CTRL_EntityKind_EntryPoint,
CTRL_EntityKind_DebugInfoPath,
CTRL_EntityKind_COUNT
}
CTRL_EntityKind;
typedef struct CTRL_Entity CTRL_Entity; typedef struct CTRL_Entity CTRL_Entity;
struct CTRL_Entity struct CTRL_Entity
{ {
@@ -108,49 +63,15 @@ struct CTRL_Entity
CTRL_Entity *prev; CTRL_Entity *prev;
CTRL_Entity *parent; CTRL_Entity *parent;
CTRL_EntityKind kind; CTRL_EntityKind kind;
Arch arch; Architecture arch;
B32 is_frozen; CTRL_MachineID machine_id;
B32 is_soloed; DMN_Handle handle;
U32 rgba;
CTRL_Handle handle;
U64 id; U64 id;
Rng1U64 vaddr_range; Rng1U64 vaddr_range;
U64 stack_base;
U64 timestamp; U64 timestamp;
CTRL_UserBreakpointFlags bp_flags;
String8 string; String8 string;
}; };
typedef struct CTRL_EntityNode CTRL_EntityNode;
struct CTRL_EntityNode
{
CTRL_EntityNode *next;
CTRL_Entity *v;
};
typedef struct CTRL_EntityList CTRL_EntityList;
struct CTRL_EntityList
{
CTRL_EntityNode *first;
CTRL_EntityNode *last;
U64 count;
};
typedef struct CTRL_EntityArray CTRL_EntityArray;
struct CTRL_EntityArray
{
CTRL_Entity **v;
U64 count;
};
typedef struct CTRL_EntityRec CTRL_EntityRec;
struct CTRL_EntityRec
{
CTRL_Entity *next;
S32 push_count;
S64 pop_count;
};
typedef struct CTRL_EntityHashNode CTRL_EntityHashNode; typedef struct CTRL_EntityHashNode CTRL_EntityHashNode;
struct CTRL_EntityHashNode struct CTRL_EntityHashNode
{ {
@@ -173,45 +94,16 @@ struct CTRL_EntityStringChunkNode
U64 size; U64 size;
}; };
read_only global U64 ctrl_entity_string_bucket_chunk_sizes[] = typedef struct CTRL_EntityStore CTRL_EntityStore;
{ struct CTRL_EntityStore
16,
64,
256,
1024,
4096,
16384,
65536,
0xffffffffffffffffull,
};
typedef struct CTRL_EntityCtx CTRL_EntityCtx;
struct CTRL_EntityCtx
{ {
Arena *arena;
CTRL_Entity *root; CTRL_Entity *root;
U64 hash_slots_count;
CTRL_EntityHashSlot *hash_slots;
U64 entity_kind_counts[CTRL_EntityKind_COUNT];
U64 entity_kind_alloc_gens[CTRL_EntityKind_COUNT];
};
typedef struct CTRL_EntityCtxRWStore CTRL_EntityCtxRWStore;
struct CTRL_EntityCtxRWStore
{
Arena *arena;
CTRL_EntityCtx ctx;
CTRL_Entity *free; CTRL_Entity *free;
CTRL_EntityHashSlot *hash_slots;
CTRL_EntityHashNode *hash_node_free; CTRL_EntityHashNode *hash_node_free;
CTRL_EntityStringChunkNode *free_string_chunks[ArrayCount(ctrl_entity_string_bucket_chunk_sizes)]; U64 hash_slots_count;
}; CTRL_EntityStringChunkNode *free_string_chunks[8];
typedef struct CTRL_EntityCtxLookupAccel CTRL_EntityCtxLookupAccel;
struct CTRL_EntityCtxLookupAccel
{
Arena *arena;
Arena *entity_kind_arrays_arenas[CTRL_EntityKind_COUNT];
CTRL_EntityArray entity_kind_arrays[CTRL_EntityKind_COUNT];
U64 entity_kind_arrays_gens[CTRL_EntityKind_COUNT];
}; };
//////////////////////////////// ////////////////////////////////
@@ -258,54 +150,6 @@ struct CTRL_Unwind
CTRL_UnwindFlags flags; CTRL_UnwindFlags flags;
}; };
////////////////////////////////
//~ rjf: Call Stack Types
typedef struct CTRL_CallStackFrame CTRL_CallStackFrame;
struct CTRL_CallStackFrame
{
U64 unwind_count;
U64 inline_depth;
void *regs;
};
typedef struct CTRL_CallStack CTRL_CallStack;
struct CTRL_CallStack
{
CTRL_CallStackFrame *frames;
U64 frames_count;
CTRL_CallStackFrame **concrete_frames;
U64 concrete_frames_count;
};
////////////////////////////////
//~ rjf: Call Stack Tree Types
typedef struct CTRL_CallStackTreeNode CTRL_CallStackTreeNode;
struct CTRL_CallStackTreeNode
{
CTRL_CallStackTreeNode *hash_next;
CTRL_CallStackTreeNode *first;
CTRL_CallStackTreeNode *last;
CTRL_CallStackTreeNode *next;
CTRL_CallStackTreeNode *parent;
U64 child_count;
U64 id;
CTRL_Handle process;
U64 vaddr;
U64 depth;
CTRL_HandleList threads;
U64 all_descendant_threads_count;
};
typedef struct CTRL_CallStackTree CTRL_CallStackTree;
struct CTRL_CallStackTree
{
CTRL_CallStackTreeNode *root;
U64 slots_count;
CTRL_CallStackTreeNode **slots;
};
//////////////////////////////// ////////////////////////////////
//~ rjf: Trap Types //~ rjf: Trap Types
@@ -351,15 +195,47 @@ struct CTRL_Spoof
}; };
//////////////////////////////// ////////////////////////////////
//~ rjf: Evaluation Spaces //~ rjf: User Breakpoint Types
typedef U64 CTRL_EvalSpaceKind; typedef enum CTRL_UserBreakpointKind
enum
{ {
CTRL_EvalSpaceKind_Entity = E_SpaceKind_FirstUserDefined, CTRL_UserBreakpointKind_FileNameAndLineColNumber,
CTRL_EvalSpaceKind_FirstUserDefined, CTRL_UserBreakpointKind_SymbolNameAndOffset,
CTRL_UserBreakpointKind_VirtualAddress,
CTRL_UserBreakpointKind_COUNT
}
CTRL_UserBreakpointKind;
typedef struct CTRL_UserBreakpoint CTRL_UserBreakpoint;
struct CTRL_UserBreakpoint
{
CTRL_UserBreakpointKind kind;
String8 string;
TxtPt pt;
U64 u64;
String8 condition;
}; };
typedef struct CTRL_UserBreakpointNode CTRL_UserBreakpointNode;
struct CTRL_UserBreakpointNode
{
CTRL_UserBreakpointNode *next;
CTRL_UserBreakpoint v;
};
typedef struct CTRL_UserBreakpointList CTRL_UserBreakpointList;
struct CTRL_UserBreakpointList
{
CTRL_UserBreakpointNode *first;
CTRL_UserBreakpointNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Generated Code
#include "generated/ctrl.meta.h"
//////////////////////////////// ////////////////////////////////
//~ rjf: Message Types //~ rjf: Message Types
@@ -369,14 +245,11 @@ typedef enum CTRL_MsgKind
CTRL_MsgKind_Launch, CTRL_MsgKind_Launch,
CTRL_MsgKind_Attach, CTRL_MsgKind_Attach,
CTRL_MsgKind_Kill, CTRL_MsgKind_Kill,
CTRL_MsgKind_KillAll,
CTRL_MsgKind_Detach, CTRL_MsgKind_Detach,
CTRL_MsgKind_Run, CTRL_MsgKind_Run,
CTRL_MsgKind_SingleStep, CTRL_MsgKind_SingleStep,
CTRL_MsgKind_SetUserEntryPoints, CTRL_MsgKind_SetUserEntryPoints,
CTRL_MsgKind_SetModuleDebugInfoPath, CTRL_MsgKind_SetModuleDebugInfoPath,
CTRL_MsgKind_FreezeThread,
CTRL_MsgKind_ThawThread,
CTRL_MsgKind_COUNT, CTRL_MsgKind_COUNT,
} }
CTRL_MsgKind; CTRL_MsgKind;
@@ -393,22 +266,21 @@ struct CTRL_Msg
CTRL_MsgKind kind; CTRL_MsgKind kind;
CTRL_RunFlags run_flags; CTRL_RunFlags run_flags;
CTRL_MsgID msg_id; CTRL_MsgID msg_id;
CTRL_Handle entity; CTRL_MachineID machine_id;
CTRL_Handle parent; DMN_Handle entity;
DMN_Handle parent;
U32 entity_id; U32 entity_id;
U32 exit_code; U32 exit_code;
B32 env_inherit; B32 env_inherit;
B32 debug_subprocesses;
U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64]; U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64];
String8 path; String8 path;
String8List entry_points; String8List entry_points;
String8List cmd_line_string_list; String8List cmd_line_string_list;
String8List env_string_list; String8List env_string_list;
String8 stdout_path;
String8 stderr_path;
String8 stdin_path;
CTRL_TrapList traps; CTRL_TrapList traps;
CTRL_UserBreakpointList user_bps; CTRL_UserBreakpointList user_bps;
CTRL_MachineIDHandlePairList freeze_state_threads; // NOTE(rjf): can be frozen or unfrozen, depending on `freeze_state_is_frozen`
B32 freeze_state_is_frozen;
}; };
typedef struct CTRL_MsgNode CTRL_MsgNode; typedef struct CTRL_MsgNode CTRL_MsgNode;
@@ -446,20 +318,12 @@ typedef enum CTRL_EventKind
CTRL_EventKind_EndThread, CTRL_EventKind_EndThread,
CTRL_EventKind_EndModule, CTRL_EventKind_EndModule,
//- rjf: thread freeze state changes
CTRL_EventKind_ThreadFrozen,
CTRL_EventKind_ThreadThawed,
//- rjf: debug info changes //- rjf: debug info changes
CTRL_EventKind_ModuleDebugInfoPathChange, CTRL_EventKind_ModuleDebugInfoPathChange,
//- rjf: debug strings / decorations / markup //- rjf: debug strings
CTRL_EventKind_DebugString, CTRL_EventKind_DebugString,
CTRL_EventKind_ThreadName, CTRL_EventKind_ThreadName,
CTRL_EventKind_ThreadColor,
CTRL_EventKind_SetBreakpoint,
CTRL_EventKind_UnsetBreakpoint,
CTRL_EventKind_SetVAddrRangeNote,
//- rjf: memory //- rjf: memory
CTRL_EventKind_MemReserve, CTRL_EventKind_MemReserve,
@@ -503,9 +367,10 @@ struct CTRL_Event
CTRL_EventCause cause; CTRL_EventCause cause;
CTRL_ExceptionKind exception_kind; CTRL_ExceptionKind exception_kind;
CTRL_MsgID msg_id; CTRL_MsgID msg_id;
CTRL_Handle entity; CTRL_MachineID machine_id;
CTRL_Handle parent; DMN_Handle entity;
Arch arch; DMN_Handle parent;
Architecture arch;
U64 u64_code; U64 u64_code;
U32 entity_id; U32 entity_id;
Rng1U64 vaddr_rng; Rng1U64 vaddr_rng;
@@ -514,8 +379,6 @@ struct CTRL_Event
U64 tls_root; U64 tls_root;
U64 timestamp; U64 timestamp;
U32 exception_code; U32 exception_code;
U32 rgba;
CTRL_UserBreakpointFlags bp_flags;
String8 string; String8 string;
}; };
@@ -535,7 +398,62 @@ struct CTRL_EventList
}; };
//////////////////////////////// ////////////////////////////////
//~ rjf: Process Memory Types //~ rjf: Process Memory Cache Types
typedef struct CTRL_ProcessMemoryRangeHashNode CTRL_ProcessMemoryRangeHashNode;
struct CTRL_ProcessMemoryRangeHashNode
{
CTRL_ProcessMemoryRangeHashNode *next;
Rng1U64 vaddr_range;
B32 zero_terminated;
Rng1U64 vaddr_range_clamped;
U128 hash;
U64 mem_gen;
U64 last_time_requested_us;
B32 is_taken;
};
typedef struct CTRL_ProcessMemoryRangeHashSlot CTRL_ProcessMemoryRangeHashSlot;
struct CTRL_ProcessMemoryRangeHashSlot
{
CTRL_ProcessMemoryRangeHashNode *first;
CTRL_ProcessMemoryRangeHashNode *last;
};
typedef struct CTRL_ProcessMemoryCacheNode CTRL_ProcessMemoryCacheNode;
struct CTRL_ProcessMemoryCacheNode
{
CTRL_ProcessMemoryCacheNode *next;
CTRL_ProcessMemoryCacheNode *prev;
Arena *arena;
CTRL_MachineID machine_id;
DMN_Handle process;
U64 range_hash_slots_count;
CTRL_ProcessMemoryRangeHashSlot *range_hash_slots;
};
typedef struct CTRL_ProcessMemoryCacheSlot CTRL_ProcessMemoryCacheSlot;
struct CTRL_ProcessMemoryCacheSlot
{
CTRL_ProcessMemoryCacheNode *first;
CTRL_ProcessMemoryCacheNode *last;
};
typedef struct CTRL_ProcessMemoryCacheStripe CTRL_ProcessMemoryCacheStripe;
struct CTRL_ProcessMemoryCacheStripe
{
OS_Handle rw_mutex;
OS_Handle cv;
};
typedef struct CTRL_ProcessMemoryCache CTRL_ProcessMemoryCache;
struct CTRL_ProcessMemoryCache
{
U64 slots_count;
CTRL_ProcessMemoryCacheSlot *slots;
U64 stripes_count;
CTRL_ProcessMemoryCacheStripe *stripes;
};
typedef struct CTRL_ProcessMemorySlice CTRL_ProcessMemorySlice; typedef struct CTRL_ProcessMemorySlice CTRL_ProcessMemorySlice;
struct CTRL_ProcessMemorySlice struct CTRL_ProcessMemorySlice
@@ -556,7 +474,8 @@ struct CTRL_ThreadRegCacheNode
{ {
CTRL_ThreadRegCacheNode *next; CTRL_ThreadRegCacheNode *next;
CTRL_ThreadRegCacheNode *prev; CTRL_ThreadRegCacheNode *prev;
CTRL_Handle handle; CTRL_MachineID machine_id;
DMN_Handle thread;
U64 block_size; U64 block_size;
void *block; void *block;
U64 reg_gen; U64 reg_gen;
@@ -573,7 +492,7 @@ typedef struct CTRL_ThreadRegCacheStripe CTRL_ThreadRegCacheStripe;
struct CTRL_ThreadRegCacheStripe struct CTRL_ThreadRegCacheStripe
{ {
Arena *arena; Arena *arena;
RWMutex rw_mutex; OS_Handle rw_mutex;
}; };
typedef struct CTRL_ThreadRegCache CTRL_ThreadRegCache; typedef struct CTRL_ThreadRegCache CTRL_ThreadRegCache;
@@ -593,15 +512,14 @@ struct CTRL_ModuleImageInfoCacheNode
{ {
CTRL_ModuleImageInfoCacheNode *next; CTRL_ModuleImageInfoCacheNode *next;
CTRL_ModuleImageInfoCacheNode *prev; CTRL_ModuleImageInfoCacheNode *prev;
CTRL_Handle module; CTRL_MachineID machine_id;
DMN_Handle module;
Arena *arena; Arena *arena;
PE_IntelPdata *pdatas; PE_IntelPdata *pdatas;
U64 pdatas_count; U64 pdatas_count;
U64 entry_point_voff; U64 entry_point_voff;
Rng1U64 tls_vaddr_range; Rng1U64 tls_vaddr_range;
String8 initial_debug_info_path; String8 initial_debug_info_path;
Rng1U64 raddbg_section_voff_range;
String8 raddbg_data;
}; };
typedef struct CTRL_ModuleImageInfoCacheSlot CTRL_ModuleImageInfoCacheSlot; typedef struct CTRL_ModuleImageInfoCacheSlot CTRL_ModuleImageInfoCacheSlot;
@@ -615,7 +533,7 @@ typedef struct CTRL_ModuleImageInfoCacheStripe CTRL_ModuleImageInfoCacheStripe;
struct CTRL_ModuleImageInfoCacheStripe struct CTRL_ModuleImageInfoCacheStripe
{ {
Arena *arena; Arena *arena;
RWMutex rw_mutex; OS_Handle rw_mutex;
}; };
typedef struct CTRL_ModuleImageInfoCache CTRL_ModuleImageInfoCache; typedef struct CTRL_ModuleImageInfoCache CTRL_ModuleImageInfoCache;
@@ -627,46 +545,6 @@ struct CTRL_ModuleImageInfoCache
CTRL_ModuleImageInfoCacheStripe *stripes; CTRL_ModuleImageInfoCacheStripe *stripes;
}; };
////////////////////////////////
//~ rjf: Touched Debug Info Directory Cache
typedef struct CTRL_DbgDirNode CTRL_DbgDirNode;
struct CTRL_DbgDirNode
{
CTRL_DbgDirNode *first;
CTRL_DbgDirNode *last;
CTRL_DbgDirNode *next;
CTRL_DbgDirNode *prev;
CTRL_DbgDirNode *parent;
String8 name;
U64 search_count;
U64 child_count;
U64 module_direct_count;
};
////////////////////////////////
//~ rjf: Control Thread Evaluation Scopes
typedef struct CTRL_EvalScope CTRL_EvalScope;
struct CTRL_EvalScope
{
Access *access;
E_BaseCtx base_ctx;
E_IRCtx ir_ctx;
E_InterpretCtx interpret_ctx;
};
////////////////////////////////
//~ rjf: Module Requirement Cache Types
typedef struct CTRL_ModuleReqCacheNode CTRL_ModuleReqCacheNode;
struct CTRL_ModuleReqCacheNode
{
CTRL_ModuleReqCacheNode *next;
CTRL_Handle module;
B32 required;
};
//////////////////////////////// ////////////////////////////////
//~ rjf: Wakeup Hook Function Types //~ rjf: Wakeup Hook Function Types
@@ -683,44 +561,35 @@ struct CTRL_State
CTRL_WakeupFunctionType *wakeup_hook; CTRL_WakeupFunctionType *wakeup_hook;
// rjf: name -> register/alias hash tables for eval // rjf: name -> register/alias hash tables for eval
E_String2NumMap arch_string2reg_tables[Arch_COUNT]; EVAL_String2NumMap arch_string2reg_tables[Architecture_COUNT];
E_String2NumMap arch_string2alias_tables[Arch_COUNT]; EVAL_String2NumMap arch_string2alias_tables[Architecture_COUNT];
// rjf: caches // rjf: caches
CTRL_ProcessMemoryCache process_memory_cache;
CTRL_ThreadRegCache thread_reg_cache; CTRL_ThreadRegCache thread_reg_cache;
CTRL_ModuleImageInfoCache module_image_info_cache; CTRL_ModuleImageInfoCache module_image_info_cache;
// rjf: generations
U64 run_gen;
U64 mem_gen;
U64 reg_gen;
// rjf: user -> ctrl msg ring buffer // rjf: user -> ctrl msg ring buffer
U64 u2c_ring_size; U64 u2c_ring_size;
U8 *u2c_ring_base; U8 *u2c_ring_base;
U64 u2c_ring_write_pos; U64 u2c_ring_write_pos;
U64 u2c_ring_read_pos; U64 u2c_ring_read_pos;
Mutex u2c_ring_mutex; OS_Handle u2c_ring_mutex;
CondVar u2c_ring_cv; OS_Handle u2c_ring_cv;
// rjf: ctrl -> user event ring buffer // rjf: ctrl -> user event ring buffer
U64 c2u_ring_size; U64 c2u_ring_size;
U64 c2u_ring_max_string_size;
U8 *c2u_ring_base; U8 *c2u_ring_base;
U64 c2u_ring_write_pos; U64 c2u_ring_write_pos;
U64 c2u_ring_read_pos; U64 c2u_ring_read_pos;
Mutex c2u_ring_mutex; OS_Handle c2u_ring_mutex;
CondVar c2u_ring_cv; OS_Handle c2u_ring_cv;
// rjf: ctrl thread state // rjf: ctrl thread state
U64 ctrl_thread_run_state;
String8 ctrl_thread_log_path; String8 ctrl_thread_log_path;
Thread ctrl_thread; OS_Handle ctrl_thread;
Log *ctrl_thread_log; Log *ctrl_thread_log;
RWMutex ctrl_thread_entity_ctx_rw_mutex; CTRL_EntityStore *ctrl_thread_entity_store;
CTRL_EntityCtxRWStore *ctrl_thread_entity_store;
E_Cache *ctrl_thread_eval_cache;
Arena *ctrl_thread_msg_process_arena;
Arena *dmn_event_arena; Arena *dmn_event_arena;
DMN_EventNode *first_dmn_event_node; DMN_EventNode *first_dmn_event_node;
DMN_EventNode *last_dmn_event_node; DMN_EventNode *last_dmn_event_node;
@@ -729,12 +598,18 @@ struct CTRL_State
String8List user_entry_points; String8List user_entry_points;
U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64]; U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64];
U64 process_counter; U64 process_counter;
Arena *dbg_dir_arena;
CTRL_DbgDirNode *dbg_dir_root; // rjf: user -> memstream ring buffer
U64 module_req_cache_slots_count; U64 u2ms_ring_size;
CTRL_ModuleReqCacheNode **module_req_cache_slots; U8 *u2ms_ring_base;
String8List msg_user_bp_touched_files; U64 u2ms_ring_write_pos;
String8List msg_user_bp_touched_symbols; U64 u2ms_ring_read_pos;
OS_Handle u2ms_ring_mutex;
OS_Handle u2ms_ring_cv;
// rjf: memory stream threads
U64 ms_thread_count;
OS_Handle *ms_threads;
}; };
//////////////////////////////// ////////////////////////////////
@@ -749,45 +624,24 @@ read_only global CTRL_Entity ctrl_entity_nil =
&ctrl_entity_nil, &ctrl_entity_nil,
&ctrl_entity_nil, &ctrl_entity_nil,
}; };
read_only global CTRL_CallStackTreeNode ctrl_call_stack_tree_node_nil =
{
0,
&ctrl_call_stack_tree_node_nil,
&ctrl_call_stack_tree_node_nil,
&ctrl_call_stack_tree_node_nil,
&ctrl_call_stack_tree_node_nil,
};
thread_static CTRL_EntityCtxLookupAccel *ctrl_entity_ctx_lookup_accel = 0;
//////////////////////////////// ////////////////////////////////
//~ rjf: Logging Markup //~ rjf: Logging Markup
#define CTRL_CtrlThreadLogScope DeferLoop(log_scope_begin(), ctrl_thread__end_and_flush_log()) #define CTRL_CtrlThreadLogScope DeferLoop(log_scope_begin(), ctrl_thread__end_and_flush_info_log())
//////////////////////////////// ////////////////////////////////
//~ rjf: Basic Type Functions //~ rjf: Basic Type Functions
internal U64 ctrl_hash_from_string(String8 string); internal U64 ctrl_hash_from_string(String8 string);
internal U64 ctrl_hash_from_handle(CTRL_Handle handle); internal U64 ctrl_hash_from_machine_id_handle(CTRL_MachineID machine_id, DMN_Handle handle);
internal CTRL_EventCause ctrl_event_cause_from_dmn_event_kind(DMN_EventKind event_kind); internal CTRL_EventCause ctrl_event_cause_from_dmn_event_kind(DMN_EventKind event_kind);
internal CTRL_ExceptionKind ctrl_exception_kind_from_dmn(DMN_ExceptionKind kind);
internal String8 ctrl_string_from_event_kind(CTRL_EventKind kind);
internal String8 ctrl_string_from_msg_kind(CTRL_MsgKind kind);
internal CTRL_EntityKind ctrl_entity_kind_from_string(String8 string);
internal DMN_TrapFlags ctrl_dmn_trap_flags_from_user_breakpoint_flags(CTRL_UserBreakpointFlags flags);
internal CTRL_UserBreakpointFlags ctrl_user_breakpoint_flags_from_dmn_trap_flags(DMN_TrapFlags flags);
//////////////////////////////// ////////////////////////////////
//~ rjf: Handle Type Functions //~ rjf: Machine/Handle Pair Type Functions
internal CTRL_Handle ctrl_handle_zero(void); internal void ctrl_machine_id_handle_pair_list_push(Arena *arena, CTRL_MachineIDHandlePairList *list, CTRL_MachineIDHandlePair *pair);
internal CTRL_Handle ctrl_handle_make(CTRL_MachineID machine_id, DMN_Handle dmn_handle); internal CTRL_MachineIDHandlePairList ctrl_machine_id_handle_pair_list_copy(Arena *arena, CTRL_MachineIDHandlePairList *src);
internal B32 ctrl_handle_match(CTRL_Handle a, CTRL_Handle b);
internal void ctrl_handle_list_push(Arena *arena, CTRL_HandleList *list, CTRL_Handle *pair);
internal CTRL_HandleList ctrl_handle_list_copy(Arena *arena, CTRL_HandleList *src);
internal CTRL_HandleArray ctrl_handle_array_from_list(Arena *arena, CTRL_HandleList *src);
internal String8 ctrl_string_from_handle(Arena *arena, CTRL_Handle handle);
internal CTRL_Handle ctrl_handle_from_string(String8 string);
//////////////////////////////// ////////////////////////////////
//~ rjf: Trap Type Functions //~ rjf: Trap Type Functions
@@ -809,8 +663,6 @@ internal void ctrl_msg_deep_copy(Arena *arena, CTRL_Msg *dst, CTRL_Msg *src);
//- rjf: list building //- rjf: list building
internal CTRL_Msg *ctrl_msg_list_push(Arena *arena, CTRL_MsgList *list); internal CTRL_Msg *ctrl_msg_list_push(Arena *arena, CTRL_MsgList *list);
internal CTRL_MsgList ctrl_msg_list_deep_copy(Arena *arena, CTRL_MsgList *src);
internal void ctrl_msg_list_concat_in_place(CTRL_MsgList *dst, CTRL_MsgList *src);
//- rjf: serialization //- rjf: serialization
internal String8 ctrl_serialized_string_from_msg_list(Arena *arena, CTRL_MsgList *msgs); internal String8 ctrl_serialized_string_from_msg_list(Arena *arena, CTRL_MsgList *msgs);
@@ -824,64 +676,34 @@ internal CTRL_Event *ctrl_event_list_push(Arena *arena, CTRL_EventList *list);
internal void ctrl_event_list_concat_in_place(CTRL_EventList *dst, CTRL_EventList *to_push); internal void ctrl_event_list_concat_in_place(CTRL_EventList *dst, CTRL_EventList *to_push);
//- rjf: serialization //- rjf: serialization
internal String8 ctrl_serialized_string_from_event(Arena *arena, CTRL_Event *event, U64 max); internal String8 ctrl_serialized_string_from_event(Arena *arena, CTRL_Event *event);
internal CTRL_Event ctrl_event_from_serialized_string(Arena *arena, String8 string); internal CTRL_Event ctrl_event_from_serialized_string(Arena *arena, String8 string);
//////////////////////////////// ////////////////////////////////
//~ rjf: Entity Type Functions //~ rjf: Entity Type Functions
//- rjf: entity list data structures //- rjf: cache creation/destruction
internal void ctrl_entity_list_push(Arena *arena, CTRL_EntityList *list, CTRL_Entity *entity); internal CTRL_EntityStore *ctrl_entity_store_alloc(void);
internal CTRL_EntityList ctrl_entity_list_from_handle_list(Arena *arena, CTRL_EntityCtx *ctx, CTRL_HandleList *list); internal void ctrl_entity_store_release(CTRL_EntityStore *store);
#define ctrl_entity_list_first(list) ((list)->first ? (list)->first->v : &ctrl_entity_nil)
//- rjf: entity array data structure
internal CTRL_EntityArray ctrl_entity_array_from_list(Arena *arena, CTRL_EntityList *list);
#define ctrl_entity_array_first(array) ((array)->count != 0 ? (array)->v[0] : &ctrl_entity_nil)
//- rjf: entity context (entity group read-only) functions
internal CTRL_Entity *ctrl_entity_from_handle(CTRL_EntityCtx *ctx, CTRL_Handle handle);
internal CTRL_Entity *ctrl_entity_child_from_kind(CTRL_Entity *parent, CTRL_EntityKind kind);
internal CTRL_Entity *ctrl_entity_ancestor_from_kind(CTRL_Entity *entity, CTRL_EntityKind kind);
internal CTRL_Entity *ctrl_process_from_entity(CTRL_Entity *entity);
internal CTRL_Entity *ctrl_module_from_process_vaddr(CTRL_Entity *process, U64 vaddr);
internal DI_Key ctrl_dbgi_key_from_module(CTRL_Entity *module);
internal CTRL_Entity *ctrl_module_from_thread_candidates(CTRL_EntityCtx *ctx, CTRL_Entity *thread, CTRL_EntityList *candidates);
internal U64 ctrl_vaddr_from_voff(CTRL_Entity *module, U64 voff);
internal U64 ctrl_voff_from_vaddr(CTRL_Entity *module, U64 vaddr);
internal Rng1U64 ctrl_vaddr_range_from_voff_range(CTRL_Entity *module, Rng1U64 voff_range);
internal Rng1U64 ctrl_voff_range_from_vaddr_range(CTRL_Entity *module, Rng1U64 vaddr_range);
internal B32 ctrl_entity_tree_is_frozen(CTRL_Entity *root);
//- rjf: entity tree iteration
internal CTRL_EntityRec ctrl_entity_rec_depth_first(CTRL_Entity *entity, CTRL_Entity *subtree_root, U64 sib_off, U64 child_off);
#define ctrl_entity_rec_depth_first_pre(entity, subtree_root) ctrl_entity_rec_depth_first((entity), (subtree_root), OffsetOf(CTRL_Entity, next), OffsetOf(CTRL_Entity, first))
#define ctrl_entity_rec_depth_first_post(entity, subtree_root) ctrl_entity_rec_depth_first((entity), (subtree_root), OffsetOf(CTRL_Entity, prev), OffsetOf(CTRL_Entity, last))
//- rjf: entity ctx r/w store state functions
internal CTRL_EntityCtxRWStore *ctrl_entity_ctx_rw_store_alloc(void);
internal void ctrl_entity_ctx_rw_store_release(CTRL_EntityCtxRWStore *store);
//- rjf: string allocation/deletion //- rjf: string allocation/deletion
internal U64 ctrl_name_bucket_num_from_string_size(U64 size); internal U64 ctrl_name_bucket_idx_from_string_size(U64 size);
internal String8 ctrl_entity_string_alloc(CTRL_EntityCtxRWStore *store, String8 string); internal String8 ctrl_entity_string_alloc(CTRL_EntityStore *store, String8 string);
internal void ctrl_entity_string_release(CTRL_EntityCtxRWStore *store, String8 string); internal void ctrl_entity_string_release(CTRL_EntityStore *store, String8 string);
//- rjf: entity construction/deletion //- rjf: entity construction/deletion
internal CTRL_Entity *ctrl_entity_alloc(CTRL_EntityCtxRWStore *store, CTRL_Entity *parent, CTRL_EntityKind kind, Arch arch, CTRL_Handle handle, U64 id); internal CTRL_Entity *ctrl_entity_alloc(CTRL_EntityStore *store, CTRL_Entity *parent, CTRL_EntityKind kind, Architecture arch, CTRL_MachineID machine_id, DMN_Handle handle, U64 id);
internal void ctrl_entity_release(CTRL_EntityCtxRWStore *store, CTRL_Entity *entity); internal void ctrl_entity_release(CTRL_EntityStore *store, CTRL_Entity *entity);
//- rjf: entity equipment //- rjf: entity equipment
internal void ctrl_entity_equip_string(CTRL_EntityCtxRWStore *store, CTRL_Entity *entity, String8 string); internal void ctrl_entity_equip_string(CTRL_EntityStore *store, CTRL_Entity *entity, String8 string);
//- rjf: accelerated entity context lookups //- rjf: entity store lookups
internal CTRL_EntityCtxLookupAccel *ctrl_thread_entity_ctx_lookup_accel(void); internal CTRL_Entity *ctrl_entity_from_machine_id_handle(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle handle);
internal CTRL_EntityArray ctrl_entity_array_from_kind(CTRL_EntityCtx *ctx, CTRL_EntityKind kind); internal CTRL_Entity *ctrl_entity_child_from_kind(CTRL_Entity *parent, CTRL_EntityKind kind);
internal CTRL_EntityList ctrl_modules_from_dbgi_key(Arena *arena, CTRL_EntityCtx *ctx, DI_Key dbgi_key);
internal CTRL_Entity *ctrl_thread_from_id(CTRL_EntityCtx *ctx, U64 id);
//- rjf: applying events to entity caches //- rjf: applying events to entity caches
internal void ctrl_entity_store_apply_events(CTRL_EntityCtxRWStore *store, CTRL_EventList *list); internal void ctrl_entity_store_apply_events(CTRL_EntityStore *store, CTRL_EventList *list);
//////////////////////////////// ////////////////////////////////
//~ rjf: Main Layer Initialization //~ rjf: Main Layer Initialization
@@ -893,49 +715,60 @@ internal void ctrl_init(void);
internal void ctrl_set_wakeup_hook(CTRL_WakeupFunctionType *wakeup_hook); internal void ctrl_set_wakeup_hook(CTRL_WakeupFunctionType *wakeup_hook);
////////////////////////////////
//~ rjf: Process Memory Functions
//- rjf: process memory cache interaction
internal U128 ctrl_calc_hash_store_key_from_process_vaddr_range(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 zero_terminated);
internal U128 ctrl_stored_hash_from_process_vaddr_range(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 zero_terminated, B32 *out_is_stale, U64 endt_us);
//- rjf: bundled key/stream helper
internal U128 ctrl_hash_store_key_from_process_vaddr_range(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 zero_terminated);
//- rjf: process memory cache reading helpers
internal CTRL_ProcessMemorySlice ctrl_query_cached_data_from_process_vaddr_range(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, U64 endt_us);
internal CTRL_ProcessMemorySlice ctrl_query_cached_zero_terminated_data_from_process_vaddr_limit(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, U64 vaddr, U64 limit, U64 element_size, U64 endt_us);
internal B32 ctrl_read_cached_process_memory(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 *is_stale_out, void *out, U64 endt_us);
#define ctrl_read_cached_process_memory_struct(machine_id, process, vaddr, is_stale_out, ptr, endt_us) ctrl_read_cached_process_memory((machine_id), (process), r1u64((vaddr), (vaddr)+(sizeof(*(ptr)))), (is_stale_out), (ptr), (endt_us))
//- rjf: process memory writing
internal B32 ctrl_process_write(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, void *src);
//////////////////////////////// ////////////////////////////////
//~ rjf: Thread Register Functions //~ rjf: Thread Register Functions
//- rjf: thread register cache reading //- rjf: thread register cache reading
internal void *ctrl_reg_block_from_thread(Arena *arena, CTRL_EntityCtx *ctx, CTRL_Handle handle); internal void *ctrl_query_cached_reg_block_from_thread(Arena *arena, CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread);
internal U64 ctrl_tls_root_vaddr_from_thread(CTRL_EntityCtx *ctx, CTRL_Handle handle); internal U64 ctrl_query_cached_tls_root_vaddr_from_thread(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread);
internal U64 ctrl_rip_from_thread(CTRL_EntityCtx *ctx, CTRL_Handle handle); internal U64 ctrl_query_cached_rip_from_thread(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread);
internal U64 ctrl_rsp_from_thread(CTRL_EntityCtx *ctx, CTRL_Handle handle);
//- rjf: thread register writing //- rjf: thread register writing
internal B32 ctrl_thread_write_reg_block(CTRL_Handle thread, void *block); internal B32 ctrl_thread_write_reg_block(CTRL_MachineID machine_id, DMN_Handle thread, void *block);
//////////////////////////////// ////////////////////////////////
//~ rjf: Module Image Info Functions //~ rjf: Module Image Info Functions
//- rjf: cache lookups //- rjf: cache lookups
internal PE_IntelPdata *ctrl_intel_pdata_from_module_voff(Arena *arena, CTRL_Handle module_handle, U64 voff); internal PE_IntelPdata *ctrl_intel_pdata_from_module_voff(Arena *arena, CTRL_MachineID machine_id, DMN_Handle module_handle, U64 voff);
internal U64 ctrl_entry_point_voff_from_module(CTRL_Handle module_handle); internal U64 ctrl_entry_point_voff_from_module(CTRL_MachineID machine_id, DMN_Handle module_handle);
internal Rng1U64 ctrl_tls_vaddr_range_from_module(CTRL_Handle module_handle); internal Rng1U64 ctrl_tls_vaddr_range_from_module(CTRL_MachineID machine_id, DMN_Handle module_handle);
internal String8 ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_Handle module_handle); internal String8 ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_MachineID machine_id, DMN_Handle module_handle);
internal String8 ctrl_raddbg_data_from_module(Arena *arena, CTRL_Handle module_handle);
//////////////////////////////// ////////////////////////////////
//~ rjf: Unwinding Functions //~ rjf: Unwinding Functions
//- rjf: unwind deep copier //- rjf: unwind deep copier
internal CTRL_Unwind ctrl_unwind_deep_copy(Arena *arena, Arch arch, CTRL_Unwind *src); internal CTRL_Unwind ctrl_unwind_deep_copy(Arena *arena, Architecture arch, CTRL_Unwind *src);
//- rjf: [x64] //- rjf: [x64]
internal REGS_Reg64 *ctrl_unwind_reg_from_pe_gpr_reg__pe_x64(REGS_RegBlockX64 *regs, PE_UnwindGprRegX64 gpr_reg); internal REGS_Reg64 *ctrl_unwind_reg_from_pe_gpr_reg__pe_x64(REGS_RegBlockX64 *regs, PE_UnwindGprRegX64 gpr_reg);
internal CTRL_UnwindStepResult ctrl_unwind_step__pe_x64(CTRL_Handle process_handle, CTRL_Handle module_handle, U64 module_base_vaddr, REGS_RegBlockX64 *regs, U64 endt_us); internal CTRL_UnwindStepResult ctrl_unwind_step__pe_x64(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle process_handle, DMN_Handle module, REGS_RegBlockX64 *regs, U64 endt_us);
//- rjf: abstracted unwind step //- rjf: abstracted unwind step
internal CTRL_UnwindStepResult ctrl_unwind_step(CTRL_Handle process, CTRL_Handle module, U64 module_base_vaddr, Arch arch, void *reg_block, U64 endt_us); internal CTRL_UnwindStepResult ctrl_unwind_step(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle process_handle, DMN_Handle module, Architecture arch, void *reg_block, U64 endt_us);
//- rjf: abstracted full unwind //- rjf: abstracted full unwind
internal CTRL_Unwind ctrl_unwind_from_thread(Arena *arena, CTRL_EntityCtx *ctx, CTRL_Handle thread, U64 endt_us); internal CTRL_Unwind ctrl_unwind_from_thread(Arena *arena, CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread, U64 endt_us);
////////////////////////////////
//~ rjf: Call Stack Building Functions
internal CTRL_CallStack ctrl_call_stack_from_unwind(Arena *arena, CTRL_Entity *process, CTRL_Unwind *base_unwind);
internal CTRL_CallStackFrame *ctrl_call_stack_frame_from_unwind_and_inline_depth(CTRL_CallStack *call_stack, U64 unwind_count, U64 inline_depth);
//////////////////////////////// ////////////////////////////////
//~ rjf: Halting All Attached Processes //~ rjf: Halting All Attached Processes
@@ -951,8 +784,8 @@ internal U64 ctrl_mem_gen(void);
internal U64 ctrl_reg_gen(void); internal U64 ctrl_reg_gen(void);
//- rjf: name -> register/alias hash tables, for eval //- rjf: name -> register/alias hash tables, for eval
internal E_String2NumMap *ctrl_string2reg_from_arch(Arch arch); internal EVAL_String2NumMap *ctrl_string2reg_from_arch(Architecture arch);
internal E_String2NumMap *ctrl_string2alias_from_arch(Arch arch); internal EVAL_String2NumMap *ctrl_string2alias_from_arch(Architecture arch);
//////////////////////////////// ////////////////////////////////
//~ rjf: Control-Thread Functions //~ rjf: Control-Thread Functions
@@ -969,64 +802,39 @@ internal CTRL_EventList ctrl_c2u_pop_events(Arena *arena);
internal void ctrl_thread__entry_point(void *p); internal void ctrl_thread__entry_point(void *p);
//- rjf: breakpoint resolution //- rjf: breakpoint resolution
internal void ctrl_thread__append_resolved_module_user_bp_traps(Arena *arena, CTRL_EvalScope *eval_scope, CTRL_Handle process, CTRL_Handle module, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out); internal void ctrl_thread__append_resolved_module_user_bp_traps(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, DMN_Handle module, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out);
internal void ctrl_thread__append_resolved_process_user_bp_traps(Arena *arena, CTRL_EvalScope *eval_scope, CTRL_Handle process, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out); internal void ctrl_thread__append_resolved_process_user_bp_traps(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out);
internal void ctrl_thread__append_program_defined_bp_traps(Arena *arena, CTRL_Entity *bp, DMN_TrapChunkList *traps_out);
//- rjf: module lifetime open/close work //- rjf: module lifetime open/close work
internal void ctrl_thread__module_open(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr_range, String8 path); internal void ctrl_thread__module_open(CTRL_MachineID machine_id, DMN_Handle process, DMN_Handle module, Rng1U64 vaddr_range, String8 path, U64 exe_timestamp);
internal void ctrl_thread__module_close(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr_range); internal void ctrl_thread__module_close(CTRL_MachineID machine_id, DMN_Handle module, String8 path);
//- rjf: attached process running/event gathering //- rjf: attached process running/event gathering
internal DMN_Event *ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg, DMN_RunCtrls *run_ctrls, CTRL_Spoof *spoof); internal DMN_Event *ctrl_thread__next_dmn_event(Arena *arena, DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg, DMN_RunCtrls *run_ctrls, CTRL_Spoof *spoof);
//- rjf: eval helpers //- rjf: eval helpers
internal U64 ctrl_eval_space_gen(E_Space space); internal B32 ctrl_eval_memory_read(void *u, void *out, U64 addr, U64 size);
internal B32 ctrl_eval_space_read(E_Space space, void *out, Rng1U64 vaddr_range);
//- rjf: control thread eval scopes
internal CTRL_EvalScope *ctrl_thread__eval_scope_begin(Arena *arena, CTRL_UserBreakpointList *user_bps, CTRL_Entity *thread);
internal void ctrl_thread__eval_scope_end(CTRL_EvalScope *scope);
//- rjf: log flusher //- rjf: log flusher
internal void ctrl_thread__end_and_flush_log(void); internal void ctrl_thread__flush_info_log(String8 string);
internal void ctrl_thread__end_and_flush_info_log(void);
//- rjf: msg kind implementations //- rjf: msg kind implementations
internal void ctrl_thread__launch(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__launch(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg);
internal void ctrl_thread__attach(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__attach(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg);
internal void ctrl_thread__kill(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__kill(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg);
internal void ctrl_thread__kill_all(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg);
internal void ctrl_thread__detach(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__detach(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg);
internal void ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg);
internal void ctrl_thread__single_step(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); internal void ctrl_thread__single_step(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg);
//////////////////////////////// ////////////////////////////////
//~ rjf: Process Memory Artifact Cache Hooks / Lookups //~ rjf: Memory-Stream Thread Functions
internal AC_Artifact ctrl_memory_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out); //- rjf: user -> memory stream communication
internal void ctrl_memory_artifact_destroy(AC_Artifact artifact); internal B32 ctrl_u2ms_enqueue_req(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 vaddr_range, B32 zero_terminated, U64 endt_us);
internal C_Key ctrl_key_from_process_vaddr_range(CTRL_Handle process, Rng1U64 vaddr_range, B32 zero_terminated, B32 wait_for_fresh, U64 endt_us, B32 *out_is_stale); internal void ctrl_u2ms_dequeue_req(CTRL_MachineID *out_machine_id, DMN_Handle *out_process, Rng1U64 *out_vaddr_range, B32 *out_zero_terminated);
//- rjf: process memory reading helpers //- rjf: entry point
internal CTRL_ProcessMemorySlice ctrl_process_memory_slice_from_vaddr_range(Arena *arena, CTRL_Handle process, Rng1U64 range, B32 wait_for_fresh, U64 endt_us); internal void ctrl_mem_stream_thread__entry_point(void *p);
internal B32 ctrl_process_memory_read(CTRL_Handle process, Rng1U64 range, B32 *is_stale_out, void *out, U64 endt_us);
#define ctrl_process_memory_read_struct(process, vaddr, is_stale_out, ptr, endt_us) ctrl_process_memory_read((process), r1u64((vaddr), (vaddr)+(sizeof(*(ptr)))), (is_stale_out), (ptr), (endt_us))
//- rjf: process memory writing
internal B32 ctrl_process_write(CTRL_Handle process, Rng1U64 range, void *src);
////////////////////////////////
//~ rjf: Call Stack Artifact Cache Hooks / Lookups
internal AC_Artifact ctrl_call_stack_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out);
internal void ctrl_call_stack_artifact_destroy(AC_Artifact artifact);
internal CTRL_CallStack ctrl_call_stack_from_thread(Access *access, CTRL_Handle thread_handle, B32 high_priority, U64 endt_us);
////////////////////////////////
//~ rjf: Call Stack Tree Artifact Cache Hooks / Lookups
internal AC_Artifact ctrl_call_stack_tree_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out);
internal void ctrl_call_stack_tree_artifact_destroy(AC_Artifact artifact);
internal CTRL_CallStackTree ctrl_call_stack_tree(Access *access, U64 endt_us);
#endif // CTRL_CORE_H #endif // CTRL_CORE_H
+1 -4
View File
@@ -1,7 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#undef LAYER_COLOR
#define LAYER_COLOR 0x969696ff
#include "ctrl_core.c" #include "ctrl_core.c"
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef CTRL_INC_H #ifndef CTRL_INC_H
+1 -33
View File
@@ -1,41 +1,9 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//- GENERATED CODE //- GENERATED CODE
C_LINKAGE_BEGIN C_LINKAGE_BEGIN
String8 ctrl_entity_kind_code_name_table[12] =
{
{0},
str8_lit_comp("root"),
str8_lit_comp("machine"),
str8_lit_comp("process"),
str8_lit_comp("thread"),
str8_lit_comp("module"),
str8_lit_comp("entry_point"),
str8_lit_comp("debug_info_path"),
str8_lit_comp("pending_thread_name"),
str8_lit_comp("pending_thread_color"),
str8_lit_comp("breakpoint"),
str8_lit_comp("address_range_annotation"),
};
String8 ctrl_entity_kind_display_string_table[12] =
{
{0},
str8_lit_comp("Root"),
str8_lit_comp("Machine"),
str8_lit_comp("Process"),
str8_lit_comp("Thread"),
str8_lit_comp("Module"),
str8_lit_comp("EntryPoint"),
str8_lit_comp("DebugInfoPath"),
str8_lit_comp("PendingThreadName"),
str8_lit_comp("PendingThreadColor"),
str8_lit_comp("Breakpoint"),
str8_lit_comp("AddressRangeAnnotation"),
};
U32 ctrl_exception_code_kind_code_table[38] = U32 ctrl_exception_code_kind_code_table[38] =
{ {
0, 0,
+1 -21
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//- GENERATED CODE //- GENERATED CODE
@@ -6,23 +6,6 @@
#ifndef CTRL_META_H #ifndef CTRL_META_H
#define CTRL_META_H #define CTRL_META_H
typedef enum CTRL_EntityKind
{
CTRL_EntityKind_Null,
CTRL_EntityKind_Root,
CTRL_EntityKind_Machine,
CTRL_EntityKind_Process,
CTRL_EntityKind_Thread,
CTRL_EntityKind_Module,
CTRL_EntityKind_EntryPoint,
CTRL_EntityKind_DebugInfoPath,
CTRL_EntityKind_PendingThreadName,
CTRL_EntityKind_PendingThreadColor,
CTRL_EntityKind_Breakpoint,
CTRL_EntityKind_AddressRangeAnnotation,
CTRL_EntityKind_COUNT,
} CTRL_EntityKind;
typedef enum CTRL_ExceptionCodeKind typedef enum CTRL_ExceptionCodeKind
{ {
CTRL_ExceptionCodeKind_Null, CTRL_ExceptionCodeKind_Null,
@@ -67,13 +50,10 @@ CTRL_ExceptionCodeKind_COUNT,
} CTRL_ExceptionCodeKind; } CTRL_ExceptionCodeKind;
C_LINKAGE_BEGIN C_LINKAGE_BEGIN
extern String8 ctrl_entity_kind_code_name_table[12];
extern String8 ctrl_entity_kind_display_string_table[12];
extern U32 ctrl_exception_code_kind_code_table[38]; extern U32 ctrl_exception_code_kind_code_table[38];
extern String8 ctrl_exception_code_kind_display_string_table[38]; extern String8 ctrl_exception_code_kind_display_string_table[38];
extern String8 ctrl_exception_code_kind_lowercase_code_string_table[38]; extern String8 ctrl_exception_code_kind_lowercase_code_string_table[38];
extern B8 ctrl_exception_code_kind_default_enable_table[38]; extern B8 ctrl_exception_code_kind_default_enable_table[38];
C_LINKAGE_END C_LINKAGE_END
#endif // CTRL_META_H #endif // CTRL_META_H
+725
View File
@@ -0,0 +1,725 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Third Party Includes
#include "third_party/udis86/config.h"
#include "third_party/udis86/udis86.h"
#include "third_party/udis86/libudis86/decode.c"
#include "third_party/udis86/libudis86/itab.c"
#include "third_party/udis86/libudis86/syn-att.c"
#include "third_party/udis86/libudis86/syn-intel.c"
#include "third_party/udis86/libudis86/syn.c"
#include "third_party/udis86/libudis86/udis86.c"
////////////////////////////////
//~ rjf: Parameter Type Functions
internal B32
dasm_params_match(DASM_Params *a, DASM_Params *b)
{
B32 result = (a->vaddr == b->vaddr &&
a->arch == b->arch &&
a->style_flags == b->style_flags &&
a->syntax == b->syntax &&
a->base_vaddr == b->base_vaddr &&
di_key_match(&a->dbgi_key, &b->dbgi_key));
return result;
}
////////////////////////////////
//~ rjf: Instruction Type Functions
internal void
dasm_inst_chunk_list_push(Arena *arena, DASM_InstChunkList *list, U64 cap, DASM_Inst *inst)
{
DASM_InstChunkNode *node = list->last;
if(node == 0 || node->count >= node->cap)
{
node = push_array(arena, DASM_InstChunkNode, 1);
node->v = push_array_no_zero(arena, DASM_Inst, cap);
node->cap = cap;
SLLQueuePush(list->first, list->last, node);
list->node_count += 1;
}
MemoryCopyStruct(&node->v[node->count], inst);
node->count += 1;
list->inst_count += 1;
}
internal DASM_InstArray
dasm_inst_array_from_chunk_list(Arena *arena, DASM_InstChunkList *list)
{
DASM_InstArray array = {0};
array.count = list->inst_count;
array.v = push_array_no_zero(arena, DASM_Inst, array.count);
U64 idx = 0;
for(DASM_InstChunkNode *n = list->first; n != 0; n = n->next)
{
MemoryCopy(array.v+idx, n->v, sizeof(DASM_Inst)*n->count);
idx += n->count;
}
return array;
}
internal U64
dasm_inst_array_idx_from_code_off__linear_scan(DASM_InstArray *array, U64 off)
{
U64 result = 0;
for(U64 idx = 0; idx < array->count; idx += 1)
{
if(array->v[idx].code_off == off)
{
result = idx;
break;
}
}
return result;
}
internal U64
dasm_inst_array_code_off_from_idx(DASM_InstArray *array, U64 idx)
{
U64 off = 0;
if(idx < array->count)
{
off = array->v[idx].code_off;
}
return off;
}
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
dasm_init(void)
{
Arena *arena = arena_alloc();
dasm_shared = push_array(arena, DASM_Shared, 1);
dasm_shared->arena = arena;
dasm_shared->slots_count = 1024;
dasm_shared->stripes_count = Min(dasm_shared->slots_count, os_logical_core_count());
dasm_shared->slots = push_array(arena, DASM_Slot, dasm_shared->slots_count);
dasm_shared->stripes = push_array(arena, DASM_Stripe, dasm_shared->stripes_count);
for(U64 idx = 0; idx < dasm_shared->stripes_count; idx += 1)
{
dasm_shared->stripes[idx].arena = arena_alloc();
dasm_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
dasm_shared->stripes[idx].cv = os_condition_variable_alloc();
}
dasm_shared->u2p_ring_size = KB(64);
dasm_shared->u2p_ring_base = push_array_no_zero(arena, U8, dasm_shared->u2p_ring_size);
dasm_shared->u2p_ring_cv = os_condition_variable_alloc();
dasm_shared->u2p_ring_mutex = os_mutex_alloc();
dasm_shared->parse_thread_count = 1;
dasm_shared->parse_threads = push_array(arena, OS_Handle, dasm_shared->parse_thread_count);
for(U64 idx = 0; idx < dasm_shared->parse_thread_count; idx += 1)
{
dasm_shared->parse_threads[idx] = os_launch_thread(dasm_parse_thread__entry_point, (void *)idx, 0);
}
dasm_shared->evictor_detector_thread = os_launch_thread(dasm_evictor_detector_thread__entry_point, 0, 0);
}
////////////////////////////////
//~ rjf: User Clock
internal void
dasm_user_clock_tick(void)
{
ins_atomic_u64_inc_eval(&dasm_shared->user_clock_idx);
}
internal U64
dasm_user_clock_idx(void)
{
U64 idx = ins_atomic_u64_eval(&dasm_shared->user_clock_idx);
return idx;
}
////////////////////////////////
//~ rjf: Scoped Access
internal DASM_Scope *
dasm_scope_open(void)
{
if(dasm_tctx == 0)
{
Arena *arena = arena_alloc();
dasm_tctx = push_array(arena, DASM_TCTX, 1);
dasm_tctx->arena = arena;
}
U64 base_pos = arena_pos(dasm_tctx->arena);
DASM_Scope *scope = push_array(dasm_tctx->arena, DASM_Scope, 1);
scope->base_pos = base_pos;
return scope;
}
internal void
dasm_scope_close(DASM_Scope *scope)
{
for(DASM_Touch *t = scope->top_touch, *next = 0; t != 0; t = next)
{
next = t->next;
U64 slot_idx = t->hash.u64[1]%dasm_shared->slots_count;
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(t->hash, n->hash) && dasm_params_match(&t->params, &n->params))
{
ins_atomic_u64_dec_eval(&n->scope_ref_count);
break;
}
}
}
}
arena_pop_to(dasm_tctx->arena, scope->base_pos);
}
internal void
dasm_scope_touch_node__stripe_r_guarded(DASM_Scope *scope, DASM_Node *node)
{
DASM_Touch *touch = push_array(dasm_tctx->arena, DASM_Touch, 1);
ins_atomic_u64_inc_eval(&node->scope_ref_count);
ins_atomic_u64_eval_assign(&node->last_time_touched_us, os_now_microseconds());
ins_atomic_u64_eval_assign(&node->last_user_clock_idx_touched, dasm_user_clock_idx());
touch->hash = node->hash;
MemoryCopyStruct(&touch->params, &node->params);
touch->params.dbgi_key = di_key_copy(dasm_tctx->arena, &touch->params.dbgi_key);
SLLStackPush(scope->top_touch, touch);
}
////////////////////////////////
//~ rjf: Cache Lookups
internal DASM_Info
dasm_info_from_hash_params(DASM_Scope *scope, U128 hash, DASM_Params *params)
{
DASM_Info info = {0};
if(!u128_match(hash, u128_zero()))
{
U64 slot_idx = hash.u64[1]%dasm_shared->slots_count;
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
B32 found = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash) && dasm_params_match(params, &n->params))
{
MemoryCopyStruct(&info, &n->info);
found = 1;
dasm_scope_touch_node__stripe_r_guarded(scope, n);
break;
}
}
}
B32 node_is_new = 0;
if(!found)
{
OS_MutexScopeW(stripe->rw_mutex)
{
DASM_Node *node = 0;
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(hash, n->hash) && dasm_params_match(params, &n->params))
{
node = n;
break;
}
}
if(node == 0)
{
log_infof("[dasm] cache miss, creating node...\n");
log_infof(" hash: [0x%I64x 0x%I64x]\n", hash.u64[0], hash.u64[1]);
log_infof(" vaddr: 0x%I64x\n", params->vaddr);
log_infof(" arch: %S\n", string_from_architecture(params->arch));
log_infof(" style_flags: 0x%x\n", params->style_flags);
log_infof(" syntax: %i\n", params->syntax);
log_infof(" base_vaddr: 0x%I64x\n", params->base_vaddr);
log_infof(" dbgi_key: [%S 0x%I64x]\n", params->dbgi_key.path, params->dbgi_key.min_timestamp);
node = stripe->free_node;
if(node)
{
SLLStackPop(stripe->free_node);
}
else
{
node = push_array_no_zero(stripe->arena, DASM_Node, 1);
}
MemoryZeroStruct(node);
DLLPushBack(slot->first, slot->last, node);
node->hash = hash;
MemoryCopyStruct(&node->params, params);
// TODO(rjf): need to make this releasable - currently all exe_paths just leak
node->params.dbgi_key = di_key_copy(stripe->arena, &node->params.dbgi_key);
node_is_new = 1;
}
}
}
if(node_is_new)
{
dasm_u2p_enqueue_req(hash, params, max_U64);
}
}
return info;
}
internal DASM_Info
dasm_info_from_key_params(DASM_Scope *scope, U128 key, DASM_Params *params, U128 *hash_out)
{
DASM_Info result = {0};
for(U64 rewind_idx = 0; rewind_idx < 2; rewind_idx += 1)
{
U128 hash = hs_hash_from_key(key, rewind_idx);
result = dasm_info_from_hash_params(scope, hash, params);
if(result.insts.count != 0)
{
if(hash_out)
{
*hash_out = hash;
}
break;
}
}
return result;
}
////////////////////////////////
//~ rjf: Parse Threads
internal B32
dasm_u2p_enqueue_req(U128 hash, DASM_Params *params, U64 endt_us)
{
B32 good = 0;
OS_MutexScope(dasm_shared->u2p_ring_mutex) for(;;)
{
U64 unconsumed_size = dasm_shared->u2p_ring_write_pos - dasm_shared->u2p_ring_read_pos;
U64 available_size = dasm_shared->u2p_ring_size - unconsumed_size;
if(available_size >= sizeof(hash)+sizeof(U64)+sizeof(Architecture)+sizeof(DASM_StyleFlags)+sizeof(DASM_Syntax)+sizeof(U64)+sizeof(U64)+params->dbgi_key.path.size+sizeof(U64))
{
good = 1;
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &hash);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->vaddr);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->arch);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->style_flags);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->syntax);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->base_vaddr);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->dbgi_key.path.size);
dasm_shared->u2p_ring_write_pos += ring_write(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, params->dbgi_key.path.str, params->dbgi_key.path.size);
dasm_shared->u2p_ring_write_pos += ring_write_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_write_pos, &params->dbgi_key.min_timestamp);
dasm_shared->u2p_ring_write_pos += 7;
dasm_shared->u2p_ring_write_pos -= dasm_shared->u2p_ring_write_pos%8;
break;
}
if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait(dasm_shared->u2p_ring_cv, dasm_shared->u2p_ring_mutex, endt_us);
}
if(good)
{
os_condition_variable_broadcast(dasm_shared->u2p_ring_cv);
}
return good;
}
internal void
dasm_u2p_dequeue_req(Arena *arena, U128 *hash_out, DASM_Params *params_out)
{
OS_MutexScope(dasm_shared->u2p_ring_mutex) for(;;)
{
U64 unconsumed_size = dasm_shared->u2p_ring_write_pos - dasm_shared->u2p_ring_read_pos;
if(unconsumed_size >= sizeof(*hash_out)+sizeof(U64)+sizeof(Architecture)+sizeof(DASM_StyleFlags)+sizeof(DASM_Syntax)+sizeof(U64)+sizeof(U64)+sizeof(U64))
{
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, hash_out);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->vaddr);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->arch);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->style_flags);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->syntax);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->base_vaddr);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->dbgi_key.path.size);
params_out->dbgi_key.path.str = push_array(arena, U8, params_out->dbgi_key.path.size);
dasm_shared->u2p_ring_read_pos += ring_read(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, params_out->dbgi_key.path.str, params_out->dbgi_key.path.size);
dasm_shared->u2p_ring_read_pos += ring_read_struct(dasm_shared->u2p_ring_base, dasm_shared->u2p_ring_size, dasm_shared->u2p_ring_read_pos, &params_out->dbgi_key.min_timestamp);
dasm_shared->u2p_ring_read_pos += 7;
dasm_shared->u2p_ring_read_pos -= dasm_shared->u2p_ring_read_pos%8;
break;
}
os_condition_variable_wait(dasm_shared->u2p_ring_cv, dasm_shared->u2p_ring_mutex, max_U64);
}
os_condition_variable_broadcast(dasm_shared->u2p_ring_mutex);
}
internal void
dasm_parse_thread__entry_point(void *p)
{
ThreadNameF("[dasm] parse thread #%I64u", (U64)p);
for(;;)
{
Temp scratch = scratch_begin(0, 0);
//- rjf: get next request
U128 hash = {0};
DASM_Params params = {0};
dasm_u2p_dequeue_req(scratch.arena, &hash, &params);
U64 change_gen = fs_change_gen();
HS_Scope *hs_scope = hs_scope_open();
DI_Scope *di_scope = di_scope_open();
TXT_Scope *txt_scope = txt_scope_open();
//- rjf: unpack hash
U64 slot_idx = hash.u64[1]%dasm_shared->slots_count;
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
//- rjf: take task
B32 got_task = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash) && dasm_params_match(&n->params, &params))
{
got_task = !ins_atomic_u32_eval_cond_assign(&n->is_working, 1, 0);
break;
}
}
}
//- rjf: get dbg info
RDI_Parsed *rdi = &di_rdi_parsed_nil;
if(got_task && params.dbgi_key.path.size != 0)
{
rdi = di_rdi_from_key(di_scope, &params.dbgi_key, max_U64);
}
//- rjf: hash -> data
String8 data = {0};
if(got_task)
{
data = hs_data_from_hash(hs_scope, hash);
}
//- rjf: data * arch * addr * dbg -> decode artifacts
DASM_InstChunkList inst_list = {0};
String8List inst_strings = {0};
if(got_task)
{
switch(params.arch)
{
default:{}break;
//- rjf: x86/x64 decoding
case Architecture_x64:
case Architecture_x86:
{
// rjf: grab context
struct ud udc;
ud_init(&udc);
ud_set_mode(&udc, bit_size_from_arch(params.arch));
ud_set_pc(&udc, params.vaddr);
ud_set_input_buffer(&udc, data.str, data.size);
ud_set_vendor(&udc, UD_VENDOR_ANY);
ud_set_syntax(&udc, params.syntax == DASM_Syntax_Intel ? UD_SYN_INTEL : UD_SYN_ATT);
// rjf: disassemble
RDI_SourceFile *last_file = &rdi_source_file_nil;
RDI_Line *last_line = 0;
for(U64 off = 0; off < data.size;)
{
// rjf: disassemble one instruction
U64 size = ud_disassemble(&udc);
if(size == 0)
{
break;
}
// rjf: analyze
struct ud_operand *first_op = (struct ud_operand *)ud_insn_opr(&udc, 0);
U64 rel_voff = (first_op != 0 && first_op->type == UD_OP_JIMM) ? ud_syn_rel_target(&udc, first_op) : 0;
U64 jump_dst_vaddr = rel_voff;
// rjf: push strings derived from voff -> line info
if(params.style_flags & (DASM_StyleFlag_SourceFilesNames|DASM_StyleFlag_SourceLines))
{
if(rdi != &di_rdi_parsed_nil)
{
U64 voff = (params.vaddr+off) - params.base_vaddr;
U32 unit_idx = rdi_vmap_idx_from_voff(rdi->unit_vmap, rdi->unit_vmap_count, voff);
RDI_Unit *unit = rdi_element_from_idx(rdi, units, unit_idx);
RDI_ParsedLineInfo unit_line_info = {0};
rdi_line_info_from_unit(rdi, unit, &unit_line_info);
U64 line_info_idx = rdi_line_info_idx_from_voff(&unit_line_info, voff);
if(line_info_idx < unit_line_info.count)
{
RDI_Line *line = &unit_line_info.lines[line_info_idx];
RDI_SourceFile *file = rdi_element_from_idx(rdi, source_files, line->file_idx);
String8 file_normalized_full_path = {0};
file_normalized_full_path.str = rdi_string_from_idx(rdi, file->normal_full_path_string_idx, &file_normalized_full_path.size);
if(file != last_file)
{
if(params.style_flags & DASM_StyleFlag_SourceFilesNames &&
file->normal_full_path_string_idx != 0 && file_normalized_full_path.size != 0)
{
DASM_Inst inst = {0};
dasm_inst_chunk_list_push(scratch.arena, &inst_list, 1024, &inst);
str8_list_pushf(scratch.arena, &inst_strings, "> %S", file_normalized_full_path);
}
if(params.style_flags & DASM_StyleFlag_SourceFilesNames && file->normal_full_path_string_idx == 0)
{
DASM_Inst inst = {0};
dasm_inst_chunk_list_push(scratch.arena, &inst_list, 1024, &inst);
str8_list_pushf(scratch.arena, &inst_strings, ">");
}
last_file = file;
}
if(line && line != last_line && file->normal_full_path_string_idx != 0 &&
params.style_flags & DASM_StyleFlag_SourceLines &&
file_normalized_full_path.size != 0)
{
FileProperties props = os_properties_from_file_path(file_normalized_full_path);
if(props.modified != 0)
{
// TODO(rjf): need redirection path - this may map to a different path on the local machine,
// need frontend to communicate path remapping info to this layer
U128 key = fs_key_from_path(file_normalized_full_path);
TXT_LangKind lang_kind = txt_lang_kind_from_extension(file_normalized_full_path);
U64 endt_us = max_U64;
U128 hash = {0};
TXT_TextInfo text_info = {0};
for(;os_now_microseconds() <= endt_us;)
{
text_info = txt_text_info_from_key_lang(txt_scope, key, lang_kind, &hash);
if(!u128_match(hash, u128_zero()))
{
break;
}
}
if(0 < line->line_num && line->line_num < text_info.lines_count)
{
String8 data = hs_data_from_hash(hs_scope, hash);
String8 line_text = str8_skip_chop_whitespace(str8_substr(data, text_info.lines_ranges[line->line_num-1]));
if(line_text.size != 0)
{
DASM_Inst inst = {0};
dasm_inst_chunk_list_push(scratch.arena, &inst_list, 1024, &inst);
str8_list_pushf(scratch.arena, &inst_strings, "> %S", line_text);
}
}
}
last_line = line;
}
}
}
}
// rjf: push
String8 addr_part = {0};
if(params.style_flags & DASM_StyleFlag_Addresses)
{
addr_part = push_str8f(scratch.arena, "%s%016I64X ", rdi != &di_rdi_parsed_nil ? " " : "", params.vaddr+off);
}
String8 code_bytes_part = {0};
if(params.style_flags & DASM_StyleFlag_CodeBytes)
{
String8List code_bytes_strings = {0};
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit("{"));
for(U64 byte_idx = 0; byte_idx < size || byte_idx < 16; byte_idx += 1)
{
if(byte_idx < size)
{
str8_list_pushf(scratch.arena, &code_bytes_strings, "%02x%s ", (U32)data.str[off+byte_idx], byte_idx == size-1 ? "}" : "");
}
else if(byte_idx < 8)
{
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit(" "));
}
}
str8_list_push(scratch.arena, &code_bytes_strings, str8_lit(" "));
code_bytes_part = str8_list_join(scratch.arena, &code_bytes_strings, 0);
}
String8 symbol_part = {0};
if(jump_dst_vaddr != 0 && rdi != &di_rdi_parsed_nil && params.style_flags & DASM_StyleFlag_SymbolNames)
{
RDI_U32 scope_idx = rdi_vmap_idx_from_voff(rdi->scope_vmap, rdi->scope_vmap_count, jump_dst_vaddr-params.base_vaddr);
if(scope_idx != 0)
{
RDI_Scope *scope = rdi_element_from_idx(rdi, scopes, scope_idx);
RDI_U32 procedure_idx = scope->proc_idx;
RDI_Procedure *procedure = rdi_element_from_idx(rdi, procedures, procedure_idx);
String8 procedure_name = {0};
procedure_name.str = rdi_string_from_idx(rdi, procedure->name_string_idx, &procedure_name.size);
if(procedure_name.size != 0)
{
symbol_part = push_str8f(scratch.arena, " (%S)", procedure_name);
}
}
}
String8 inst_string = push_str8f(scratch.arena, "%S%S%s%S", addr_part, code_bytes_part, udc.asm_buf, symbol_part);
DASM_Inst inst = {off, rel_voff, r1u64(inst_strings.total_size + inst_strings.node_count,
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
dasm_inst_chunk_list_push(scratch.arena, &inst_list, 1024, &inst);
str8_list_push(scratch.arena, &inst_strings, inst_string);
// rjf: increment
off += size;
}
}break;
}
}
//- rjf: artifacts -> value bundle
Arena *info_arena = 0;
DASM_Info info = {0};
if(got_task)
{
//- rjf: produce joined text
Arena *text_arena = arena_alloc();
StringJoin text_join = {0};
text_join.sep = str8_lit("\n");
String8 text = str8_list_join(text_arena, &inst_strings, &text_join);
//- rjf: produce unique key for this disassembly's text
U128 text_key = {0};
{
U64 hash_data[] =
{
hash.u64[0],
hash.u64[1],
params.vaddr,
(U64)params.arch,
(U64)params.style_flags,
(U64)params.syntax,
(U64)rdi,
0x4d534144,
};
text_key = hs_hash_from_data(str8((U8 *)hash_data, sizeof(hash_data)));
}
//- rjf: submit text data to hash store
U128 text_hash = hs_submit_data(text_key, &text_arena, text);
//- rjf: produce value bundle
info_arena = arena_alloc();
info.text_key = text_key;
info.insts = dasm_inst_array_from_chunk_list(info_arena, &inst_list);
}
//- rjf: commit results to cache
if(got_task) OS_MutexScopeW(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(u128_match(n->hash, hash) && dasm_params_match(&n->params, &params))
{
n->info_arena = info_arena;
MemoryCopyStruct(&n->info, &info);
if(rdi != &di_rdi_parsed_nil && params.style_flags & (DASM_StyleFlag_SourceLines|DASM_StyleFlag_SourceFilesNames))
{
n->change_gen = change_gen;
}
else
{
n->change_gen = 0;
}
ins_atomic_u32_eval_assign(&n->is_working, 0);
ins_atomic_u64_inc_eval(&n->load_count);
break;
}
}
}
txt_scope_close(txt_scope);
di_scope_close(di_scope);
hs_scope_close(hs_scope);
scratch_end(scratch);
}
}
////////////////////////////////
//~ rjf: Evictor/Detector Thread
internal void
dasm_evictor_detector_thread__entry_point(void *p)
{
ThreadNameF("[dasm] evictor/detector thread");
for(;;)
{
U64 change_gen = fs_change_gen();
U64 check_time_us = os_now_microseconds();
U64 check_time_user_clocks = dasm_user_clock_idx();
U64 evict_threshold_us = 10*1000000;
U64 retry_threshold_us = 1*1000000;
U64 evict_threshold_user_clocks = 10;
U64 retry_threshold_user_clocks = 10;
for(U64 slot_idx = 0; slot_idx < dasm_shared->slots_count; slot_idx += 1)
{
U64 stripe_idx = slot_idx%dasm_shared->stripes_count;
DASM_Slot *slot = &dasm_shared->slots[slot_idx];
DASM_Stripe *stripe = &dasm_shared->stripes[stripe_idx];
B32 slot_has_work = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first; n != 0; n = n->next)
{
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
slot_has_work = 1;
break;
}
if(n->change_gen != 0 && n->change_gen != change_gen &&
n->last_time_requested_us+retry_threshold_us <= check_time_us &&
n->last_user_clock_idx_requested+retry_threshold_user_clocks <= check_time_user_clocks)
{
slot_has_work = 1;
break;
}
}
}
if(slot_has_work) OS_MutexScopeW(stripe->rw_mutex)
{
for(DASM_Node *n = slot->first, *next = 0; n != 0; n = next)
{
next = n->next;
if(n->scope_ref_count == 0 &&
n->last_time_touched_us+evict_threshold_us <= check_time_us &&
n->last_user_clock_idx_touched+evict_threshold_user_clocks <= check_time_user_clocks &&
n->load_count != 0 &&
n->is_working == 0)
{
DLLRemove(slot->first, slot->last, n);
if(n->info_arena != 0)
{
arena_release(n->info_arena);
}
SLLStackPush(stripe->free_node, n);
}
if(n->change_gen != 0 && n->change_gen != change_gen &&
n->last_time_requested_us+retry_threshold_us <= check_time_us &&
n->last_user_clock_idx_requested+retry_threshold_user_clocks <= check_time_user_clocks)
{
if(dasm_u2p_enqueue_req(n->hash, &n->params, max_U64))
{
n->last_time_requested_us = os_now_microseconds();
n->last_user_clock_idx_requested = check_time_user_clocks;
}
}
}
}
}
os_sleep_milliseconds(100);
}
}
+251
View File
@@ -0,0 +1,251 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DASM_CACHE_H
#define DASM_CACHE_H
////////////////////////////////
//~ rjf: Stringification Types
typedef U32 DASM_StyleFlags;
enum
{
DASM_StyleFlag_Addresses = (1<<0),
DASM_StyleFlag_CodeBytes = (1<<1),
DASM_StyleFlag_SourceFilesNames = (1<<2),
DASM_StyleFlag_SourceLines = (1<<3),
DASM_StyleFlag_SymbolNames = (1<<4),
};
typedef enum DASM_Syntax
{
DASM_Syntax_Intel,
DASM_Syntax_ATT,
DASM_Syntax_COUNT
}
DASM_Syntax;
////////////////////////////////
//~ rjf: Disassembling Parameters Bundle
typedef struct DASM_Params DASM_Params;
struct DASM_Params
{
U64 vaddr;
Architecture arch;
DASM_StyleFlags style_flags;
DASM_Syntax syntax;
U64 base_vaddr;
DI_Key dbgi_key;
};
////////////////////////////////
//~ rjf: Instruction Types
typedef struct DASM_Inst DASM_Inst;
struct DASM_Inst
{
U64 code_off;
U64 addr;
Rng1U64 text_range;
};
typedef struct DASM_InstChunkNode DASM_InstChunkNode;
struct DASM_InstChunkNode
{
DASM_InstChunkNode *next;
DASM_Inst *v;
U64 cap;
U64 count;
};
typedef struct DASM_InstChunkList DASM_InstChunkList;
struct DASM_InstChunkList
{
DASM_InstChunkNode *first;
DASM_InstChunkNode *last;
U64 node_count;
U64 inst_count;
};
typedef struct DASM_InstArray DASM_InstArray;
struct DASM_InstArray
{
DASM_Inst *v;
U64 count;
};
////////////////////////////////
//~ rjf: Value Bundle Type
typedef struct DASM_Info DASM_Info;
struct DASM_Info
{
U128 text_key;
DASM_InstArray insts;
};
////////////////////////////////
//~ rjf: Cache Types
typedef struct DASM_Node DASM_Node;
struct DASM_Node
{
// rjf: links
DASM_Node *next;
DASM_Node *prev;
// rjf: key
U128 hash;
DASM_Params params;
// rjf: generations
U64 change_gen;
// rjf: value
Arena *info_arena;
DASM_Info info;
// rjf: metadata
B32 is_working;
U64 scope_ref_count;
U64 last_time_touched_us;
U64 last_user_clock_idx_touched;
U64 load_count;
U64 last_time_requested_us;
U64 last_user_clock_idx_requested;
};
typedef struct DASM_Slot DASM_Slot;
struct DASM_Slot
{
DASM_Node *first;
DASM_Node *last;
};
typedef struct DASM_Stripe DASM_Stripe;
struct DASM_Stripe
{
Arena *arena;
OS_Handle rw_mutex;
OS_Handle cv;
DASM_Node *free_node;
};
////////////////////////////////
//~ rjf: Scoped Access Types
typedef struct DASM_Touch DASM_Touch;
struct DASM_Touch
{
DASM_Touch *next;
U128 hash;
DASM_Params params;
};
typedef struct DASM_Scope DASM_Scope;
struct DASM_Scope
{
DASM_Scope *next;
DASM_Touch *top_touch;
U64 base_pos;
};
////////////////////////////////
//~ rjf: Thread Context
typedef struct DASM_TCTX DASM_TCTX;
struct DASM_TCTX
{
Arena *arena;
};
////////////////////////////////
//~ rjf: Shared State
typedef struct DASM_Shared DASM_Shared;
struct DASM_Shared
{
Arena *arena;
// rjf: user clock
U64 user_clock_idx;
// rjf: cache
U64 slots_count;
U64 stripes_count;
DASM_Slot *slots;
DASM_Stripe *stripes;
// rjf: user -> parse thread
U64 u2p_ring_size;
U8 *u2p_ring_base;
U64 u2p_ring_write_pos;
U64 u2p_ring_read_pos;
OS_Handle u2p_ring_cv;
OS_Handle u2p_ring_mutex;
// rjf: parse threads
U64 parse_thread_count;
OS_Handle *parse_threads;
// rjf: evictor/detector thread
OS_Handle evictor_detector_thread;
};
////////////////////////////////
//~ rjf: Globals
thread_static DASM_TCTX *dasm_tctx = 0;
global DASM_Shared *dasm_shared = 0;
////////////////////////////////
//~ rjf: Parameter Type Functions
internal B32 dasm_params_match(DASM_Params *a, DASM_Params *b);
////////////////////////////////
//~ rjf: Instruction Type Functions
internal void dasm_inst_chunk_list_push(Arena *arena, DASM_InstChunkList *list, U64 cap, DASM_Inst *inst);
internal DASM_InstArray dasm_inst_array_from_chunk_list(Arena *arena, DASM_InstChunkList *list);
internal U64 dasm_inst_array_idx_from_code_off__linear_scan(DASM_InstArray *array, U64 off);
internal U64 dasm_inst_array_code_off_from_idx(DASM_InstArray *array, U64 idx);
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void dasm_init(void);
////////////////////////////////
//~ rjf: User Clock
internal void dasm_user_clock_tick(void);
internal U64 dasm_user_clock_idx(void);
////////////////////////////////
//~ rjf: Scoped Access
internal DASM_Scope *dasm_scope_open(void);
internal void dasm_scope_close(DASM_Scope *scope);
internal void dasm_scope_touch_node__stripe_r_guarded(DASM_Scope *scope, DASM_Node *node);
////////////////////////////////
//~ rjf: Cache Lookups
internal DASM_Info dasm_info_from_hash_params(DASM_Scope *scope, U128 hash, DASM_Params *params);
internal DASM_Info dasm_info_from_key_params(DASM_Scope *scope, U128 key, DASM_Params *params, U128 *hash_out);
////////////////////////////////
//~ rjf: Parse Threads
internal B32 dasm_u2p_enqueue_req(U128 hash, DASM_Params *params, U64 endt_us);
internal void dasm_u2p_dequeue_req(Arena *arena, U128 *hash_out, DASM_Params *params_out);
internal void dasm_parse_thread__entry_point(void *p);
////////////////////////////////
//~ rjf: Evictor/Detector Thread
internal void dasm_evictor_detector_thread__entry_point(void *p);
#endif // DASM_CACHE_H
-90
View File
@@ -1,90 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Built-In Command Tables
@table(name ui_vis ipc_docs_vis text_pt_vis text_rng_vis q_expr q_slot q_view q_ent_kind q_ctrl_ent_kind q_allow_files q_allow_folders q_keep_oi q_select_oi q_is_code q_floating q_required canonical_icon string display_name desc search_tags ctx_filter)
// / | | | | | | \___ _________________________________/ | | | | | |
// / | | | | | | \ / | | | | | |
D_CmdTable: // | | | | | | | | | | | | | |
{
//- rjf: low-level target control operations
{LaunchAndRun 1 1 0 0 "query:targets" Cfg null Target Null 0 0 0 0 0 1 1 Play "launch_and_run" "Launch and Run" "Starts debugging a new instance of a target, then runs." "launch,start,run,target" "" }
{LaunchAndStepInto 1 1 0 0 "query:targets" Cfg null Target Null 0 0 0 0 0 1 1 PlayStepForward "launch_and_step_into" "Launch and Step Into" "Starts debugging a new instance of a target, then stops at the program's entry point." "launch,start,entry,point" "" }
{Kill 1 1 0 0 "query:processes" Process null Nil Process 0 0 0 0 0 1 1 X "kill" "Kill" "Kills the specified existing attached process(es)." "stop,kill" "" }
{KillAll 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Stop "kill_all" "Kill All" "Kills all attached processes." "stop,kill,all" "" }
{Detach 1 1 0 0 "query:processes" Process null Nil Process 0 0 0 0 0 1 1 Null "detach" "Detach" "Detaches the specified attached process(es)." "detach" "" }
{Continue 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Play "continue" "Continue" "Continues executing all attached processes." "" "" }
{StepIntoInst 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepInto "step_into_inst" "Step Into (Assembly)" "Performs a step that goes into calls, at the instruction level." "single,step,thread" "" }
{StepOverInst 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepOver "step_over_inst" "Step Over (Assembly)" "Performs a step that skips calls, at the instruction level." "single,step,thread" "" }
{StepIntoLine 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepInto "step_into_line" "Step Into (Line)" "Performs a step that goes into calls, at the source code line level." "step,thread" "" }
{StepOverLine 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepOver "step_over_line" "Step Over (Line)" "Performs a step that skips calls, at the source code line level." "step,thread" "" }
{StepOut 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepOut "step_out" "Step Out" "Runs to the end of the current function and exits it." "" "" }
{Halt 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Pause "halt" "Halt" "Halts all attached processes." "pause" "" }
{SoftHaltRefresh 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Refresh "soft_halt_refresh" "Soft Halt Refresh" "Interrupts all attached processes to collect data, and then resumes them." "" "" }
{SetThreadIP 0 1 0 0 "" Vaddr null Nil Null 0 0 0 0 1 1 1 Null "set_thread_ip" "Set Thread IP" "Sets the specified thread's instruction pointer at the specified address." "" "" }
//- rjf: high-level composite target control operations
{RunToLine 1 1 1 0 "" Null null Nil Null 0 0 0 0 0 0 0 Play "run_to_line" "Run To Line" "Runs until a particular source line is hit." "" "$text_pt," }
{Run 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Play "run" "Run" "Runs all targets after starting them if they have not been started yet." "play" "" }
{Restart 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Redo "restart" "Restart" "Kills all attached processes, then launches all active targets." "restart,retry" "" }
{StepInto 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepInto "step_into" "Step Into" "Steps once, possibly into function calls, for either source lines or instructions (whichever is selected)." "" "" }
{StepOver 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 StepOver "step_over" "Step Over" "Steps once, always over function calls, for either source lines or instructions." "" "" }
//- rjf: debug control context management operations
{FreezeThread 1 1 0 0 "query:threads" Thread null Nil Thread 0 0 0 0 0 1 1 Locked "freeze_thread" "Freeze Thread" "Freezes the passed thread." "callstack,unwind" "" }
{ThawThread 1 1 0 0 "query:threads" Thread null Nil Thread 0 0 0 0 0 1 1 Unlocked "thaw_thread" "Thaw Thread" "Thaws the passed thread." "" "" }
{FreezeProcess 1 1 0 0 "query:processes" Process null Nil Process 0 0 0 0 0 1 1 Locked "freeze_process" "Freeze Process" "Freezes the passed process." "" "" }
{ThawProcess 1 1 0 0 "query:processes" Process null Nil Process 0 0 0 0 0 1 1 Unlocked "thaw_process" "Thaw Process" "Thaws the passed process." "" "" }
{FreezeMachine 0 1 0 0 "query:machines" Machine null Nil Machine 0 0 0 0 0 1 1 Locked "freeze_machine" "Freeze Machine" "Freezes the passed machine." "" "" }
{ThawMachine 0 1 0 0 "query:machines" Machine null Nil Machine 0 0 0 0 0 1 1 Unlocked "thaw_machine" "Thaw Machine" "Thaws the passed machine." "" "" }
{FreezeLocalMachine 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Machine "freeze_local_machine" "Freeze Local Machine" "Freezes the local machine." "" "" }
{ThawLocalMachine 1 1 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Machine "thaw_local_machine" "Thaw Local Machine" "Thaws the local machine." "" "" }
{FreezeEntity 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Unlocked "freeze_entity" "Freeze Entity" "Freezes an entity." "" "" }
{ThawEntity 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Locked "thaw_entity" "Thaw Entity" "Thaws an entity." "" "" }
//- rjf: entity decoration
{SetEntityColor 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Null "set_entity_color" "Set Entity Color" "Sets the passed entity's color." "" "" }
{SetEntityName 0 0 0 0 "" Null null Nil Null 0 0 0 0 0 0 0 Null "set_entity_name" "Set Entity Name" "Sets the passed entity's name." "" "" }
//- rjf: attaching
{Attach 1 1 0 0 "query:unattached_processes" PID null Nil Null 0 0 0 0 0 1 1 Null "attach" "Attach" "Attaches to a process that is already running on the local machine." "" "" }
}
@enum D_CmdKind:
{
Null,
@expand(D_CmdTable, a) `$(a.name)`,
COUNT,
}
////////////////////////////////
//~ rjf: Developer Toggles
@table(name)
D_DevToggleTable:
{
{always_refresh}
{simulate_lag}
{draw_ui_text_pos}
{draw_ui_focus_debug}
{draw_ui_box_heatmap}
{eval_compiler_tooltips}
{eval_watch_key_tooltips}
{cmd_context_tooltips}
{updating_indicator}
}
@gen
{
@expand(D_DevToggleTable a) `global B32 DEV_$(a.name) = 0;`
}
@gen
{
`struct {B32 *value_ptr; String8 name;} DEV_toggle_table[] =`;
`{`;
@expand(D_DevToggleTable a) `{&DEV_$(a.name), str8_lit_comp("$(a.name)")},`
`};`;
}
File diff suppressed because it is too large Load Diff
-438
View File
@@ -1,438 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DBG_ENGINE_CORE_H
#define DBG_ENGINE_CORE_H
////////////////////////////////
//~ rjf: Tick Input Types
typedef struct D_Target D_Target;
struct D_Target
{
String8 exe;
String8 args;
String8 working_directory;
String8 custom_entry_point_name;
String8 stdout_path;
String8 stderr_path;
String8 stdin_path;
B32 debug_subprocesses;
String8List env;
};
typedef struct D_TargetArray D_TargetArray;
struct D_TargetArray
{
D_Target *v;
U64 count;
};
typedef U32 D_BreakpointFlags;
enum
{
D_BreakpointFlag_BreakOnWrite = (1<<0),
D_BreakpointFlag_BreakOnRead = (1<<1),
D_BreakpointFlag_BreakOnExecute = (1<<2),
};
typedef struct D_Breakpoint D_Breakpoint;
struct D_Breakpoint
{
D_BreakpointFlags flags;
U64 id;
String8 file_path;
TxtPt pt;
String8 vaddr_expr;
String8 condition;
U64 size;
};
typedef struct D_BreakpointArray D_BreakpointArray;
struct D_BreakpointArray
{
D_Breakpoint *v;
U64 count;
};
typedef struct D_PathMap D_PathMap;
struct D_PathMap
{
String8 src;
String8 dst;
};
typedef struct D_PathMapArray D_PathMapArray;
struct D_PathMapArray
{
D_PathMap *v;
U64 count;
};
////////////////////////////////
//~ rjf: Trap Nets
typedef struct D_TrapNet D_TrapNet;
struct D_TrapNet
{
CTRL_TrapList traps;
B32 good_line_info;
B32 good_read;
};
////////////////////////////////
//~ rjf: Tick Output Types
typedef enum D_EventKind
{
D_EventKind_Null,
D_EventKind_ModuleLoad,
D_EventKind_ProcessEnd,
D_EventKind_Stop,
D_EventKind_COUNT
}
D_EventKind;
typedef enum D_EventCause
{
D_EventCause_Null,
D_EventCause_UserBreakpoint,
D_EventCause_Halt,
D_EventCause_SoftHalt,
D_EventCause_COUNT
}
D_EventCause;
typedef struct D_Event D_Event;
struct D_Event
{
D_EventKind kind;
D_EventCause cause;
CTRL_Handle module;
CTRL_Handle thread;
U64 vaddr;
U64 code;
U64 id;
};
typedef struct D_EventNode D_EventNode;
struct D_EventNode
{
D_EventNode *next;
D_Event v;
};
typedef struct D_EventList D_EventList;
struct D_EventList
{
D_EventNode *first;
D_EventNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Line Info Types
typedef struct D_Line D_Line;
struct D_Line
{
String8 file_path;
TxtPt pt;
Rng1U64 voff_range;
DI_Key dbgi_key;
};
typedef struct D_LineNode D_LineNode;
struct D_LineNode
{
D_LineNode *next;
D_Line v;
};
typedef struct D_LineList D_LineList;
struct D_LineList
{
D_LineNode *first;
D_LineNode *last;
U64 count;
};
typedef struct D_LineListArray D_LineListArray;
struct D_LineListArray
{
D_LineList *v;
U64 count;
DI_KeyList dbgi_keys;
};
////////////////////////////////
//~ rjf: Debug Engine Control Communication Types
typedef enum D_RunKind
{
D_RunKind_Run,
D_RunKind_SingleStep,
D_RunKind_Step,
D_RunKind_COUNT
}
D_RunKind;
////////////////////////////////
//~ rjf: Generated Code
#include "dbg_engine/generated/dbg_engine.meta.h"
////////////////////////////////
//~ rjf: Command Types
typedef struct D_CmdParams D_CmdParams;
struct D_CmdParams
{
CTRL_Handle machine;
CTRL_Handle process;
CTRL_Handle thread;
CTRL_Handle entity;
String8 string;
String8 file_path;
TxtPt cursor;
U64 vaddr;
B32 prefer_disasm;
U32 pid;
U32 rgba;
D_TargetArray targets;
U64 retry_idx;
};
typedef struct D_Cmd D_Cmd;
struct D_Cmd
{
D_CmdKind kind;
D_CmdParams params;
};
typedef struct D_CmdNode D_CmdNode;
struct D_CmdNode
{
D_CmdNode *next;
D_CmdNode *prev;
D_Cmd cmd;
};
typedef struct D_CmdList D_CmdList;
struct D_CmdList
{
D_CmdNode *first;
D_CmdNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Main State Caches
//- rjf: per-run tls-base-vaddr cache
typedef struct D_RunTLSBaseCacheNode D_RunTLSBaseCacheNode;
struct D_RunTLSBaseCacheNode
{
D_RunTLSBaseCacheNode *hash_next;
CTRL_Handle process;
U64 root_vaddr;
U64 rip_vaddr;
U64 tls_base_vaddr;
};
typedef struct D_RunTLSBaseCacheSlot D_RunTLSBaseCacheSlot;
struct D_RunTLSBaseCacheSlot
{
D_RunTLSBaseCacheNode *first;
D_RunTLSBaseCacheNode *last;
};
typedef struct D_RunTLSBaseCache D_RunTLSBaseCache;
struct D_RunTLSBaseCache
{
Arena *arena;
U64 slots_count;
D_RunTLSBaseCacheSlot *slots;
};
//- rjf: per-run locals cache
typedef struct D_RunLocalsCacheNode D_RunLocalsCacheNode;
struct D_RunLocalsCacheNode
{
D_RunLocalsCacheNode *hash_next;
DI_Key dbgi_key;
U64 voff;
E_String2NumMap *locals_map;
};
typedef struct D_RunLocalsCacheSlot D_RunLocalsCacheSlot;
struct D_RunLocalsCacheSlot
{
D_RunLocalsCacheNode *first;
D_RunLocalsCacheNode *last;
};
typedef struct D_RunLocalsCache D_RunLocalsCache;
struct D_RunLocalsCache
{
Arena *arena;
U64 table_size;
D_RunLocalsCacheSlot *table;
};
////////////////////////////////
//~ rjf: Main State Types
typedef struct D_State D_State;
struct D_State
{
// rjf: top-level state
Arena *arena;
U64 frame_index;
// rjf: commands
Arena *cmds_arena;
D_CmdList cmds;
// rjf: output log key
C_Key output_log_key;
// rjf: per-run caches
U64 tls_base_cache_reggen_idx;
U64 tls_base_cache_memgen_idx;
D_RunTLSBaseCache tls_base_caches[2];
U64 tls_base_cache_gen;
U64 locals_cache_reggen_idx;
D_RunLocalsCache locals_caches[2];
U64 locals_cache_gen;
U64 member_cache_reggen_idx;
D_RunLocalsCache member_caches[2];
U64 member_cache_gen;
// rjf: user -> ctrl driving state
Arena *ctrl_last_run_arena;
D_RunKind ctrl_last_run_kind;
U64 ctrl_last_run_frame_idx;
CTRL_Handle ctrl_last_run_thread_handle;
CTRL_RunFlags ctrl_last_run_flags;
CTRL_TrapList ctrl_last_run_traps;
D_BreakpointArray ctrl_last_run_extra_bps;
U128 ctrl_last_run_param_state_hash;
B32 ctrl_is_running;
B32 ctrl_thread_run_state;
B32 ctrl_soft_halt_issued;
Arena *ctrl_msg_arena;
CTRL_MsgList ctrl_msgs;
// rjf: ctrl -> user reading state
CTRL_EntityCtxRWStore *ctrl_entity_store;
Arena *ctrl_stop_arena;
CTRL_Event ctrl_last_stop_event;
};
////////////////////////////////
//~ rjf: Globals
global D_State *d_state = 0;
////////////////////////////////
//~ rjf: Basic Helpers
internal U64 d_hash_from_seed_string(U64 seed, String8 string);
internal U64 d_hash_from_string(String8 string);
internal U64 d_hash_from_seed_string__case_insensitive(U64 seed, String8 string);
internal U64 d_hash_from_string__case_insensitive(String8 string);
////////////////////////////////
//~ rjf: Breakpoints
internal D_BreakpointArray d_breakpoint_array_copy(Arena *arena, D_BreakpointArray *src);
////////////////////////////////
//~ rjf: Path Map Application
internal String8List d_possible_path_overrides_from_maps_path(Arena *arena, D_PathMapArray *path_maps, String8 file_path);
////////////////////////////////
//~ rjf: Debug Info Extraction Type Pure Functions
internal D_LineList d_line_list_copy(Arena *arena, D_LineList *list);
////////////////////////////////
//~ rjf: Command Type Functions
//- rjf: command parameters
internal D_CmdParams d_cmd_params_copy(Arena *arena, D_CmdParams *src);
//- rjf: command lists
internal void d_cmd_list_push_new(Arena *arena, D_CmdList *cmds, D_CmdKind kind, D_CmdParams *params);
////////////////////////////////
//~ rjf: Stepping "Trap Net" Builders
internal D_TrapNet d_trap_net_from_thread__step_over_inst(Arena *arena, CTRL_Entity *thread);
internal D_TrapNet d_trap_net_from_thread__step_over_line(Arena *arena, CTRL_Entity *thread);
internal D_TrapNet d_trap_net_from_thread__step_into_line(Arena *arena, CTRL_Entity *thread);
////////////////////////////////
//~ rjf: Debug Info Lookups
//- rjf: voff -> line info
internal D_LineList d_lines_from_dbgi_key_voff(Arena *arena, DI_Key dbgi_key, U64 voff);
//- rjf: file:line -> line info
// TODO(rjf): this depends on file path maps, needs to move
// TODO(rjf): need to clean this up & dedup
internal D_LineListArray d_lines_array_from_dbgi_key_file_path_line_range(Arena *arena, DI_Key dbgi_key, String8 file_path, Rng1S64 line_num_range);
internal D_LineListArray d_lines_array_from_file_path_line_range(Arena *arena, String8 file_path, Rng1S64 line_num_range);
internal D_LineList d_lines_from_dbgi_key_file_path_line_num(Arena *arena, DI_Key dbgi_key, String8 file_path, S64 line_num);
internal D_LineList d_lines_from_file_path_line_num(Arena *arena, String8 file_path, S64 line_num);
////////////////////////////////
//~ rjf: Process/Thread/Module Info Lookups
internal U64 d_tls_base_vaddr_from_process_root_rip(CTRL_Entity *process, U64 root_vaddr, U64 rip_vaddr);
////////////////////////////////
//~ rjf: Target Controls
//- rjf: stopped info from the control thread
internal CTRL_Event d_ctrl_last_stop_event(void);
////////////////////////////////
//~ rjf: Main State Accessors/Mutators
//- rjf: frame data
internal U64 d_frame_index(void);
//- rjf: control state
internal D_RunKind d_ctrl_last_run_kind(void);
internal U64 d_ctrl_last_run_frame_idx(void);
internal B32 d_ctrl_targets_running(void);
//- rjf: active entity based queries
internal DI_KeyList d_push_active_dbgi_key_list(Arena *arena);
//- rjf: per-run caches
internal U64 d_query_cached_rip_from_thread(CTRL_Entity *thread);
internal U64 d_query_cached_rip_from_thread_unwind(CTRL_Entity *thread, U64 unwind_count);
internal U64 d_query_cached_tls_base_vaddr_from_process_root_rip(CTRL_Entity *process, U64 root_vaddr, U64 rip_vaddr);
internal E_String2NumMap *d_query_cached_locals_map_from_dbgi_key_voff(DI_Key dbgi_key, U64 voff);
internal E_String2NumMap *d_query_cached_member_map_from_dbgi_key_voff(DI_Key dbgi_key, U64 voff);
//- rjf: top-level command dispatch
internal void d_push_cmd(D_CmdKind kind, D_CmdParams *params);
#define d_cmd(kind, ...) d_push_cmd((kind), &(D_CmdParams){.thread = {0}, __VA_ARGS__})
//- rjf: command iteration
internal B32 d_next_cmd(D_Cmd **cmd);
////////////////////////////////
//~ rjf: Main Layer Top-Level Calls
internal void d_init(void);
internal D_EventList d_tick(Arena *arena, D_TargetArray *targets, D_BreakpointArray *breakpoints, D_PathMapArray *path_maps, U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64]);
#endif // DBG_ENGINE_CORE_H
-4
View File
@@ -1,4 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "dbg_engine_core.c"
-9
View File
@@ -1,9 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DBG_ENGINE_INC_H
#define DBG_ENGINE_INC_H
#include "dbg_engine_core.h"
#endif // DBG_ENGINE_INC_H
@@ -1,5 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
//- GENERATED CODE
@@ -1,68 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
//- GENERATED CODE
#ifndef DBG_ENGINE_META_H
#define DBG_ENGINE_META_H
typedef enum D_CmdKind
{
D_CmdKind_Null,
D_CmdKind_LaunchAndRun,
D_CmdKind_LaunchAndStepInto,
D_CmdKind_Kill,
D_CmdKind_KillAll,
D_CmdKind_Detach,
D_CmdKind_Continue,
D_CmdKind_StepIntoInst,
D_CmdKind_StepOverInst,
D_CmdKind_StepIntoLine,
D_CmdKind_StepOverLine,
D_CmdKind_StepOut,
D_CmdKind_Halt,
D_CmdKind_SoftHaltRefresh,
D_CmdKind_SetThreadIP,
D_CmdKind_RunToLine,
D_CmdKind_Run,
D_CmdKind_Restart,
D_CmdKind_StepInto,
D_CmdKind_StepOver,
D_CmdKind_FreezeThread,
D_CmdKind_ThawThread,
D_CmdKind_FreezeProcess,
D_CmdKind_ThawProcess,
D_CmdKind_FreezeMachine,
D_CmdKind_ThawMachine,
D_CmdKind_FreezeLocalMachine,
D_CmdKind_ThawLocalMachine,
D_CmdKind_FreezeEntity,
D_CmdKind_ThawEntity,
D_CmdKind_SetEntityColor,
D_CmdKind_SetEntityName,
D_CmdKind_Attach,
D_CmdKind_COUNT,
} D_CmdKind;
global B32 DEV_always_refresh = 0;
global B32 DEV_simulate_lag = 0;
global B32 DEV_draw_ui_text_pos = 0;
global B32 DEV_draw_ui_focus_debug = 0;
global B32 DEV_draw_ui_box_heatmap = 0;
global B32 DEV_eval_compiler_tooltips = 0;
global B32 DEV_eval_watch_key_tooltips = 0;
global B32 DEV_cmd_context_tooltips = 0;
global B32 DEV_updating_indicator = 0;
struct {B32 *value_ptr; String8 name;} DEV_toggle_table[] =
{
{&DEV_always_refresh, str8_lit_comp("always_refresh")},
{&DEV_simulate_lag, str8_lit_comp("simulate_lag")},
{&DEV_draw_ui_text_pos, str8_lit_comp("draw_ui_text_pos")},
{&DEV_draw_ui_focus_debug, str8_lit_comp("draw_ui_focus_debug")},
{&DEV_draw_ui_box_heatmap, str8_lit_comp("draw_ui_box_heatmap")},
{&DEV_eval_compiler_tooltips, str8_lit_comp("eval_compiler_tooltips")},
{&DEV_eval_watch_key_tooltips, str8_lit_comp("eval_watch_key_tooltips")},
{&DEV_cmd_context_tooltips, str8_lit_comp("cmd_context_tooltips")},
{&DEV_updating_indicator, str8_lit_comp("updating_indicator")},
};
#endif // DBG_ENGINE_META_H
File diff suppressed because it is too large Load Diff
-365
View File
@@ -1,365 +0,0 @@
// Copyright (c) Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DBG_INFO_H
#define DBG_INFO_H
////////////////////////////////
//~ rjf: Unique Debug Info Key
typedef struct DI_Key DI_Key;
struct DI_Key
{
U64 u64[2];
};
typedef struct DI_KeyNode DI_KeyNode;
struct DI_KeyNode
{
DI_KeyNode *next;
DI_Key v;
};
typedef struct DI_KeyList DI_KeyList;
struct DI_KeyList
{
DI_KeyNode *first;
DI_KeyNode *last;
U64 count;
};
typedef struct DI_KeyArray DI_KeyArray;
struct DI_KeyArray
{
DI_Key *v;
U64 count;
};
////////////////////////////////
//~ rjf: Debug Info Path / Timestamp => Key Cache Types
typedef struct DI_KeyPathNode DI_KeyPathNode;
struct DI_KeyPathNode
{
DI_KeyPathNode *next;
DI_KeyPathNode *prev;
String8 path;
U64 min_timestamp;
DI_Key key;
};
typedef struct DI_KeySlot DI_KeySlot;
struct DI_KeySlot
{
DI_KeyPathNode *first;
DI_KeyPathNode *last;
};
////////////////////////////////
//~ rjf: Debug Info Cache Types
typedef struct DI_Node DI_Node;
struct DI_Node
{
// rjf: links
DI_Node *next;
DI_Node *prev;
// rjf: key
DI_Key key;
// rjf: value
OS_Handle file;
OS_Handle file_map;
void *file_base;
FileProperties file_props;
Arena *arena;
RDI_Parsed rdi;
// rjf: metadata
AccessPt access_pt;
U64 refcount;
U64 batch_request_counts[2];
U64 working_count;
U64 completion_count;
};
typedef struct DI_Slot DI_Slot;
struct DI_Slot
{
DI_Node *first;
DI_Node *last;
};
////////////////////////////////
//~ rjf: Requests
typedef struct DI_Request DI_Request;
struct DI_Request
{
DI_Key key;
};
typedef struct DI_RequestNode DI_RequestNode;
struct DI_RequestNode
{
DI_RequestNode *next;
DI_Request v;
};
typedef struct DI_RequestBatch DI_RequestBatch;
struct DI_RequestBatch
{
Mutex mutex;
Arena *arena;
DI_RequestNode *first;
DI_RequestNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Load Tasks
typedef enum DI_LoadTaskStatus
{
DI_LoadTaskStatus_Null,
DI_LoadTaskStatus_Active,
DI_LoadTaskStatus_Done,
}
DI_LoadTaskStatus;
typedef struct DI_LoadTask DI_LoadTask;
struct DI_LoadTask
{
DI_LoadTask *next;
DI_LoadTask *prev;
DI_Key key;
DI_LoadTaskStatus status;
B32 og_analyzed;
B32 og_is_rdi;
U64 og_size;
B32 rdi_analyzed;
B32 rdi_is_stale;
U64 thread_count;
OS_Handle process;
};
typedef struct DI_LoadCompletion DI_LoadCompletion;
struct DI_LoadCompletion
{
DI_LoadCompletion *next;
U64 code;
};
////////////////////////////////
//~ rjf: Search Types
typedef struct DI_SearchItem DI_SearchItem;
struct DI_SearchItem
{
U64 idx;
DI_Key key;
U64 missed_size;
FuzzyMatchRangeList match_ranges;
};
typedef struct DI_SearchItemChunk DI_SearchItemChunk;
struct DI_SearchItemChunk
{
DI_SearchItemChunk *next;
U64 base_idx;
DI_SearchItem *v;
U64 count;
U64 cap;
};
typedef struct DI_SearchItemChunkList DI_SearchItemChunkList;
struct DI_SearchItemChunkList
{
DI_SearchItemChunk *first;
DI_SearchItemChunk *last;
U64 chunk_count;
U64 total_count;
};
typedef struct DI_SearchItemArray DI_SearchItemArray;
struct DI_SearchItemArray
{
DI_SearchItem *v;
U64 count;
};
////////////////////////////////
//~ rjf: Match Types
typedef struct DI_Match DI_Match;
struct DI_Match
{
DI_Key key;
RDI_SectionKind section_kind;
U32 idx;
};
////////////////////////////////
//~ rjf: Events
typedef enum DI_EventKind
{
DI_EventKind_Null,
DI_EventKind_ConversionStarted,
DI_EventKind_ConversionEnded,
DI_EventKind_COUNT
}
DI_EventKind;
typedef struct DI_Event DI_Event;
struct DI_Event
{
DI_EventKind kind;
String8 string;
};
typedef struct DI_EventNode DI_EventNode;
struct DI_EventNode
{
DI_EventNode *next;
DI_Event v;
};
typedef struct DI_EventList DI_EventList;
struct DI_EventList
{
DI_EventNode *first;
DI_EventNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Shared State
typedef struct DI_Shared DI_Shared;
struct DI_Shared
{
Arena *arena;
U64 load_gen;
U64 load_count;
// rjf: key -> path cache
U64 key2path_slots_count;
DI_KeySlot *key2path_slots;
StripeArray key2path_stripes;
// rjf: path -> key cache
U64 path2key_slots_count;
DI_KeySlot *path2key_slots;
StripeArray path2key_stripes;
// rjf: debug info cache
U64 slots_count;
DI_Slot *slots;
StripeArray stripes;
// rjf: requests
DI_RequestBatch req_batches[2]; // [0] -> high priority, [1] -> low priority
// rjf: conversion tasks
DI_LoadTask *first_load_task[2];
DI_LoadTask *last_load_task[2];
DI_LoadTask *free_load_task;
U64 conversion_process_count;
U64 conversion_thread_count;
// rjf: conversion completion receiving thread
U64 conversion_completion_code;
String8 conversion_completion_lock_semaphore_name;
String8 conversion_completion_signal_semaphore_name;
String8 conversion_completion_shared_memory_name;
Semaphore conversion_completion_lock_semaphore;
Semaphore conversion_completion_signal_semaphore;
OS_Handle conversion_completion_shared_memory;
U64 *conversion_completion_shared_memory_base;
Thread conversion_completion_signal_receiver_thread;
// rjf: completion batch
Mutex completion_mutex;
Arena *completion_arena;
DI_LoadCompletion *first_completion;
DI_LoadCompletion *last_completion;
// rjf: events
Mutex event_mutex;
Arena *event_arena;
DI_EventList events;
};
////////////////////////////////
//~ rjf: Globals
global DI_Shared *di_shared = 0;
////////////////////////////////
//~ rjf: Helpers
internal DI_Key di_key_zero(void);
internal B32 di_key_match(DI_Key a, DI_Key b);
internal void di_key_list_push(Arena *arena, DI_KeyList *list, DI_Key key);
internal DI_KeyArray di_key_array_from_list(Arena *arena, DI_KeyList *list);
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void di_init(CmdLine *cmdline);
////////////////////////////////
//~ rjf: Path * Timestamp Cache Submission & Lookup
internal DI_Key di_key_from_path_timestamp(String8 path, U64 min_timestamp);
////////////////////////////////
//~ rjf: Debug Info Opening / Closing
internal void di_open(DI_Key key);
internal void di_close(DI_Key key, B32 force_closed);
////////////////////////////////
//~ rjf: Debug Info Lookups
internal U64 di_load_gen(void);
internal U64 di_load_count(void);
internal DI_KeyArray di_push_all_loaded_keys(Arena *arena);
internal RDI_Parsed *di_rdi_from_key(Access *access, DI_Key key, B32 high_priority, U64 endt_us);
////////////////////////////////
//~ rjf: Events
internal DI_EventList di_get_events(Arena *arena);
////////////////////////////////
//~ rjf: Asynchronous Tick
internal void di_async_tick(void);
////////////////////////////////
//~ rjf: Conversion Completion Signal Receiver Thread
internal void di_signal_completion(void);
internal void di_conversion_completion_signal_receiver_thread_entry_point(void *p);
////////////////////////////////
//~ rjf: Search Artifact Cache Hooks / Lookups
internal AC_Artifact di_search_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out);
internal void di_search_artifact_destroy(AC_Artifact artifact);
internal DI_SearchItemArray di_search_item_array_from_target_query(Access *access, RDI_SectionKind target, String8 query, U64 endt_us, B32 *stale_out);
////////////////////////////////
//~ rjf: Match Artifact Cache Hooks / Lookups
internal AC_Artifact di_match_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out);
internal DI_Match di_match_from_string(String8 string, U64 index, DI_Key preferred_dbgi_key, U64 endt_us);
#endif // DBG_INFO_H
+923
View File
@@ -0,0 +1,923 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Basic Helpers
internal U64
di_hash_from_string(String8 string, StringMatchFlags match_flags)
{
U64 result = 5381;
for(U64 i = 0; i < string.size; i += 1)
{
result = ((result << 5) + result) + ((match_flags & StringMatchFlag_CaseInsensitive) ? char_to_lower(string.str[i]) : string.str[i]);
}
return result;
}
internal U64
di_hash_from_key(DI_Key *k)
{
U64 hash = di_hash_from_string(k->path, StringMatchFlag_CaseInsensitive);
return hash;
}
internal B32
di_key_match(DI_Key *a, DI_Key *b)
{
return (str8_match(a->path, b->path, StringMatchFlag_CaseInsensitive) && a->min_timestamp == b->min_timestamp);
}
internal DI_Key
di_key_copy(Arena *arena, DI_Key *src)
{
DI_Key dst = {0};
MemoryCopyStruct(&dst, src);
dst.path = push_str8_copy(arena, src->path);
return dst;
}
internal DI_Key
di_normalized_key_from_key(Arena *arena, DI_Key *src)
{
DI_Key dst = {path_normalized_from_string(arena, src->path), src->min_timestamp};
return dst;
}
internal void
di_key_list_push(Arena *arena, DI_KeyList *list, DI_Key *key)
{
DI_KeyNode *n = push_array(arena, DI_KeyNode, 1);
MemoryCopyStruct(&n->v, key);
SLLQueuePush(list->first, list->last, n);
list->count += 1;
}
internal DI_KeyArray
di_key_array_from_list(Arena *arena, DI_KeyList *list)
{
DI_KeyArray array = {0};
array.count = list->count;
array.v = push_array_no_zero(arena, DI_Key, array.count);
U64 idx = 0;
for(DI_KeyNode *n = list->first; n != 0; n = n->next, idx += 1)
{
MemoryCopyStruct(&array.v[idx], &n->v);
}
return array;
}
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void
di_init(void)
{
Arena *arena = arena_alloc();
di_shared = push_array(arena, DI_Shared, 1);
di_shared->arena = arena;
di_shared->slots_count = 1024;
di_shared->slots = push_array(arena, DI_Slot, di_shared->slots_count);
di_shared->stripes_count = Min(di_shared->slots_count, os_logical_core_count());
di_shared->stripes = push_array(arena, DI_Stripe, di_shared->stripes_count);
for(U64 idx = 0; idx < di_shared->stripes_count; idx += 1)
{
di_shared->stripes[idx].arena = arena_alloc();
di_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc();
di_shared->stripes[idx].cv = os_condition_variable_alloc();
}
di_shared->u2p_ring_mutex = os_mutex_alloc();
di_shared->u2p_ring_cv = os_condition_variable_alloc();
di_shared->u2p_ring_size = KB(64);
di_shared->u2p_ring_base = push_array_no_zero(arena, U8, di_shared->u2p_ring_size);
di_shared->p2u_ring_mutex = os_mutex_alloc();
di_shared->p2u_ring_cv = os_condition_variable_alloc();
di_shared->p2u_ring_size = KB(64);
di_shared->p2u_ring_base = push_array_no_zero(arena, U8, di_shared->p2u_ring_size);
di_shared->parse_thread_count = Max(2, os_logical_core_count()/2);
di_shared->parse_threads = push_array(arena, OS_Handle, di_shared->parse_thread_count);
for(U64 idx = 0; idx < di_shared->parse_thread_count; idx += 1)
{
di_shared->parse_threads[idx] = os_launch_thread(di_parse_thread__entry_point, (void *)idx, 0);
}
}
////////////////////////////////
//~ rjf: Scope Functions
internal DI_Scope *
di_scope_open(void)
{
if(di_tctx == 0)
{
Arena *arena = arena_alloc();
di_tctx = push_array(arena, DI_TCTX, 1);
di_tctx->arena = arena;
}
DI_Scope *scope = di_tctx->free_scope;
if(scope != 0)
{
SLLStackPop(di_tctx->free_scope);
}
else
{
scope = push_array_no_zero(di_tctx->arena, DI_Scope, 1);
}
MemoryZeroStruct(scope);
return scope;
}
internal void
di_scope_close(DI_Scope *scope)
{
for(DI_Touch *t = scope->first_touch, *next = 0; t != 0; t = next)
{
next = t->next;
SLLStackPush(di_tctx->free_touch, t);
if(t->node != 0)
{
ins_atomic_u64_dec_eval(&t->node->touch_count);
}
}
SLLStackPush(di_tctx->free_scope, scope);
}
internal void
di_scope_touch_node__stripe_mutex_r_guarded(DI_Scope *scope, DI_Node *node)
{
if(node != 0)
{
ins_atomic_u64_inc_eval(&node->touch_count);
}
DI_Touch *touch = di_tctx->free_touch;
if(touch != 0)
{
SLLStackPop(di_tctx->free_touch);
}
else
{
touch = push_array_no_zero(di_tctx->arena, DI_Touch, 1);
}
MemoryZeroStruct(touch);
SLLQueuePush(scope->first_touch, scope->last_touch, touch);
touch->node = node;
}
////////////////////////////////
//~ rjf: Per-Slot Functions
internal DI_Node *
di_node_from_key_slot__stripe_mutex_r_guarded(DI_Slot *slot, DI_Key *key)
{
DI_Node *node = 0;
StringMatchFlags match_flags = path_match_flags_from_os(operating_system_from_context());
U64 most_recent_timestamp = max_U64;
for(DI_Node *n = slot->first; n != 0; n = n->next)
{
if(str8_match(n->key.path, key->path, match_flags) &&
key->min_timestamp <= n->key.min_timestamp &&
(n->key.min_timestamp - key->min_timestamp) <= most_recent_timestamp)
{
node = n;
most_recent_timestamp = (n->key.min_timestamp - key->min_timestamp);
}
}
return node;
}
////////////////////////////////
//~ rjf: Per-Stripe Functions
internal U64
di_string_bucket_idx_from_string_size(U64 size)
{
U64 size_rounded = u64_up_to_pow2(size+1);
size_rounded = ClampBot((1<<4), size_rounded);
U64 bucket_idx = 0;
switch(size_rounded)
{
case 1<<4: {bucket_idx = 0;}break;
case 1<<5: {bucket_idx = 1;}break;
case 1<<6: {bucket_idx = 2;}break;
case 1<<7: {bucket_idx = 3;}break;
case 1<<8: {bucket_idx = 4;}break;
case 1<<9: {bucket_idx = 5;}break;
case 1<<10:{bucket_idx = 6;}break;
default:{bucket_idx = ArrayCount(((DI_Stripe *)0)->free_string_chunks)-1;}break;
}
return bucket_idx;
}
internal String8
di_string_alloc__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string)
{
if(string.size == 0) {return str8_zero();}
U64 bucket_idx = di_string_bucket_idx_from_string_size(string.size);
DI_StringChunkNode *node = stripe->free_string_chunks[bucket_idx];
// rjf: pull from bucket free list
if(node != 0)
{
if(bucket_idx == ArrayCount(stripe->free_string_chunks)-1)
{
node = 0;
DI_StringChunkNode *prev = 0;
for(DI_StringChunkNode *n = stripe->free_string_chunks[bucket_idx];
n != 0;
prev = n, n = n->next)
{
if(n->size >= string.size+1)
{
if(prev == 0)
{
stripe->free_string_chunks[bucket_idx] = n->next;
}
else
{
prev->next = n->next;
}
node = n;
break;
}
}
}
else
{
SLLStackPop(stripe->free_string_chunks[bucket_idx]);
}
}
// rjf: no found node -> allocate new
if(node == 0)
{
U64 chunk_size = 0;
if(bucket_idx < ArrayCount(stripe->free_string_chunks)-1)
{
chunk_size = 1<<(bucket_idx+4);
}
else
{
chunk_size = u64_up_to_pow2(string.size);
}
U8 *chunk_memory = push_array(stripe->arena, U8, chunk_size);
node = (DI_StringChunkNode *)chunk_memory;
}
// rjf: fill string & return
String8 allocated_string = str8((U8 *)node, string.size);
MemoryCopy((U8 *)node, string.str, string.size);
return allocated_string;
}
internal void
di_string_release__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string)
{
if(string.size == 0) {return;}
U64 bucket_idx = di_string_bucket_idx_from_string_size(string.size);
DI_StringChunkNode *node = (DI_StringChunkNode *)string.str;
node->size = u64_up_to_pow2(string.size);
SLLStackPush(stripe->free_string_chunks[bucket_idx], node);
}
////////////////////////////////
//~ rjf: Key Opening/Closing
internal void
di_open(DI_Key *key)
{
Temp scratch = scratch_begin(0, 0);
if(key->path.size != 0)
{
DI_Key key_normalized = di_normalized_key_from_key(scratch.arena, key);
U64 hash = di_hash_from_key(&key_normalized);
U64 slot_idx = hash%di_shared->slots_count;
U64 stripe_idx = slot_idx%di_shared->stripes_count;
DI_Slot *slot = &di_shared->slots[slot_idx];
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
log_infof("opening debug info: %S [0x%I64x]\n", key_normalized.path, key_normalized.min_timestamp);
OS_MutexScopeW(stripe->rw_mutex)
{
//- rjf: find existing node
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key_normalized);
//- rjf: allocate node if none exists; insert into slot
if(node == 0)
{
U64 current_timestamp = os_properties_from_file_path(key_normalized.path).modified;
if(current_timestamp == 0)
{
current_timestamp = key_normalized.min_timestamp;
}
node = stripe->free_node;
if(node != 0)
{
SLLStackPop(stripe->free_node);
}
else
{
node = push_array_no_zero(stripe->arena, DI_Node, 1);
}
MemoryZeroStruct(node);
DLLPushBack(slot->first, slot->last, node);
String8 path_stored = di_string_alloc__stripe_mutex_w_guarded(stripe, key_normalized.path);
node->key.path = path_stored;
node->key.min_timestamp = current_timestamp;
}
//- rjf: increment node reference count
if(node != 0)
{
node->ref_count += 1;
if(node->ref_count == 1)
{
di_u2p_enqueue_key(&key_normalized, max_U64);
}
}
}
}
scratch_end(scratch);
}
internal void
di_close(DI_Key *key)
{
Temp scratch = scratch_begin(0, 0);
if(key->path.size != 0)
{
DI_Key key_normalized = di_normalized_key_from_key(scratch.arena, key);
U64 hash = di_hash_from_key(&key_normalized);
U64 slot_idx = hash%di_shared->slots_count;
U64 stripe_idx = slot_idx%di_shared->stripes_count;
DI_Slot *slot = &di_shared->slots[slot_idx];
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
log_infof("closing debug info: %S [0x%I64x]\n", key_normalized.path, key_normalized.min_timestamp);
OS_MutexScopeW(stripe->rw_mutex)
{
//- rjf: find existing node
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key_normalized);
//- rjf: node exists -> decrement reference count; release
if(node != 0)
{
node->ref_count -= 1;
if(node->ref_count == 0) for(;;)
{
//- rjf: wait for touch count to go to 0
if(ins_atomic_u64_eval(&node->touch_count) != 0)
{
os_rw_mutex_drop_w(stripe->rw_mutex);
for(U64 start_t = os_now_microseconds(); os_now_microseconds() <= start_t + 250;);
os_rw_mutex_take_w(stripe->rw_mutex);
}
//- rjf: release
if(node->ref_count == 0 && ins_atomic_u64_eval(&node->touch_count) == 0)
{
di_string_release__stripe_mutex_w_guarded(stripe, node->key.path);
if(node->file_base != 0)
{
os_file_map_view_close(node->file_map, node->file_base);
}
if(!os_handle_match(node->file_map, os_handle_zero()))
{
os_file_map_close(node->file_map);
}
if(!os_handle_match(node->file, os_handle_zero()))
{
os_file_close(node->file);
}
if(node->arena != 0)
{
arena_release(node->arena);
}
DLLRemove(slot->first, slot->last, node);
SLLStackPush(stripe->free_node, node);
break;
}
}
}
}
}
scratch_end(scratch);
}
////////////////////////////////
//~ rjf: Cache Lookups
internal RDI_Parsed *
di_rdi_from_key(DI_Scope *scope, DI_Key *key, U64 endt_us)
{
RDI_Parsed *result = &di_rdi_parsed_nil;
if(key->path.size != 0)
{
Temp scratch = scratch_begin(0, 0);
DI_Key key_normalized = di_normalized_key_from_key(scratch.arena, key);
U64 hash = di_hash_from_key(&key_normalized);
U64 slot_idx = hash%di_shared->slots_count;
U64 stripe_idx = slot_idx%di_shared->stripes_count;
DI_Slot *slot = &di_shared->slots[slot_idx];
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
OS_MutexScopeR(stripe->rw_mutex) for(;;)
{
//- rjf: find existing node
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key_normalized);
//- rjf: no node? this path is not opened
if(node == 0)
{
break;
}
//- rjf: parse done -> touch, grab result
if(node != 0 && node->parse_done)
{
di_scope_touch_node__stripe_mutex_r_guarded(scope, node);
result = &node->rdi;
break;
}
//- rjf: parse not done, not working, asked a while ago -> ask for parse
B32 sent = 0;
if(node != 0 && !node->parse_done && !node->is_working && ins_atomic_u64_eval(&node->last_time_requested_us)+1000000<os_now_microseconds())
{
sent = di_u2p_enqueue_key(&key_normalized, endt_us);
if(sent)
{
ins_atomic_u64_eval_assign(&node->last_time_requested_us, os_now_microseconds());
}
}
//- rjf: time expired -> break
if(os_now_microseconds() >= endt_us)
{
break;
}
//- rjf: wait on this stripe
{
os_condition_variable_wait_rw_r(stripe->cv, stripe->rw_mutex, endt_us);
}
}
scratch_end(scratch);
}
return result;
}
////////////////////////////////
//~ rjf: Parse Threads
internal B32
di_u2p_enqueue_key(DI_Key *key, U64 endt_us)
{
B32 sent = 0;
OS_MutexScope(di_shared->u2p_ring_mutex) for(;;)
{
U64 unconsumed_size = di_shared->u2p_ring_write_pos - di_shared->u2p_ring_read_pos;
U64 available_size = di_shared->u2p_ring_size - unconsumed_size;
if(available_size >= sizeof(key->path.size) + key->path.size + sizeof(key->min_timestamp))
{
di_shared->u2p_ring_write_pos += ring_write_struct(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_write_pos, &key->path.size);
di_shared->u2p_ring_write_pos += ring_write(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_write_pos, key->path.str, key->path.size);
di_shared->u2p_ring_write_pos += ring_write_struct(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_write_pos, &key->min_timestamp);
di_shared->u2p_ring_write_pos += 7;
di_shared->u2p_ring_write_pos -= di_shared->u2p_ring_write_pos%8;
sent = 1;
break;
}
if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait(di_shared->u2p_ring_cv, di_shared->u2p_ring_mutex, endt_us);
}
if(sent)
{
os_condition_variable_broadcast(di_shared->u2p_ring_cv);
}
return sent;
}
internal void
di_u2p_dequeue_key(Arena *arena, DI_Key *out_key)
{
OS_MutexScope(di_shared->u2p_ring_mutex) for(;;)
{
U64 unconsumed_size = di_shared->u2p_ring_write_pos - di_shared->u2p_ring_read_pos;
if(unconsumed_size >= sizeof(out_key->path.size) + sizeof(out_key->min_timestamp))
{
di_shared->u2p_ring_read_pos += ring_read_struct(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_read_pos, &out_key->path.size);
out_key->path.str = push_array(arena, U8, out_key->path.size);
di_shared->u2p_ring_read_pos += ring_read(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_read_pos, out_key->path.str, out_key->path.size);
di_shared->u2p_ring_read_pos += ring_read_struct(di_shared->u2p_ring_base, di_shared->u2p_ring_size, di_shared->u2p_ring_read_pos, &out_key->min_timestamp);
di_shared->u2p_ring_read_pos += 7;
di_shared->u2p_ring_read_pos -= di_shared->u2p_ring_read_pos%8;
break;
}
os_condition_variable_wait(di_shared->u2p_ring_cv, di_shared->u2p_ring_mutex, max_U64);
}
os_condition_variable_broadcast(di_shared->u2p_ring_cv);
}
internal void
di_p2u_push_event(DI_Event *event)
{
OS_MutexScope(di_shared->p2u_ring_mutex) for(;;)
{
U64 unconsumed_size = (di_shared->p2u_ring_write_pos-di_shared->p2u_ring_read_pos);
U64 available_size = di_shared->p2u_ring_size-unconsumed_size;
U64 needed_size = sizeof(DI_EventKind) + sizeof(U64) + event->string.size;
if(available_size >= needed_size)
{
di_shared->p2u_ring_write_pos += ring_write_struct(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_write_pos, &event->kind);
di_shared->p2u_ring_write_pos += ring_write_struct(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_write_pos, &event->string.size);
di_shared->p2u_ring_write_pos += ring_write(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_write_pos, event->string.str, event->string.size);
di_shared->p2u_ring_write_pos += 7;
di_shared->p2u_ring_write_pos -= di_shared->p2u_ring_write_pos%8;
break;
}
os_condition_variable_wait(di_shared->p2u_ring_cv, di_shared->p2u_ring_mutex, max_U64);
}
os_condition_variable_broadcast(di_shared->p2u_ring_cv);
}
internal DI_EventList
di_p2u_pop_events(Arena *arena, U64 endt_us)
{
DI_EventList events = {0};
OS_MutexScope(di_shared->p2u_ring_mutex) for(;;)
{
U64 unconsumed_size = (di_shared->p2u_ring_write_pos-di_shared->p2u_ring_read_pos);
if(unconsumed_size >= sizeof(DI_EventKind) + sizeof(U64))
{
DI_EventNode *n = push_array(arena, DI_EventNode, 1);
SLLQueuePush(events.first, events.last, n);
events.count += 1;
di_shared->p2u_ring_read_pos += ring_read_struct(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_read_pos, &n->v.kind);
di_shared->p2u_ring_read_pos += ring_read_struct(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_read_pos, &n->v.string.size);
n->v.string.str = push_array_no_zero(arena, U8, n->v.string.size);
di_shared->p2u_ring_read_pos += ring_read(di_shared->p2u_ring_base, di_shared->p2u_ring_size, di_shared->p2u_ring_read_pos, n->v.string.str, n->v.string.size);
di_shared->p2u_ring_read_pos += 7;
di_shared->p2u_ring_read_pos -= di_shared->p2u_ring_read_pos%8;
}
else if(os_now_microseconds() >= endt_us)
{
break;
}
os_condition_variable_wait(di_shared->p2u_ring_cv, di_shared->p2u_ring_mutex, endt_us);
}
os_condition_variable_broadcast(di_shared->p2u_ring_cv);
return events;
}
internal void
di_parse_thread__entry_point(void *p)
{
ThreadNameF("[di] parse #%I64u", (U64)p);
for(;;)
{
Temp scratch = scratch_begin(0, 0);
////////////////////////////
//- rjf: grab next key
//
DI_Key key = {0};
di_u2p_dequeue_key(scratch.arena, &key);
String8 og_path = key.path;
U64 min_timestamp = key.min_timestamp;
////////////////////////////
//- rjf: unpack key
//
U64 hash = di_hash_from_string(og_path, StringMatchFlag_CaseInsensitive);
U64 slot_idx = hash%di_shared->slots_count;
U64 stripe_idx = slot_idx%di_shared->stripes_count;
DI_Slot *slot = &di_shared->slots[slot_idx];
DI_Stripe *stripe = &di_shared->stripes[stripe_idx];
////////////////////////////
//- rjf: take task
//
B32 got_task = 0;
OS_MutexScopeR(stripe->rw_mutex)
{
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key);
if(node != 0)
{
got_task = !ins_atomic_u64_eval_cond_assign(&node->is_working, 1, 0);
}
}
////////////////////////////
//- rjf: got task -> open O.G. file (may or may not be RDI)
//
B32 og_format_is_known = 0;
B32 og_is_pe = 0;
B32 og_is_pdb = 0;
B32 og_is_elf = 0;
B32 og_is_rdi = 0;
FileProperties og_props = {0};
if(got_task) ProfScope("analyze %.*s", str8_varg(og_path))
{
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, og_path);
OS_Handle file_map = os_file_map_open(OS_AccessFlag_Read, file);
FileProperties props = og_props = os_properties_from_file(file);
void *base = os_file_map_view_open(file_map, OS_AccessFlag_Read, r1u64(0, props.size));
String8 data = str8((U8 *)base, props.size);
if(!og_format_is_known)
{
String8 msf20_magic = str8_lit("Microsoft C/C++ program database 2.00\r\n\x1aJG\0\0");
String8 msf70_magic = str8_lit("Microsoft C/C++ MSF 7.00\r\n\032DS\0\0");
String8 msfxx_magic = str8_lit("Microsoft C/C++");
if((data.size >= msf20_magic.size && str8_match(data, msf20_magic, StringMatchFlag_RightSideSloppy)) ||
(data.size >= msf70_magic.size && str8_match(data, msf70_magic, StringMatchFlag_RightSideSloppy)) ||
(data.size >= msfxx_magic.size && str8_match(data, msfxx_magic, StringMatchFlag_RightSideSloppy)))
{
og_format_is_known = 1;
og_is_pdb = 1;
}
}
if(!og_format_is_known)
{
if(data.size >= 8 && *(U64 *)data.str == RDI_MAGIC_CONSTANT)
{
og_format_is_known = 1;
og_is_rdi = 1;
}
}
if(!og_format_is_known)
{
if(data.size >= 4 &&
data.str[0] == 0x7f &&
data.str[1] == 'E' &&
data.str[2] == 'L' &&
data.str[3] == 'F')
{
og_format_is_known = 1;
og_is_elf = 1;
}
}
if(!og_format_is_known)
{
if(data.size >= 2 && *(U16 *)data.str == 0x5a4d)
{
og_format_is_known = 1;
og_is_pe = 1;
}
}
os_file_map_view_close(file_map, base);
os_file_map_close(file_map);
os_file_close(file);
}
////////////////////////////
//- rjf: given O.G. path & analysis, determine RDI path
//
String8 rdi_path = {0};
if(got_task)
{
if(og_is_rdi)
{
rdi_path = og_path;
}
else if(og_format_is_known && og_is_pdb)
{
rdi_path = push_str8f(scratch.arena, "%S.rdi", str8_chop_last_dot(og_path));
}
}
////////////////////////////
//- rjf: check if rdi file is up-to-date
//
B32 rdi_file_is_up_to_date = 0;
if(got_task)
{
if(rdi_path.size != 0) ProfScope("check %.*s is up-to-date", str8_varg(rdi_path))
{
FileProperties props = os_properties_from_file_path(rdi_path);
rdi_file_is_up_to_date = (props.modified > og_props.modified);
}
}
////////////////////////////
//- rjf: if raddbg file is up to date based on timestamp, check the
// encoding generation number & size, to see if we need to regenerate it
// regardless
//
if(got_task && rdi_file_is_up_to_date) ProfScope("check %.*s version matches our's", str8_varg(rdi_path))
{
OS_Handle file = {0};
OS_Handle file_map = {0};
FileProperties file_props = {0};
void *file_base = 0;
file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, rdi_path);
file_map = os_file_map_open(OS_AccessFlag_Read, file);
file_props = os_properties_from_file(file);
file_base = os_file_map_view_open(file_map, OS_AccessFlag_Read, r1u64(0, file_props.size));
if(sizeof(RDI_Header) <= file_props.size)
{
RDI_Header *header = (RDI_Header*)file_base;
if(header->encoding_version != RDI_ENCODING_VERSION)
{
rdi_file_is_up_to_date = 0;
}
}
else
{
rdi_file_is_up_to_date = 0;
}
os_file_map_view_close(file_map, file_base);
os_file_map_close(file_map);
os_file_close(file);
}
////////////////////////////
//- rjf: heuristically choose compression settings
//
B32 should_compress = 0;
#if 0
if(og_dbg_props.size > MB(64))
{
should_compress = 1;
}
#endif
////////////////////////////
//- rjf: rdi file not up-to-date? we need to generate it
//
if(got_task && !rdi_file_is_up_to_date) ProfScope("generate %.*s", str8_varg(rdi_path))
{
if(og_is_pdb)
{
//- rjf: push conversion task begin event
{
DI_Event event = {DI_EventKind_ConversionStarted};
event.string = rdi_path;
di_p2u_push_event(&event);
}
//- rjf: kick off process
OS_Handle process = {0};
{
OS_LaunchOptions opts = {0};
opts.path = os_string_from_system_path(scratch.arena, OS_SystemPath_Binary);
opts.inherit_env = 1;
opts.consoleless = 1;
str8_list_pushf(scratch.arena, &opts.cmd_line, "raddbg");
str8_list_pushf(scratch.arena, &opts.cmd_line, "--convert");
str8_list_pushf(scratch.arena, &opts.cmd_line, "--quiet");
if(should_compress)
{
str8_list_pushf(scratch.arena, &opts.cmd_line, "--compress");
}
//str8_list_pushf(scratch.arena, &opts.cmd_line, "--capture");
str8_list_pushf(scratch.arena, &opts.cmd_line, "--pdb:%S", og_path);
str8_list_pushf(scratch.arena, &opts.cmd_line, "--out:%S", rdi_path);
os_launch_process(&opts, &process);
}
//- rjf: wait for process to complete
{
U64 start_wait_t = os_now_microseconds();
for(;;)
{
B32 wait_done = os_process_wait(process, os_now_microseconds()+1000);
if(wait_done)
{
rdi_file_is_up_to_date = 1;
break;
}
}
}
//- rjf: push conversion task end event
{
DI_Event event = {DI_EventKind_ConversionEnded};
event.string = rdi_path;
di_p2u_push_event(&event);
}
}
else
{
// NOTE(rjf): we cannot convert from this O.G. debug info format right now.
//- rjf: push conversion task failure event
{
DI_Event event = {DI_EventKind_ConversionFailureUnsupportedFormat};
event.string = rdi_path;
di_p2u_push_event(&event);
}
}
}
////////////////////////////
//- rjf: got task -> open file
//
OS_Handle file = {0};
OS_Handle file_map = {0};
FileProperties file_props = {0};
void *file_base = 0;
if(got_task)
{
file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite, rdi_path);
file_map = os_file_map_open(OS_AccessFlag_Read, file);
file_props = os_properties_from_file(file);
file_base = os_file_map_view_open(file_map, OS_AccessFlag_Read, r1u64(0, file_props.size));
}
////////////////////////////
//- rjf: do initial parse of rdi
//
RDI_Parsed rdi_parsed_maybe_compressed = di_rdi_parsed_nil;
if(got_task)
{
RDI_ParseStatus parse_status = rdi_parse((U8 *)file_base, file_props.size, &rdi_parsed_maybe_compressed);
(void)parse_status;
}
////////////////////////////
//- rjf: decompress & re-parse, if necessary
//
Arena *rdi_parsed_arena = 0;
RDI_Parsed rdi_parsed = rdi_parsed_maybe_compressed;
if(got_task)
{
U64 decompressed_size = file_props.size;
for(U64 dsec_idx = 0; dsec_idx < rdi_parsed_maybe_compressed.dsec_count; dsec_idx += 1)
{
decompressed_size += (rdi_parsed_maybe_compressed.dsecs[dsec_idx].unpacked_size - rdi_parsed_maybe_compressed.dsecs[dsec_idx].encoded_size);
}
if(decompressed_size > file_props.size)
{
rdi_parsed_arena = arena_alloc();
U8 *decompressed_data = push_array_no_zero(rdi_parsed_arena, U8, decompressed_size);
// rjf: copy header
RDI_Header *src_header = (RDI_Header *)file_base;
RDI_Header *dst_header = (RDI_Header *)decompressed_data;
{
MemoryCopy(dst_header, src_header, sizeof(RDI_Header));
}
// rjf: copy & adjust sections for decompressed version
if(rdi_parsed_maybe_compressed.dsec_count != 0)
{
RDI_DataSection *dsec_base = (RDI_DataSection *)(decompressed_data + dst_header->data_section_off);
MemoryCopy(dsec_base, (U8 *)file_base + src_header->data_section_off, sizeof(RDI_DataSection) * rdi_parsed_maybe_compressed.dsec_count);
U64 off = dst_header->data_section_off + sizeof(RDI_DataSection) * rdi_parsed_maybe_compressed.dsec_count;
off += 7;
off -= off%8;
for(U64 idx = 0; idx < rdi_parsed_maybe_compressed.dsec_count; idx += 1)
{
dsec_base[idx].encoding = RDI_DataSectionEncoding_Unpacked;
dsec_base[idx].off = off;
dsec_base[idx].encoded_size = dsec_base[idx].unpacked_size;
off += dsec_base[idx].unpacked_size;
off += 7;
off -= off%8;
}
}
// rjf: decompress sections into new decompressed file buffer
if(rdi_parsed_maybe_compressed.dsec_count != 0)
{
RDI_DataSection *src_first = rdi_parsed_maybe_compressed.dsecs;
RDI_DataSection *dst_first = (RDI_DataSection *)(decompressed_data + dst_header->data_section_off);
RDI_DataSection *src_opl = src_first + rdi_parsed_maybe_compressed.dsec_count;
RDI_DataSection *dst_opl = dst_first + rdi_parsed_maybe_compressed.dsec_count;
for(RDI_DataSection *src = src_first, *dst = dst_first;
src < src_opl && dst < dst_opl;
src += 1, dst += 1)
{
rr_lzb_simple_decode((U8*)file_base + src->off, src->encoded_size,
decompressed_data + dst->off, dst->unpacked_size);
}
}
// rjf: re-parse
RDI_ParseStatus parse_status = rdi_parse(decompressed_data, decompressed_size, &rdi_parsed);
(void)parse_status;
}
}
////////////////////////////
//- rjf: commit parsed info to cache
//
if(got_task) OS_MutexScopeW(stripe->rw_mutex)
{
DI_Node *node = di_node_from_key_slot__stripe_mutex_r_guarded(slot, &key);
if(node != 0)
{
node->is_working = 0;
node->file = file;
node->file_map = file_map;
node->file_base = file_base;
node->file_props = file_props;
node->arena = rdi_parsed_arena;
node->rdi = rdi_parsed;
node->parse_done = 1;
}
}
os_condition_variable_broadcast(stripe->cv);
scratch_end(scratch);
}
}
+288
View File
@@ -0,0 +1,288 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DI_H
#define DI_H
////////////////////////////////
//~ rjf: Cache Key Type
typedef struct DI_Key DI_Key;
struct DI_Key
{
String8 path;
U64 min_timestamp;
};
typedef struct DI_KeyNode DI_KeyNode;
struct DI_KeyNode
{
DI_KeyNode *next;
DI_Key v;
};
typedef struct DI_KeyList DI_KeyList;
struct DI_KeyList
{
DI_KeyNode *first;
DI_KeyNode *last;
U64 count;
};
typedef struct DI_KeyArray DI_KeyArray;
struct DI_KeyArray
{
DI_Key *v;
U64 count;
};
////////////////////////////////
//~ rjf: Event Types
typedef enum DI_EventKind
{
DI_EventKind_Null,
DI_EventKind_ConversionStarted,
DI_EventKind_ConversionEnded,
DI_EventKind_ConversionFailureUnsupportedFormat,
DI_EventKind_COUNT
}
DI_EventKind;
typedef struct DI_Event DI_Event;
struct DI_Event
{
DI_EventKind kind;
String8 string;
};
typedef struct DI_EventNode DI_EventNode;
struct DI_EventNode
{
DI_EventNode *next;
DI_Event v;
};
typedef struct DI_EventList DI_EventList;
struct DI_EventList
{
DI_EventNode *first;
DI_EventNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Cache Types
typedef struct DI_StringChunkNode DI_StringChunkNode;
struct DI_StringChunkNode
{
DI_StringChunkNode *next;
U64 size;
};
typedef struct DI_Node DI_Node;
struct DI_Node
{
// rjf: links
DI_Node *next;
DI_Node *prev;
// rjf: metadata
U64 ref_count;
U64 touch_count;
U64 is_working;
U64 last_time_requested_us;
// rjf: key
DI_Key key;
// rjf: file handles
OS_Handle file;
OS_Handle file_map;
void *file_base;
FileProperties file_props;
// rjf: parse artifacts
Arena *arena;
RDI_Parsed rdi;
B32 parse_done;
};
typedef struct DI_Slot DI_Slot;
struct DI_Slot
{
DI_Node *first;
DI_Node *last;
};
typedef struct DI_Stripe DI_Stripe;
struct DI_Stripe
{
Arena *arena;
DI_Node *free_node;
DI_StringChunkNode *free_string_chunks[8];
OS_Handle rw_mutex;
OS_Handle cv;
};
////////////////////////////////
//~ rjf: Scoped Access Types
typedef struct DI_Touch DI_Touch;
struct DI_Touch
{
DI_Touch *next;
DI_Node *node;
};
typedef struct DI_Scope DI_Scope;
struct DI_Scope
{
DI_Scope *next;
DI_Touch *first_touch;
DI_Touch *last_touch;
};
typedef struct DI_TCTX DI_TCTX;
struct DI_TCTX
{
Arena *arena;
DI_Scope *free_scope;
DI_Touch *free_touch;
};
////////////////////////////////
//~ rjf: Shared State Types
typedef struct DI_Shared DI_Shared;
struct DI_Shared
{
Arena *arena;
// rjf: node cache
U64 slots_count;
DI_Slot *slots;
U64 stripes_count;
DI_Stripe *stripes;
// rjf: user -> parse ring
OS_Handle u2p_ring_mutex;
OS_Handle u2p_ring_cv;
U64 u2p_ring_size;
U8 *u2p_ring_base;
U64 u2p_ring_write_pos;
U64 u2p_ring_read_pos;
// rjf: parse -> user event ring
OS_Handle p2u_ring_mutex;
OS_Handle p2u_ring_cv;
U64 p2u_ring_size;
U8 *p2u_ring_base;
U64 p2u_ring_write_pos;
U64 p2u_ring_read_pos;
// rjf: threads
U64 parse_thread_count;
OS_Handle *parse_threads;
};
////////////////////////////////
//~ rjf: Globals
global DI_Shared *di_shared = 0;
thread_static DI_TCTX *di_tctx = 0;
global RDI_Parsed di_rdi_parsed_nil =
{
0,
0,
0,
0,
{0},
0,
0,
0,
0,
0,
0,
&rdi_top_level_info_nil,
&rdi_binary_section_nil, 1,
&rdi_file_path_node_nil, 1,
&rdi_source_file_nil, 1,
&rdi_unit_nil, 1,
&rdi_vmap_entry_nil, 1,
&rdi_type_node_nil, 1,
&rdi_udt_nil, 1,
&rdi_member_nil, 1,
&rdi_enum_member_nil, 1,
&rdi_global_variable_nil, 1,
&rdi_vmap_entry_nil, 1,
&rdi_thread_variable_nil, 1,
&rdi_procedure_nil, 1,
&rdi_scope_nil, 1,
&rdi_voff_nil, 1,
&rdi_vmap_entry_nil, 1,
&rdi_local_nil, 1,
&rdi_location_block_nil, 1,
0, 0,
0, 0,
};
////////////////////////////////
//~ rjf: Basic Helpers
internal U64 di_hash_from_string(String8 string, StringMatchFlags match_flags);
internal U64 di_hash_from_key(DI_Key *k);
internal B32 di_key_match(DI_Key *a, DI_Key *b);
internal DI_Key di_key_copy(Arena *arena, DI_Key *src);
internal DI_Key di_normalized_key_from_key(Arena *arena, DI_Key *src);
internal void di_key_list_push(Arena *arena, DI_KeyList *list, DI_Key *key);
internal DI_KeyArray di_key_array_from_list(Arena *arena, DI_KeyList *list);
////////////////////////////////
//~ rjf: Main Layer Initialization
internal void di_init(void);
////////////////////////////////
//~ rjf: Scope Functions
internal DI_Scope *di_scope_open(void);
internal void di_scope_close(DI_Scope *scope);
internal void di_scope_touch_node__stripe_mutex_r_guarded(DI_Scope *scope, DI_Node *node);
////////////////////////////////
//~ rjf: Per-Slot Functions
internal DI_Node *di_node_from_key_slot__stripe_mutex_r_guarded(DI_Slot *slot, DI_Key *key);
////////////////////////////////
//~ rjf: Per-Stripe Functions
internal U64 di_string_bucket_idx_from_string_size(U64 size);
internal String8 di_string_alloc__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string);
internal void di_string_release__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string);
////////////////////////////////
//~ rjf: Key Opening/Closing
internal void di_open(DI_Key *key);
internal void di_close(DI_Key *key);
////////////////////////////////
//~ rjf: Cache Lookups
internal RDI_Parsed *di_rdi_from_key(DI_Scope *scope, DI_Key *key, U64 endt_us);
////////////////////////////////
//~ rjf: Parse Threads
internal B32 di_u2p_enqueue_key(DI_Key *key, U64 endt_us);
internal void di_u2p_dequeue_key(Arena *arena, DI_Key *out_key);
internal void di_p2u_push_event(DI_Event *event);
internal DI_EventList di_p2u_pop_events(Arena *arena, U64 endt_us);
internal void di_parse_thread__entry_point(void *p);
#endif // DI_H
+5 -29
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
//////////////////////////////// ////////////////////////////////
@@ -131,8 +131,8 @@ dmn_rip_from_thread(DMN_Handle thread)
U64 result = 0; U64 result = 0;
Temp scratch = scratch_begin(0, 0); Temp scratch = scratch_begin(0, 0);
{ {
Arch arch = dmn_arch_from_thread(thread); Architecture arch = dmn_arch_from_thread(thread);
U64 reg_block_size = regs_block_size_from_arch(arch); U64 reg_block_size = regs_block_size_from_architecture(arch);
void *reg_block = push_array(scratch.arena, U8, reg_block_size); void *reg_block = push_array(scratch.arena, U8, reg_block_size);
dmn_thread_read_reg_block(thread, reg_block); dmn_thread_read_reg_block(thread, reg_block);
result = regs_rip_from_arch_block(arch, reg_block); result = regs_rip_from_arch_block(arch, reg_block);
@@ -147,8 +147,8 @@ dmn_rsp_from_thread(DMN_Handle thread)
U64 result = 0; U64 result = 0;
Temp scratch = scratch_begin(0, 0); Temp scratch = scratch_begin(0, 0);
{ {
Arch arch = dmn_arch_from_thread(thread); Architecture arch = dmn_arch_from_thread(thread);
U64 reg_block_size = regs_block_size_from_arch(arch); U64 reg_block_size = regs_block_size_from_architecture(arch);
void *reg_block = push_array(scratch.arena, U8, reg_block_size); void *reg_block = push_array(scratch.arena, U8, reg_block_size);
dmn_thread_read_reg_block(thread, reg_block); dmn_thread_read_reg_block(thread, reg_block);
result = regs_rsp_from_arch_block(arch, reg_block); result = regs_rsp_from_arch_block(arch, reg_block);
@@ -156,27 +156,3 @@ dmn_rsp_from_thread(DMN_Handle thread)
scratch_end(scratch); scratch_end(scratch);
return result; return result;
} }
////////////////////////////////
//~ rjf: Process Reading Helper Functions (Helpers, Implemented Once)
internal String8
dmn_process_read_cstring(Arena *arena, DMN_Handle process, U64 addr)
{
Temp scratch = scratch_begin(&arena, 1);
String8List block_list = {0};
for(U64 cursor = addr, stride = 256; ; cursor += stride)
{
U8 *raw_block = push_array_no_zero(scratch.arena, U8, stride);
U64 read_size = dmn_process_read(process, r1u64(cursor, cursor + stride), raw_block);
String8 block = str8_cstring_capped(raw_block, raw_block + read_size);
str8_list_push(scratch.arena, &block_list, block);
if(read_size != stride || (block.size+1 <= read_size && block.str[block.size] == 0))
{
break;
}
}
String8 result = str8_list_join(arena, &block_list, 0);
scratch_end(scratch);
return result;
}
+11 -27
View File
@@ -1,4 +1,4 @@
// Copyright (c) Epic Games Tools // Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/) // Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef DEMON_CORE_H #ifndef DEMON_CORE_H
@@ -15,7 +15,7 @@
typedef struct DMN_CtrlCtx DMN_CtrlCtx; typedef struct DMN_CtrlCtx DMN_CtrlCtx;
struct DMN_CtrlCtx struct DMN_CtrlCtx
{ {
U64 u64[1]; U64 u64 [1];
}; };
//////////////////////////////// ////////////////////////////////
@@ -68,12 +68,12 @@ struct DMN_Event
DMN_Handle process; DMN_Handle process;
DMN_Handle thread; DMN_Handle thread;
DMN_Handle module; DMN_Handle module;
Arch arch; Architecture arch;
U64 address; U64 address;
U64 size; U64 size;
String8 string; String8 string;
U32 code; // code gives pid & tid on CreateProcess and CreateThread (respectfully) U32 code; // code gives pid & tid on CreateProcess and CreateThread (respectfully)
U32 flags; // DMN_TrapFlags, if `DMN_EventKind_SetBreakpoint` U32 flags;
S32 signo; S32 signo;
S32 sigcode; S32 sigcode;
U64 instruction_pointer; U64 instruction_pointer;
@@ -100,22 +100,12 @@ struct DMN_EventList
//////////////////////////////// ////////////////////////////////
//~ rjf: Run Control Types //~ rjf: Run Control Types
typedef U32 DMN_TrapFlags;
enum
{
DMN_TrapFlag_BreakOnWrite = (1<<0),
DMN_TrapFlag_BreakOnRead = (1<<1),
DMN_TrapFlag_BreakOnExecute = (1<<2),
};
typedef struct DMN_Trap DMN_Trap; typedef struct DMN_Trap DMN_Trap;
struct DMN_Trap struct DMN_Trap
{ {
DMN_Handle process; DMN_Handle process;
U64 vaddr; U64 vaddr;
U64 id; U64 id;
DMN_TrapFlags flags;
U32 size;
}; };
typedef struct DMN_TrapChunkNode DMN_TrapChunkNode; typedef struct DMN_TrapChunkNode DMN_TrapChunkNode;
@@ -139,7 +129,6 @@ struct DMN_TrapChunkList
typedef struct DMN_RunCtrls DMN_RunCtrls; typedef struct DMN_RunCtrls DMN_RunCtrls;
struct DMN_RunCtrls struct DMN_RunCtrls
{ {
DMN_Handle priority_thread;
DMN_Handle single_step_thread; DMN_Handle single_step_thread;
B8 ignore_previous_exception; B8 ignore_previous_exception;
B8 run_entities_are_unfrozen; B8 run_entities_are_unfrozen;
@@ -191,11 +180,6 @@ internal DMN_Event *dmn_event_list_push(Arena *arena, DMN_EventList *list);
internal U64 dmn_rip_from_thread(DMN_Handle thread); internal U64 dmn_rip_from_thread(DMN_Handle thread);
internal U64 dmn_rsp_from_thread(DMN_Handle thread); internal U64 dmn_rsp_from_thread(DMN_Handle thread);
////////////////////////////////
//~ rjf: Process Reading Helper Functions (Helpers, Implemented Once)
internal String8 dmn_process_read_cstring(Arena *arena, DMN_Handle process, U64 addr);
//////////////////////////////// ////////////////////////////////
//~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS) //~ rjf: @dmn_os_hooks Main Layer Initialization (Implemented Per-OS)
@@ -208,7 +192,7 @@ internal DMN_CtrlCtx *dmn_ctrl_begin(void);
internal void dmn_ctrl_exclusive_access_begin(void); internal void dmn_ctrl_exclusive_access_begin(void);
internal void dmn_ctrl_exclusive_access_end(void); internal void dmn_ctrl_exclusive_access_end(void);
#define DMN_CtrlExclusiveAccessScope DeferLoop(dmn_ctrl_exclusive_access_begin(), dmn_ctrl_exclusive_access_end()) #define DMN_CtrlExclusiveAccessScope DeferLoop(dmn_ctrl_exclusive_access_begin(), dmn_ctrl_exclusive_access_end())
internal U32 dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_ProcessLaunchParams *params); internal U32 dmn_ctrl_launch(DMN_CtrlCtx *ctx, OS_LaunchOptions *options);
internal B32 dmn_ctrl_attach(DMN_CtrlCtx *ctx, U32 pid); internal B32 dmn_ctrl_attach(DMN_CtrlCtx *ctx, U32 pid);
internal B32 dmn_ctrl_kill(DMN_CtrlCtx *ctx, DMN_Handle process, U32 exit_code); internal B32 dmn_ctrl_kill(DMN_CtrlCtx *ctx, DMN_Handle process, U32 exit_code);
internal B32 dmn_ctrl_detach(DMN_CtrlCtx *ctx, DMN_Handle process); internal B32 dmn_ctrl_detach(DMN_CtrlCtx *ctx, DMN_Handle process);
@@ -222,24 +206,24 @@ internal void dmn_halt(U64 code, U64 user_data);
//////////////////////////////// ////////////////////////////////
//~ rjf: @dmn_os_hooks Introspection Functions (Implemented Per-OS) //~ rjf: @dmn_os_hooks Introspection Functions (Implemented Per-OS)
//- rjf: run/memory/register counters
internal U64 dmn_run_gen(void);
internal U64 dmn_mem_gen(void);
internal U64 dmn_reg_gen(void);
//- rjf: non-blocking-control-thread access barriers //- rjf: non-blocking-control-thread access barriers
internal B32 dmn_access_open(void); internal B32 dmn_access_open(void);
internal void dmn_access_close(void); internal void dmn_access_close(void);
#define DMN_AccessScope DeferLoopChecked(dmn_access_open(), dmn_access_close()) #define DMN_AccessScope DeferLoopChecked(dmn_access_open(), dmn_access_close())
//- rjf: processes //- rjf: processes
internal U64 dmn_process_memory_reserve(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_commit(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_decommit(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_release(DMN_Handle process, U64 vaddr, U64 size);
internal void dmn_process_memory_protect(DMN_Handle process, U64 vaddr, U64 size, OS_AccessFlags flags);
internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst); internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst);
internal B32 dmn_process_write(DMN_Handle process, Rng1U64 range, void *src); internal B32 dmn_process_write(DMN_Handle process, Rng1U64 range, void *src);
#define dmn_process_read_struct(process, vaddr, ptr) dmn_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) #define dmn_process_read_struct(process, vaddr, ptr) dmn_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
#define dmn_process_write_struct(process, vaddr, ptr) dmn_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) #define dmn_process_write_struct(process, vaddr, ptr) dmn_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
//- rjf: threads //- rjf: threads
internal Arch dmn_arch_from_thread(DMN_Handle handle); internal Architecture dmn_arch_from_thread(DMN_Handle handle);
internal U64 dmn_stack_base_vaddr_from_thread(DMN_Handle handle); internal U64 dmn_stack_base_vaddr_from_thread(DMN_Handle handle);
internal U64 dmn_tls_root_vaddr_from_thread(DMN_Handle handle); internal U64 dmn_tls_root_vaddr_from_thread(DMN_Handle handle);
internal B32 dmn_thread_read_reg_block(DMN_Handle handle, void *reg_block); internal B32 dmn_thread_read_reg_block(DMN_Handle handle, void *reg_block);

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