mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-09-22 17:20:05 +00:00
Compare commits
43
Commits
57252a8e29
..
odin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee086a06f8 | ||
|
|
e9abd2ab3a | ||
|
|
b6f9766fe5 | ||
|
|
b6dca789df | ||
|
|
2fe6a0884c | ||
|
|
d1391c5f91 | ||
|
|
fc8dfcc506 | ||
|
|
1028318de2 | ||
|
|
4fdd2aee71 | ||
|
|
245560552e | ||
|
|
1101854de5 | ||
|
|
0bbf33514b | ||
|
|
9086233d89 | ||
|
|
ad1b2fa23d | ||
|
|
f67098f54b | ||
|
|
ea4e671a44 | ||
|
|
0c4c18c64f | ||
|
|
ba9d68951d | ||
|
|
340d504f10 | ||
|
|
e8f03562bf | ||
|
|
c946f88b2a | ||
|
|
56e7bae038 | ||
|
|
c1741b4f39 | ||
|
|
ba41543543 | ||
|
|
5ae60aedc5 | ||
|
|
f23979e20a | ||
|
|
57fb0eb833 | ||
|
|
5d2d13d9d9 | ||
|
|
8342cfbd53 | ||
|
|
2721adf088 | ||
|
|
ffcecd659e | ||
|
|
0ccfb51102 | ||
|
|
f41f8dd719 | ||
|
|
fa6b04ab24 | ||
|
|
156615a3ba | ||
|
|
57a35c6b28 | ||
|
|
76ce8a79c2 | ||
|
|
932db741fa | ||
|
|
0add3ef72c | ||
|
|
e417a96ce4 | ||
|
|
b2262532a7 | ||
|
|
b24326f502 | ||
|
|
66c1fa337c |
@@ -13,17 +13,6 @@ jobs:
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
- raddbg
|
||||
- radlink
|
||||
- radbin
|
||||
compiler:
|
||||
- msvc
|
||||
- clang
|
||||
mode:
|
||||
- debug
|
||||
- release
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
@@ -31,22 +20,13 @@ jobs:
|
||||
shell: cmd
|
||||
run: |
|
||||
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
|
||||
|
||||
run-torture:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
# - name: Install ASAN
|
||||
# shell: cmd
|
||||
# run: |
|
||||
# "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
|
||||
call build raddbg msvc debug || exit /b 1
|
||||
call build rdi_from_pdb msvc debug || exit /b 1
|
||||
call build rdi_from_dwarf msvc debug || exit /b 1
|
||||
call build rdi_dump msvc debug || exit /b 1
|
||||
call build raddbg clang debug || exit /b 1
|
||||
call build rdi_from_pdb clang debug || exit /b 1
|
||||
call build rdi_from_dwarf clang debug || exit /b 1
|
||||
call build rdi_dump clang debug || exit /b 1
|
||||
call build radlink msvc debug || exit /b 1
|
||||
call build radlink clang debug || exit /b 1
|
||||
@@ -1,4 +1,3 @@
|
||||
/build/
|
||||
/local/
|
||||
*~
|
||||
.vscode/settings.json
|
||||
|
||||
-137
@@ -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,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
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
|
||||
@@ -1,93 +1,45 @@
|
||||
# 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's README, which includes usage instructions and tips, can be found
|
||||
packaged along with debugger releases, or within the `build` folder after a
|
||||
local copy has been built. You can find pre-built release binaries
|
||||
[here](https://github.com/EpicGamesExt/raddebugger/releases)._
|
||||
local copy has been built._
|
||||
|
||||
The RAD Debugger is a native, user-mode, multi-process, graphical debugger. It
|
||||
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
|
||||
support native Linux debugging and DWARF debug info.
|
||||
|
||||
The debugger is currently in *ALPHA*. In order to get the debugger
|
||||
bullet-proof, it'd greatly help out if you submitted the issues you find
|
||||
[here](https://github.com/EpicGamesExt/raddebugger/issues), along with any
|
||||
information you can gather, like dump files (along with the build you used),
|
||||
instructions to reproduce, test executables, and so on.
|
||||
The RAD Debugger is currently in *ALPHA*. In order to get the debugger bullet-
|
||||
proof, it'd greatly help out if you submitted the issues you find here, along
|
||||
with any information you can gather, like dump files (along with the build you
|
||||
used), instructions to reproduce, test executables, and so on.
|
||||
|
||||
In addition to the debugger, we aim to further improve the toolchain with two
|
||||
additional related technologies: **(1)** the RAD Debug Info (RDI) format, and
|
||||
**(2)** the RAD Linker.
|
||||
You can download pre-built binaries for the debugger
|
||||
[here](https://github.com/EpicGamesExt/raddebugger/releases).
|
||||
|
||||
## The RAD Debug Info (RDI) Format
|
||||
|
||||
The RAD Debug Info (RDI) format is our custom debug information format, which
|
||||
the debugger parses and uses, rather than the debug information natively
|
||||
produced by toolchains, like PDB or DWARF. To work with these existing
|
||||
toolchains, we convert PDB (and eventually PE/ELF files with embedded DWARF)
|
||||
into the RDI format on-demand.
|
||||
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
|
||||
Debug Info (RDI) format, which is what the debugger parses and uses. To work
|
||||
with existing 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
|
||||
`src/lib_rdi` folder. In [`rdi.h`](src/lib_rdi/rdi.h) and
|
||||
[`rdi.c`](src/lib_rdi/rdi.c), the types and functions which define the format
|
||||
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.
|
||||
`src/lib_rdi_format` folder. The other relevant folders for working with the
|
||||
format are:
|
||||
|
||||
We also have an in-progress library for constructing and serializing RDI data,
|
||||
located within the `src/lib_rdi_make` folder.
|
||||
- `lib_rdi_make`: The "RAD Debug Info Make" library, for making RDI debug info.
|
||||
- `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`
|
||||
command line argument) is capable of converting native debug information formats
|
||||
to RDI, and of producing textual dumps of contents stored within RDI files.
|
||||
## Development Setup Instructions
|
||||
|
||||
## 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.
|
||||
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:
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
# Project Development Setup Instructions
|
||||
|
||||
**NOTE: Currently, only x64 Windows development is supported for the project.**
|
||||
|
||||
## 1. Installing the Required Tools (MSVC & Windows SDK)
|
||||
### 1. Installing the Required Tools (MSVC & Windows SDK)
|
||||
|
||||
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
|
||||
@@ -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++
|
||||
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
|
||||
ability to call either MSVC or Clang from command line.
|
||||
@@ -138,11 +90,10 @@ You should see the following output:
|
||||
```
|
||||
[debug mode]
|
||||
[msvc compile]
|
||||
[default mode, assuming `raddbg` build]
|
||||
metagen_main.c
|
||||
searching C:\devel\raddebugger/src... 458 files found
|
||||
parsing metadesk... 16 metadesk files parsed
|
||||
gathering tables... 97 tables found
|
||||
searching C:\devel\raddebugger/src... 309 files found
|
||||
parsing metadesk... 15 metadesk files parsed
|
||||
gathering tables... 96 tables found
|
||||
generating layer code...
|
||||
raddbg_main.c
|
||||
```
|
||||
@@ -150,53 +101,23 @@ raddbg_main.c
|
||||
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`.
|
||||
|
||||
This `raddbg.exe` will have been built in **debug mode**, which is not built
|
||||
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
|
||||
## Short-To-Medium-Term Roadmap
|
||||
|
||||
### The Initial Alpha Battle-Testing Phase
|
||||
|
||||
The first priority for the project is to ensure that the most crucial components
|
||||
are functioning extremely reliably for local, x64, Windows development.
|
||||
For the debugger, this would include parts like debug info conversion, debug
|
||||
info loading, process control, stepping, evaluation (correct usage of both
|
||||
location info and type info), and a robust frontend which ensures the lower
|
||||
level parts are usable. For the linker, this is a matter of reliability and
|
||||
convergence with existing linker behavior.
|
||||
The first priority for the project is to ensure that the most crucial debugger
|
||||
components are functioning extremely reliably for local, x64, Windows
|
||||
debugging. This would include parts like debug info conversion, debug info
|
||||
loading, process control, stepping, evaluation (correct usage of both location
|
||||
info and type info), and a robust frontend which ensures the lower level parts
|
||||
are usable.
|
||||
|
||||
We feel that we've already come a long way in all of these respects, but given
|
||||
the massive set of possible combinations of languages, build settings,
|
||||
toolchains, used language features, and patterns of generated code, we still
|
||||
expect some issues, and are prioritizing these issues being resolved first.
|
||||
|
||||
We also hope to continue to improve performance in this phase. For the debugger,
|
||||
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).
|
||||
We feel that the debugger has already come a long way in all of these respects,
|
||||
but given the massive set of possible combinations of languages, build
|
||||
settings, toolchains, used language features, and patterns of generated code,
|
||||
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,
|
||||
such that the debugging experience is rock solid.
|
||||
|
||||
### Local x64 Linux Debugging Phase
|
||||
|
||||
@@ -239,7 +160,42 @@ But for now, we're mostly focused on those first two phases.
|
||||
|
||||
---
|
||||
|
||||
# Codebase Introduction
|
||||
# The RAD Linker
|
||||
|
||||
The RAD Linker is a new performance linker for generating x64 PE/COFF binaries. It is designed to be very fast when creating gigantic executables. It generates standard PDB files for debugging, but it can also optionally create RAD Debugger debug info too (useful 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 and 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 dead-code-elimination or link-time-optimizations, but these features are on the road map.
|
||||
|
||||
By default, the RAD 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.
|
||||
|
||||
## Short Term Roadmap
|
||||
- Porting linker to Linux (for Windows executables, just running on Linux).
|
||||
- Debug info features
|
||||
- Get DWARF debug info converter up-and-running.
|
||||
- Smooth out rough edges in RADDBGI builder.
|
||||
- Improve build speed further (especially for tiny and mid sizes projects).
|
||||
- Other features to come
|
||||
- Dead-code-elimination via `/opt:ref`.
|
||||
- Link Time Optimizations with the help of clang (we won't support LTCG from MSVC compiler since it is undocumented).
|
||||
|
||||
## To build the RAD Linker
|
||||
- Setup development environment, [see](#Development-Setup-Instructions)
|
||||
- Run `build radlink release` or if you have clang installed `build radlink release clang`. We favor latter option for better code generation.
|
||||
|
||||
If build was successful linker executable is placed in `build` folder under `radlink.exe`.
|
||||
|
||||
## Benchmarks
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Top-Level Directory Descriptions
|
||||
|
||||
@@ -254,7 +210,7 @@ also exist:
|
||||
- `local`: Local files, used for local build configuration input files. Not
|
||||
checked in to version control.
|
||||
|
||||
## Layer Descriptions
|
||||
## Codebase Introduction
|
||||
|
||||
The codebase is organized into *layers*. Layers are separated either to isolate
|
||||
certain problems, and to allow inclusion into various builds without needing to
|
||||
@@ -282,85 +238,86 @@ 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
|
||||
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
|
||||
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:
|
||||
- `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.
|
||||
- `async` (`ASYNC_`): Implements a system for asynchronous work to be queued
|
||||
and executed on a thread pool.
|
||||
- `base` (no namespace): Universal, codebase-wide constructs. Strings, math,
|
||||
memory allocators, helper macros, command-line parsing, and so on. Requires
|
||||
no other codebase layers.
|
||||
- `codeview` (`CV_`): Code for parsing and writing the CodeView format.
|
||||
- `coff` (`COFF_`): Code for parsing and writing the COFF (Common Object File
|
||||
memory allocators, helper macros, command-line parsing, and so on. Depends
|
||||
on no other codebase layers.
|
||||
- `codeview` (`CV_`): Code for parsing and/or writing the CodeView format.
|
||||
- `coff` (`COFF_`): Code for parsing and/or writing the COFF (Common Object File
|
||||
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
|
||||
asynchronous process control, stepping, and breakpoints for all attached
|
||||
processes. Runs in lockstep with attached processes. When it runs, attached
|
||||
processes are halted. When attached processes are running, it is halted.
|
||||
Driven by a debugger frontend on another thread.
|
||||
- `dasm_cache` (`DASM_`): An asynchronous disassembly decoder and cache. Users
|
||||
ask for disassembly for some data, with a particular architecture, and other
|
||||
various parameters, and threads implemented in this layer decode and cache the
|
||||
disassembly for that data with those parameters.
|
||||
- `dbgi` (`DI_`): An asynchronous debug info loader and cache. Loads debug info
|
||||
stored in the RDI format. Users ask for debug info for a particular path, and
|
||||
on separate threads, this layer loads the associated debug info file. If
|
||||
necessary, it will launch a separate conversion process to convert original
|
||||
debug info into the RDI format.
|
||||
- `dbg_engine` (`D_`): Implements the core debugger system, without any
|
||||
graphical components. This contains top-level logic for things like stepping,
|
||||
launching, freezing threads, mid-run breakpoint addition, some caches, and so
|
||||
on.
|
||||
- `dbg_info` (`DI_`): Implements asynchronous debug info conversion and loading.
|
||||
Maintains a cache for loaded debug info. Loads RAD Debug Info (RDI) files.
|
||||
Launches separate processes for on-demand conversion to the RDI format if
|
||||
necessary. Also provides various asynchronous operations for using debug info,
|
||||
like fuzzy searching across all records in loaded debug info.
|
||||
launching, freezing threads, mid-run breakpoint addition, some caching layers,
|
||||
and so on.
|
||||
- `demon` (`DMN_`): An abstraction layer for local-machine, low-level process
|
||||
control. The abstraction is used to provide a common interface for process
|
||||
control on target platforms. Used to implement part of `ctrl`.
|
||||
- `disasm` (`DASM_`): Implements disassembly generation, including exposing the
|
||||
ability to compute and cache disassembly asynchronously.
|
||||
- `draw` (`DR_`): Implements a high-level graphics drawing API for the
|
||||
debugger's purposes, using the underlying `render` abstraction layer. Provides
|
||||
high-level APIs for various draw commands, but takes care of batching them,
|
||||
and so on.
|
||||
- `dwarf` (`DW_`): Code for parsing the DWARF format.
|
||||
- `eh` (`EH_`): Code for parsing the EH frame format.
|
||||
- `elf` (`ELF_`): Code for parsing the ELF format.
|
||||
- `eval` (`E_`): A compiler for an expression language, built for evaluation of
|
||||
variables, registers, types, and more, from debugger-attached processes,
|
||||
debug info, debugger state, and files. Broken into several phases mostly
|
||||
corresponding to traditional compiler phases: lexer, parser, type-checker, IR
|
||||
generation, and IR evaluation.
|
||||
- `eval` (`E_`): Implements a compiler for an expression language built for
|
||||
evaluation of variables, registers, types, and more, from debugger-attached
|
||||
processes, debug info, debugger state, and files. Broken into several phases
|
||||
mostly corresponding to traditional compiler phases - lexer, parser,
|
||||
type-checker, IR generation, and IR evaluation.
|
||||
- `eval_visualization` (`EV_`): Implements the core non-graphical evaluation
|
||||
visualization engine, which can be used to visualize evaluations (provided by
|
||||
the `eval` layer) in a number of ways. Implements core data structures and
|
||||
transforms for watch tables.
|
||||
- `file_stream` (`FS_`): Implements asynchronous file streaming, storing the
|
||||
artifacts inside of the cache implemented by the `content` and
|
||||
`artifact_cache` layers, hot-reloading the contents of files when they change.
|
||||
Allows callers to map file paths to data hashes, which can then be used to
|
||||
obtain the file's data.
|
||||
transforms for the `Watch` view.
|
||||
- `file_stream` (`FS_`): Provides asynchronous file loading, storing the
|
||||
artifacts inside of the cache implemented by the `hash_store` layer, and
|
||||
hot-reloading the contents of files when they change. 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
|
||||
and font rasterization backends.
|
||||
- `fuzzy_search` (`FZY_`): Provides a fuzzy searching engine for doing
|
||||
large, asynchronous fuzzy searches. Used by the debugger for implementing
|
||||
things like the symbol lister or the `Procedures` view, which search across
|
||||
all loaded debug info records, using fuzzy matching rules.
|
||||
- `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 visualization.
|
||||
- `hash_store` (`HS_`): Implements a cache for general data blobs, keyed by a
|
||||
128-bit hash of the data. Also implements a 128-bit key cache on top, where
|
||||
the keys refer to a unique identity, associated with a 128-bit hash, where the
|
||||
hash may change across time. Used as a general data store by other layers.
|
||||
- `lib_raddbg_markup` (`RADDBG_`): Standalone library for marking up user
|
||||
programs to work with various features in the debugger. Does not depend on
|
||||
`base`, and can be independently relocated to other codebases.
|
||||
- `lib_rdi` (`RDI_`): Standalone library which defines the core RDI types
|
||||
- `lib_rdi_format` (`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
|
||||
data. Does not depend on `base`, and can be independently relocated
|
||||
to other codebases.
|
||||
- `linker` (`LNK_`): The layer which implements the RAD Linker executable
|
||||
itself.
|
||||
- `mdesk` (`MD_`): Code for parsing Metadesk files (stored as `.mdesk`), which
|
||||
is the JSON-like (technically a JSON superset) text format used for the
|
||||
debugger's user and project configuration files and metacode, which is parsed
|
||||
and used to generate code with the `metagen` layer.
|
||||
debugger's user and project configuration files, view rules, 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
|
||||
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
|
||||
@@ -369,13 +326,18 @@ 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
|
||||
associated data tables. There are also a number of other generation features,
|
||||
like embedding binary files or complex multi-line strings into source code.
|
||||
- `msf` (`MSF_`): Code for parsing and writing the MSF file format.
|
||||
- `msvc_crt` (`MSCRT_`): Code for parsing that's specific to the MSVC CRT.
|
||||
This layer cannot depend on any other layer in the codebase directly,
|
||||
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
|
||||
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.
|
||||
used to implement the `Output` view.
|
||||
- `natvis` (no namespace): NatVis files for type visualization of the codebase's
|
||||
types in other debuggers.
|
||||
- `os/core` (`OS_`): An abstraction layer providing core, non-graphical
|
||||
@@ -384,27 +346,26 @@ 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
|
||||
graphical operating system features under an abstract API, which is
|
||||
implemented per-target-operating-system.
|
||||
- `pdb` (`PDB_`): Code for parsing and writing the PDB file format.
|
||||
- `pe` (`PE_`): Code for parsing and writing the PE (Portable Executable) file
|
||||
format.
|
||||
- `radbin` (`RB_`): The layer implementing the `radbin` binary utility
|
||||
executable.
|
||||
- `path` (`PATH_`): Small helpers for manipulating file path strings.
|
||||
- `pdb` (`PDB_`): Code for parsing and/or writing the PDB file format.
|
||||
- `pe` (`PE_`): Code for parsing and/or writing the PE (Portable Executable)
|
||||
file format.
|
||||
- `raddbg` (`RD_`): The layer which ties everything together for the main
|
||||
graphical debugger executable. Implements the debugger's graphical frontend,
|
||||
all of the debugger-specific UI, the debugger executable's command line
|
||||
interface, and all of the built-in visualizers.
|
||||
- `rdi` (`RDI_`): A layer which includes the `lib_rdi` layer and bundles it with
|
||||
codebase-specific helpers, to easily include the library in codebase programs,
|
||||
and have it be integrated with codebase constructs.
|
||||
- `rdi_from_coff` (`C2R_`): Code for converting information in COFF files to the
|
||||
equivalent RDI data.
|
||||
- `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
|
||||
graphical debugger. Implements the debugger's graphical frontend, all of the
|
||||
debugger-specific UI, the debugger executable's command line interface, and
|
||||
all of the built-in visualizers.
|
||||
- `rdi_breakpad_from_pdb` (`P2B_`): Our implementation, using the codebase's RDI
|
||||
technology, for extracting information from PDBs and generating Breakpad text
|
||||
dumps.
|
||||
- `rdi_dump` (no namespace): A dumper utility program for dumping
|
||||
textualizations of RDI debug info files.
|
||||
- `rdi_format` (no namespace): A layer which includes the `lib_rdi_format` layer
|
||||
and bundles it with codebase-specific helpers, to easily include the library
|
||||
in codebase programs, and have it be integrated with codebase constructs.
|
||||
- `rdi_from_dwarf` (`D2R_`): Our in-progress implementation of DWARF-to-RDI
|
||||
conversion.
|
||||
- `rdi_from_pdb` (`P2R_`): Our implementation of PDB-to-RDI conversion.
|
||||
- `rdi_make` (no namespace): 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
|
||||
@@ -416,10 +377,13 @@ A list of the layers in the codebase and their associated namespaces is below:
|
||||
as-needed basis. Higher level drawing features are implemented in the `draw`
|
||||
layer.
|
||||
- `scratch` (no namespace): Scratch space for small and transient test programs.
|
||||
- `tester` (no namespace): A program used for automated testing.
|
||||
- `text` (`TXT_`): Implements text processing functions, like parsing line
|
||||
breaks, and lexing and parsing source code. Also offers an API to do this
|
||||
asynchronously.
|
||||
- `texture_cache` (`TEX_`): Implements an asynchronously-filled cache for GPU
|
||||
texture data, filled by data sourced in the `hash_store` layer's cache. Used
|
||||
for asynchronously preparing data for visualization.
|
||||
- `text_cache` (`TXT_`): Implements an asynchronously-filled cache for textual
|
||||
analysis data (tokens, line ranges, and so on), filled by data sourced in the
|
||||
`hash_store` layer's cache. Used for asynchronously preparing data for
|
||||
visualization (like for the source code viewer).
|
||||
- `third_party` (no namespace): External code from other projects, which some
|
||||
layers in the codebase depend on. All external code is included and built
|
||||
directly within the codebase.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
@echo off
|
||||
setlocal enabledelayedexpansion
|
||||
cd /D "%~dp0"
|
||||
:restart
|
||||
|
||||
:: --- Usage Notes (2024/1/10) ------------------------------------------------
|
||||
::
|
||||
@@ -25,10 +24,9 @@ cd /D "%~dp0"
|
||||
::
|
||||
:: - `asan`: enable address sanitizer
|
||||
:: - `telemetry`: enable RAD telemetry profiling support
|
||||
:: - `spall`: enable spall profiling support
|
||||
|
||||
:: --- 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 "%release%"=="1" set debug=1
|
||||
if "%debug%"=="1" set release=0 && echo [debug mode]
|
||||
@@ -41,49 +39,33 @@ if "%~1"=="release" if "%~2"=="" echo [default mode, assuming `raddbg` build] &&
|
||||
:: --- Unpack Command Line Build Arguments ------------------------------------
|
||||
set auto_compile_flags=
|
||||
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 "%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 ------------------------------------------
|
||||
set cl_common= /I..\src\ /I..\local\ /nologo /FC /Z7
|
||||
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 -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
|
||||
set cl_debug= call cl /Od /Ob1 /DBUILD_DEBUG=1 %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_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 /pdbaltpath:%%%%_PDB%%%%
|
||||
set clang_link= -fuse-ld=lld -Xlinker /MANIFEST:EMBED -Xlinker /pdbaltpath:%%%%_PDB%%%%
|
||||
set cl_out= /out:
|
||||
set clang_out= -o
|
||||
set clang_linker= -Xlinker
|
||||
|
||||
:: --- NATVIS -----------------------------------------------------------------
|
||||
set natvis= /NATVIS:"%~dp0\src\natvis\base.natvis"
|
||||
if "%radlink%"=="1" set natvis= %natvis% /NATVIS:"%~dp0\src\linker\linker.natvis"
|
||||
|
||||
if "%clang%"=="1" (
|
||||
set result=
|
||||
for %%n in (%natvis%) do set result= !result! -Xlinker %%n
|
||||
set natvis= !result!
|
||||
)
|
||||
|
||||
:: --- Per-Build Settings -----------------------------------------------------
|
||||
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 "%clang%"=="1" set only_compile=-c
|
||||
if "%msvc%"=="1" set EHsc=/EHsc
|
||||
@@ -105,6 +87,9 @@ if "%clang%"=="1" set out=%clang_out%
|
||||
if "%debug%"=="1" set compile=%compile_debug%
|
||||
if "%release%"=="1" set compile=%compile_release%
|
||||
|
||||
:: --- Append NATVIS to link line ---------------------------------------------
|
||||
set compile_link= %compile_link% %natvis%
|
||||
|
||||
:: --- Prep Directories -------------------------------------------------------
|
||||
if not exist build mkdir build
|
||||
if not exist local mkdir local
|
||||
@@ -119,8 +104,8 @@ for /f %%i in ('call git describe --always --dirty') do set compile=%compile%
|
||||
for /f %%i in ('call git rev-parse HEAD') do set compile=%compile% -DBUILD_GIT_HASH_FULL=\"%%i\"
|
||||
|
||||
:: --- Build & Run Metaprogram ------------------------------------------------
|
||||
if "%meta%"=="1" (
|
||||
echo [doing metagen]
|
||||
if "%no_meta%"=="1" echo [skipping metagen]
|
||||
if not "%no_meta%"=="1" (
|
||||
pushd build
|
||||
%compile_debug% ..\src\metagen\metagen_main.c %compile_link% %out%metagen.exe || exit /b 1
|
||||
metagen.exe || exit /b 1
|
||||
@@ -130,21 +115,18 @@ if "%meta%"=="1" (
|
||||
:: --- Build Everything (@build_targets) --------------------------------------
|
||||
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 "%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 "%radbin%"=="1" set didbuild=1 && %compile% ..\src\radbin\radbin_main.c %compile_link% %out%radbin.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 "%radlink%"=="1" set didbuild=1 && %compile% ..\src\linker\lnk.c %compile_link% %out%radlink.exe || exit /b 1
|
||||
if "%rdi_from_pdb%"=="1" set didbuild=1 && %compile% ..\src\rdi_from_pdb\rdi_from_pdb_main.c %compile_link% %out%rdi_from_pdb.exe || exit /b 1
|
||||
if "%rdi_from_dwarf%"=="1" set didbuild=1 && %compile% ..\src\rdi_from_dwarf\rdi_from_dwarf.c %compile_link% %out%rdi_from_dwarf.exe || exit /b 1
|
||||
if "%rdi_dump%"=="1" set didbuild=1 && %compile% ..\src\rdi_dump\rdi_dump_main.c %compile_link% %out%rdi_dump.exe || exit /b 1
|
||||
if "%rdi_breakpad_from_pdb%"=="1" set didbuild=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 "%tester%"=="1" set didbuild=1 && %compile% ..\src\tester\tester_main.c %compile_link% %out%tester.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 "%eval_scratch%"=="1" set didbuild=1 && %compile% ..\src\scratch\eval_scratch.c %compile_link% %out%eval_scratch.exe || exit /b 1
|
||||
if "%textperf%"=="1" set didbuild=1 && %compile% ..\src\scratch\textperf.c %compile_link% %out%textperf.exe || exit /b 1
|
||||
if "%convertperf%"=="1" set didbuild=1 && %compile% ..\src\scratch\convertperf.c %compile_link% %out%convertperf.exe || exit /b 1
|
||||
if "%debugstringperf%"=="1" set didbuild=1 && %compile% ..\src\scratch\debugstringperf.c %compile_link% %out%debugstringperf.exe || 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 "%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" (
|
||||
set didbuild=1
|
||||
if exist mule_peb_trample.exe move mule_peb_trample.exe mule_peb_trample_old_%random%.exe
|
||||
@@ -160,13 +142,3 @@ if "%didbuild%"=="" (
|
||||
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`.
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
:: --- PGO Run ----------------------------------------------------------------
|
||||
if "%pgo_run%"=="1" (
|
||||
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
|
||||
)
|
||||
|
||||
@@ -15,11 +15,11 @@ if [ -v gcc ]; then compiler="${CC:-gcc}"; echo "[gcc compile]"; fi
|
||||
auto_compile_flags=''
|
||||
|
||||
# --- Get Current Git Commit Id -----------------------------------------------
|
||||
git_hash=$(git describe --always --dirty)
|
||||
git_hash=$(git rev-parse HEAD)
|
||||
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_common="-I../src/ -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"
|
||||
@@ -33,8 +33,6 @@ 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
|
||||
@@ -64,13 +62,13 @@ 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 raddbg ]; then didbuild=1 && $compile ../src/raddbg/raddbg_main.c $compile_link $link_os_gfx $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
|
||||
if [ -v ryan_scratch ]; then didbuild=1 && $compile ../src/scratch/ryan_scratch.c $compile_link $link_os_gfx $out ryan_scratch; fi
|
||||
cd ..
|
||||
|
||||
# --- Warn On No Builds -------------------------------------------------------
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+6
-25
@@ -45,31 +45,12 @@ load_paths =
|
||||
|
||||
commands =
|
||||
{
|
||||
//- rjf: [raddbg]
|
||||
.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, },
|
||||
|
||||
//- rjf: [raddbg wsl]
|
||||
// .f1 = { .win = "wsl ./build.sh raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: [scratch]
|
||||
.f2 = { .win = "build radbin debug telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- 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, },
|
||||
|
||||
//- rjf: [tester]
|
||||
// .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, },
|
||||
|
||||
//- rjf: [radbin]
|
||||
// .f1 = { .win = "raddbg_stable --ipc kill_all && build radbin", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
|
||||
//- rjf: running target
|
||||
.f3 = { .win = "raddbg_stable --ipc run", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f3 = { .win = "wsl ./build/raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f3 = { .win = "pushd build && raddbg --user:dev.raddbg_user && popd", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .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, },
|
||||
// .f3 = { .win = "wsl_launch /mnt/c/devel/raddebugger/build/raddbg", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
//- rjf: fkey command slots (change locally but do not commit)
|
||||
.f1 = { .win = "build raddbg telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.f2 = { .win = "build rdi_from_pdb", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
.f3 = { .win = "pushd build && raddbg.exe --user:local_dev.raddbg_user --project:local_dev.raddbg_project --xuto_run && popd",.linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f1 = { .win = "build textperf release telemetry", .linux = "", .out = "*compilation*", .footer_panel = true, .save_dirty_files = true, .cursor_at_end = false, },
|
||||
// .f3 = { .win = "pushd build && textperf.exe --capture && popd",.linux = "", .out = "*compilation*", .footer_panel = true, .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, },
|
||||
|
||||
@@ -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(¶ms->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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,240 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Top-Level Layer Initialization
|
||||
|
||||
internal void
|
||||
async_init(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
async_shared = push_array(arena, ASYNC_Shared, 1);
|
||||
async_shared->arena = arena;
|
||||
for EachEnumVal(ASYNC_Priority, p)
|
||||
{
|
||||
ASYNC_Ring *ring = &async_shared->rings[p];
|
||||
ring->ring_size = MB(8);
|
||||
ring->ring_base = push_array_no_zero(arena, U8, ring->ring_size);
|
||||
ring->ring_mutex = os_mutex_alloc();
|
||||
ring->ring_cv = os_condition_variable_alloc();
|
||||
}
|
||||
async_shared->ring_mutex = os_mutex_alloc();
|
||||
async_shared->ring_cv = os_condition_variable_alloc();
|
||||
async_shared->work_threads_count = Max(1, os_get_system_info()->logical_processor_count-1);
|
||||
async_shared->work_threads = push_array(arena, OS_Handle, async_shared->work_threads_count);
|
||||
for EachIndex(idx, async_shared->work_threads_count)
|
||||
{
|
||||
async_shared->work_threads[idx] = os_thread_launch(async_work_thread__entry_point, (void *)idx, 0);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Top-Level Accessors
|
||||
|
||||
internal U64
|
||||
async_thread_count(void)
|
||||
{
|
||||
return async_shared->work_threads_count;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Work Kickoffs
|
||||
|
||||
internal B32
|
||||
async_push_work_(ASYNC_WorkFunctionType *work_function, ASYNC_WorkParams *params)
|
||||
{
|
||||
// rjf: choose ring
|
||||
ASYNC_Ring *ring = &async_shared->rings[params->priority];
|
||||
|
||||
// rjf: build work package
|
||||
ASYNC_Work work = {0};
|
||||
work.work_function = work_function;
|
||||
work.input = params->input;
|
||||
work.output = params->output;
|
||||
work.semaphore = params->semaphore;
|
||||
work.completion_counter = params->completion_counter;
|
||||
|
||||
// rjf: loop; try to write into user -> writer ring buffer. if we're on a
|
||||
// worker thread, determine if we need to execute this task locally on this
|
||||
// thread, and skip ring buffer if so.
|
||||
B32 queued_in_ring_buffer = 0;
|
||||
B32 need_to_execute_on_this_thread = 0;
|
||||
OS_MutexScope(ring->ring_mutex) for(;;)
|
||||
{
|
||||
U64 num_available_work_threads = (async_shared->work_threads_count - ins_atomic_u64_eval(&async_shared->work_threads_live_count));
|
||||
if(num_available_work_threads == 0 && async_work_thread_depth > 0)
|
||||
{
|
||||
need_to_execute_on_this_thread = 1;
|
||||
break;
|
||||
}
|
||||
U64 unconsumed_size = ring->ring_write_pos - ring->ring_read_pos;
|
||||
U64 available_size = ring->ring_size - unconsumed_size;
|
||||
if(available_size >= sizeof(work))
|
||||
{
|
||||
queued_in_ring_buffer = 1;
|
||||
if(!os_handle_match(params->semaphore, os_handle_zero()))
|
||||
{
|
||||
os_semaphore_take(params->semaphore, max_U64);
|
||||
}
|
||||
ring->ring_write_pos += ring_write_struct(ring->ring_base, ring->ring_size, ring->ring_write_pos, &work);
|
||||
break;
|
||||
}
|
||||
if(os_now_microseconds() >= params->endt_us)
|
||||
{
|
||||
break;
|
||||
}
|
||||
os_condition_variable_wait(ring->ring_cv, ring->ring_mutex, params->endt_us);
|
||||
}
|
||||
|
||||
// rjf: broadcast ring buffer cv if we wrote successfully
|
||||
if(queued_in_ring_buffer)
|
||||
{
|
||||
os_condition_variable_broadcast(ring->ring_cv);
|
||||
os_condition_variable_broadcast(async_shared->ring_cv);
|
||||
}
|
||||
|
||||
// rjf: if we did not queue successfully, and we have determined that
|
||||
// we need to execute this work on the current thread, then execute the
|
||||
// work before returning
|
||||
if(need_to_execute_on_this_thread)
|
||||
{
|
||||
async_execute_work(work);
|
||||
}
|
||||
|
||||
// rjf: return success signal
|
||||
B32 result = (queued_in_ring_buffer || need_to_execute_on_this_thread);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Task-Based Work Helper
|
||||
|
||||
internal void
|
||||
async_task_list_push(Arena *arena, ASYNC_TaskList *list, ASYNC_Task *t)
|
||||
{
|
||||
ASYNC_TaskNode *n = push_array(arena, ASYNC_TaskNode, 1);
|
||||
SLLQueuePush(list->first, list->last, n);
|
||||
n->v = t;
|
||||
list->count += 1;
|
||||
}
|
||||
|
||||
internal ASYNC_Task *
|
||||
async_task_launch_(Arena *arena, ASYNC_WorkFunctionType *work_function, ASYNC_WorkParams *params)
|
||||
{
|
||||
ASYNC_Task *task = push_array(arena, ASYNC_Task, 1);
|
||||
task->semaphore = os_semaphore_alloc(1, 1, str8_zero());
|
||||
ASYNC_WorkParams params_refined = {0};
|
||||
MemoryCopyStruct(¶ms_refined, params);
|
||||
params_refined.endt_us = max_U64;
|
||||
params_refined.semaphore = task->semaphore;
|
||||
if(params_refined.output == 0)
|
||||
{
|
||||
params_refined.output = &task->output;
|
||||
}
|
||||
async_push_work_(work_function, ¶ms_refined);
|
||||
return task;
|
||||
}
|
||||
|
||||
internal void *
|
||||
async_task_join(ASYNC_Task *task)
|
||||
{
|
||||
void *result = 0;
|
||||
if(task != 0 && !os_handle_match(task->semaphore, os_handle_zero()))
|
||||
{
|
||||
os_semaphore_take(task->semaphore, max_U64);
|
||||
os_semaphore_release(task->semaphore);
|
||||
MemoryZeroStruct(&task->semaphore);
|
||||
result = (void *)ins_atomic_u64_eval(&task->output);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Work Execution
|
||||
|
||||
internal ASYNC_Work
|
||||
async_pop_work(void)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
ASYNC_Work work = {0};
|
||||
B32 done = 0;
|
||||
ASYNC_Priority taken_priority = ASYNC_Priority_Low;
|
||||
OS_MutexScope(async_shared->ring_mutex) for(;!done;)
|
||||
{
|
||||
for(ASYNC_Priority priority = ASYNC_Priority_High;; priority = (ASYNC_Priority)(priority - 1))
|
||||
{
|
||||
ASYNC_Ring *ring = &async_shared->rings[priority];
|
||||
OS_MutexScope(ring->ring_mutex)
|
||||
{
|
||||
U64 unconsumed_size = ring->ring_write_pos - ring->ring_read_pos;
|
||||
if(unconsumed_size >= sizeof(work))
|
||||
{
|
||||
ring->ring_read_pos += ring_read_struct(ring->ring_base, ring->ring_size, ring->ring_read_pos, &work);
|
||||
done = 1;
|
||||
taken_priority = priority;
|
||||
}
|
||||
}
|
||||
if(done)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(priority == ASYNC_Priority_Low)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!done)
|
||||
{
|
||||
os_condition_variable_wait(async_shared->ring_cv, async_shared->ring_mutex, max_U64);
|
||||
}
|
||||
}
|
||||
os_condition_variable_broadcast(async_shared->ring_cv);
|
||||
os_condition_variable_broadcast(async_shared->rings[taken_priority].ring_cv);
|
||||
ProfEnd();
|
||||
return work;
|
||||
}
|
||||
|
||||
internal void
|
||||
async_execute_work(ASYNC_Work work)
|
||||
{
|
||||
//- rjf: run work
|
||||
async_work_thread_depth += 1;
|
||||
void *work_out = work.work_function(async_work_thread_idx, work.input);
|
||||
async_work_thread_depth -= 1;
|
||||
|
||||
//- rjf: store output
|
||||
if(work.output != 0)
|
||||
{
|
||||
ins_atomic_u64_eval_assign((U64 *)work.output, (U64)work_out);
|
||||
}
|
||||
|
||||
//- rjf: release semaphore
|
||||
if(!os_handle_match(work.semaphore, os_handle_zero()))
|
||||
{
|
||||
os_semaphore_drop(work.semaphore);
|
||||
}
|
||||
|
||||
//- rjf: increment completion counter
|
||||
if(work.completion_counter != 0)
|
||||
{
|
||||
ins_atomic_u64_inc_eval(work.completion_counter);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Work Thread Entry Point
|
||||
|
||||
internal void
|
||||
async_work_thread__entry_point(void *p)
|
||||
{
|
||||
U64 thread_idx = (U64)p;
|
||||
ThreadNameF("[async] work thread #%I64u", thread_idx);
|
||||
async_work_thread_idx = thread_idx;
|
||||
for(;;)
|
||||
{
|
||||
ASYNC_Work work = async_pop_work();
|
||||
ins_atomic_u64_inc_eval(&async_shared->work_threads_live_count);
|
||||
async_execute_work(work);
|
||||
ins_atomic_u64_dec_eval(&async_shared->work_threads_live_count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef ASYNC_H
|
||||
#define ASYNC_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Work Function Type
|
||||
|
||||
#define ASYNC_WORK_SIG(name) void *name(U64 thread_idx, void *input)
|
||||
#define ASYNC_WORK_DEF(name) internal ASYNC_WORK_SIG(name)
|
||||
typedef ASYNC_WORK_SIG(ASYNC_WorkFunctionType);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Work Types
|
||||
|
||||
typedef enum ASYNC_Priority
|
||||
{
|
||||
ASYNC_Priority_Low,
|
||||
ASYNC_Priority_High,
|
||||
ASYNC_Priority_COUNT
|
||||
}
|
||||
ASYNC_Priority;
|
||||
|
||||
typedef struct ASYNC_WorkParams ASYNC_WorkParams;
|
||||
struct ASYNC_WorkParams
|
||||
{
|
||||
void *input;
|
||||
void **output;
|
||||
OS_Handle semaphore;
|
||||
U64 *completion_counter;
|
||||
U64 endt_us;
|
||||
ASYNC_Priority priority;
|
||||
};
|
||||
|
||||
typedef struct ASYNC_Work ASYNC_Work;
|
||||
struct ASYNC_Work
|
||||
{
|
||||
ASYNC_WorkFunctionType *work_function;
|
||||
void *input;
|
||||
void **output;
|
||||
OS_Handle semaphore;
|
||||
U64 *completion_counter;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Task-Based Work Types
|
||||
|
||||
typedef struct ASYNC_Task ASYNC_Task;
|
||||
struct ASYNC_Task
|
||||
{
|
||||
OS_Handle semaphore;
|
||||
void *output;
|
||||
};
|
||||
|
||||
typedef struct ASYNC_TaskNode ASYNC_TaskNode;
|
||||
struct ASYNC_TaskNode
|
||||
{
|
||||
ASYNC_TaskNode *next;
|
||||
ASYNC_Task *v;
|
||||
};
|
||||
|
||||
typedef struct ASYNC_TaskList ASYNC_TaskList;
|
||||
struct ASYNC_TaskList
|
||||
{
|
||||
ASYNC_TaskNode *first;
|
||||
ASYNC_TaskNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Shared State Bundle
|
||||
|
||||
typedef struct ASYNC_Ring ASYNC_Ring;
|
||||
struct ASYNC_Ring
|
||||
{
|
||||
U64 ring_size;
|
||||
U8 *ring_base;
|
||||
U64 ring_write_pos;
|
||||
U64 ring_read_pos;
|
||||
OS_Handle ring_mutex;
|
||||
OS_Handle ring_cv;
|
||||
};
|
||||
|
||||
typedef struct ASYNC_Shared ASYNC_Shared;
|
||||
struct ASYNC_Shared
|
||||
{
|
||||
Arena *arena;
|
||||
|
||||
// rjf: user -> work thread ring buffers
|
||||
ASYNC_Ring rings[ASYNC_Priority_COUNT];
|
||||
OS_Handle ring_mutex;
|
||||
OS_Handle ring_cv;
|
||||
|
||||
// rjf: work threads
|
||||
OS_Handle *work_threads;
|
||||
U64 work_threads_count;
|
||||
U64 work_threads_live_count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
thread_static B32 async_work_thread_depth = 0;
|
||||
thread_static U64 async_work_thread_idx = 0;
|
||||
global ASYNC_Shared *async_shared = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Top-Level Layer Initialization
|
||||
|
||||
internal void async_init(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Top-Level Accessors
|
||||
|
||||
internal U64 async_thread_count(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Work Kickoffs
|
||||
|
||||
internal B32 async_push_work_(ASYNC_WorkFunctionType *work_function, ASYNC_WorkParams *params);
|
||||
#define async_push_work(function, ...) async_push_work_((function), &(ASYNC_WorkParams){.endt_us = max_U64, .priority = ASYNC_Priority_High, __VA_ARGS__})
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Task-Based Work Helper
|
||||
|
||||
internal void async_task_list_push(Arena *arena, ASYNC_TaskList *list, ASYNC_Task *t);
|
||||
internal ASYNC_Task *async_task_launch_(Arena *arena, ASYNC_WorkFunctionType *work_function, ASYNC_WorkParams *params);
|
||||
#define async_task_launch(arena, work_function, ...) async_task_launch_((arena), (work_function), &(ASYNC_WorkParams){.endt_us = max_U64, __VA_ARGS__})
|
||||
internal void *async_task_join(ASYNC_Task *task);
|
||||
#define async_task_join_struct(task, T) (T *)async_task_join(task)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Work Execution
|
||||
|
||||
internal ASYNC_Work async_pop_work(void);
|
||||
internal void async_execute_work(ASYNC_Work work);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Work Thread Entry Point
|
||||
|
||||
internal void async_work_thread__entry_point(void *p);
|
||||
|
||||
#endif // ASYNC_H
|
||||
+9
-23
@@ -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/)
|
||||
|
||||
////////////////////////////////
|
||||
@@ -37,7 +37,6 @@ arena_alloc_(ArenaParams *params)
|
||||
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
|
||||
@@ -59,9 +58,8 @@ arena_alloc_(ArenaParams *params)
|
||||
arena->pos = ARENA_HEADER_SIZE;
|
||||
arena->cmt = commit_size;
|
||||
arena->res = reserve_size;
|
||||
arena->allocation_site_file = params->allocation_site_file;
|
||||
arena->allocation_site_line = params->allocation_site_line;
|
||||
#if ARENA_FREE_LIST
|
||||
arena->free_size = 0;
|
||||
arena->free_last = 0;
|
||||
#endif
|
||||
AsanPoisonMemoryRegion(base, commit_size);
|
||||
@@ -82,7 +80,7 @@ arena_release(Arena *arena)
|
||||
//- rjf: arena push/pop core functions
|
||||
|
||||
internal void *
|
||||
arena_push(Arena *arena, U64 size, U64 align, B32 zero)
|
||||
arena_push(Arena *arena, U64 size, U64 align)
|
||||
{
|
||||
Arena *current = arena->current;
|
||||
U64 pos_pre = AlignPow2(current->pos, align);
|
||||
@@ -94,11 +92,10 @@ arena_push(Arena *arena, U64 size, U64 align, B32 zero)
|
||||
Arena *new_block = 0;
|
||||
|
||||
#if ARENA_FREE_LIST
|
||||
{
|
||||
Arena *prev_block;
|
||||
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(new_block->res >= AlignPow2(size, align))
|
||||
{
|
||||
if(prev_block)
|
||||
{
|
||||
@@ -108,10 +105,11 @@ arena_push(Arena *arena, U64 size, U64 align, B32 zero)
|
||||
{
|
||||
arena->free_last = new_block->prev;
|
||||
}
|
||||
arena->free_size -= new_block->res_size;
|
||||
AsanUnpoisonMemoryRegion((U8*)new_block + ARENA_HEADER_SIZE, new_block->res_size - ARENA_HEADER_SIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(new_block == 0)
|
||||
@@ -125,9 +123,7 @@ arena_push(Arena *arena, U64 size, U64 align, B32 zero)
|
||||
}
|
||||
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);
|
||||
.flags = current->flags);
|
||||
}
|
||||
|
||||
new_block->base_pos = current->base_pos + current->res;
|
||||
@@ -138,13 +134,6 @@ arena_push(Arena *arena, U64 size, U64 align, B32 zero)
|
||||
pos_pst = pos_pre + 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)
|
||||
{
|
||||
@@ -171,10 +160,6 @@ arena_push(Arena *arena, U64 size, U64 align, B32 zero)
|
||||
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
|
||||
@@ -208,8 +193,9 @@ arena_pop_to(Arena *arena, U64 pos)
|
||||
{
|
||||
prev = current->prev;
|
||||
current->pos = ARENA_HEADER_SIZE;
|
||||
arena->free_size += current->res_size;
|
||||
SLLStackPush_N(arena->free_last, current, prev);
|
||||
AsanPoisonMemoryRegion((U8*)current + ARENA_HEADER_SIZE, current->res - ARENA_HEADER_SIZE);
|
||||
AsanPoisonMemoryRegion((U8*)current + ARENA_HEADER_SIZE, current->res_size - ARENA_HEADER_SIZE);
|
||||
}
|
||||
#else
|
||||
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
|
||||
|
||||
+14
-11
@@ -1,14 +1,17 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef BASE_ARENA_H
|
||||
#define BASE_ARENA_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Arena Types
|
||||
//~ rjf: Constants
|
||||
|
||||
#define ARENA_HEADER_SIZE 128
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Types
|
||||
|
||||
typedef U64 ArenaFlags;
|
||||
enum
|
||||
{
|
||||
@@ -23,8 +26,6 @@ struct ArenaParams
|
||||
U64 reserve_size;
|
||||
U64 commit_size;
|
||||
void *optional_backing_buffer;
|
||||
char *allocation_site_file;
|
||||
int allocation_site_line;
|
||||
};
|
||||
|
||||
typedef struct Arena Arena;
|
||||
@@ -39,9 +40,8 @@ struct Arena
|
||||
U64 pos;
|
||||
U64 cmt;
|
||||
U64 res;
|
||||
char *allocation_site_file;
|
||||
int allocation_site_line;
|
||||
#if ARENA_FREE_LIST
|
||||
U64 free_size;
|
||||
Arena *free_last;
|
||||
#endif
|
||||
};
|
||||
@@ -55,19 +55,22 @@ struct Temp
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Arena Functions
|
||||
//~ rjf: Global Defaults
|
||||
|
||||
global U64 arena_default_reserve_size = MB(64);
|
||||
global U64 arena_default_commit_size = KB(64);
|
||||
global ArenaFlags arena_default_flags = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Arena Functions
|
||||
|
||||
//- rjf: arena creation/destruction
|
||||
internal Arena *arena_alloc_(ArenaParams *params);
|
||||
#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__})
|
||||
#define arena_alloc(...) arena_alloc_(&(ArenaParams){.reserve_size = arena_default_reserve_size, .commit_size = arena_default_commit_size, .flags = arena_default_flags, __VA_ARGS__})
|
||||
internal void arena_release(Arena *arena);
|
||||
|
||||
//- rjf: arena push/pop/pos core functions
|
||||
internal void *arena_push(Arena *arena, U64 size, U64 align, B32 zero);
|
||||
internal void *arena_push(Arena *arena, U64 size, U64 align);
|
||||
internal U64 arena_pos(Arena *arena);
|
||||
internal void arena_pop_to(Arena *arena, U64 pos);
|
||||
|
||||
@@ -80,8 +83,8 @@ internal Temp temp_begin(Arena *arena);
|
||||
internal void temp_end(Temp temp);
|
||||
|
||||
//- rjf: push helper macros
|
||||
#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (0))
|
||||
#define push_array_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align), (1))
|
||||
#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align))
|
||||
#define push_array_aligned(a, T, c, align) (T *)MemoryZero(push_array_no_zero_aligned(a, T, c, align), sizeof(T)*(c))
|
||||
#define push_array_no_zero(a, T, c) push_array_no_zero_aligned(a, T, c, Max(8, AlignOf(T)))
|
||||
#define push_array(a, T, c) push_array_aligned(a, T, c, Max(8, AlignOf(T)))
|
||||
|
||||
|
||||
@@ -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/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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 **
|
||||
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;
|
||||
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;
|
||||
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->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->value_strings = values;
|
||||
StringJoin join = {0};
|
||||
@@ -72,25 +83,28 @@ cmd_line_from_string_list(Arena *arena, String8List command_line)
|
||||
{
|
||||
CmdLine parsed = {0};
|
||||
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 first_passthrough = 1;
|
||||
for(String8Node *node = command_line.first->next, *next = 0; node != 0; 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;
|
||||
if(!after_passthrough_option)
|
||||
|
||||
// NOTE(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 input files.
|
||||
B32 is_option = 1;
|
||||
if(after_passthrough_option == 0)
|
||||
{
|
||||
is_option = 1;
|
||||
if(str8_match(node->string, str8_lit("--"), 0))
|
||||
{
|
||||
after_passthrough_option = 1;
|
||||
@@ -104,7 +118,7 @@ cmd_line_from_string_list(Arena *arena, String8List command_line)
|
||||
{
|
||||
option_name = str8_skip(option_name, 1);
|
||||
}
|
||||
else if(OperatingSystem_CURRENT == OperatingSystem_Windows &&
|
||||
else if(operating_system_from_context() == OperatingSystem_Windows &&
|
||||
str8_match(str8_prefix(node->string, 1), str8_lit("/"), 0))
|
||||
{
|
||||
option_name = str8_skip(option_name, 1);
|
||||
@@ -114,61 +128,69 @@ cmd_line_from_string_list(Arena *arena, String8List command_line)
|
||||
is_option = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
is_option = 0;
|
||||
}
|
||||
|
||||
//- rjf: string is an option
|
||||
// NOTE(rjf): This string is an option.
|
||||
if(is_option)
|
||||
{
|
||||
// rjf: unpack option prefix
|
||||
B32 has_values = 0;
|
||||
U64 value_signifier_position1 = str8_find_needle(option_name, 0, str8_lit(":"), 0);
|
||||
U64 value_signifier_position2 = str8_find_needle(option_name, 0, str8_lit("="), 0);
|
||||
U64 value_signifier_position = Min(value_signifier_position1, value_signifier_position2);
|
||||
String8 value_portion_this_string = str8_skip(option_name, value_signifier_position+1);
|
||||
if(value_signifier_position < option_name.size)
|
||||
B32 has_arguments = 0;
|
||||
U64 arg_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 arg_signifier_position = Min(arg_signifier_position1, arg_signifier_position2);
|
||||
String8 arg_portion_this_string = str8_skip(option_name, arg_signifier_position+1);
|
||||
if(arg_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 values = {0};
|
||||
if(has_values)
|
||||
String8List arguments = {0};
|
||||
|
||||
// NOTE(rjf): Parse arguments.
|
||||
if(has_arguments)
|
||||
{
|
||||
for(String8Node *n = node; n; n = n->next)
|
||||
{
|
||||
next = n->next;
|
||||
|
||||
String8 string = n->string;
|
||||
if(n == node)
|
||||
{
|
||||
string = value_portion_this_string;
|
||||
string = arg_portion_this_string;
|
||||
}
|
||||
|
||||
U8 splits[] = { ',' };
|
||||
String8List values_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)
|
||||
String8List args_in_this_string = str8_split(arena, string, splits, ArrayCount(splits), 0);
|
||||
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) &&
|
||||
(n != node || value_portion_this_string.size != 0))
|
||||
(n != node || arg_portion_this_string.size != 0))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: store
|
||||
cmd_line_insert_opt(arena, &parsed, option_name, values);
|
||||
// NOTE(rjf): Register config variable.
|
||||
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)
|
||||
{
|
||||
str8_list_push(arena, &parsed.inputs, node->string);
|
||||
after_passthrough_option = 1;
|
||||
first_passthrough = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: fill argc/argv
|
||||
// rjf: fill argc/argv
|
||||
parsed.argc = command_line.node_count;
|
||||
parsed.argv = push_array(arena, char *, parsed.argc);
|
||||
{
|
||||
@@ -217,12 +239,12 @@ internal B32
|
||||
cmd_line_has_flag(CmdLine *cmd_line, String8 name)
|
||||
{
|
||||
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
|
||||
return (var != 0);
|
||||
return(var != 0);
|
||||
}
|
||||
|
||||
internal B32
|
||||
cmd_line_has_argument(CmdLine *cmd_line, String8 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);
|
||||
}
|
||||
|
||||
@@ -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/)
|
||||
|
||||
#ifndef BASE_COMMAND_LINE_H
|
||||
@@ -39,8 +39,9 @@ struct CmdLine
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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_opt_from_slot(CmdLineOpt **slot, String8 string);
|
||||
internal void cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var);
|
||||
|
||||
@@ -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/)
|
||||
|
||||
#ifndef BASE_CONTEXT_CRACKING_H
|
||||
@@ -159,7 +159,7 @@
|
||||
#endif
|
||||
|
||||
#if !defined(BUILD_VERSION_PATCH)
|
||||
# define BUILD_VERSION_PATCH 24
|
||||
# define BUILD_VERSION_PATCH 14
|
||||
#endif
|
||||
|
||||
#define BUILD_VERSION_STRING_LITERAL Stringify(BUILD_VERSION_MAJOR) "." Stringify(BUILD_VERSION_MINOR) "." Stringify(BUILD_VERSION_PATCH)
|
||||
|
||||
+49
-195
@@ -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/)
|
||||
|
||||
////////////////////////////////
|
||||
@@ -41,7 +41,7 @@ u128_zero(void)
|
||||
internal U128
|
||||
u128_make(U64 v0, U64 v1)
|
||||
{
|
||||
U128 v = { .u64 = { v0, v1 }};
|
||||
U128 v = {v0, v1};
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -246,8 +246,7 @@ sign_from_side_F32(Side side){
|
||||
//~ rjf: Memory Functions
|
||||
|
||||
internal B32
|
||||
memory_is_zero(void *ptr, U64 size)
|
||||
{
|
||||
memory_is_zero(void *ptr, U64 size){
|
||||
B32 result = 1;
|
||||
|
||||
// break down size
|
||||
@@ -258,10 +257,8 @@ memory_is_zero(void *ptr, U64 size)
|
||||
U64 *p64 = (U64*)ptr;
|
||||
if(result)
|
||||
{
|
||||
for(U64 i = 0; i < count8; i += 1, p64 += 1)
|
||||
{
|
||||
if(*p64 != 0)
|
||||
{
|
||||
for (U64 i = 0; i < count8; i += 1, p64 += 1){
|
||||
if (*p64 != 0){
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
@@ -272,10 +269,8 @@ memory_is_zero(void *ptr, U64 size)
|
||||
if(result)
|
||||
{
|
||||
U8 *p8 = (U8*)p64;
|
||||
for(U64 i = 0; i < extra; i += 1, p8 += 1)
|
||||
{
|
||||
if(*p8 != 0)
|
||||
{
|
||||
for (U64 i = 0; i < extra; i += 1, p8 += 1){
|
||||
if (*p8 != 0){
|
||||
result = 0;
|
||||
goto done;
|
||||
}
|
||||
@@ -283,7 +278,7 @@ memory_is_zero(void *ptr, U64 size)
|
||||
}
|
||||
|
||||
done:;
|
||||
return result;
|
||||
return(result);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
@@ -407,12 +402,6 @@ bit_size_from_arch(Arch arch)
|
||||
return arch_bitsize;
|
||||
}
|
||||
|
||||
internal U64
|
||||
byte_size_from_arch(Arch arch)
|
||||
{
|
||||
return bit_size_from_arch(arch) / 8;
|
||||
}
|
||||
|
||||
internal U64
|
||||
max_instruction_size_from_arch(Arch arch)
|
||||
{
|
||||
@@ -420,6 +409,47 @@ max_instruction_size_from_arch(Arch arch)
|
||||
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 Arch
|
||||
arch_from_context(void){
|
||||
Arch arch = Arch_Null;
|
||||
#if ARCH_X64
|
||||
arch = Arch_x64;
|
||||
#elif ARCH_X86
|
||||
arch = Arch_x86;
|
||||
#elif ARCH_ARM64
|
||||
arch = Arch_arm64;
|
||||
#elif ARCH_ARM32
|
||||
arch = Arch_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
|
||||
|
||||
@@ -484,60 +514,6 @@ date_time_from_micro_seconds(U64 time){
|
||||
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
|
||||
|
||||
@@ -572,125 +548,3 @@ ring_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_data, U64 read_s
|
||||
}
|
||||
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,
|
||||
};
|
||||
|
||||
+43
-229
@@ -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/)
|
||||
|
||||
#ifndef BASE_CORE_H
|
||||
@@ -13,13 +13,6 @@
|
||||
#include <string.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
|
||||
|
||||
@@ -45,24 +38,12 @@
|
||||
# define thread_static __declspec(thread)
|
||||
#elif COMPILER_CLANG || COMPILER_GCC
|
||||
# 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
|
||||
|
||||
////////////////////////////////
|
||||
@@ -84,45 +65,6 @@
|
||||
# define C_LINKAGE
|
||||
#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
|
||||
|
||||
@@ -168,14 +110,6 @@
|
||||
# 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
|
||||
|
||||
@@ -194,8 +128,6 @@
|
||||
#define EachElement(it, array) (U64 it = 0; it < ArrayCount(array); 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
|
||||
@@ -208,7 +140,6 @@
|
||||
#define MemoryCopyStruct(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 MemoryCopyStr8(dst, s) MemoryCopy(dst, (s).str, (s).size)
|
||||
|
||||
#define MemoryZero(s,z) memset((s),0,(z))
|
||||
#define MemoryZeroStruct(s) MemoryZero((s),sizeof(*(s)))
|
||||
@@ -219,8 +150,6 @@
|
||||
#define MemoryMatchStruct(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 MemoryConsume(T,p,e) ( ((p)+sizeof(T)<=(e))?((p)+=sizeof(T),*(T*)((p)-sizeof(T))):((p)=(e),0) )
|
||||
|
||||
@@ -252,46 +181,44 @@
|
||||
#if COMPILER_MSVC
|
||||
# include <intrin.h>
|
||||
# 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) *((volatile U64 *)(x))
|
||||
# define ins_atomic_u64_inc_eval(x) InterlockedIncrement64((__int64 *)(x))
|
||||
# define ins_atomic_u64_dec_eval(x) InterlockedDecrement64((__int64 *)(x))
|
||||
# define ins_atomic_u64_eval_assign(x,c) InterlockedExchange64((__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((__int64 *)(x),(k),(c))
|
||||
# define ins_atomic_u64_inc_eval(x) InterlockedIncrement64((volatile __int64 *)(x))
|
||||
# define ins_atomic_u64_dec_eval(x) InterlockedDecrement64((volatile __int64 *)(x))
|
||||
# define ins_atomic_u64_eval_assign(x,c) InterlockedExchange64((volatile __int64 *)(x),(c))
|
||||
# define ins_atomic_u64_add_eval(x,c) InterlockedAdd64((volatile __int64 *)(x), c)
|
||||
# define ins_atomic_u64_eval_cond_assign(x,k,c) InterlockedCompareExchange64((volatile __int64 *)(x),(k),(c))
|
||||
# define ins_atomic_u32_eval(x) *((volatile U32 *)(x))
|
||||
# define ins_atomic_u32_inc_eval(x) InterlockedIncrement((LONG *)(x))
|
||||
# define ins_atomic_u32_dec_eval(x) InterlockedDecrement((LONG *)(x))
|
||||
# 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))
|
||||
# define ins_atomic_u32_inc_eval(x) InterlockedIncrement((volatile LONG *)x)
|
||||
# define ins_atomic_u32_eval_assign(x,c) InterlockedExchange((volatile LONG *)(x),(c))
|
||||
# define ins_atomic_u32_eval_cond_assign(x,k,c) InterlockedCompareExchange((volatile LONG *)(x),(k),(c))
|
||||
# define ins_atomic_u32_add_eval(x,c) InterlockedAdd((volatile LONG *)(x), c)
|
||||
# else
|
||||
# error Atomic intrinsics not defined for this compiler / architecture combination.
|
||||
# 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_inc_eval(x) (__atomic_fetch_add((volatile U64 *)(x), 1, __ATOMIC_SEQ_CST) + 1)
|
||||
# define ins_atomic_u64_dec_eval(x) (__atomic_fetch_sub((volatile 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_u64_add_eval(x,c) (__atomic_fetch_add((volatile U64 *)(x), c, __ATOMIC_SEQ_CST) + (c))
|
||||
# define ins_atomic_u64_eval_cond_assign(x,k,c) ({ U64 _new = (c); __atomic_compare_exchange_n((volatile 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)
|
||||
# define ins_atomic_u32_inc_eval(x) (__atomic_fetch_add((volatile U32 *)(x), 1, __ATOMIC_SEQ_CST) + 1)
|
||||
# define ins_atomic_u32_add_eval(x,c) (__atomic_fetch_add((volatile 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((volatile U32 *)(x),&_new,(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST); _new; })
|
||||
#else
|
||||
# error Atomic intrinsics not defined for this compiler / architecture.
|
||||
#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)
|
||||
# define ins_atomic_ptr_eval_cond_assign(x,k,c) (void*)ins_atomic_u64_eval_cond_assign((volatile U64 *)(x), (U64)(k), (U64)(c))
|
||||
# define ins_atomic_ptr_eval_assign(x,c) (void*)ins_atomic_u64_eval_assign((volatile U64 *)(x), (U64)(c))
|
||||
# define ins_atomic_ptr_eval(x) (void*)ins_atomic_u64_eval((volatile U64 *)x)
|
||||
#elif ARCH_32BIT
|
||||
# define ins_atomic_ptr_eval_cond_assign(x,k,c) (void*)ins_atomic_u32_eval_cond_assign((volatile U32 *)(x), (U32)(k), (U32)(c))
|
||||
# define ins_atomic_ptr_eval_assign(x,c) (void*)ins_atomic_u32_eval_assign((volatile U32 *)(x), (U32)(c))
|
||||
# define ins_atomic_ptr_eval(x) (void*)ins_atomic_u32_eval((volatile U32 *)x)
|
||||
#else
|
||||
# error Atomic intrinsics for pointers not defined for this architecture.
|
||||
#endif
|
||||
@@ -379,6 +306,7 @@ CheckNil(nil,p) ? \
|
||||
#endif
|
||||
|
||||
#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_unpoison_memory_region(void const volatile *addr, size_t size);
|
||||
# define AsanPoisonMemoryRegion(addr, size) __asan_poison_memory_region((addr), (size))
|
||||
@@ -410,10 +338,9 @@ C_LINKAGE void __asan_unpoison_memory_region(void const volatile *addr, size_t s
|
||||
#else
|
||||
# error Missing pointer-to-integer cast for this architecture.
|
||||
#endif
|
||||
#define PtrFromInt(i) (void*)(i)
|
||||
#define PtrFromInt(i) (void*)((U8*)0 + (i))
|
||||
|
||||
#define Compose64Bit(a,b) ((((U64)a) << 32) | ((U64)b))
|
||||
#define Compose32Bit(a,b) ((((U32)a) << 16) | ((U32)b))
|
||||
#define Compose64Bit(a,b) ((((U64)a) << 32) | ((U64)b));
|
||||
#define AlignPow2(x,b) (((x) + (b) - 1)&(~((b) - 1)))
|
||||
#define AlignDownPow2(x,b) ((x)&(~((b) - 1)))
|
||||
#define AlignPadPow2(x,b) ((0-(x)) & ((b) - 1))
|
||||
@@ -421,9 +348,6 @@ C_LINKAGE void __asan_unpoison_memory_region(void const volatile *addr, size_t s
|
||||
#define IsPow2OrZero(x) ((((x) - 1)&(x)) == 0)
|
||||
|
||||
#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
|
||||
# define zero_struct {}
|
||||
@@ -455,64 +379,11 @@ typedef S64 B64;
|
||||
typedef float F32;
|
||||
typedef double F64;
|
||||
typedef void VoidProc(void);
|
||||
typedef union U128 U128;
|
||||
union U128
|
||||
typedef struct U128 U128;
|
||||
struct U128
|
||||
{
|
||||
U8 u8[16];
|
||||
U16 u16[8];
|
||||
U32 u32[4];
|
||||
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
|
||||
@@ -580,29 +451,9 @@ typedef enum OperatingSystem
|
||||
OperatingSystem_Linux,
|
||||
OperatingSystem_Mac,
|
||||
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;
|
||||
|
||||
typedef enum ExecutableImageKind
|
||||
{
|
||||
ExecutableImageKind_Null,
|
||||
ExecutableImageKind_CoffPe,
|
||||
ExecutableImageKind_Elf32,
|
||||
ExecutableImageKind_Elf64,
|
||||
ExecutableImageKind_Macho,
|
||||
ExecutableImageKind_COUNT
|
||||
}
|
||||
ExecutableImageKind;
|
||||
|
||||
typedef enum Arch
|
||||
{
|
||||
Arch_Null,
|
||||
@@ -611,17 +462,6 @@ typedef enum Arch
|
||||
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;
|
||||
|
||||
@@ -632,15 +472,6 @@ typedef enum Compiler
|
||||
Compiler_gcc,
|
||||
Compiler_clang,
|
||||
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;
|
||||
|
||||
@@ -662,7 +493,7 @@ struct TxtRng
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globally Unique Ids
|
||||
//~ Globally Unique Ids
|
||||
|
||||
typedef union Guid Guid;
|
||||
union Guid
|
||||
@@ -679,7 +510,7 @@ union Guid
|
||||
StaticAssert(sizeof(Guid) == 16, g_guid_size_check);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Constants
|
||||
//~ NOTE(allen): Constants
|
||||
|
||||
global U32 sign32 = 0x80000000;
|
||||
global U32 exponent32 = 0x7F800000;
|
||||
@@ -697,15 +528,15 @@ global U32 max_U32 = 0xffffffff;
|
||||
global U16 max_U16 = 0xffff;
|
||||
global U8 max_U8 = 0xff;
|
||||
|
||||
global S64 max_S64 = (S64)0x7fffffffffffffffll;
|
||||
global S64 max_S64 = (S64)0x7fffffffffffffffull;
|
||||
global S32 max_S32 = (S32)0x7fffffff;
|
||||
global S16 max_S16 = (S16)0x7fff;
|
||||
global S8 max_S8 = (S8)0x7f;
|
||||
|
||||
global S64 min_S64 = (S64)0x8000000000000000ll;
|
||||
global S32 min_S32 = (S32)0x80000000;
|
||||
global S16 min_S16 = (S16)0x8000;
|
||||
global S8 min_S8 = (S8)0x80;
|
||||
global S64 min_S64 = (S64)0xffffffffffffffffull;
|
||||
global S32 min_S32 = (S32)0xffffffff;
|
||||
global S16 min_S16 = (S16)0xffff;
|
||||
global S8 min_S8 = (S8)0xff;
|
||||
|
||||
global const U32 bitmask1 = 0x00000001;
|
||||
global const U32 bitmask2 = 0x00000003;
|
||||
@@ -840,7 +671,7 @@ global const U64 bit63 = (1ull<<62);
|
||||
global const U64 bit64 = (1ull<<63);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Time Types
|
||||
//~ allen: Time
|
||||
|
||||
typedef enum WeekDay
|
||||
{
|
||||
@@ -898,7 +729,7 @@ struct DateTime
|
||||
typedef U64 DenseTime;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: File Types
|
||||
//~ allen: Files
|
||||
|
||||
typedef U32 FilePropertyFlags;
|
||||
enum
|
||||
@@ -944,16 +775,6 @@ internal U16 bswap_u16(U16 x);
|
||||
internal U32 bswap_u32(U32 x);
|
||||
internal U64 bswap_u64(U64 x);
|
||||
|
||||
#if ARCH_LITTLE_ENDIAN
|
||||
# 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_set64(U64 val);
|
||||
|
||||
@@ -990,16 +811,18 @@ internal B32 txt_rng_contains(TxtRng r, TxtPt pt);
|
||||
//~ rjf: Toolchain/Environment Enum Functions
|
||||
|
||||
internal U64 bit_size_from_arch(Arch arch);
|
||||
internal U64 byte_size_from_arch(Arch arch);
|
||||
internal U64 max_instruction_size_from_arch(Arch arch);
|
||||
|
||||
internal OperatingSystem operating_system_from_context(void);
|
||||
internal Arch arch_from_context(void);
|
||||
internal Compiler compiler_from_context(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Time Functions
|
||||
|
||||
internal DenseTime dense_time_from_date_time(DateTime date_time);
|
||||
internal DateTime date_time_from_dense_time(DenseTime 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
|
||||
@@ -1014,13 +837,4 @@ internal U64 ring_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_dat
|
||||
|
||||
#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
|
||||
|
||||
+30
-134
@@ -1,26 +1,13 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Copyright (c) 2024 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
|
||||
main_thread_base_entry_point(int arguments_count, char **arguments)
|
||||
{
|
||||
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();
|
||||
ThreadNameF("[main thread]");
|
||||
|
||||
//- rjf: set up telemetry
|
||||
#if PROFILE_TELEMETRY
|
||||
@@ -30,19 +17,8 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
|
||||
tmInitialize(sizeof(tm_data), tm_data);
|
||||
#endif
|
||||
|
||||
//- rjf: set up spall
|
||||
#if PROFILE_SPALL
|
||||
spall_profile = spall_init_file_ex("spall_capture", 1, 0);
|
||||
#endif
|
||||
|
||||
//- 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]));
|
||||
}
|
||||
}
|
||||
String8List command_line_argument_strings = os_string_list_from_argcv(scratch.arena, arguments_count, arguments);
|
||||
CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings);
|
||||
|
||||
//- rjf: begin captures
|
||||
@@ -50,27 +26,36 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
|
||||
if(capture)
|
||||
{
|
||||
ProfBeginCapture(arguments[0]);
|
||||
ProfMsg(BUILD_TITLE);
|
||||
}
|
||||
|
||||
#if PROFILE_TELEMETRY
|
||||
tmMessage(0, TMMF_ICON_NOTE, BUILD_TITLE);
|
||||
#endif
|
||||
|
||||
//- rjf: initialize all included layers
|
||||
#if defined(ARTIFACT_CACHE_H) && !defined(AC_INIT_MANUAL)
|
||||
ac_init();
|
||||
#endif
|
||||
#if defined(ASYNC_H) && !defined(ASYNC_INIT_MANUAL)
|
||||
async_init(&cmdline);
|
||||
async_init();
|
||||
#endif
|
||||
#if defined(CONTENT_H) && !defined(C_INIT_MANUAL)
|
||||
c_init();
|
||||
#if defined(RDI_FROM_PDB_H) && !defined(P2R_INIT_MANUAL)
|
||||
p2r_init();
|
||||
#endif
|
||||
#if defined(HASH_STORE_H) && !defined(HS_INIT_MANUAL)
|
||||
hs_init();
|
||||
#endif
|
||||
#if defined(FILE_STREAM_H) && !defined(FS_INIT_MANUAL)
|
||||
fs_init();
|
||||
#endif
|
||||
#if defined(TEXT_CACHE_H) && !defined(TXT_INIT_MANUAL)
|
||||
txt_init();
|
||||
#endif
|
||||
#if defined(MUTABLE_TEXT_H) && !defined(MTX_INIT_MANUAL)
|
||||
mtx_init();
|
||||
#endif
|
||||
#if defined(DBG_INFO_H) && !defined(DI_INIT_MANUAL)
|
||||
di_init(&cmdline);
|
||||
#if defined(DASM_CACHE_H) && !defined(DASM_INIT_MANUAL)
|
||||
dasm_init();
|
||||
#endif
|
||||
#if defined(DBGI_H) && !defined(DI_INIT_MANUAL)
|
||||
di_init();
|
||||
#endif
|
||||
#if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL)
|
||||
dmn_init();
|
||||
@@ -87,6 +72,12 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
|
||||
#if defined(RENDER_CORE_H) && !defined(R_INIT_MANUAL)
|
||||
r_init(&cmdline);
|
||||
#endif
|
||||
#if defined(TEXTURE_CACHE_H) && !defined(TEX_INIT_MANUAL)
|
||||
tex_init();
|
||||
#endif
|
||||
#if defined(GEO_CACHE_H) && !defined(GEO_INIT_MANUAL)
|
||||
geo_init();
|
||||
#endif
|
||||
#if defined(FONT_CACHE_H) && !defined(FNT_INIT_MANUAL)
|
||||
fnt_init();
|
||||
#endif
|
||||
@@ -97,48 +88,9 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
|
||||
rd_init(&cmdline);
|
||||
#endif
|
||||
|
||||
//- rjf: launch async threads
|
||||
Thread *async_threads = 0;
|
||||
U64 lane_broadcast_val = 0;
|
||||
{
|
||||
U64 num_main_threads = 1;
|
||||
#if defined(CTRL_CORE_H)
|
||||
num_main_threads += 1;
|
||||
#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);
|
||||
|
||||
//- 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)
|
||||
{
|
||||
@@ -151,10 +103,10 @@ main_thread_base_entry_point(int arguments_count, char **arguments)
|
||||
internal void
|
||||
supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params)
|
||||
{
|
||||
TCTX *tctx = tctx_alloc();
|
||||
tctx_select(tctx);
|
||||
TCTX tctx;
|
||||
tctx_init_and_equip(&tctx);
|
||||
entry_point(params);
|
||||
tctx_release(tctx);
|
||||
tctx_release();
|
||||
}
|
||||
|
||||
internal U64
|
||||
@@ -169,9 +121,6 @@ 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
|
||||
@@ -179,56 +128,3 @@ update(void)
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/)
|
||||
|
||||
#ifndef BASE_ENTRY_POINT_H
|
||||
@@ -8,6 +8,5 @@ internal void main_thread_base_entry_point(int argc, char **argv);
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
-5
@@ -1,19 +1,17 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Base Includes
|
||||
|
||||
#undef LAYER_COLOR
|
||||
#define LAYER_COLOR 0x3399ccff
|
||||
#undef MARKUP_LAYER_COLOR
|
||||
#define MARKUP_LAYER_COLOR 0.20f, 0.60f, 0.80f
|
||||
|
||||
#include "base_core.c"
|
||||
#include "base_profile.c"
|
||||
#include "base_arena.c"
|
||||
#include "base_math.c"
|
||||
#include "base_strings.c"
|
||||
#include "base_hash.c"
|
||||
#include "base_threads.c"
|
||||
#include "base_thread_context.c"
|
||||
#include "base_command_line.c"
|
||||
#include "base_markup.c"
|
||||
|
||||
+1
-3
@@ -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/)
|
||||
|
||||
#ifndef BASE_INC_H
|
||||
@@ -14,8 +14,6 @@
|
||||
#include "base_arena.h"
|
||||
#include "base_math.h"
|
||||
#include "base_strings.h"
|
||||
#include "base_hash.h"
|
||||
#include "base_threads.h"
|
||||
#include "base_thread_context.h"
|
||||
#include "base_command_line.h"
|
||||
#include "base_markup.h"
|
||||
|
||||
+1
-1
@@ -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/)
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
+4
-4
@@ -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/)
|
||||
|
||||
#ifndef BASE_LOG_H
|
||||
@@ -49,12 +49,12 @@ internal void log_select(Log *log);
|
||||
internal void log_msg(LogMsgKind kind, String8 string);
|
||||
internal void log_msgf(LogMsgKind kind, char *fmt, ...);
|
||||
#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_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"))
|
||||
#define LogInfoNamedBlockF(fmt, ...) DeferLoop((log_infof(fmt, __VA_ARGS__), log_infof(":\n{\n")), log_infof("}\n"))
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Log Scopes
|
||||
|
||||
@@ -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/)
|
||||
|
||||
internal void
|
||||
|
||||
+3
-13
@@ -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/)
|
||||
|
||||
#ifndef BASE_MARKUP_H
|
||||
#define BASE_MARKUP_H
|
||||
|
||||
#define RADDBG_MARKUP_IMPLEMENTATION
|
||||
#define RADDBG_MARKUP_VSNPRINTF raddbg_vsnprintf
|
||||
#if OS_LINUX
|
||||
# define RADDBG_MARKUP_STUBS
|
||||
#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))
|
||||
#define ThreadNameF(...) (set_thread_namef(__VA_ARGS__))
|
||||
#define ThreadName(str) (set_thread_name(str))
|
||||
|
||||
#endif // BASE_MARKUP_H
|
||||
|
||||
+22
-236
@@ -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/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scalar Math Ops
|
||||
//~ rjf: Scalar Ops
|
||||
|
||||
internal F32
|
||||
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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;}
|
||||
|
||||
////////////////////////////////
|
||||
@@ -389,21 +386,6 @@ derotate_4x4f32(Mat4x4F32 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
|
||||
|
||||
@@ -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 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 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 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;}
|
||||
@@ -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 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 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 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;}
|
||||
@@ -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 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 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 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;}
|
||||
@@ -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 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 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 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;}
|
||||
@@ -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 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 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 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;}
|
||||
@@ -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 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 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 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;}
|
||||
@@ -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 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 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 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;}
|
||||
@@ -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 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 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 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;}
|
||||
@@ -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 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 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 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;}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Color Operations
|
||||
|
||||
//- rjf: hsv <-> rgb
|
||||
//~ rjf: Miscellaneous Ops
|
||||
|
||||
internal Vec3F32
|
||||
hsv_from_rgb(Vec3F32 rgb)
|
||||
@@ -587,102 +567,16 @@ rgba_from_hsva(Vec4F32 hsva)
|
||||
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
|
||||
linear_from_srgba(Vec4F32 srgba)
|
||||
rgba_from_u32(U32 hex)
|
||||
{
|
||||
Vec4F32 result;
|
||||
result.xyz = linear_from_srgb(srgba.xyz);
|
||||
result.w = srgba.w;
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
u32_from_rgba(Vec4F32 rgba)
|
||||
{
|
||||
@@ -694,100 +588,9 @@ u32_from_rgba(Vec4F32 rgba)
|
||||
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
|
||||
|
||||
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
|
||||
rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng)
|
||||
{
|
||||
@@ -811,20 +614,3 @@ rng1s64_array_from_list(Arena *arena, Rng1S64List *list)
|
||||
}
|
||||
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
@@ -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/)
|
||||
|
||||
#ifndef BASE_MATH_H
|
||||
@@ -327,29 +327,7 @@ union Rng2S64
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Range 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;
|
||||
};
|
||||
//~ rjf: List Types
|
||||
|
||||
typedef struct Rng1S64Node Rng1S64Node;
|
||||
struct Rng1S64Node
|
||||
@@ -374,20 +352,19 @@ struct Rng1S64Array
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Scalar Math Ops
|
||||
//~ rjf: Scalar Ops
|
||||
|
||||
#define abs_s64(v) (S64)llabs(v)
|
||||
|
||||
#define sqrt_f32(v) sqrtf(v)
|
||||
#define cbrt_f32(v) cbrtf(v)
|
||||
#define mod_f32(a, b) fmodf((a), (b))
|
||||
#define pow_f32(b, e) powf((b), (e))
|
||||
#define ceil_f32(v) ceilf(v)
|
||||
#define floor_f32(v) floorf(v)
|
||||
#define round_f32(v) roundf(v)
|
||||
#define abs_f32(v) fabsf(v)
|
||||
#define radians_from_turns_f32(v) ((v)*(2*3.1415926535897f))
|
||||
#define turns_from_radians_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 degrees_from_turns_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)))
|
||||
@@ -397,15 +374,14 @@ struct Rng1S64Array
|
||||
#define tan_f32(v) tanf(radians_from_turns_f32(v))
|
||||
|
||||
#define sqrt_f64(v) sqrt(v)
|
||||
#define cbrt_f64(v) cbrt(v)
|
||||
#define mod_f64(a, b) fmod((a), (b))
|
||||
#define pow_f64(b, e) pow((b), (e))
|
||||
#define ceil_f64(v) ceil(v)
|
||||
#define floor_f64(v) floor(v)
|
||||
#define round_f64(v) round(v)
|
||||
#define abs_f64(v) fabs(v)
|
||||
#define radians_from_turns_f64(v) ((v)*(2*3.1415926535897))
|
||||
#define turns_from_radians_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 degrees_from_turns_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)))
|
||||
@@ -443,6 +419,7 @@ internal Vec2S64 scale_2s64(Vec2S64 v, S64 s);
|
||||
internal S64 dot_2s64(Vec2S64 a, Vec2S64 b);
|
||||
internal S64 length_squared_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);
|
||||
|
||||
#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 length_squared_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);
|
||||
|
||||
#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 length_squared_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);
|
||||
|
||||
#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 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t);
|
||||
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))
|
||||
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 length_squared_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 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 length_squared_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);
|
||||
|
||||
////////////////////////////////
|
||||
@@ -541,7 +521,6 @@ internal Mat4x4F32 mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b);
|
||||
internal Mat4x4F32 scale_4x4f32(Mat4x4F32 m, F32 scale);
|
||||
internal Mat4x4F32 inverse_4x4f32(Mat4x4F32 m);
|
||||
internal Mat4x4F32 derotate_4x4f32(Mat4x4F32 mat);
|
||||
internal Mat4x4F32 transpose_4x4f32(Mat4x4F32 mat);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Range Ops
|
||||
@@ -650,46 +629,21 @@ internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b);
|
||||
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 rgb_from_hsv(Vec3F32 hsv);
|
||||
internal Vec4F32 hsva_from_rgba(Vec4F32 rgba);
|
||||
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 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 }
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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 Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list);
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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
|
||||
#endif // BASE_MATH_H
|
||||
|
||||
@@ -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/)
|
||||
|
||||
////////////////////////////////
|
||||
@@ -45,6 +45,7 @@ typed_data_rebase_ptrs(Type *type, String8 data, void *base_ptr)
|
||||
{
|
||||
default:{}break;
|
||||
case TypeKind_Ptr:
|
||||
if(!(t->type->flags & TypeFlag_IsExternal))
|
||||
{
|
||||
*(U64 *)t->ptr = ((U64)(*(U8 **)t->ptr - (U8 *)base_ptr));
|
||||
}break;
|
||||
|
||||
@@ -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/)
|
||||
|
||||
#ifndef BASE_META_H
|
||||
@@ -100,9 +100,10 @@ TypeKind;
|
||||
typedef U32 TypeFlags;
|
||||
enum
|
||||
{
|
||||
TypeFlag_IsPlainText = (1<<0),
|
||||
TypeFlag_IsCodeText = (1<<1),
|
||||
TypeFlag_IsPathText = (1<<2),
|
||||
TypeFlag_IsExternal = (1<<0),
|
||||
TypeFlag_IsPlainText = (1<<1),
|
||||
TypeFlag_IsCodeText = (1<<2),
|
||||
TypeFlag_IsPathText = (1<<3),
|
||||
};
|
||||
|
||||
typedef U32 MemberFlags;
|
||||
|
||||
+1
-30
@@ -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/)
|
||||
|
||||
#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
|
||||
|
||||
+23
-53
@@ -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/)
|
||||
|
||||
#ifndef BASE_PROFILE_H
|
||||
@@ -10,8 +10,9 @@
|
||||
#if !defined(PROFILE_TELEMETRY)
|
||||
# define PROFILE_TELEMETRY 0
|
||||
#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
|
||||
|
||||
////////////////////////////////
|
||||
@@ -20,11 +21,8 @@
|
||||
#if PROFILE_TELEMETRY
|
||||
# include "rad_tm.h"
|
||||
# if OS_WINDOWS
|
||||
# pragma comment(lib, "ws2_32.lib")
|
||||
# pragma comment(lib, "rad_tm_win64.lib")
|
||||
# endif
|
||||
#elif PROFILE_SPALL
|
||||
# include "spall.h"
|
||||
#endif
|
||||
|
||||
////////////////////////////////
|
||||
@@ -44,55 +42,27 @@
|
||||
# define ProfEndLockWait(...) tmEndWaitForLock(0)
|
||||
# define ProfLockTake(...) tmAcquiredLock(0, 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); \
|
||||
}
|
||||
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(...)
|
||||
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
|
||||
|
||||
////////////////////////////////
|
||||
|
||||
+415
-1011
File diff suppressed because it is too large
Load Diff
+46
-95
@@ -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/)
|
||||
|
||||
#ifndef 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
|
||||
|
||||
@@ -38,6 +44,13 @@ struct String8Node
|
||||
String8 string;
|
||||
};
|
||||
|
||||
typedef struct String8MetaNode String8MetaNode;
|
||||
struct String8MetaNode
|
||||
{
|
||||
String8MetaNode *next;
|
||||
String8Node *node;
|
||||
};
|
||||
|
||||
typedef struct String8List String8List;
|
||||
struct String8List
|
||||
{
|
||||
@@ -52,7 +65,6 @@ struct String8Array
|
||||
{
|
||||
String8 *v;
|
||||
U64 count;
|
||||
U64 total_size;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
@@ -84,7 +96,7 @@ typedef enum PathStyle
|
||||
#elif OS_LINUX
|
||||
PathStyle_SystemAbsolute = PathStyle_UnixAbsolute
|
||||
#else
|
||||
# error Absolute path style is undefined for this OS.
|
||||
# error "absolute path style is undefined for this OS"
|
||||
#endif
|
||||
}
|
||||
PathStyle;
|
||||
@@ -146,9 +158,9 @@ internal B32 char_is_lower(U8 c);
|
||||
internal B32 char_is_alpha(U8 c);
|
||||
internal B32 char_is_slash(U8 c);
|
||||
internal B32 char_is_digit(U8 c, U32 base);
|
||||
internal U8 lower_from_char(U8 c);
|
||||
internal U8 upper_from_char(U8 c);
|
||||
internal U8 correct_slash_from_char(U8 c);
|
||||
internal U8 char_to_lower(U8 c);
|
||||
internal U8 char_to_upper(U8 c);
|
||||
internal U8 char_to_correct_slash(U8 c);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: C-String Measurement
|
||||
@@ -194,13 +206,10 @@ internal String8 backslashed_from_str8(Arena *arena, String8 string);
|
||||
////////////////////////////////
|
||||
//~ 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 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_is_before(String8 a, String8 b);
|
||||
#define str8_ends_with(string, end, flags) str8_match(str8_postfix((string), (end).size), (end), (flags))
|
||||
internal B32 str8_ends_with(String8 string, String8 end, StringMatchFlags flags);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Slicing
|
||||
@@ -211,21 +220,14 @@ internal String8 str8_skip(String8 str, U64 amt);
|
||||
internal String8 str8_postfix(String8 str, U64 size);
|
||||
internal String8 str8_chop(String8 str, U64 amt);
|
||||
internal String8 str8_skip_chop_whitespace(String8 string);
|
||||
internal String8 str8_skip_chop_slashes(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String Formatting & Copying
|
||||
|
||||
internal String8 str8_cat(Arena *arena, String8 s1, String8 s2);
|
||||
internal String8 str8_copy(Arena *arena, String8 s);
|
||||
internal String8 str8fv(Arena *arena, char *fmt, va_list args);
|
||||
internal String8 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.
|
||||
internal String8 push_str8_cat(Arena *arena, String8 s1, String8 s2);
|
||||
internal String8 push_str8_copy(Arena *arena, String8 s);
|
||||
internal String8 push_str8fv(Arena *arena, char *fmt, va_list args);
|
||||
internal String8 push_str8f(Arena *arena, char *fmt, ...);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String <=> Integer Conversions
|
||||
@@ -235,8 +237,6 @@ internal S64 sign_from_str8(String8 string, String8 *string_tail);
|
||||
internal B32 str8_is_integer(String8 string, U32 radix);
|
||||
internal U64 u64_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_s64_from_str8_c_rules(String8 string, S64 *x);
|
||||
|
||||
@@ -256,12 +256,12 @@ internal F64 f64_from_str8(String8 string);
|
||||
////////////////////////////////
|
||||
//~ rjf: String List Construction Functions
|
||||
|
||||
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_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(Arena *arena, String8List *list, String8 string);
|
||||
internal String8Node *str8_list_push_front(Arena *arena, String8List *list, String8 string);
|
||||
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_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(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 String8Node* str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align);
|
||||
internal String8Node* str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...);
|
||||
@@ -274,44 +274,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_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);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Data Stringification Helpers
|
||||
|
||||
internal String8List numeric_str8_list_from_data(Arena *arena, U32 radix, String8 data, U64 stride);
|
||||
internal void str8_list_from_flags(Arena *arena, String8List *list, U32 flags, String8 *flag_string_table, U32 flag_string_count);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf; String Arrays
|
||||
|
||||
internal String8Array str8_array_zero(void);
|
||||
internal String8Array str8_array_from_list(Arena *arena, String8List *list);
|
||||
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
|
||||
|
||||
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_skip_last_slash(String8 string);
|
||||
internal String8 str8_chop_last_dot(String8 string);
|
||||
@@ -324,29 +299,6 @@ internal String8 str8_path_list_join_by_style(Arena *arena, String8List *pat
|
||||
|
||||
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
|
||||
|
||||
@@ -354,6 +306,7 @@ internal UnicodeDecode utf8_decode(U8 *str, U64 max);
|
||||
internal UnicodeDecode utf16_decode(U16 *str, U64 max);
|
||||
internal U32 utf8_encode(U8 *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
|
||||
@@ -364,21 +317,29 @@ internal String8 str8_from_32(Arena *arena, String32 in);
|
||||
internal String32 str32_from_8(Arena *arena, String8 in);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Types & Space Enum <-> String Conversions
|
||||
//~ String -> Enum Conversions
|
||||
|
||||
internal OperatingSystem operating_system_from_string(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Types & Space Enum -> String Conversions
|
||||
|
||||
internal String8 string_from_dimension(Dimension dimension);
|
||||
internal String8 string_from_side(Side side);
|
||||
internal String8 string_from_operating_system(OperatingSystem os);
|
||||
internal String8 string_from_arch(Arch arch);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Time Types -> String
|
||||
|
||||
internal String8 string_from_week_day(WeekDay week_day);
|
||||
internal String8 string_from_month(Month month);
|
||||
internal String8 string_from_date_time(Arena *arena, DateTime *date_time);
|
||||
internal String8 string_from_date_time__file_name(Arena *arena, DateTime *date_time);
|
||||
internal String8 push_date_time_string(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);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: String <-> Globally Unique IDs
|
||||
//~ Globally Unique Ids
|
||||
|
||||
internal String8 string_from_guid(Arena *arena, Guid guid);
|
||||
internal B32 try_guid_from_string(String8 string, Guid *guid_out);
|
||||
@@ -428,7 +389,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_cstr(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_struct(arena, srl, ptr) str8_serial_push_array(arena, srl, ptr, 1)
|
||||
|
||||
@@ -437,19 +397,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_find_first_match(String8 string, U64 off, U16 scan_val);
|
||||
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 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_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);
|
||||
#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)
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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);
|
||||
#define str8_deserial_read_struct(string, off, ptr) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr)), sizeof(*(ptr)))
|
||||
|
||||
#endif // BASE_STRINGS_H
|
||||
|
||||
+38
-176
@@ -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/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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
|
||||
C_LINKAGE thread_static TCTX *tctx_thread_local = 0;
|
||||
C_LINKAGE thread_static TCTX* tctx_thread_local = 0;
|
||||
#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
|
||||
tctx_release(TCTX *tctx)
|
||||
{
|
||||
arena_release(tctx->arenas[1]);
|
||||
arena_release(tctx->arenas[0]);
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_select(TCTX *tctx)
|
||||
{
|
||||
tctx_init_and_equip(TCTX *tctx){
|
||||
MemoryZeroStruct(tctx);
|
||||
Arena **arena_ptr = tctx->arenas;
|
||||
for (U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1){
|
||||
*arena_ptr = arena_alloc();
|
||||
}
|
||||
tctx_thread_local = tctx;
|
||||
}
|
||||
|
||||
internal TCTX *
|
||||
tctx_selected(void)
|
||||
internal 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 **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;
|
||||
B32 has_conflict = 0;
|
||||
for(U64 j = 0; j < count; j += 1, conflict_ptr += 1)
|
||||
{
|
||||
if(*arena_ptr == *conflict_ptr)
|
||||
{
|
||||
for (U64 j = 0; j < count; j += 1, conflict_ptr += 1){
|
||||
if (*arena_ptr == *conflict_ptr){
|
||||
has_conflict = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!has_conflict)
|
||||
{
|
||||
if (!has_conflict){
|
||||
result = *arena_ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//- rjf: lane metadata
|
||||
|
||||
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;
|
||||
return(result);
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_lane_barrier_wait(void *broadcast_ptr, U64 broadcast_size, U64 broadcast_src_lane_idx)
|
||||
{
|
||||
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();
|
||||
tctx_set_thread_name(String8 string){
|
||||
TCTX *tctx = tctx_get_equipped();
|
||||
U64 size = ClampTop(string.size, sizeof(tctx->thread_name));
|
||||
MemoryCopy(tctx->thread_name, string.str, size);
|
||||
tctx->thread_name_size = size;
|
||||
}
|
||||
|
||||
internal String8
|
||||
tctx_get_thread_name(void)
|
||||
{
|
||||
TCTX *tctx = tctx_selected();
|
||||
tctx_get_thread_name(void){
|
||||
TCTX *tctx = tctx_get_equipped();
|
||||
String8 result = str8(tctx->thread_name, tctx->thread_name_size);
|
||||
return result;
|
||||
return(result);
|
||||
}
|
||||
|
||||
//- rjf: thread source-locations
|
||||
|
||||
internal void
|
||||
tctx_write_srcloc(char *file_name, U64 line_number)
|
||||
{
|
||||
TCTX *tctx = tctx_selected();
|
||||
tctx_write_srcloc(char *file_name, U64 line_number){
|
||||
TCTX *tctx = tctx_get_equipped();
|
||||
tctx->file_name = file_name;
|
||||
tctx->line_number = line_number;
|
||||
}
|
||||
|
||||
internal void
|
||||
tctx_read_srcloc(char **file_name, U64 *line_number)
|
||||
{
|
||||
TCTX *tctx = tctx_selected();
|
||||
tctx_read_srcloc(char **file_name, U64 *line_number){
|
||||
TCTX *tctx = tctx_get_equipped();
|
||||
*file_name = tctx->file_name;
|
||||
*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;
|
||||
}
|
||||
|
||||
@@ -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/)
|
||||
|
||||
#ifndef BASE_THREAD_CONTEXT_H
|
||||
#define BASE_THREAD_CONTEXT_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Lane 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
|
||||
// NOTE(allen): Thread Context
|
||||
|
||||
typedef struct TCTX TCTX;
|
||||
struct TCTX
|
||||
{
|
||||
// rjf: scratch arenas
|
||||
Arena *arenas[2];
|
||||
|
||||
// rjf: thread name
|
||||
U8 thread_name[32];
|
||||
U64 thread_name_size;
|
||||
|
||||
// rjf: lane context
|
||||
LaneCtx lane_ctx;
|
||||
|
||||
// rjf: source location info
|
||||
char *file_name;
|
||||
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 TCTX *tctx_alloc(void);
|
||||
internal void tctx_release(TCTX *tctx);
|
||||
internal void tctx_select(TCTX *tctx);
|
||||
internal TCTX *tctx_selected(void);
|
||||
internal void tctx_init_and_equip(TCTX *tctx);
|
||||
internal void tctx_release(void);
|
||||
internal TCTX* tctx_get_equipped(void);
|
||||
|
||||
//- 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_end(scratch) temp_end(scratch)
|
||||
internal Arena* tctx_get_scratch(Arena **conflicts, U64 countt);
|
||||
|
||||
//- rjf: lane metadata
|
||||
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)
|
||||
#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count)))
|
||||
#define scratch_end(scratch) temp_end(scratch)
|
||||
|
||||
#endif // BASE_THREAD_CONTEXT_H
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
+594
-1057
File diff suppressed because it is too large
Load Diff
+1246
-1146
File diff suppressed because it is too large
Load Diff
+33
-33
@@ -395,48 +395,48 @@ CV_BasicTypeTable:
|
||||
{FBASICSTR 0x06 "" }
|
||||
{NOTTRANS 0x07 "" }
|
||||
{HRESULT 0x08 "HRESULT" }
|
||||
{CHAR 0x10 "CHAR" }
|
||||
{SHORT 0x11 "SHORT" }
|
||||
{LONG 0x12 "LONG" }
|
||||
{QUAD 0x13 "QUAD" }
|
||||
{OCT 0x14 "OCT" }
|
||||
{CHAR 0x10 "char" }
|
||||
{SHORT 0x11 "S16" }
|
||||
{LONG 0x12 "S32" }
|
||||
{QUAD 0x13 "S64" }
|
||||
{OCT 0x14 "S128" }
|
||||
{UCHAR 0x20 "UCHAR" }
|
||||
{USHORT 0x21 "USHORT" }
|
||||
{ULONG 0x22 "ULONG" }
|
||||
{UQUAD 0x23 "UQUAD" }
|
||||
{UOCT 0x24 "UOCT" }
|
||||
{BOOL8 0x30 "BOOL8" }
|
||||
{BOOL16 0x31 "BOOL16" }
|
||||
{BOOL32 0x32 "BOOL32" }
|
||||
{BOOL64 0x33 "BOOL64" }
|
||||
{FLOAT32 0x40 "FLOAT32" }
|
||||
{FLOAT64 0x41 "FLOAT64" }
|
||||
{FLOAT80 0x42 "FLOAT80" }
|
||||
{FLOAT128 0x43 "FLOAT128" }
|
||||
{FLOAT48 0x44 "FLOAT48" }
|
||||
{FLOAT32PP 0x45 "FLOAT32PP" }
|
||||
{FLOAT16 0x46 "FLOAT16" }
|
||||
{USHORT 0x21 "U16" }
|
||||
{ULONG 0x22 "U32" }
|
||||
{UQUAD 0x23 "U64" }
|
||||
{UOCT 0x24 "U128" }
|
||||
{BOOL8 0x30 "B8" }
|
||||
{BOOL16 0x31 "B16" }
|
||||
{BOOL32 0x32 "B32" }
|
||||
{BOOL64 0x33 "B64" }
|
||||
{FLOAT32 0x40 "F32" }
|
||||
{FLOAT64 0x41 "F64" }
|
||||
{FLOAT80 0x42 "F80" }
|
||||
{FLOAT128 0x43 "F128" }
|
||||
{FLOAT48 0x44 "F48" }
|
||||
{FLOAT32PP 0x45 "F32PP" }
|
||||
{FLOAT16 0x46 "F16" }
|
||||
{COMPLEX32 0x50 "ComplexF32" }
|
||||
{COMPLEX64 0x51 "ComplexF64" }
|
||||
{COMPLEX80 0x52 "ComplexF80" }
|
||||
{COMPLEX128 0x53 "ComplexF128" }
|
||||
{BIT 0x60 "" }
|
||||
{PASCHAR 0x61 "" }
|
||||
{BOOL32FF 0x62 "BOOL32FF" }
|
||||
{INT8 0x68 "int8" }
|
||||
{UINT8 0x69 "uint8" }
|
||||
{BOOL32FF 0x62 "B32FF" }
|
||||
{INT8 0x68 "S8" }
|
||||
{UINT8 0x69 "U8" }
|
||||
{RCHAR 0x70 "char" }
|
||||
{WCHAR 0x71 "WCHAR" }
|
||||
{INT16 0x72 "int16" }
|
||||
{UINT16 0x73 "uint16" }
|
||||
{INT32 0x74 "int32" }
|
||||
{UINT32 0x75 "uint32" }
|
||||
{INT64 0x76 "int64" }
|
||||
{UINT64 0x77 "uint64" }
|
||||
{INT128 0x78 "int128" }
|
||||
{UINT128 0x79 "uint128" }
|
||||
{CHAR16 0x7a "char16" }
|
||||
{CHAR32 0x7b "char32" }
|
||||
{INT16 0x72 "S16" }
|
||||
{UINT16 0x73 "U16" }
|
||||
{INT32 0x74 "S32" }
|
||||
{UINT32 0x75 "U32" }
|
||||
{INT64 0x76 "S64" }
|
||||
{UINT64 0x77 "U64" }
|
||||
{INT128 0x78 "S128" }
|
||||
{UINT128 0x79 "U128" }
|
||||
{CHAR16 0x7a "CHAR16" }
|
||||
{CHAR32 0x7b "CHAR32" }
|
||||
{CHAR8 0x7c "char" }
|
||||
{PTR 0xf0 "PTR" }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -0,0 +1,84 @@
|
||||
// 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_C13SubSectionKind 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);
|
||||
|
||||
internal String8 cv_string_from_inline_range_kind(CV_InlineRangeKind kind);
|
||||
|
||||
#endif // CODEVIEW_STRINGIZE_H
|
||||
@@ -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/)
|
||||
|
||||
//- 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_NOTTRANS:{result = str8_lit("");}break;
|
||||
case CV_BasicType_HRESULT:{result = str8_lit("HRESULT");}break;
|
||||
case CV_BasicType_CHAR:{result = str8_lit("CHAR");}break;
|
||||
case CV_BasicType_SHORT:{result = str8_lit("SHORT");}break;
|
||||
case CV_BasicType_LONG:{result = str8_lit("LONG");}break;
|
||||
case CV_BasicType_QUAD:{result = str8_lit("QUAD");}break;
|
||||
case CV_BasicType_OCT:{result = str8_lit("OCT");}break;
|
||||
case CV_BasicType_CHAR:{result = str8_lit("char");}break;
|
||||
case CV_BasicType_SHORT:{result = str8_lit("S16");}break;
|
||||
case CV_BasicType_LONG:{result = str8_lit("S32");}break;
|
||||
case CV_BasicType_QUAD:{result = str8_lit("S64");}break;
|
||||
case CV_BasicType_OCT:{result = str8_lit("S128");}break;
|
||||
case CV_BasicType_UCHAR:{result = str8_lit("UCHAR");}break;
|
||||
case CV_BasicType_USHORT:{result = str8_lit("USHORT");}break;
|
||||
case CV_BasicType_ULONG:{result = str8_lit("ULONG");}break;
|
||||
case CV_BasicType_UQUAD:{result = str8_lit("UQUAD");}break;
|
||||
case CV_BasicType_UOCT:{result = str8_lit("UOCT");}break;
|
||||
case CV_BasicType_BOOL8:{result = str8_lit("BOOL8");}break;
|
||||
case CV_BasicType_BOOL16:{result = str8_lit("BOOL16");}break;
|
||||
case CV_BasicType_BOOL32:{result = str8_lit("BOOL32");}break;
|
||||
case CV_BasicType_BOOL64:{result = str8_lit("BOOL64");}break;
|
||||
case CV_BasicType_FLOAT32:{result = str8_lit("FLOAT32");}break;
|
||||
case CV_BasicType_FLOAT64:{result = str8_lit("FLOAT64");}break;
|
||||
case CV_BasicType_FLOAT80:{result = str8_lit("FLOAT80");}break;
|
||||
case CV_BasicType_FLOAT128:{result = str8_lit("FLOAT128");}break;
|
||||
case CV_BasicType_FLOAT48:{result = str8_lit("FLOAT48");}break;
|
||||
case CV_BasicType_FLOAT32PP:{result = str8_lit("FLOAT32PP");}break;
|
||||
case CV_BasicType_FLOAT16:{result = str8_lit("FLOAT16");}break;
|
||||
case CV_BasicType_USHORT:{result = str8_lit("U16");}break;
|
||||
case CV_BasicType_ULONG:{result = str8_lit("U32");}break;
|
||||
case CV_BasicType_UQUAD:{result = str8_lit("U64");}break;
|
||||
case CV_BasicType_UOCT:{result = str8_lit("U128");}break;
|
||||
case CV_BasicType_BOOL8:{result = str8_lit("B8");}break;
|
||||
case CV_BasicType_BOOL16:{result = str8_lit("B16");}break;
|
||||
case CV_BasicType_BOOL32:{result = str8_lit("B32");}break;
|
||||
case CV_BasicType_BOOL64:{result = str8_lit("B64");}break;
|
||||
case CV_BasicType_FLOAT32:{result = str8_lit("F32");}break;
|
||||
case CV_BasicType_FLOAT64:{result = str8_lit("F64");}break;
|
||||
case CV_BasicType_FLOAT80:{result = str8_lit("F80");}break;
|
||||
case CV_BasicType_FLOAT128:{result = str8_lit("F128");}break;
|
||||
case CV_BasicType_FLOAT48:{result = str8_lit("F48");}break;
|
||||
case CV_BasicType_FLOAT32PP:{result = str8_lit("F32PP");}break;
|
||||
case CV_BasicType_FLOAT16:{result = str8_lit("F16");}break;
|
||||
case CV_BasicType_COMPLEX32:{result = str8_lit("ComplexF32");}break;
|
||||
case CV_BasicType_COMPLEX64:{result = str8_lit("ComplexF64");}break;
|
||||
case CV_BasicType_COMPLEX80:{result = str8_lit("ComplexF80");}break;
|
||||
case CV_BasicType_COMPLEX128:{result = str8_lit("ComplexF128");}break;
|
||||
case CV_BasicType_BIT:{result = str8_lit("");}break;
|
||||
case CV_BasicType_PASCHAR:{result = str8_lit("");}break;
|
||||
case CV_BasicType_BOOL32FF:{result = str8_lit("BOOL32FF");}break;
|
||||
case CV_BasicType_INT8:{result = str8_lit("int8");}break;
|
||||
case CV_BasicType_UINT8:{result = str8_lit("uint8");}break;
|
||||
case CV_BasicType_BOOL32FF:{result = str8_lit("B32FF");}break;
|
||||
case CV_BasicType_INT8:{result = str8_lit("S8");}break;
|
||||
case CV_BasicType_UINT8:{result = str8_lit("U8");}break;
|
||||
case CV_BasicType_RCHAR:{result = str8_lit("char");}break;
|
||||
case CV_BasicType_WCHAR:{result = str8_lit("WCHAR");}break;
|
||||
case CV_BasicType_INT16:{result = str8_lit("int16");}break;
|
||||
case CV_BasicType_UINT16:{result = str8_lit("uint16");}break;
|
||||
case CV_BasicType_INT32:{result = str8_lit("int32");}break;
|
||||
case CV_BasicType_UINT32:{result = str8_lit("uint32");}break;
|
||||
case CV_BasicType_INT64:{result = str8_lit("int64");}break;
|
||||
case CV_BasicType_UINT64:{result = str8_lit("uint64");}break;
|
||||
case CV_BasicType_INT128:{result = str8_lit("int128");}break;
|
||||
case CV_BasicType_UINT128:{result = str8_lit("uint128");}break;
|
||||
case CV_BasicType_CHAR16:{result = str8_lit("char16");}break;
|
||||
case CV_BasicType_CHAR32:{result = str8_lit("char32");}break;
|
||||
case CV_BasicType_INT16:{result = str8_lit("S16");}break;
|
||||
case CV_BasicType_UINT16:{result = str8_lit("U16");}break;
|
||||
case CV_BasicType_INT32:{result = str8_lit("S32");}break;
|
||||
case CV_BasicType_UINT32:{result = str8_lit("U32");}break;
|
||||
case CV_BasicType_INT64:{result = str8_lit("S64");}break;
|
||||
case CV_BasicType_UINT64:{result = str8_lit("U64");}break;
|
||||
case CV_BasicType_INT128:{result = str8_lit("S128");}break;
|
||||
case CV_BasicType_UINT128:{result = str8_lit("U128");}break;
|
||||
case CV_BasicType_CHAR16:{result = str8_lit("CHAR16");}break;
|
||||
case CV_BasicType_CHAR32:{result = str8_lit("CHAR32");}break;
|
||||
case CV_BasicType_CHAR8:{result = str8_lit("char");}break;
|
||||
case CV_BasicType_PTR:{result = str8_lit("PTR");}break;
|
||||
}
|
||||
|
||||
@@ -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/)
|
||||
|
||||
//- GENERATED CODE
|
||||
|
||||
+1158
-717
File diff suppressed because it is too large
Load Diff
+701
-454
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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 +0,0 @@
|
||||
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = §_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 = §ab[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 = §_n->v;
|
||||
sect->name = name;
|
||||
sect->flags = flags;
|
||||
|
||||
if (data.size) {
|
||||
str8_list_push(obj_writer->arena, §->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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
+9
-95
@@ -1,23 +1,19 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Entity Kinds
|
||||
|
||||
@table(name code_name display_string)
|
||||
@table(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" }
|
||||
{Root "Root" }
|
||||
{Machine "Machine" }
|
||||
{Process "Process" }
|
||||
{Thread "Thread" }
|
||||
{Module "Module" }
|
||||
{EntryPoint "Entry Point" }
|
||||
{DebugInfoPath "Debug Info Path" }
|
||||
}
|
||||
|
||||
@enum CTRL_EntityKind:
|
||||
@@ -27,12 +23,6 @@ CTRL_EntityKindTable:
|
||||
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}`,
|
||||
@@ -114,79 +104,3 @@ CTRL_ExceptionCodeKindTable:
|
||||
`0`;
|
||||
@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 }
|
||||
}
|
||||
|
||||
+1556
-2181
File diff suppressed because it is too large
Load Diff
+413
-252
@@ -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/)
|
||||
|
||||
#ifndef CTRL_CORE_H
|
||||
@@ -13,51 +13,227 @@ typedef U64 CTRL_MachineID;
|
||||
#define CTRL_MachineID_Local (1)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: User Breakpoint Types
|
||||
//~ rjf: Meta Evaluation Types
|
||||
|
||||
typedef U32 CTRL_UserBreakpointFlags;
|
||||
enum
|
||||
//- rjf: auto-checkbox b32s
|
||||
|
||||
typedef struct CTRL_CheckB32 CTRL_CheckB32;
|
||||
struct CTRL_CheckB32
|
||||
{
|
||||
CTRL_UserBreakpointFlag_BreakOnWrite = (1<<0),
|
||||
CTRL_UserBreakpointFlag_BreakOnRead = (1<<1),
|
||||
CTRL_UserBreakpointFlag_BreakOnExecute = (1<<2),
|
||||
B32 b32;
|
||||
};
|
||||
|
||||
typedef enum CTRL_UserBreakpointKind
|
||||
struct_members(CTRL_CheckB32)
|
||||
{
|
||||
CTRL_UserBreakpointKind_Null,
|
||||
CTRL_UserBreakpointKind_FileNameAndLineColNumber,
|
||||
CTRL_UserBreakpointKind_Expression,
|
||||
CTRL_UserBreakpointKind_COUNT
|
||||
}
|
||||
CTRL_UserBreakpointKind;
|
||||
member_lit_comp(CTRL_CheckB32, type(B32), b32),
|
||||
};
|
||||
struct_type(CTRL_CheckB32);
|
||||
|
||||
typedef struct CTRL_UserBreakpoint CTRL_UserBreakpoint;
|
||||
struct CTRL_UserBreakpoint
|
||||
//- rjf: styled string types
|
||||
|
||||
ptr_type(CTRL_PlainString8__str_ptr_type, type(U8), .flags = TypeFlag_IsPlainText,.count_delimiter_name = str8_lit_comp("size"));
|
||||
ptr_type(CTRL_CodeString8__str_ptr_type, type(U8), .flags = TypeFlag_IsCodeText, .count_delimiter_name = str8_lit_comp("size"));
|
||||
ptr_type(CTRL_PathString8__str_ptr_type, type(U8), .flags = TypeFlag_IsPathText, .count_delimiter_name = str8_lit_comp("size"));
|
||||
Member CTRL_PlainString8__members[] =
|
||||
{
|
||||
CTRL_UserBreakpointKind kind;
|
||||
CTRL_UserBreakpointFlags flags;
|
||||
U64 id;
|
||||
String8 string;
|
||||
TxtPt pt;
|
||||
U64 size;
|
||||
String8 condition;
|
||||
member_lit_comp(String8, &CTRL_PlainString8__str_ptr_type, str, .pretty_name = str8_lit_comp("Contents")),
|
||||
member_lit_comp(String8, type(U64), size, .pretty_name = str8_lit_comp("Size")),
|
||||
};
|
||||
Member CTRL_CodeString8__members[] =
|
||||
{
|
||||
member_lit_comp(String8, &CTRL_CodeString8__str_ptr_type, str, .pretty_name = str8_lit_comp("Contents")),
|
||||
member_lit_comp(String8, type(U64), size, .pretty_name = str8_lit_comp("Size")),
|
||||
};
|
||||
Member CTRL_PathString8__members[] =
|
||||
{
|
||||
member_lit_comp(String8, &CTRL_PathString8__str_ptr_type, str, .pretty_name = str8_lit_comp("Contents")),
|
||||
member_lit_comp(String8, type(U64), size, .pretty_name = str8_lit_comp("Size")),
|
||||
};
|
||||
named_struct_type(CTRL_PlainString8, String8, .name = str8_lit_comp("string"));
|
||||
named_struct_type(CTRL_CodeString8, String8, .name = str8_lit_comp("string"));
|
||||
named_struct_type(CTRL_PathString8, String8, .name = str8_lit_comp("string"));
|
||||
|
||||
//- rjf: meta evaluation callstack types
|
||||
|
||||
typedef struct CTRL_MetaEvalFrame CTRL_MetaEvalFrame;
|
||||
struct CTRL_MetaEvalFrame
|
||||
{
|
||||
U64 vaddr;
|
||||
U64 inline_depth;
|
||||
};
|
||||
ptr_type(CTRL_MetaEvalFrame__vaddr_type, type(void), .flags = TypeFlag_IsExternal, .size = sizeof(U64));
|
||||
struct_members(CTRL_MetaEvalFrame)
|
||||
{
|
||||
member_lit_comp(CTRL_MetaEvalFrame, &CTRL_MetaEvalFrame__vaddr_type, vaddr),
|
||||
member_lit_comp(CTRL_MetaEvalFrame, type(U64), inline_depth),
|
||||
};
|
||||
struct_type(CTRL_MetaEvalFrame, .name = str8_lit_comp("callstack_frame"));
|
||||
typedef struct CTRL_MetaEvalFrameArray CTRL_MetaEvalFrameArray;
|
||||
struct CTRL_MetaEvalFrameArray
|
||||
{
|
||||
U64 count;
|
||||
CTRL_MetaEvalFrame *v;
|
||||
};
|
||||
ptr_type(CTRL_MetaEvalFrameArray__v_ptr_type, type(CTRL_MetaEvalFrame), .count_delimiter_name = str8_lit_comp("count"));
|
||||
struct_members(CTRL_MetaEvalFrameArray)
|
||||
{
|
||||
member_lit_comp(CTRL_MetaEvalFrameArray, type(U64), count, .pretty_name = str8_lit_comp("Frame Count")),
|
||||
{str8_lit_comp("v"), str8_lit_comp("Frame Addresses"), &CTRL_MetaEvalFrameArray__v_ptr_type, OffsetOf(CTRL_MetaEvalFrameArray, v)},
|
||||
};
|
||||
struct_type(CTRL_MetaEvalFrameArray, .name = str8_lit_comp("callstack_frames"));
|
||||
|
||||
//- rjf: meta evaluation instance types
|
||||
|
||||
typedef struct CTRL_MetaEval CTRL_MetaEval;
|
||||
struct CTRL_MetaEval
|
||||
{
|
||||
#define CTRL_MetaEval_MemberXList \
|
||||
X(B32, enabled, "Enabled")\
|
||||
X(B32, frozen, "Frozen")\
|
||||
X(U64, hit_count, "Hit Count")\
|
||||
X(U64, id, "ID")\
|
||||
X(Rng1U64, vaddr_range, "Address Range")\
|
||||
X(U32, color, "Color")\
|
||||
X(CTRL_CheckB32, debug_subprocesses,"Debug Subprocesses")\
|
||||
Y(String8, type(CTRL_CodeString8), label, "Label")\
|
||||
Y(String8, type(CTRL_PathString8), exe, "Executable Path")\
|
||||
Y(String8, type(CTRL_PathString8), dbg, "Debug Info Path")\
|
||||
Y(String8, type(CTRL_PlainString8), args, "Arguments")\
|
||||
Y(String8, type(CTRL_PathString8), working_directory, "Working Directory")\
|
||||
Y(String8, type(CTRL_CodeString8), entry_point, "Custom Entry Point")\
|
||||
Y(String8, type(CTRL_PathString8), stdout_path, "Standard Output Path")\
|
||||
Y(String8, type(CTRL_PathString8), stderr_path, "Standard Error Path")\
|
||||
Y(String8, type(CTRL_PathString8), stdin_path, "Standard Input Path")\
|
||||
Y(String8, type(CTRL_PathString8), source_location, "Source Location")\
|
||||
Y(String8, type(CTRL_CodeString8), function_location, "Function Location")\
|
||||
Y(String8, type(CTRL_CodeString8), address_location, "Address Location")\
|
||||
Y(String8, type(CTRL_PathString8), source_path, "Source Path")\
|
||||
Y(String8, type(CTRL_PathString8), destination_path, "Destination Path")\
|
||||
Y(String8, type(CTRL_CodeString8), type, "Type")\
|
||||
Y(String8, type(CTRL_CodeString8), view_rule, "View Rule")\
|
||||
Y(String8, type(CTRL_CodeString8), condition, "Condition")\
|
||||
X(CTRL_MetaEvalFrameArray, callstack, "Call Stack")
|
||||
#define X(T, name, pretty_name) T name;
|
||||
#define Y(T, ti, name, pretty_name) T name;
|
||||
CTRL_MetaEval_MemberXList
|
||||
#undef X
|
||||
#undef Y
|
||||
};
|
||||
struct_members(CTRL_MetaEval)
|
||||
{
|
||||
#define X(T, name, pretty_name_) member_lit_comp(CTRL_MetaEval, type(T), name, .pretty_name = str8_lit_comp(pretty_name_)),
|
||||
#define Y(T, ti, name, pretty_name_) member_lit_comp(CTRL_MetaEval, (ti), name, .pretty_name = str8_lit_comp(pretty_name_)),
|
||||
CTRL_MetaEval_MemberXList
|
||||
#undef X
|
||||
#undef Y
|
||||
};
|
||||
struct_type(CTRL_MetaEval);
|
||||
|
||||
//- rjf: filters on main meta evaluation bundle
|
||||
|
||||
struct_members(CTRL_BreakpointMetaEval)
|
||||
{
|
||||
member_lit_comp(CTRL_MetaEval, type(B32), enabled, .pretty_name = str8_lit_comp("Enabled")),
|
||||
member_lit_comp(CTRL_MetaEval, type(U32), color, .pretty_name = str8_lit_comp("Color")),
|
||||
member_lit_comp(CTRL_MetaEval, type(U64), hit_count, .pretty_name = str8_lit_comp("Hit Count")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), label, .pretty_name = str8_lit_comp("Label")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), condition, .pretty_name = str8_lit_comp("Condition")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), source_location, .pretty_name = str8_lit_comp("Source Location")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), function_location, .pretty_name = str8_lit_comp("Function Location")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), address_location, .pretty_name = str8_lit_comp("Address Location")),
|
||||
};
|
||||
|
||||
typedef struct CTRL_UserBreakpointNode CTRL_UserBreakpointNode;
|
||||
struct CTRL_UserBreakpointNode
|
||||
struct_members(CTRL_TargetMetaEval)
|
||||
{
|
||||
CTRL_UserBreakpointNode *next;
|
||||
CTRL_UserBreakpoint v;
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), label, .pretty_name = str8_lit_comp("Label")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), exe, .pretty_name = str8_lit_comp("Executable")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PlainString8),args, .pretty_name = str8_lit_comp("Arguments")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), working_directory, .pretty_name = str8_lit_comp("Working Directory")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), entry_point, .pretty_name = str8_lit_comp("Custom Entry Point")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), stdout_path, .pretty_name = str8_lit_comp("Standard Output Path")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), stderr_path, .pretty_name = str8_lit_comp("Standard Error Path")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), stdin_path, .pretty_name = str8_lit_comp("Standard Input Path")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CheckB32), debug_subprocesses, .pretty_name = str8_lit_comp("Debug Subprocesses")),
|
||||
};
|
||||
|
||||
typedef struct CTRL_UserBreakpointList CTRL_UserBreakpointList;
|
||||
struct CTRL_UserBreakpointList
|
||||
struct_members(CTRL_PinMetaEval)
|
||||
{
|
||||
CTRL_UserBreakpointNode *first;
|
||||
CTRL_UserBreakpointNode *last;
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), label, .pretty_name = str8_lit_comp("Expression")),
|
||||
member_lit_comp(CTRL_MetaEval, type(U32), color, .pretty_name = str8_lit_comp("Color")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), source_location, .pretty_name = str8_lit_comp("Source Location")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), address_location, .pretty_name = str8_lit_comp("Address Location")),
|
||||
};
|
||||
|
||||
struct_members(CTRL_FilePathMapMetaEval)
|
||||
{
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), source_path, .pretty_name = str8_lit_comp("Source Path")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), destination_path, .pretty_name = str8_lit_comp("Destination Path")),
|
||||
};
|
||||
|
||||
struct_members(CTRL_AutoViewRuleMetaEval)
|
||||
{
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), type, .pretty_name = str8_lit_comp("Type")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), view_rule, .pretty_name = str8_lit_comp("View Rule")),
|
||||
};
|
||||
|
||||
struct_members(CTRL_MachineMetaEval)
|
||||
{
|
||||
member_lit_comp(CTRL_MetaEval, type(B32), frozen, .pretty_name = str8_lit_comp("Frozen")),
|
||||
member_lit_comp(CTRL_MetaEval, type(U32), color, .pretty_name = str8_lit_comp("Color")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), label, .pretty_name = str8_lit_comp("Name")),
|
||||
};
|
||||
|
||||
struct_members(CTRL_ProcessMetaEval)
|
||||
{
|
||||
member_lit_comp(CTRL_MetaEval, type(B32), frozen, .pretty_name = str8_lit_comp("Frozen")),
|
||||
member_lit_comp(CTRL_MetaEval, type(U32), color, .pretty_name = str8_lit_comp("Color")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), label, .pretty_name = str8_lit_comp("Name")),
|
||||
member_lit_comp(CTRL_MetaEval, type(U64), id, .pretty_name = str8_lit_comp("ID")),
|
||||
};
|
||||
|
||||
struct_members(CTRL_ModuleMetaEval)
|
||||
{
|
||||
member_lit_comp(CTRL_MetaEval, type(U32), color, .pretty_name = str8_lit_comp("Color")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), label, .pretty_name = str8_lit_comp("Name")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), exe, .pretty_name = str8_lit_comp("Executable Path")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_PathString8), dbg, .pretty_name = str8_lit_comp("Debug Info Path")),
|
||||
member_lit_comp(CTRL_MetaEval, type(Rng1U64), vaddr_range, .pretty_name = str8_lit_comp("Address Range")),
|
||||
};
|
||||
|
||||
struct_members(CTRL_ThreadMetaEval)
|
||||
{
|
||||
member_lit_comp(CTRL_MetaEval, type(B32), frozen, .pretty_name = str8_lit_comp("Frozen")),
|
||||
member_lit_comp(CTRL_MetaEval, type(U32), color, .pretty_name = str8_lit_comp("Color")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_CodeString8), label, .pretty_name = str8_lit_comp("Name")),
|
||||
member_lit_comp(CTRL_MetaEval, type(U64), id, .pretty_name = str8_lit_comp("ID")),
|
||||
member_lit_comp(CTRL_MetaEval, type(CTRL_MetaEvalFrameArray), callstack, .pretty_name = str8_lit_comp("Call Stack")),
|
||||
};
|
||||
|
||||
named_struct_type(CTRL_BreakpointMetaEval, CTRL_MetaEval, .name = str8_lit_comp("breakpoint"));
|
||||
named_struct_type(CTRL_TargetMetaEval, CTRL_MetaEval, .name = str8_lit_comp("target"));
|
||||
named_struct_type(CTRL_PinMetaEval, CTRL_MetaEval, .name = str8_lit_comp("pin"));
|
||||
named_struct_type(CTRL_FilePathMapMetaEval, CTRL_MetaEval, .name = str8_lit_comp("file_path_map"));
|
||||
named_struct_type(CTRL_AutoViewRuleMetaEval,CTRL_MetaEval, .name = str8_lit_comp("auto_view_rule"));
|
||||
named_struct_type(CTRL_MachineMetaEval, CTRL_MetaEval, .name = str8_lit_comp("machine"));
|
||||
named_struct_type(CTRL_ProcessMetaEval, CTRL_MetaEval, .name = str8_lit_comp("process"));
|
||||
named_struct_type(CTRL_ModuleMetaEval, CTRL_MetaEval, .name = str8_lit_comp("module"));
|
||||
named_struct_type(CTRL_ThreadMetaEval, CTRL_MetaEval, .name = str8_lit_comp("thread"));
|
||||
|
||||
//- rjf: meta evaluation array
|
||||
|
||||
typedef struct CTRL_MetaEvalArray CTRL_MetaEvalArray;
|
||||
struct CTRL_MetaEvalArray
|
||||
{
|
||||
CTRL_MetaEval *v;
|
||||
U64 count;
|
||||
};
|
||||
ptr_type(CTRL_MetaEvalArray__v_ptr_type, type(CTRL_BreakpointMetaEval), .count_delimiter_name = str8_lit_comp("count"));
|
||||
struct_members(CTRL_MetaEvalArray)
|
||||
{
|
||||
{str8_lit_comp("v"), {0}, &CTRL_MetaEvalArray__v_ptr_type, OffsetOf(CTRL_MetaEvalArray, v)},
|
||||
member_lit_comp(CTRL_MetaEvalArray, type(U64), count),
|
||||
};
|
||||
struct_type(CTRL_MetaEvalArray);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Entity Handle Types
|
||||
@@ -84,13 +260,6 @@ struct CTRL_HandleList
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct CTRL_HandleArray CTRL_HandleArray;
|
||||
struct CTRL_HandleArray
|
||||
{
|
||||
CTRL_Handle *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Generated Code
|
||||
|
||||
@@ -110,14 +279,12 @@ struct CTRL_Entity
|
||||
CTRL_EntityKind kind;
|
||||
Arch arch;
|
||||
B32 is_frozen;
|
||||
B32 is_soloed;
|
||||
U32 rgba;
|
||||
CTRL_Handle handle;
|
||||
U64 id;
|
||||
Rng1U64 vaddr_range;
|
||||
U64 stack_base;
|
||||
U64 timestamp;
|
||||
CTRL_UserBreakpointFlags bp_flags;
|
||||
String8 string;
|
||||
};
|
||||
|
||||
@@ -173,45 +340,21 @@ struct CTRL_EntityStringChunkNode
|
||||
U64 size;
|
||||
};
|
||||
|
||||
read_only global U64 ctrl_entity_string_bucket_chunk_sizes[] =
|
||||
{
|
||||
16,
|
||||
64,
|
||||
256,
|
||||
1024,
|
||||
4096,
|
||||
16384,
|
||||
65536,
|
||||
0xffffffffffffffffull,
|
||||
};
|
||||
|
||||
typedef struct CTRL_EntityCtx CTRL_EntityCtx;
|
||||
struct CTRL_EntityCtx
|
||||
typedef struct CTRL_EntityStore CTRL_EntityStore;
|
||||
struct CTRL_EntityStore
|
||||
{
|
||||
Arena *arena;
|
||||
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_EntityHashSlot *hash_slots;
|
||||
CTRL_EntityHashNode *hash_node_free;
|
||||
CTRL_EntityStringChunkNode *free_string_chunks[ArrayCount(ctrl_entity_string_bucket_chunk_sizes)];
|
||||
};
|
||||
|
||||
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];
|
||||
U64 hash_slots_count;
|
||||
CTRL_EntityStringChunkNode *free_string_chunks[8];
|
||||
U64 entity_kind_counts[CTRL_EntityKind_COUNT];
|
||||
Arena *entity_kind_lists_arenas[CTRL_EntityKind_COUNT];
|
||||
U64 entity_kind_lists_gens[CTRL_EntityKind_COUNT];
|
||||
U64 entity_kind_alloc_gens[CTRL_EntityKind_COUNT];
|
||||
CTRL_EntityList entity_kind_lists[CTRL_EntityKind_COUNT];
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
@@ -261,49 +404,32 @@ struct CTRL_Unwind
|
||||
////////////////////////////////
|
||||
//~ rjf: Call Stack Types
|
||||
|
||||
typedef struct CTRL_CallStackInlineFrame CTRL_CallStackInlineFrame;
|
||||
struct CTRL_CallStackInlineFrame
|
||||
{
|
||||
CTRL_CallStackInlineFrame *next;
|
||||
CTRL_CallStackInlineFrame *prev;
|
||||
RDI_InlineSite *inline_site;
|
||||
};
|
||||
|
||||
typedef struct CTRL_CallStackFrame CTRL_CallStackFrame;
|
||||
struct CTRL_CallStackFrame
|
||||
{
|
||||
U64 unwind_count;
|
||||
U64 inline_depth;
|
||||
CTRL_CallStackInlineFrame *first_inline_frame;
|
||||
CTRL_CallStackInlineFrame *last_inline_frame;
|
||||
U64 inline_frame_count;
|
||||
void *regs;
|
||||
RDI_Parsed *rdi;
|
||||
RDI_Procedure *procedure;
|
||||
};
|
||||
|
||||
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;
|
||||
U64 concrete_frame_count;
|
||||
U64 inline_frame_count;
|
||||
U64 total_frame_count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
@@ -350,6 +476,44 @@ struct CTRL_Spoof
|
||||
U64 new_ip_value;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: User Breakpoint Types
|
||||
|
||||
typedef enum CTRL_UserBreakpointKind
|
||||
{
|
||||
CTRL_UserBreakpointKind_Null,
|
||||
CTRL_UserBreakpointKind_FileNameAndLineColNumber,
|
||||
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: Evaluation Spaces
|
||||
|
||||
@@ -357,7 +521,7 @@ typedef U64 CTRL_EvalSpaceKind;
|
||||
enum
|
||||
{
|
||||
CTRL_EvalSpaceKind_Entity = E_SpaceKind_FirstUserDefined,
|
||||
CTRL_EvalSpaceKind_FirstUserDefined,
|
||||
CTRL_EvalSpaceKind_Meta,
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
@@ -409,6 +573,7 @@ struct CTRL_Msg
|
||||
String8 stdin_path;
|
||||
CTRL_TrapList traps;
|
||||
CTRL_UserBreakpointList user_bps;
|
||||
CTRL_MetaEvalArray meta_evals;
|
||||
};
|
||||
|
||||
typedef struct CTRL_MsgNode CTRL_MsgNode;
|
||||
@@ -453,13 +618,10 @@ typedef enum CTRL_EventKind
|
||||
//- rjf: debug info changes
|
||||
CTRL_EventKind_ModuleDebugInfoPathChange,
|
||||
|
||||
//- rjf: debug strings / decorations / markup
|
||||
//- rjf: debug strings / decorations
|
||||
CTRL_EventKind_DebugString,
|
||||
CTRL_EventKind_ThreadName,
|
||||
CTRL_EventKind_ThreadColor,
|
||||
CTRL_EventKind_SetBreakpoint,
|
||||
CTRL_EventKind_UnsetBreakpoint,
|
||||
CTRL_EventKind_SetVAddrRangeNote,
|
||||
|
||||
//- rjf: memory
|
||||
CTRL_EventKind_MemReserve,
|
||||
@@ -515,7 +677,6 @@ struct CTRL_Event
|
||||
U64 timestamp;
|
||||
U32 exception_code;
|
||||
U32 rgba;
|
||||
CTRL_UserBreakpointFlags bp_flags;
|
||||
String8 string;
|
||||
};
|
||||
|
||||
@@ -535,7 +696,61 @@ 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_Handle handle;
|
||||
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;
|
||||
struct CTRL_ProcessMemorySlice
|
||||
@@ -573,7 +788,7 @@ typedef struct CTRL_ThreadRegCacheStripe CTRL_ThreadRegCacheStripe;
|
||||
struct CTRL_ThreadRegCacheStripe
|
||||
{
|
||||
Arena *arena;
|
||||
RWMutex rw_mutex;
|
||||
OS_Handle rw_mutex;
|
||||
};
|
||||
|
||||
typedef struct CTRL_ThreadRegCache CTRL_ThreadRegCache;
|
||||
@@ -600,8 +815,6 @@ struct CTRL_ModuleImageInfoCacheNode
|
||||
U64 entry_point_voff;
|
||||
Rng1U64 tls_vaddr_range;
|
||||
String8 initial_debug_info_path;
|
||||
Rng1U64 raddbg_section_voff_range;
|
||||
String8 raddbg_data;
|
||||
};
|
||||
|
||||
typedef struct CTRL_ModuleImageInfoCacheSlot CTRL_ModuleImageInfoCacheSlot;
|
||||
@@ -615,7 +828,7 @@ typedef struct CTRL_ModuleImageInfoCacheStripe CTRL_ModuleImageInfoCacheStripe;
|
||||
struct CTRL_ModuleImageInfoCacheStripe
|
||||
{
|
||||
Arena *arena;
|
||||
RWMutex rw_mutex;
|
||||
OS_Handle rw_mutex;
|
||||
};
|
||||
|
||||
typedef struct CTRL_ModuleImageInfoCache CTRL_ModuleImageInfoCache;
|
||||
@@ -644,29 +857,6 @@ struct CTRL_DbgDirNode
|
||||
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
|
||||
|
||||
@@ -687,21 +877,17 @@ struct CTRL_State
|
||||
E_String2NumMap arch_string2alias_tables[Arch_COUNT];
|
||||
|
||||
// rjf: caches
|
||||
CTRL_ProcessMemoryCache process_memory_cache;
|
||||
CTRL_ThreadRegCache thread_reg_cache;
|
||||
CTRL_ModuleImageInfoCache module_image_info_cache;
|
||||
|
||||
// rjf: generations
|
||||
U64 run_gen;
|
||||
U64 mem_gen;
|
||||
U64 reg_gen;
|
||||
|
||||
// rjf: user -> ctrl msg ring buffer
|
||||
U64 u2c_ring_size;
|
||||
U8 *u2c_ring_base;
|
||||
U64 u2c_ring_write_pos;
|
||||
U64 u2c_ring_read_pos;
|
||||
Mutex u2c_ring_mutex;
|
||||
CondVar u2c_ring_cv;
|
||||
OS_Handle u2c_ring_mutex;
|
||||
OS_Handle u2c_ring_cv;
|
||||
|
||||
// rjf: ctrl -> user event ring buffer
|
||||
U64 c2u_ring_size;
|
||||
@@ -709,32 +895,34 @@ struct CTRL_State
|
||||
U8 *c2u_ring_base;
|
||||
U64 c2u_ring_write_pos;
|
||||
U64 c2u_ring_read_pos;
|
||||
Mutex c2u_ring_mutex;
|
||||
CondVar c2u_ring_cv;
|
||||
OS_Handle c2u_ring_mutex;
|
||||
OS_Handle c2u_ring_cv;
|
||||
|
||||
// rjf: ctrl thread state
|
||||
U64 ctrl_thread_run_state;
|
||||
String8 ctrl_thread_log_path;
|
||||
Thread ctrl_thread;
|
||||
OS_Handle ctrl_thread;
|
||||
Log *ctrl_thread_log;
|
||||
RWMutex ctrl_thread_entity_ctx_rw_mutex;
|
||||
CTRL_EntityCtxRWStore *ctrl_thread_entity_store;
|
||||
E_Cache *ctrl_thread_eval_cache;
|
||||
Arena *ctrl_thread_msg_process_arena;
|
||||
CTRL_EntityStore *ctrl_thread_entity_store;
|
||||
Arena *dmn_event_arena;
|
||||
DMN_EventNode *first_dmn_event_node;
|
||||
DMN_EventNode *last_dmn_event_node;
|
||||
DMN_EventNode *free_dmn_event_node;
|
||||
Arena *user_entry_point_arena;
|
||||
String8List user_entry_points;
|
||||
Arena *user_meta_eval_arena;
|
||||
CTRL_MetaEvalArray user_meta_evals;
|
||||
U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64];
|
||||
U64 process_counter;
|
||||
Arena *dbg_dir_arena;
|
||||
CTRL_DbgDirNode *dbg_dir_root;
|
||||
U64 module_req_cache_slots_count;
|
||||
CTRL_ModuleReqCacheNode **module_req_cache_slots;
|
||||
String8List msg_user_bp_touched_files;
|
||||
String8List msg_user_bp_touched_symbols;
|
||||
|
||||
// rjf: user -> memstream ring buffer
|
||||
U64 u2ms_ring_size;
|
||||
U8 *u2ms_ring_base;
|
||||
U64 u2ms_ring_write_pos;
|
||||
U64 u2ms_ring_read_pos;
|
||||
OS_Handle u2ms_ring_mutex;
|
||||
OS_Handle u2ms_ring_cv;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
@@ -749,20 +937,11 @@ read_only global CTRL_Entity 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
|
||||
|
||||
#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
|
||||
@@ -770,12 +949,8 @@ thread_static CTRL_EntityCtxLookupAccel *ctrl_entity_ctx_lookup_accel = 0;
|
||||
internal U64 ctrl_hash_from_string(String8 string);
|
||||
internal U64 ctrl_hash_from_handle(CTRL_Handle handle);
|
||||
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
|
||||
@@ -785,9 +960,6 @@ internal CTRL_Handle ctrl_handle_make(CTRL_MachineID machine_id, DMN_Handle dmn_
|
||||
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
|
||||
@@ -832,21 +1004,38 @@ internal CTRL_Event ctrl_event_from_serialized_string(Arena *arena, String8 stri
|
||||
|
||||
//- rjf: entity list data structures
|
||||
internal void ctrl_entity_list_push(Arena *arena, CTRL_EntityList *list, CTRL_Entity *entity);
|
||||
internal CTRL_EntityList ctrl_entity_list_from_handle_list(Arena *arena, CTRL_EntityCtx *ctx, CTRL_HandleList *list);
|
||||
internal CTRL_EntityList ctrl_entity_list_from_handle_list(Arena *arena, CTRL_EntityStore *store, CTRL_HandleList *list);
|
||||
#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);
|
||||
//- rjf: cache creation/destruction
|
||||
internal CTRL_EntityStore *ctrl_entity_store_alloc(void);
|
||||
internal void ctrl_entity_store_release(CTRL_EntityStore *store);
|
||||
|
||||
//- rjf: string allocation/deletion
|
||||
internal U64 ctrl_name_bucket_idx_from_string_size(U64 size);
|
||||
internal String8 ctrl_entity_string_alloc(CTRL_EntityStore *store, String8 string);
|
||||
internal void ctrl_entity_string_release(CTRL_EntityStore *store, String8 string);
|
||||
|
||||
//- rjf: entity construction/deletion
|
||||
internal CTRL_Entity *ctrl_entity_alloc(CTRL_EntityStore *store, CTRL_Entity *parent, CTRL_EntityKind kind, Arch arch, CTRL_Handle handle, U64 id);
|
||||
internal void ctrl_entity_release(CTRL_EntityStore *store, CTRL_Entity *entity);
|
||||
|
||||
//- rjf: entity equipment
|
||||
internal void ctrl_entity_equip_string(CTRL_EntityStore *store, CTRL_Entity *entity, String8 string);
|
||||
|
||||
//- rjf: entity store lookups
|
||||
internal CTRL_Entity *ctrl_entity_from_handle(CTRL_EntityStore *store, 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 CTRL_EntityList ctrl_modules_from_dbgi_key(Arena *arena, CTRL_EntityStore *store, DI_Key *dbgi_key);
|
||||
internal CTRL_Entity *ctrl_module_from_thread_candidates(CTRL_EntityStore *store, CTRL_Entity *thread, CTRL_EntityList *candidates);
|
||||
internal CTRL_EntityList ctrl_entity_list_from_kind(CTRL_EntityStore *store, CTRL_EntityKind kind);
|
||||
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);
|
||||
@@ -858,30 +1047,8 @@ internal CTRL_EntityRec ctrl_entity_rec_depth_first(CTRL_Entity *entity, CTRL_En
|
||||
#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
|
||||
internal U64 ctrl_name_bucket_num_from_string_size(U64 size);
|
||||
internal String8 ctrl_entity_string_alloc(CTRL_EntityCtxRWStore *store, String8 string);
|
||||
internal void ctrl_entity_string_release(CTRL_EntityCtxRWStore *store, String8 string);
|
||||
|
||||
//- 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 void ctrl_entity_release(CTRL_EntityCtxRWStore *store, CTRL_Entity *entity);
|
||||
|
||||
//- rjf: entity equipment
|
||||
internal void ctrl_entity_equip_string(CTRL_EntityCtxRWStore *store, CTRL_Entity *entity, String8 string);
|
||||
|
||||
//- rjf: accelerated entity context lookups
|
||||
internal CTRL_EntityCtxLookupAccel *ctrl_thread_entity_ctx_lookup_accel(void);
|
||||
internal CTRL_EntityArray ctrl_entity_array_from_kind(CTRL_EntityCtx *ctx, 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
|
||||
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
|
||||
@@ -893,14 +1060,33 @@ internal void ctrl_init(void);
|
||||
|
||||
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_Handle process, Rng1U64 range, B32 zero_terminated);
|
||||
internal U128 ctrl_stored_hash_from_process_vaddr_range(CTRL_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_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_Handle process, Rng1U64 range, U64 endt_us);
|
||||
internal CTRL_ProcessMemorySlice ctrl_query_cached_zero_terminated_data_from_process_vaddr_limit(Arena *arena, CTRL_Handle process, U64 vaddr, U64 limit, U64 element_size, U64 endt_us);
|
||||
internal B32 ctrl_read_cached_process_memory(CTRL_Handle process, Rng1U64 range, B32 *is_stale_out, void *out, U64 endt_us);
|
||||
#define ctrl_read_cached_process_memory_struct(process, vaddr, is_stale_out, ptr, endt_us) ctrl_read_cached_process_memory((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: Thread Register Functions
|
||||
|
||||
//- rjf: thread register cache reading
|
||||
internal void *ctrl_reg_block_from_thread(Arena *arena, CTRL_EntityCtx *ctx, CTRL_Handle handle);
|
||||
internal U64 ctrl_tls_root_vaddr_from_thread(CTRL_EntityCtx *ctx, CTRL_Handle handle);
|
||||
internal U64 ctrl_rip_from_thread(CTRL_EntityCtx *ctx, CTRL_Handle handle);
|
||||
internal U64 ctrl_rsp_from_thread(CTRL_EntityCtx *ctx, CTRL_Handle handle);
|
||||
internal void *ctrl_query_cached_reg_block_from_thread(Arena *arena, CTRL_EntityStore *store, CTRL_Handle handle);
|
||||
internal U64 ctrl_query_cached_tls_root_vaddr_from_thread(CTRL_EntityStore *store, CTRL_Handle handle);
|
||||
internal U64 ctrl_query_cached_rip_from_thread(CTRL_EntityStore *store, CTRL_Handle handle);
|
||||
internal U64 ctrl_query_cached_rsp_from_thread(CTRL_EntityStore *store, CTRL_Handle handle);
|
||||
|
||||
//- rjf: thread register writing
|
||||
internal B32 ctrl_thread_write_reg_block(CTRL_Handle thread, void *block);
|
||||
@@ -913,7 +1099,6 @@ internal PE_IntelPdata *ctrl_intel_pdata_from_module_voff(Arena *arena, CTRL_Han
|
||||
internal U64 ctrl_entry_point_voff_from_module(CTRL_Handle module_handle);
|
||||
internal Rng1U64 ctrl_tls_vaddr_range_from_module(CTRL_Handle module_handle);
|
||||
internal String8 ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_Handle module_handle);
|
||||
internal String8 ctrl_raddbg_data_from_module(Arena *arena, CTRL_Handle module_handle);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Unwinding Functions
|
||||
@@ -923,19 +1108,18 @@ internal CTRL_Unwind ctrl_unwind_deep_copy(Arena *arena, Arch arch, CTRL_Unwind
|
||||
|
||||
//- rjf: [x64]
|
||||
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_Handle process_handle, CTRL_Handle module_handle, REGS_RegBlockX64 *regs, U64 endt_us);
|
||||
|
||||
//- 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_Handle process, CTRL_Handle module, Arch arch, void *reg_block, U64 endt_us);
|
||||
|
||||
//- 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_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);
|
||||
internal CTRL_CallStack ctrl_call_stack_from_unwind(Arena *arena, DI_Scope *di_scope, CTRL_Entity *process, CTRL_Unwind *base_unwind);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Halting All Attached Processes
|
||||
@@ -969,27 +1153,22 @@ internal CTRL_EventList ctrl_c2u_pop_events(Arena *arena);
|
||||
internal void ctrl_thread__entry_point(void *p);
|
||||
|
||||
//- 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_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_program_defined_bp_traps(Arena *arena, CTRL_Entity *bp, DMN_TrapChunkList *traps_out);
|
||||
internal void ctrl_thread__append_resolved_module_user_bp_traps(Arena *arena, CTRL_Handle process, CTRL_Handle module, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out);
|
||||
internal void ctrl_thread__append_resolved_process_user_bp_traps(Arena *arena, CTRL_Handle process, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out);
|
||||
|
||||
//- 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_close(CTRL_Handle process, CTRL_Handle module, Rng1U64 vaddr_range);
|
||||
internal void ctrl_thread__module_close(CTRL_Handle module);
|
||||
|
||||
//- 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);
|
||||
|
||||
//- rjf: eval helpers
|
||||
internal U64 ctrl_eval_space_gen(E_Space space);
|
||||
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);
|
||||
internal B32 ctrl_eval_space_read(void *u, E_Space space, void *out, Rng1U64 vaddr_range);
|
||||
|
||||
//- 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
|
||||
internal void ctrl_thread__launch(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg);
|
||||
@@ -1001,32 +1180,14 @@ 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);
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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);
|
||||
internal void ctrl_memory_artifact_destroy(AC_Artifact artifact);
|
||||
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);
|
||||
//- rjf: user -> memory stream communication
|
||||
internal B32 ctrl_u2ms_enqueue_req(CTRL_Handle process, Rng1U64 vaddr_range, B32 zero_terminated, U64 endt_us);
|
||||
internal void ctrl_u2ms_dequeue_req(CTRL_Handle *out_process, Rng1U64 *out_vaddr_range, B32 *out_zero_terminated);
|
||||
|
||||
//- rjf: process memory reading helpers
|
||||
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 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);
|
||||
//- rjf: entry point
|
||||
ASYNC_WORK_DEF(ctrl_mem_stream_work);
|
||||
internal void ctrl_mem_stream_thread__entry_point(void *p);
|
||||
|
||||
#endif // CTRL_CORE_H
|
||||
|
||||
+1
-4
@@ -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/)
|
||||
|
||||
#undef LAYER_COLOR
|
||||
#define LAYER_COLOR 0x969696ff
|
||||
|
||||
#include "ctrl_core.c"
|
||||
|
||||
+1
-1
@@ -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/)
|
||||
|
||||
#ifndef CTRL_INC_H
|
||||
|
||||
@@ -1,26 +1,10 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- GENERATED CODE
|
||||
|
||||
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] =
|
||||
String8 ctrl_entity_kind_display_string_table[8] =
|
||||
{
|
||||
{0},
|
||||
str8_lit_comp("Root"),
|
||||
@@ -30,10 +14,6 @@ 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] =
|
||||
|
||||
@@ -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/)
|
||||
|
||||
//- GENERATED CODE
|
||||
@@ -16,10 +16,6 @@ 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;
|
||||
|
||||
@@ -67,8 +63,7 @@ CTRL_ExceptionCodeKind_COUNT,
|
||||
} CTRL_ExceptionCodeKind;
|
||||
|
||||
C_LINKAGE_BEGIN
|
||||
extern String8 ctrl_entity_kind_code_name_table[12];
|
||||
extern String8 ctrl_entity_kind_display_string_table[12];
|
||||
extern String8 ctrl_entity_kind_display_string_table[8];
|
||||
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_lowercase_code_string_table[38];
|
||||
|
||||
@@ -0,0 +1,856 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Instruction Decoding/Disassembling Type Functions
|
||||
|
||||
#if !defined(ZYDIS_H)
|
||||
#include "third_party/zydis/zydis.h"
|
||||
#include "third_party/zydis/zydis.c"
|
||||
#endif
|
||||
|
||||
internal DASM_Inst
|
||||
dasm_inst_from_code(Arena *arena, Arch arch, U64 vaddr, String8 code, DASM_Syntax syntax)
|
||||
{
|
||||
DASM_Inst inst = {0};
|
||||
switch(arch)
|
||||
{
|
||||
default:{}break;
|
||||
|
||||
//- rjf: x86/x64 disassembly
|
||||
case Arch_x86:
|
||||
case Arch_x64:
|
||||
{
|
||||
// rjf: determine zydis formatter style
|
||||
ZydisFormatterStyle style = ZYDIS_FORMATTER_STYLE_INTEL;
|
||||
switch(syntax)
|
||||
{
|
||||
default:{}break;
|
||||
case DASM_Syntax_Intel:{style = ZYDIS_FORMATTER_STYLE_INTEL;}break;
|
||||
case DASM_Syntax_ATT: {style = ZYDIS_FORMATTER_STYLE_ATT;}break;
|
||||
}
|
||||
|
||||
// rjf: disassemble one instruction
|
||||
ZydisDisassembledInstruction zinst = {0};
|
||||
ZyanStatus status = ZydisDisassemble(ZYDIS_MACHINE_MODE_LONG_64, vaddr, code.str, code.size, &zinst, style);
|
||||
|
||||
// rjf: analyze
|
||||
DASM_InstFlags flags = 0;
|
||||
U64 jump_dest_vaddr = 0;
|
||||
{
|
||||
ZydisDecodedOperand *first_visible_op = (zinst.info.operand_count_visible > 0 ? &zinst.operands[0] : 0);
|
||||
ZydisDecodedOperand *first_op = (zinst.info.operand_count > 0 ? &zinst.operands[0] : 0);
|
||||
ZydisDecodedOperand *second_op = (zinst.info.operand_count > 1 ? &zinst.operands[1] : 0);
|
||||
if(first_visible_op != 0 &&
|
||||
(first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM8 ||
|
||||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM16 ||
|
||||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM32 ||
|
||||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM64 ||
|
||||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM16_32_64 ||
|
||||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM32_32_64 ||
|
||||
first_visible_op->encoding == ZYDIS_OPERAND_ENCODING_JIMM16_32_32))
|
||||
{
|
||||
ZydisCalcAbsoluteAddress(&zinst.info, first_visible_op, vaddr, &jump_dest_vaddr);
|
||||
}
|
||||
if(first_op != 0 && second_op != 0 && first_op->type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
||||
(first_op->reg.value == ZYDIS_REGISTER_RSP ||
|
||||
first_op->reg.value == ZYDIS_REGISTER_ESP ||
|
||||
first_op->reg.value == ZYDIS_REGISTER_SP))
|
||||
{
|
||||
flags |= DASM_InstFlag_ChangesStackPointer;
|
||||
if(second_op->type != ZYDIS_OPERAND_TYPE_IMMEDIATE)
|
||||
{
|
||||
flags |= DASM_InstFlag_ChangesStackPointerVariably;
|
||||
}
|
||||
}
|
||||
if(zinst.info.attributes & (ZYDIS_ATTRIB_HAS_REP|
|
||||
ZYDIS_ATTRIB_HAS_REPE|
|
||||
ZYDIS_ATTRIB_HAS_REPZ|
|
||||
ZYDIS_ATTRIB_HAS_REPNZ|
|
||||
ZYDIS_ATTRIB_HAS_REPNE))
|
||||
{
|
||||
flags |= DASM_InstFlag_Repeats;
|
||||
}
|
||||
switch(zinst.info.mnemonic)
|
||||
{
|
||||
case ZYDIS_MNEMONIC_CALL:
|
||||
{
|
||||
flags |= DASM_InstFlag_Call;
|
||||
}break;
|
||||
|
||||
case ZYDIS_MNEMONIC_JB:
|
||||
case ZYDIS_MNEMONIC_JBE:
|
||||
case ZYDIS_MNEMONIC_JCXZ:
|
||||
case ZYDIS_MNEMONIC_JECXZ:
|
||||
case ZYDIS_MNEMONIC_JKNZD:
|
||||
case ZYDIS_MNEMONIC_JKZD:
|
||||
case ZYDIS_MNEMONIC_JL:
|
||||
case ZYDIS_MNEMONIC_JLE:
|
||||
case ZYDIS_MNEMONIC_JNB:
|
||||
case ZYDIS_MNEMONIC_JNBE:
|
||||
case ZYDIS_MNEMONIC_JNL:
|
||||
case ZYDIS_MNEMONIC_JNLE:
|
||||
case ZYDIS_MNEMONIC_JNO:
|
||||
case ZYDIS_MNEMONIC_JNP:
|
||||
case ZYDIS_MNEMONIC_JNS:
|
||||
case ZYDIS_MNEMONIC_JNZ:
|
||||
case ZYDIS_MNEMONIC_JO:
|
||||
case ZYDIS_MNEMONIC_JP:
|
||||
case ZYDIS_MNEMONIC_JRCXZ:
|
||||
case ZYDIS_MNEMONIC_JS:
|
||||
case ZYDIS_MNEMONIC_JZ:
|
||||
case ZYDIS_MNEMONIC_LOOP:
|
||||
case ZYDIS_MNEMONIC_LOOPE:
|
||||
case ZYDIS_MNEMONIC_LOOPNE:
|
||||
{
|
||||
flags |= DASM_InstFlag_Branch;
|
||||
}break;
|
||||
|
||||
case ZYDIS_MNEMONIC_JMP:
|
||||
{
|
||||
flags |= DASM_InstFlag_UnconditionalJump;
|
||||
}break;
|
||||
|
||||
case ZYDIS_MNEMONIC_RET:
|
||||
{
|
||||
flags |= DASM_InstFlag_Return;
|
||||
}break;
|
||||
|
||||
case ZYDIS_MNEMONIC_PUSH:
|
||||
case ZYDIS_MNEMONIC_POP:
|
||||
{
|
||||
flags |= DASM_InstFlag_ChangesStackPointer;
|
||||
}break;
|
||||
|
||||
default:
|
||||
{
|
||||
flags |= DASM_InstFlag_NonFlow;
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: convert
|
||||
{
|
||||
inst.flags = flags;
|
||||
inst.size = zinst.info.length;
|
||||
inst.string = push_str8_copy(arena, str8_cstring(zinst.text));
|
||||
inst.jump_dest_vaddr = jump_dest_vaddr;
|
||||
}
|
||||
}break;
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Control Flow Analysis
|
||||
|
||||
internal DASM_CtrlFlowInfo
|
||||
dasm_ctrl_flow_info_from_arch_vaddr_code(Arena *arena, DASM_InstFlags exit_points_mask, Arch arch, U64 vaddr, String8 code)
|
||||
{
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
DASM_CtrlFlowInfo info = {0};
|
||||
for(U64 offset = 0; offset < code.size;)
|
||||
{
|
||||
DASM_Inst inst = dasm_inst_from_code(scratch.arena, arch, vaddr+offset, str8_skip(code, offset), DASM_Syntax_Intel);
|
||||
U64 inst_vaddr = vaddr+offset;
|
||||
offset += inst.size;
|
||||
info.total_size += inst.size;
|
||||
if(inst.flags & exit_points_mask)
|
||||
{
|
||||
DASM_CtrlFlowPoint point = {0};
|
||||
point.inst_flags = inst.flags;
|
||||
point.vaddr = inst_vaddr;
|
||||
point.jump_dest_vaddr = inst.jump_dest_vaddr;
|
||||
DASM_CtrlFlowPointNode *node = push_array(arena, DASM_CtrlFlowPointNode, 1);
|
||||
node->v = point;
|
||||
SLLQueuePush(info.exit_points.first, info.exit_points.last, node);
|
||||
info.exit_points.count += 1;
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return info;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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: Line Type Functions
|
||||
|
||||
internal void
|
||||
dasm_line_chunk_list_push(Arena *arena, DASM_LineChunkList *list, U64 cap, DASM_Line *inst)
|
||||
{
|
||||
DASM_LineChunkNode *node = list->last;
|
||||
if(node == 0 || node->count >= node->cap)
|
||||
{
|
||||
node = push_array(arena, DASM_LineChunkNode, 1);
|
||||
node->v = push_array_no_zero(arena, DASM_Line, cap);
|
||||
node->cap = cap;
|
||||
SLLQueuePush(list->first, list->last, node);
|
||||
list->node_count += 1;
|
||||
}
|
||||
MemoryCopyStruct(&node->v[node->count], inst);
|
||||
node->count += 1;
|
||||
list->line_count += 1;
|
||||
}
|
||||
|
||||
internal DASM_LineArray
|
||||
dasm_line_array_from_chunk_list(Arena *arena, DASM_LineChunkList *list)
|
||||
{
|
||||
DASM_LineArray array = {0};
|
||||
array.count = list->line_count;
|
||||
array.v = push_array_no_zero(arena, DASM_Line, array.count);
|
||||
U64 idx = 0;
|
||||
for(DASM_LineChunkNode *n = list->first; n != 0; n = n->next)
|
||||
{
|
||||
MemoryCopy(array.v+idx, n->v, sizeof(DASM_Line)*n->count);
|
||||
idx += n->count;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
internal U64
|
||||
dasm_line_array_idx_from_code_off__linear_scan(DASM_LineArray *array, U64 off)
|
||||
{
|
||||
U64 result = 0;
|
||||
for(U64 idx = 0; idx < array->count; idx += 1)
|
||||
{
|
||||
U64 next_off = (idx+1 < array->count ? array->v[idx+1].code_off : max_U64);
|
||||
if(array->v[idx].code_off <= off && off < next_off)
|
||||
{
|
||||
result = idx;
|
||||
if(!(array->v[idx].flags & DASM_LineFlag_Decorative))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal U64
|
||||
dasm_line_array_code_off_from_idx(DASM_LineArray *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_get_system_info()->logical_processor_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->evictor_detector_thread = os_thread_launch(dasm_evictor_detector_thread__entry_point, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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, update_tick_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)
|
||||
{
|
||||
LogInfoNamedBlockF("dasm_new_node")
|
||||
{
|
||||
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_arch(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);
|
||||
async_push_work(dasm_parse_work);
|
||||
}
|
||||
}
|
||||
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 < HS_KEY_HASH_HISTORY_COUNT; rewind_idx += 1)
|
||||
{
|
||||
U128 hash = hs_hash_from_key(key, rewind_idx);
|
||||
result = dasm_info_from_hash_params(scope, hash, params);
|
||||
if(result.lines.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(Arch)+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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->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, ¶ms->dbgi_key.min_timestamp);
|
||||
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(Arch)+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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_out->dbgi_key.min_timestamp);
|
||||
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_cv);
|
||||
}
|
||||
|
||||
ASYNC_WORK_DEF(dasm_parse_work)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
HS_Scope *hs_scope = hs_scope_open();
|
||||
DI_Scope *di_scope = di_scope_open();
|
||||
TXT_Scope *txt_scope = txt_scope_open();
|
||||
|
||||
//- rjf: get next request
|
||||
U128 hash = {0};
|
||||
DASM_Params params = {0};
|
||||
dasm_u2p_dequeue_req(scratch.arena, &hash, ¶ms);
|
||||
U64 change_gen = fs_change_gen();
|
||||
|
||||
//- 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, ¶ms))
|
||||
{
|
||||
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, ¶ms.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_LineChunkList line_list = {0};
|
||||
String8List inst_strings = {0};
|
||||
if(got_task)
|
||||
{
|
||||
switch(params.arch)
|
||||
{
|
||||
default:{}break;
|
||||
|
||||
//- rjf: x86/x64 decoding
|
||||
case Arch_x64:
|
||||
case Arch_x86:
|
||||
{
|
||||
// rjf: disassemble
|
||||
RDI_SourceFile *last_file = &rdi_nil_element_union.source_file;
|
||||
RDI_Line *last_line = 0;
|
||||
for(U64 off = 0; off < data.size;)
|
||||
{
|
||||
// rjf: disassemble one instruction
|
||||
DASM_Inst inst = dasm_inst_from_code(scratch.arena, params.arch, params.vaddr+off, str8_skip(data, off), params.syntax);
|
||||
if(inst.size == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// 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_section_kind_voff(rdi, RDI_SectionKind_UnitVMap, voff);
|
||||
RDI_Unit *unit = rdi_element_from_name_idx(rdi, Units, unit_idx);
|
||||
RDI_LineTable *line_table = rdi_element_from_name_idx(rdi, LineTables, unit->line_table_idx);
|
||||
RDI_ParsedLineTable unit_line_info = {0};
|
||||
rdi_parsed_from_line_table(rdi, line_table, &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_name_idx(rdi, SourceFiles, 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)
|
||||
{
|
||||
String8 inst_string = push_str8f(scratch.arena, "> %S", file_normalized_full_path);
|
||||
DASM_Line inst = {u32_from_u64_saturate(off), DASM_LineFlag_Decorative, 0, r1u64(inst_strings.total_size + inst_strings.node_count,
|
||||
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
|
||||
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &inst);
|
||||
str8_list_push(scratch.arena, &inst_strings, inst_string);
|
||||
}
|
||||
if(params.style_flags & DASM_StyleFlag_SourceFilesNames && file->normal_full_path_string_idx == 0)
|
||||
{
|
||||
String8 inst_string = str8_lit(">");
|
||||
DASM_Line inst = {u32_from_u64_saturate(off), DASM_LineFlag_Decorative, 0, r1u64(inst_strings.total_size + inst_strings.node_count,
|
||||
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
|
||||
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &inst);
|
||||
str8_list_push(scratch.arena, &inst_strings, inst_string);
|
||||
}
|
||||
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_range(file_normalized_full_path, r1u64(0, max_U64));
|
||||
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)
|
||||
{
|
||||
String8 inst_string = push_str8f(scratch.arena, "> %S", line_text);
|
||||
DASM_Line inst = {u32_from_u64_saturate(off), DASM_LineFlag_Decorative, 0, r1u64(inst_strings.total_size + inst_strings.node_count,
|
||||
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
|
||||
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &inst);
|
||||
str8_list_push(scratch.arena, &inst_strings, inst_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
last_line = line;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: push line
|
||||
String8 addr_part = {0};
|
||||
if(params.style_flags & DASM_StyleFlag_Addresses)
|
||||
{
|
||||
addr_part = push_str8f(scratch.arena, "%s0x%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 < inst.size || byte_idx < 16; byte_idx += 1)
|
||||
{
|
||||
if(byte_idx < inst.size)
|
||||
{
|
||||
str8_list_pushf(scratch.arena, &code_bytes_strings, "%02x%s ", (U32)data.str[off+byte_idx], byte_idx == inst.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(inst.jump_dest_vaddr != 0 && rdi != &di_rdi_parsed_nil && params.style_flags & DASM_StyleFlag_SymbolNames)
|
||||
{
|
||||
RDI_U32 scope_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_ScopeVMap, inst.jump_dest_vaddr-params.base_vaddr);
|
||||
if(scope_idx != 0)
|
||||
{
|
||||
RDI_Scope *scope = rdi_element_from_name_idx(rdi, Scopes, scope_idx);
|
||||
RDI_U32 procedure_idx = scope->proc_idx;
|
||||
RDI_Procedure *procedure = rdi_element_from_name_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, inst.string, symbol_part);
|
||||
DASM_Line line = {u32_from_u64_saturate(off), 0, inst.jump_dest_vaddr, r1u64(inst_strings.total_size + inst_strings.node_count,
|
||||
inst_strings.total_size + inst_strings.node_count + inst_string.size)};
|
||||
dasm_line_chunk_list_push(scratch.arena, &line_list, 1024, &line);
|
||||
str8_list_push(scratch.arena, &inst_strings, inst_string);
|
||||
|
||||
// rjf: increment
|
||||
off += inst.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.lines = dasm_line_array_from_chunk_list(info_arena, &line_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, ¶ms))
|
||||
{
|
||||
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);
|
||||
ProfEnd();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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 = update_tick_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))
|
||||
{
|
||||
async_push_work(dasm_parse_work);
|
||||
n->last_time_requested_us = os_now_microseconds();
|
||||
n->last_user_clock_idx_requested = check_time_user_clocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
os_sleep_milliseconds(100);
|
||||
}
|
||||
}
|
||||
@@ -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/)
|
||||
|
||||
#ifndef DISASM_H
|
||||
#define DISASM_H
|
||||
#ifndef DASM_CACHE_H
|
||||
#define DASM_CACHE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Disassembly Syntax Types
|
||||
@@ -100,24 +100,6 @@ struct DASM_Params
|
||||
DI_Key dbgi_key;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Disassembly Request Bundle
|
||||
|
||||
typedef struct DASM_Request DASM_Request;
|
||||
struct DASM_Request
|
||||
{
|
||||
C_Root root;
|
||||
U128 hash;
|
||||
DASM_Params params;
|
||||
};
|
||||
|
||||
typedef struct DASM_RequestNode DASM_RequestNode;
|
||||
struct DASM_RequestNode
|
||||
{
|
||||
DASM_RequestNode *next;
|
||||
DASM_Request v;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Disassembly Text Line Types
|
||||
|
||||
@@ -161,16 +143,133 @@ struct DASM_LineArray
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Disassembly Result Bundle
|
||||
|
||||
typedef struct DASM_Result DASM_Result;
|
||||
struct DASM_Result
|
||||
{
|
||||
String8 text;
|
||||
DASM_LineArray lines;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Value Bundle Type
|
||||
|
||||
typedef struct DASM_Info DASM_Info;
|
||||
struct DASM_Info
|
||||
{
|
||||
C_Key text_key;
|
||||
U128 text_key;
|
||||
DASM_LineArray lines;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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: 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: evictor/detector thread
|
||||
OS_Handle evictor_detector_thread;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
thread_static DASM_TCTX *dasm_tctx = 0;
|
||||
global DASM_Shared *dasm_shared = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Instruction Decoding/Disassembling Type Functions
|
||||
|
||||
@@ -195,11 +294,33 @@ internal U64 dasm_line_array_idx_from_code_off__linear_scan(DASM_LineArray *arra
|
||||
internal U64 dasm_line_array_code_off_from_idx(DASM_LineArray *array, U64 idx);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Artifact Cache Hooks / Lookups
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal AC_Artifact dasm_artifact_create(String8 key, B32 *cancel_signal, B32 *retry_out, U64 *gen_out);
|
||||
internal void dasm_artifact_destroy(AC_Artifact artifact);
|
||||
internal DASM_Info dasm_info_from_hash_params(Access *access, U128 hash, DASM_Params *params);
|
||||
internal DASM_Info dasm_info_from_key_params(Access *access, C_Key key, DASM_Params *params, U128 *hash_out);
|
||||
internal void dasm_init(void);
|
||||
|
||||
#endif // DISASM_H
|
||||
////////////////////////////////
|
||||
//~ 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);
|
||||
ASYNC_WORK_DEF(dasm_parse_work);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Evictor/Detector Thread
|
||||
|
||||
internal void dasm_evictor_detector_thread__entry_point(void *p);
|
||||
|
||||
#endif // DASM_CACHE_H
|
||||
@@ -1,55 +1,56 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Copyright (c) 2024 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: // | | | | | | | | | | | | | |
|
||||
@table(name ui_vis ipc_docs_vis 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_required canonical_icon string display_name desc search_tags )
|
||||
// / | | | \___ _________________________________/ | | | | |
|
||||
// / | | | \ / | | | | |
|
||||
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." "" "" }
|
||||
{LaunchAndRun 1 1 Entity null Target Null 0 0 0 0 0 1 Play "launch_and_run" "Launch and Run" "Starts debugging a new instance of a target, then runs." "launch,start,run,target" }
|
||||
{LaunchAndInit 1 1 Entity null Target Null 0 0 0 0 0 1 PlayStepForward "launch_and_init" "Launch and Initialize" "Starts debugging a new instance of a target, then stops at the program's entry point." "launch,start,entry,point" }
|
||||
{Kill 1 1 Process null Nil Process 0 0 0 0 0 1 X "kill" "Kill" "Kills the specified existing attached process(es)." "stop,kill" }
|
||||
{KillAll 1 1 Null null Nil Null 0 0 0 0 0 0 Stop "kill_all" "Kill All" "Kills all attached processes." "stop,kill,all" }
|
||||
{Detach 1 1 Process null Nil Process 0 0 0 0 0 1 Null "detach" "Detach" "Detaches the specified attached process(es)." "detach" }
|
||||
{Continue 1 1 Null null Nil Null 0 0 0 0 0 0 Play "continue" "Continue" "Continues executing all attached processes." "" }
|
||||
{StepIntoInst 1 1 Null null Nil Null 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 Null null Nil Null 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 Null null Nil Null 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 Null null Nil Null 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 Null null Nil Null 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 Null null Nil Null 0 0 0 0 0 0 Pause "halt" "Halt" "Halts all attached processes." "pause" }
|
||||
{SoftHaltRefresh 1 1 Null null Nil Null 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 Vaddr null Nil Null 0 0 0 0 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." "" "" }
|
||||
{RunToLine 0 1 Null null Nil Null 0 0 0 0 0 0 Play "run_to_line" "Run To Line" "Runs until a particular source line is hit." "" }
|
||||
{RunToAddress 1 1 Vaddr null Nil Null 0 0 0 0 1 1 PlayStepForward "run_to_address" "Run To Address" "Runs until a particular address is hit." "" }
|
||||
{Run 1 1 Null null Nil Null 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 Null null Nil Null 0 0 0 0 0 0 Redo "restart" "Restart" "Kills all attached processes, then launches all active targets." "restart,retry" }
|
||||
{StepInto 1 1 Null null Nil Null 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 Null null Nil Null 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." "" "" }
|
||||
{FreezeThread 1 1 Thread null Nil Thread 0 0 0 0 0 1 Locked "freeze_thread" "Freeze Thread" "Freezes the passed thread." "callstack,unwind" }
|
||||
{ThawThread 1 1 Thread null Nil Thread 0 0 0 0 0 1 Unlocked "thaw_thread" "Thaw Thread" "Thaws the passed thread." "" }
|
||||
{FreezeProcess 1 1 Process null Nil Process 0 0 0 0 0 1 Locked "freeze_process" "Freeze Process" "Freezes the passed process." "" }
|
||||
{ThawProcess 1 1 Process null Nil Process 0 0 0 0 0 1 Unlocked "thaw_process" "Thaw Process" "Thaws the passed process." "" }
|
||||
{FreezeMachine 0 1 Machine null Nil Machine 0 0 0 0 0 1 Locked "freeze_machine" "Freeze Machine" "Freezes the passed machine." "" }
|
||||
{ThawMachine 0 1 Machine null Nil Machine 0 0 0 0 0 1 Unlocked "thaw_machine" "Thaw Machine" "Thaws the passed machine." "" }
|
||||
{FreezeLocalMachine 1 1 Null null Nil Null 0 0 0 0 0 0 Machine "freeze_local_machine" "Freeze Local Machine" "Freezes the local machine." "" }
|
||||
{ThawLocalMachine 1 1 Null null Nil Null 0 0 0 0 0 0 Machine "thaw_local_machine" "Thaw Local Machine" "Thaws the local machine." "" }
|
||||
{FreezeEntity 0 0 Null null Nil Null 0 0 0 0 0 0 Null "freeze_entity" "Freeze Entity" "Freezes an entity." "" }
|
||||
{ThawEntity 0 0 Null null Nil Null 0 0 0 0 0 0 Null "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." "" "" }
|
||||
{SetEntityColor 0 0 Null null Nil Null 0 0 0 0 0 0 Null "set_entity_color" "Set Entity Color" "Sets the passed entity's color." "" }
|
||||
{SetEntityName 0 0 Null null Nil Null 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." "" "" }
|
||||
{Attach 1 1 PID null Nil Null 0 0 0 0 0 1 Null "attach" "Attach" "Attaches to a process that is already running on the local machine." "" }
|
||||
}
|
||||
|
||||
@enum D_CmdKind:
|
||||
@@ -59,13 +60,53 @@ D_CmdTable: // | | | |
|
||||
COUNT,
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Built-In VieNull w Rules
|
||||
|
||||
@table(coverage_check name name_lower string ih ex xp vb display_name docs schema description)
|
||||
D_ViewRuleTable:
|
||||
{
|
||||
{x Default default "default" - - - x "Default" - "" "" }
|
||||
{x Array array "array" - - x - "Array" x "x:{expr}" "Specifies that a pointer points to N elements, rather than only 1." }
|
||||
{x Slice slice "slice" - - x - "Slice" x "" "Specifies that a pointer within a struct, also containing an integer, points to the number of elements encoded by the integer." }
|
||||
{- List list "list" - - - x "List" - "x:{member}" "Specifies that some struct, union, or class forms the top of a linked list, and the member which points at the following element in the list." }
|
||||
{x ByteSwap bswap "bswap" x - x - "Byte Swap" x "" "Specifies that all integral evaluations should be byte-swapped, such that their endianness is reversed." }
|
||||
{x Cast cast "cast" - - x - "Cast" x "x:{type}" "Specifies that the expression to which the view rule is applied should be casted to the provided type." }
|
||||
{- BaseDec base_dec "dec" x - - - "Decimal Base (Base 10)" x "" "Specifies that all integral evaluations should appear in base-10 form." }
|
||||
{- BaseBin base_bin "bin" x - - - "Binary Base (Base 2)" x "" "Specifies that all integral evaluations should appear in base-2 form." }
|
||||
{- BaseOct base_oct "oct" x - - - "Octal Base (Base 8)" x "" "Specifies that all integral evaluations should appear in base-8 form." }
|
||||
{- BaseHex base_hex "hex" x - - - "Hexadecimal Base (Base 16)" x "" "Specifies that all integral evaluations should appear in base-16 form." }
|
||||
{- Only only "only" x - - x "Only Specified Members" x "x:{member}" "Specifies that only the specified members should appear in struct, union, or class evaluations." }
|
||||
{- Omit omit "omit" x - - x "Omit Specified Members" x "x:{member}" "Omits a list of member names from appearing in struct, union, or class evaluations." }
|
||||
{- NoAddr no_addr "no_addr" x - - - "Disable Address Values" x "" "Displays only what pointers point to, if possible, without the pointer's address value." }
|
||||
{x Checkbox checkbox "checkbox" - - - - "Checkbox" x "" "Displays simple integer values as checkboxes, encoding zero or nonzero values." }
|
||||
{- ColorRGBA color_rgba "color_rgba" - x - x "Color (RGBA)" x "" "Displays as a color, interpreting the data as encoding R, G, B, and A values." }
|
||||
{x Text text "text" - x - x "Text" x "x:{'lang':lang, 'size':expr}" "Displays as text." }
|
||||
{x Disasm disasm "disasm" - x - x "Disassembly" x "x:{'arch':arch, 'size':expr}" "Displays as disassembled instructions, interpreting the data as raw machine code." }
|
||||
{x Memory memory "memory" - x - x "Memory" x "x:{'size':expr}" "Displays as a raw memory grid." }
|
||||
{- Graph graph "graph" - x - x "Graph" x "" "Displays as a pointer graph, visualizing nodes and edges formed by pointers directly." }
|
||||
{x Bitmap bitmap "bitmap" - x - x "Bitmap" x "x:{'w':expr, 'h':expr, 'fmt':tex2dformat}" "Displays as a bitmap, interpreting the data as raw pixel data." }
|
||||
{- Geo3D geo3d "geo3d" - x - x "Geometry (3D)" x "x:{'count':expr, 'vtx':expr, 'vtx_size':expr}" "Displays as geometry, interpreting the data as index or vertex data." }
|
||||
}
|
||||
|
||||
@enum D_ViewRuleKind:
|
||||
{
|
||||
@expand(D_ViewRuleTable a) `$(a.name)`,
|
||||
COUNT,
|
||||
}
|
||||
|
||||
@data(D_ViewRuleSpecInfo) @c_file d_core_view_rule_spec_info_table:
|
||||
{
|
||||
@expand(D_ViewRuleTable a)
|
||||
```{str8_lit_comp("$(a.string)"), str8_lit_comp("$(a.display_name)"), str8_lit_comp("$(a.schema)"), str8_lit_comp("$(a.description)"), (D_ViewRuleSpecInfoFlag_Inherited*$(a.ih == "x"))|(D_ViewRuleSpecInfoFlag_Expandable*$(a.ex == "x"))|(D_ViewRuleSpecInfoFlag_ExprResolution*$(a.xp == "x"))|(D_ViewRuleSpecInfoFlag_VizBlockProd*$(a.vb == "x")), }```;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Developer Toggles
|
||||
|
||||
@table(name)
|
||||
D_DevToggleTable:
|
||||
{
|
||||
{always_refresh}
|
||||
{simulate_lag}
|
||||
{draw_ui_text_pos}
|
||||
{draw_ui_focus_debug}
|
||||
@@ -73,6 +114,7 @@ D_DevToggleTable:
|
||||
{eval_compiler_tooltips}
|
||||
{eval_watch_key_tooltips}
|
||||
{cmd_context_tooltips}
|
||||
{scratch_mouse_draw}
|
||||
{updating_indicator}
|
||||
}
|
||||
|
||||
|
||||
+727
-260
File diff suppressed because it is too large
Load Diff
@@ -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/)
|
||||
|
||||
#ifndef DBG_ENGINE_CORE_H
|
||||
@@ -28,24 +28,14 @@ struct D_TargetArray
|
||||
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 symbol_name;
|
||||
U64 vaddr;
|
||||
String8 condition;
|
||||
U64 size;
|
||||
};
|
||||
|
||||
typedef struct D_BreakpointArray D_BreakpointArray;
|
||||
@@ -69,24 +59,12 @@ struct D_PathMapArray
|
||||
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
|
||||
@@ -98,7 +76,6 @@ typedef enum D_EventCause
|
||||
D_EventCause_Null,
|
||||
D_EventCause_UserBreakpoint,
|
||||
D_EventCause_Halt,
|
||||
D_EventCause_SoftHalt,
|
||||
D_EventCause_COUNT
|
||||
}
|
||||
D_EventCause;
|
||||
@@ -108,11 +85,9 @@ 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;
|
||||
@@ -182,6 +157,42 @@ D_RunKind;
|
||||
|
||||
#include "dbg_engine/generated/dbg_engine.meta.h"
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: View Rules
|
||||
|
||||
typedef U32 D_ViewRuleSpecInfoFlags; // NOTE(rjf): see @view_rule_info
|
||||
enum
|
||||
{
|
||||
D_ViewRuleSpecInfoFlag_Inherited = (1<<0),
|
||||
D_ViewRuleSpecInfoFlag_Expandable = (1<<1),
|
||||
D_ViewRuleSpecInfoFlag_ExprResolution = (1<<2),
|
||||
D_ViewRuleSpecInfoFlag_VizBlockProd = (1<<3),
|
||||
};
|
||||
|
||||
typedef struct D_ViewRuleSpecInfo D_ViewRuleSpecInfo;
|
||||
struct D_ViewRuleSpecInfo
|
||||
{
|
||||
String8 string;
|
||||
String8 display_string;
|
||||
String8 schema;
|
||||
String8 description;
|
||||
D_ViewRuleSpecInfoFlags flags;
|
||||
};
|
||||
|
||||
typedef struct D_ViewRuleSpecInfoArray D_ViewRuleSpecInfoArray;
|
||||
struct D_ViewRuleSpecInfoArray
|
||||
{
|
||||
D_ViewRuleSpecInfo *v;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
typedef struct D_ViewRuleSpec D_ViewRuleSpec;
|
||||
struct D_ViewRuleSpec
|
||||
{
|
||||
D_ViewRuleSpec *hash_next;
|
||||
D_ViewRuleSpecInfo info;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Command Types
|
||||
|
||||
@@ -200,7 +211,6 @@ struct D_CmdParams
|
||||
U32 pid;
|
||||
U32 rgba;
|
||||
D_TargetArray targets;
|
||||
U64 retry_idx;
|
||||
};
|
||||
|
||||
typedef struct D_Cmd D_Cmd;
|
||||
@@ -229,6 +239,35 @@ struct D_CmdList
|
||||
////////////////////////////////
|
||||
//~ rjf: Main State Caches
|
||||
|
||||
//- rjf: per-thread unwind cache
|
||||
|
||||
typedef struct D_UnwindCacheNode D_UnwindCacheNode;
|
||||
struct D_UnwindCacheNode
|
||||
{
|
||||
D_UnwindCacheNode *next;
|
||||
D_UnwindCacheNode *prev;
|
||||
U64 reggen;
|
||||
U64 memgen;
|
||||
Arena *arena;
|
||||
CTRL_Handle thread;
|
||||
CTRL_Unwind unwind;
|
||||
};
|
||||
|
||||
typedef struct D_UnwindCacheSlot D_UnwindCacheSlot;
|
||||
struct D_UnwindCacheSlot
|
||||
{
|
||||
D_UnwindCacheNode *first;
|
||||
D_UnwindCacheNode *last;
|
||||
};
|
||||
|
||||
typedef struct D_UnwindCache D_UnwindCache;
|
||||
struct D_UnwindCache
|
||||
{
|
||||
U64 slots_count;
|
||||
D_UnwindCacheSlot *slots;
|
||||
D_UnwindCacheNode *free_node;
|
||||
};
|
||||
|
||||
//- rjf: per-run tls-base-vaddr cache
|
||||
|
||||
typedef struct D_RunTLSBaseCacheNode D_RunTLSBaseCacheNode;
|
||||
@@ -291,15 +330,17 @@ struct D_State
|
||||
// rjf: top-level state
|
||||
Arena *arena;
|
||||
U64 frame_index;
|
||||
U64 frame_eval_memread_endt_us;
|
||||
|
||||
// rjf: commands
|
||||
Arena *cmds_arena;
|
||||
D_CmdList cmds;
|
||||
|
||||
// rjf: output log key
|
||||
C_Key output_log_key;
|
||||
U128 output_log_key;
|
||||
|
||||
// rjf: per-run caches
|
||||
D_UnwindCache unwind_cache;
|
||||
U64 tls_base_cache_reggen_idx;
|
||||
U64 tls_base_cache_memgen_idx;
|
||||
D_RunTLSBaseCache tls_base_caches[2];
|
||||
@@ -311,6 +352,10 @@ struct D_State
|
||||
D_RunLocalsCache member_caches[2];
|
||||
U64 member_cache_gen;
|
||||
|
||||
// rjf: view rule specification table
|
||||
U64 view_rule_spec_table_size;
|
||||
D_ViewRuleSpec **view_rule_spec_table;
|
||||
|
||||
// rjf: user -> ctrl driving state
|
||||
Arena *ctrl_last_run_arena;
|
||||
D_RunKind ctrl_last_run_kind;
|
||||
@@ -327,7 +372,7 @@ struct D_State
|
||||
CTRL_MsgList ctrl_msgs;
|
||||
|
||||
// rjf: ctrl -> user reading state
|
||||
CTRL_EntityCtxRWStore *ctrl_entity_store;
|
||||
CTRL_EntityStore *ctrl_entity_store;
|
||||
Arena *ctrl_stop_arena;
|
||||
CTRL_Event ctrl_last_stop_event;
|
||||
};
|
||||
@@ -335,6 +380,7 @@ struct D_State
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
read_only global D_ViewRuleSpec d_nil_core_view_rule_spec = {0};
|
||||
global D_State *d_state = 0;
|
||||
|
||||
////////////////////////////////
|
||||
@@ -369,18 +415,32 @@ 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: View Rule Spec Stateful Functions
|
||||
|
||||
internal void d_register_view_rule_specs(D_ViewRuleSpecInfoArray specs);
|
||||
internal D_ViewRuleSpec *d_view_rule_spec_from_string(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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);
|
||||
internal CTRL_TrapList d_trap_net_from_thread__step_over_inst(Arena *arena, CTRL_Entity *thread);
|
||||
internal CTRL_TrapList d_trap_net_from_thread__step_over_line(Arena *arena, CTRL_Entity *thread);
|
||||
internal CTRL_TrapList d_trap_net_from_thread__step_into_line(Arena *arena, CTRL_Entity *thread);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Debug Info Lookups
|
||||
|
||||
//- rjf: voff|vaddr -> symbol lookups
|
||||
internal String8 d_symbol_name_from_dbgi_key_voff(Arena *arena, DI_Key *dbgi_key, U64 voff, B32 decorated);
|
||||
internal String8 d_symbol_name_from_process_vaddr(Arena *arena, CTRL_Entity *process, U64 vaddr, B32 decorated);
|
||||
|
||||
//- rjf: symbol -> voff lookups
|
||||
internal U64 d_voff_from_dbgi_key_symbol_name(DI_Key *dbgi_key, String8 symbol_name);
|
||||
internal U64 d_type_num_from_dbgi_key_name(DI_Key *dbgi_key, String8 name);
|
||||
|
||||
//- rjf: voff -> line info
|
||||
internal D_LineList d_lines_from_dbgi_key_voff(Arena *arena, DI_Key dbgi_key, U64 voff);
|
||||
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
|
||||
@@ -416,11 +476,12 @@ internal B32 d_ctrl_targets_running(void);
|
||||
internal DI_KeyList d_push_active_dbgi_key_list(Arena *arena);
|
||||
|
||||
//- rjf: per-run caches
|
||||
internal CTRL_Unwind d_query_cached_unwind_from_thread(CTRL_Entity *thread);
|
||||
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);
|
||||
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);
|
||||
@@ -433,6 +494,6 @@ 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]);
|
||||
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], CTRL_MetaEvalArray *meta_evals);
|
||||
|
||||
#endif // DBG_ENGINE_CORE_H
|
||||
|
||||
@@ -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/)
|
||||
|
||||
#include "dbg_engine_core.c"
|
||||
|
||||
@@ -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/)
|
||||
|
||||
#ifndef DBG_ENGINE_INC_H
|
||||
|
||||
@@ -1,5 +1,33 @@
|
||||
// Copyright (c) Epic Games Tools
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
//- GENERATED CODE
|
||||
|
||||
C_LINKAGE_BEGIN
|
||||
D_ViewRuleSpecInfo d_core_view_rule_spec_info_table[21] =
|
||||
{
|
||||
{str8_lit_comp("default"), str8_lit_comp("Default"), str8_lit_comp(""), str8_lit_comp(""), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
{str8_lit_comp("array"), str8_lit_comp("Array"), str8_lit_comp("x:{expr}"), str8_lit_comp("Specifies that a pointer points to N elements, rather than only 1."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*1)|(D_ViewRuleSpecInfoFlag_VizBlockProd*0), },
|
||||
{str8_lit_comp("slice"), str8_lit_comp("Slice"), str8_lit_comp(""), str8_lit_comp("Specifies that a pointer within a struct, also containing an integer, points to the number of elements encoded by the integer."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*1)|(D_ViewRuleSpecInfoFlag_VizBlockProd*0), },
|
||||
{str8_lit_comp("list"), str8_lit_comp("List"), str8_lit_comp("x:{member}"), str8_lit_comp("Specifies that some struct, union, or class forms the top of a linked list, and the member which points at the following element in the list."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
{str8_lit_comp("bswap"), str8_lit_comp("Byte Swap"), str8_lit_comp(""), str8_lit_comp("Specifies that all integral evaluations should be byte-swapped, such that their endianness is reversed."), (D_ViewRuleSpecInfoFlag_Inherited*1)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*1)|(D_ViewRuleSpecInfoFlag_VizBlockProd*0), },
|
||||
{str8_lit_comp("cast"), str8_lit_comp("Cast"), str8_lit_comp("x:{type}"), str8_lit_comp("Specifies that the expression to which the view rule is applied should be casted to the provided type."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*1)|(D_ViewRuleSpecInfoFlag_VizBlockProd*0), },
|
||||
{str8_lit_comp("dec"), str8_lit_comp("Decimal Base (Base 10)"), str8_lit_comp(""), str8_lit_comp("Specifies that all integral evaluations should appear in base-10 form."), (D_ViewRuleSpecInfoFlag_Inherited*1)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*0), },
|
||||
{str8_lit_comp("bin"), str8_lit_comp("Binary Base (Base 2)"), str8_lit_comp(""), str8_lit_comp("Specifies that all integral evaluations should appear in base-2 form."), (D_ViewRuleSpecInfoFlag_Inherited*1)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*0), },
|
||||
{str8_lit_comp("oct"), str8_lit_comp("Octal Base (Base 8)"), str8_lit_comp(""), str8_lit_comp("Specifies that all integral evaluations should appear in base-8 form."), (D_ViewRuleSpecInfoFlag_Inherited*1)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*0), },
|
||||
{str8_lit_comp("hex"), str8_lit_comp("Hexadecimal Base (Base 16)"), str8_lit_comp(""), str8_lit_comp("Specifies that all integral evaluations should appear in base-16 form."), (D_ViewRuleSpecInfoFlag_Inherited*1)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*0), },
|
||||
{str8_lit_comp("only"), str8_lit_comp("Only Specified Members"), str8_lit_comp("x:{member}"), str8_lit_comp("Specifies that only the specified members should appear in struct, union, or class evaluations."), (D_ViewRuleSpecInfoFlag_Inherited*1)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
{str8_lit_comp("omit"), str8_lit_comp("Omit Specified Members"), str8_lit_comp("x:{member}"), str8_lit_comp("Omits a list of member names from appearing in struct, union, or class evaluations."), (D_ViewRuleSpecInfoFlag_Inherited*1)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
{str8_lit_comp("no_addr"), str8_lit_comp("Disable Address Values"), str8_lit_comp(""), str8_lit_comp("Displays only what pointers point to, if possible, without the pointer's address value."), (D_ViewRuleSpecInfoFlag_Inherited*1)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*0), },
|
||||
{str8_lit_comp("checkbox"), str8_lit_comp("Checkbox"), str8_lit_comp(""), str8_lit_comp("Displays simple integer values as checkboxes, encoding zero or nonzero values."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*0)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*0), },
|
||||
{str8_lit_comp("color_rgba"), str8_lit_comp("Color (RGBA)"), str8_lit_comp(""), str8_lit_comp("Displays as a color, interpreting the data as encoding R, G, B, and A values."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*1)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
{str8_lit_comp("text"), str8_lit_comp("Text"), str8_lit_comp("x:{'lang':lang, 'size':expr}"), str8_lit_comp("Displays as text."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*1)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
{str8_lit_comp("disasm"), str8_lit_comp("Disassembly"), str8_lit_comp("x:{'arch':arch, 'size':expr}"), str8_lit_comp("Displays as disassembled instructions, interpreting the data as raw machine code."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*1)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
{str8_lit_comp("memory"), str8_lit_comp("Memory"), str8_lit_comp("x:{'size':expr}"), str8_lit_comp("Displays as a raw memory grid."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*1)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
{str8_lit_comp("graph"), str8_lit_comp("Graph"), str8_lit_comp(""), str8_lit_comp("Displays as a pointer graph, visualizing nodes and edges formed by pointers directly."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*1)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
{str8_lit_comp("bitmap"), str8_lit_comp("Bitmap"), str8_lit_comp("x:{'w':expr, 'h':expr, 'fmt':tex2dformat}"), str8_lit_comp("Displays as a bitmap, interpreting the data as raw pixel data."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*1)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
{str8_lit_comp("geo3d"), str8_lit_comp("Geometry (3D)"), str8_lit_comp("x:{'count':expr, 'vtx':expr, 'vtx_size':expr}"), str8_lit_comp("Displays as geometry, interpreting the data as index or vertex data."), (D_ViewRuleSpecInfoFlag_Inherited*0)|(D_ViewRuleSpecInfoFlag_Expandable*1)|(D_ViewRuleSpecInfoFlag_ExprResolution*0)|(D_ViewRuleSpecInfoFlag_VizBlockProd*1), },
|
||||
};
|
||||
|
||||
C_LINKAGE_END
|
||||
|
||||
|
||||
@@ -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/)
|
||||
|
||||
//- GENERATED CODE
|
||||
@@ -10,7 +10,7 @@ typedef enum D_CmdKind
|
||||
{
|
||||
D_CmdKind_Null,
|
||||
D_CmdKind_LaunchAndRun,
|
||||
D_CmdKind_LaunchAndStepInto,
|
||||
D_CmdKind_LaunchAndInit,
|
||||
D_CmdKind_Kill,
|
||||
D_CmdKind_KillAll,
|
||||
D_CmdKind_Detach,
|
||||
@@ -24,6 +24,7 @@ D_CmdKind_Halt,
|
||||
D_CmdKind_SoftHaltRefresh,
|
||||
D_CmdKind_SetThreadIP,
|
||||
D_CmdKind_RunToLine,
|
||||
D_CmdKind_RunToAddress,
|
||||
D_CmdKind_Run,
|
||||
D_CmdKind_Restart,
|
||||
D_CmdKind_StepInto,
|
||||
@@ -44,7 +45,32 @@ D_CmdKind_Attach,
|
||||
D_CmdKind_COUNT,
|
||||
} D_CmdKind;
|
||||
|
||||
global B32 DEV_always_refresh = 0;
|
||||
typedef enum D_ViewRuleKind
|
||||
{
|
||||
D_ViewRuleKind_Default,
|
||||
D_ViewRuleKind_Array,
|
||||
D_ViewRuleKind_Slice,
|
||||
D_ViewRuleKind_List,
|
||||
D_ViewRuleKind_ByteSwap,
|
||||
D_ViewRuleKind_Cast,
|
||||
D_ViewRuleKind_BaseDec,
|
||||
D_ViewRuleKind_BaseBin,
|
||||
D_ViewRuleKind_BaseOct,
|
||||
D_ViewRuleKind_BaseHex,
|
||||
D_ViewRuleKind_Only,
|
||||
D_ViewRuleKind_Omit,
|
||||
D_ViewRuleKind_NoAddr,
|
||||
D_ViewRuleKind_Checkbox,
|
||||
D_ViewRuleKind_ColorRGBA,
|
||||
D_ViewRuleKind_Text,
|
||||
D_ViewRuleKind_Disasm,
|
||||
D_ViewRuleKind_Memory,
|
||||
D_ViewRuleKind_Graph,
|
||||
D_ViewRuleKind_Bitmap,
|
||||
D_ViewRuleKind_Geo3D,
|
||||
D_ViewRuleKind_COUNT,
|
||||
} D_ViewRuleKind;
|
||||
|
||||
global B32 DEV_simulate_lag = 0;
|
||||
global B32 DEV_draw_ui_text_pos = 0;
|
||||
global B32 DEV_draw_ui_focus_debug = 0;
|
||||
@@ -52,10 +78,10 @@ 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_scratch_mouse_draw = 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")},
|
||||
@@ -63,6 +89,7 @@ struct {B32 *value_ptr; String8 name;} DEV_toggle_table[] =
|
||||
{&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_scratch_mouse_draw, str8_lit_comp("scratch_mouse_draw")},
|
||||
{&DEV_updating_indicator, str8_lit_comp("updating_indicator")},
|
||||
};
|
||||
#endif // DBG_ENGINE_META_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
+1800
File diff suppressed because it is too large
Load Diff
+472
@@ -0,0 +1,472 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef DBGI_H
|
||||
#define DBGI_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: Debug Info 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;
|
||||
|
||||
// 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: Search Cache Types
|
||||
|
||||
typedef struct DI_SearchItem DI_SearchItem;
|
||||
struct DI_SearchItem
|
||||
{
|
||||
U64 idx;
|
||||
U64 dbgi_idx;
|
||||
U64 missed_size;
|
||||
FuzzyMatchRangeList match_ranges;
|
||||
};
|
||||
|
||||
typedef struct DI_SearchItemChunk DI_SearchItemChunk;
|
||||
struct DI_SearchItemChunk
|
||||
{
|
||||
DI_SearchItemChunk *next;
|
||||
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;
|
||||
};
|
||||
|
||||
typedef struct DI_SearchParams DI_SearchParams;
|
||||
struct DI_SearchParams
|
||||
{
|
||||
RDI_SectionKind target;
|
||||
DI_KeyArray dbgi_keys;
|
||||
};
|
||||
|
||||
typedef struct DI_SearchBucket DI_SearchBucket;
|
||||
struct DI_SearchBucket
|
||||
{
|
||||
Arena *arena;
|
||||
String8 query;
|
||||
U64 params_hash;
|
||||
DI_SearchParams params;
|
||||
};
|
||||
|
||||
typedef struct DI_SearchNode DI_SearchNode;
|
||||
struct DI_SearchNode
|
||||
{
|
||||
DI_SearchNode *next;
|
||||
DI_SearchNode *prev;
|
||||
U128 key;
|
||||
U64 scope_refcount;
|
||||
U64 work_refcount;
|
||||
U64 last_update_tick_idx;
|
||||
U64 bucket_read_gen;
|
||||
U64 bucket_write_gen;
|
||||
U64 bucket_items_gen;
|
||||
DI_SearchBucket buckets[6];
|
||||
DI_SearchItemArray items;
|
||||
};
|
||||
|
||||
typedef struct DI_SearchSlot DI_SearchSlot;
|
||||
struct DI_SearchSlot
|
||||
{
|
||||
DI_SearchNode *first;
|
||||
DI_SearchNode *last;
|
||||
};
|
||||
|
||||
typedef struct DI_SearchStripe DI_SearchStripe;
|
||||
struct DI_SearchStripe
|
||||
{
|
||||
Arena *arena;
|
||||
DI_SearchNode *free_node;
|
||||
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;
|
||||
DI_SearchNode *search_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: Search Thread State Types
|
||||
|
||||
typedef struct DI_SearchThread DI_SearchThread;
|
||||
struct DI_SearchThread
|
||||
{
|
||||
OS_Handle thread;
|
||||
OS_Handle ring_mutex;
|
||||
OS_Handle ring_cv;
|
||||
U64 ring_size;
|
||||
U8 *ring_base;
|
||||
U64 ring_write_pos;
|
||||
U64 ring_read_pos;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Match Cache State Types
|
||||
|
||||
typedef struct DI_Match DI_Match;
|
||||
struct DI_Match
|
||||
{
|
||||
DI_Match *next;
|
||||
DI_Match *prev;
|
||||
U64 dbgi_idx;
|
||||
RDI_SectionKind section;
|
||||
U32 idx;
|
||||
};
|
||||
|
||||
typedef struct DI_MatchNameNode DI_MatchNameNode;
|
||||
struct DI_MatchNameNode
|
||||
{
|
||||
// rjf: synchronously written by usage code
|
||||
DI_MatchNameNode *next;
|
||||
DI_MatchNameNode *prev;
|
||||
DI_MatchNameNode *lru_next;
|
||||
DI_MatchNameNode *lru_prev;
|
||||
U64 alloc_gen;
|
||||
U64 first_gen_touched;
|
||||
U64 last_gen_touched;
|
||||
U64 req_params_hash;
|
||||
U64 req_count;
|
||||
String8 name;
|
||||
U64 hash;
|
||||
|
||||
// rjf: atomically written by match work
|
||||
U64 cmp_count;
|
||||
U64 cmp_params_hash;
|
||||
RDI_SectionKind section_kind;
|
||||
// DI_Match *first_match;
|
||||
// DI_Match *last_match;
|
||||
};
|
||||
|
||||
typedef struct DI_MatchNameSlot DI_MatchNameSlot;
|
||||
struct DI_MatchNameSlot
|
||||
{
|
||||
DI_MatchNameNode *first;
|
||||
DI_MatchNameNode *last;
|
||||
};
|
||||
|
||||
typedef struct DI_MatchStore DI_MatchStore;
|
||||
struct DI_MatchStore
|
||||
{
|
||||
Arena *arena;
|
||||
U64 gen;
|
||||
Arena *gen_arenas[2];
|
||||
|
||||
// rjf: parameters
|
||||
Arena *params_arena;
|
||||
OS_Handle params_rw_mutex;
|
||||
U64 params_hash;
|
||||
DI_KeyArray params_keys;
|
||||
|
||||
// rjf: match cache
|
||||
U64 match_name_slots_count;
|
||||
DI_MatchNameSlot *match_name_slots;
|
||||
DI_MatchNameNode *first_free_match_name;
|
||||
DI_Match *first_free_match;
|
||||
DI_MatchNameNode *first_lru_match_name;
|
||||
DI_MatchNameNode *last_lru_match_name;
|
||||
U64 active_match_name_nodes_count;
|
||||
OS_Handle match_rw_mutex;
|
||||
OS_Handle match_cv;
|
||||
|
||||
// rjf: user -> match work ring buffer
|
||||
OS_Handle u2m_ring_cv;
|
||||
OS_Handle u2m_ring_mutex;
|
||||
U64 u2m_ring_size;
|
||||
U8 *u2m_ring_base;
|
||||
U64 u2m_ring_write_pos;
|
||||
U64 u2m_ring_read_pos;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Shared State Types
|
||||
|
||||
typedef struct DI_Shared DI_Shared;
|
||||
struct DI_Shared
|
||||
{
|
||||
Arena *arena;
|
||||
|
||||
// rjf: debug info cache
|
||||
U64 slots_count;
|
||||
DI_Slot *slots;
|
||||
U64 stripes_count;
|
||||
DI_Stripe *stripes;
|
||||
|
||||
// rjf: search cache
|
||||
U64 search_slots_count;
|
||||
DI_SearchSlot *search_slots;
|
||||
U64 search_stripes_count;
|
||||
DI_SearchStripe *search_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: search threads
|
||||
U64 search_threads_count;
|
||||
DI_SearchThread *search_threads;
|
||||
OS_Handle search_evictor_thread;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
global DI_Shared *di_shared = 0;
|
||||
thread_static DI_TCTX *di_tctx = 0;
|
||||
global RDI_Parsed di_rdi_parsed_nil = {0};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Helpers
|
||||
|
||||
internal U64 di_hash_from_seed_string(U64 seed, String8 string, StringMatchFlags match_flags);
|
||||
internal U64 di_hash_from_string(String8 string, StringMatchFlags match_flags);
|
||||
internal U64 di_hash_from_key(DI_Key *k);
|
||||
internal DI_Key di_key_zero(void);
|
||||
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);
|
||||
internal DI_KeyArray di_key_array_copy(Arena *arena, DI_KeyArray *src);
|
||||
internal DI_SearchParams di_search_params_copy(Arena *arena, DI_SearchParams *src);
|
||||
internal U64 di_hash_from_search_params(DI_SearchParams *params);
|
||||
internal void di_search_item_chunk_list_concat_in_place(DI_SearchItemChunkList *dst, DI_SearchItemChunkList *to_push);
|
||||
internal U64 di_search_item_num_from_array_element_idx__linear_search(DI_SearchItemArray *array, U64 element_idx);
|
||||
internal String8 di_search_item_string_from_rdi_target_element_idx(RDI_Parsed *rdi, RDI_SectionKind target, U64 element_idx);
|
||||
|
||||
////////////////////////////////
|
||||
//~ 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);
|
||||
internal void di_scope_touch_search_node__stripe_mutex_r_guarded(DI_Scope *scope, DI_SearchNode *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: Debug Info Cache Lookups
|
||||
|
||||
internal RDI_Parsed *di_rdi_from_key(DI_Scope *scope, DI_Key *key, U64 endt_us);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Search Cache Lookups
|
||||
|
||||
internal DI_SearchItemArray di_search_items_from_key_params_query(DI_Scope *scope, U128 key, DI_SearchParams *params, String8 query, U64 endt_us, B32 *stale_out);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Asynchronous Parse Work
|
||||
|
||||
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);
|
||||
|
||||
ASYNC_WORK_DEF(di_parse_work);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Search Threads
|
||||
|
||||
internal B32 di_u2s_enqueue_req(U128 key, U64 endt_us);
|
||||
internal U128 di_u2s_dequeue_req(U64 thread_idx);
|
||||
|
||||
ASYNC_WORK_DEF(di_search_work);
|
||||
internal int di_qsort_compare_search_items(DI_SearchItem *a, DI_SearchItem *b);
|
||||
internal void di_search_thread__entry_point(void *p);
|
||||
|
||||
internal void di_search_evictor_thread__entry_point(void *p);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Match Store
|
||||
|
||||
internal DI_MatchStore *di_match_store_alloc(void);
|
||||
internal void di_match_store_begin(DI_MatchStore *store, DI_KeyArray keys);
|
||||
internal RDI_SectionKind di_match_store_section_kind_from_name(DI_MatchStore *store, String8 name, U64 endt_us);
|
||||
ASYNC_WORK_DEF(di_match_work);
|
||||
|
||||
#endif // DBGI_H
|
||||
@@ -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/)
|
||||
|
||||
////////////////////////////////
|
||||
@@ -158,25 +158,31 @@ dmn_rsp_from_thread(DMN_Handle thread)
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Process Reading Helper Functions (Helpers, Implemented Once)
|
||||
//~ Memory Helpers
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
+8
-18
@@ -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/)
|
||||
|
||||
#ifndef DEMON_CORE_H
|
||||
@@ -73,7 +73,7 @@ struct DMN_Event
|
||||
U64 size;
|
||||
String8 string;
|
||||
U32 code; // code gives pid & tid on CreateProcess and CreateThread (respectfully)
|
||||
U32 flags; // DMN_TrapFlags, if `DMN_EventKind_SetBreakpoint`
|
||||
U32 flags;
|
||||
S32 signo;
|
||||
S32 sigcode;
|
||||
U64 instruction_pointer;
|
||||
@@ -100,22 +100,12 @@ struct DMN_EventList
|
||||
////////////////////////////////
|
||||
//~ 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;
|
||||
struct DMN_Trap
|
||||
{
|
||||
DMN_Handle process;
|
||||
U64 vaddr;
|
||||
U64 id;
|
||||
DMN_TrapFlags flags;
|
||||
U32 size;
|
||||
};
|
||||
|
||||
typedef struct DMN_TrapChunkNode DMN_TrapChunkNode;
|
||||
@@ -139,7 +129,6 @@ struct DMN_TrapChunkList
|
||||
typedef struct DMN_RunCtrls DMN_RunCtrls;
|
||||
struct DMN_RunCtrls
|
||||
{
|
||||
DMN_Handle priority_thread;
|
||||
DMN_Handle single_step_thread;
|
||||
B8 ignore_previous_exception;
|
||||
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_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)
|
||||
|
||||
@@ -222,6 +206,11 @@ internal void dmn_halt(U64 code, U64 user_data);
|
||||
////////////////////////////////
|
||||
//~ 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
|
||||
internal B32 dmn_access_open(void);
|
||||
internal void dmn_access_close(void);
|
||||
@@ -237,6 +226,7 @@ internal U64 dmn_process_read(DMN_Handle process, Rng1U64 range, void *dst);
|
||||
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_write_struct(process, vaddr, ptr) dmn_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr)
|
||||
internal String8 dmn_process_read_cstring(Arena *arena, DMN_Handle process, U64 addr);
|
||||
|
||||
//- rjf: threads
|
||||
internal Arch dmn_arch_from_thread(DMN_Handle handle);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user