diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 677fcb27..140381c3 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -1,30 +1,30 @@ -name: builds - -on: - push: - paths-ignore: - - '**.md' - pull_request: - paths-ignore: - - '**.md' - -jobs: - build-windows-2022: - runs-on: windows-2022 - strategy: - fail-fast: false - steps: - - name: checkout - uses: actions/checkout@v2 - - name: build (vs 2022) - shell: cmd - run: | - call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 - 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 +name: builds + +on: + push: + paths-ignore: + - '**.md' + pull_request: + paths-ignore: + - '**.md' + +jobs: + build-windows-2022: + runs-on: windows-2022 + strategy: + fail-fast: false + steps: + - name: checkout + uses: actions/checkout@v2 + - name: build (vs 2022) + shell: cmd + run: | + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 + 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 diff --git a/.gitignore b/.gitignore index f47970a9..c1dcca67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -/build/ -/local/ -*~ +/build/ +/local/ +*~ diff --git a/LICENSE b/LICENSE index 84e63ac5..ef60e20b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,19 @@ -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 -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +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 +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 0dfe8775..1adbf516 100644 --- a/README.md +++ b/README.md @@ -1,327 +1,327 @@ -# The RAD Debugger Project - -_**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._ - -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 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. - -You can download pre-built binaries for the debugger -[here](https://github.com/EpicGames/raddebugger/releases). - -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_format` folder. The other relevant folders for working with the -format are: - -- `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. - -## Development Setup Instructions - -**Note: Currently, only x64 Windows development is supported.** - -### 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 -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 - -Building the codebase can be done in a terminal which is equipped with the -ability to call either MSVC or Clang from command line. - -This is generally done by calling `vcvarsall.bat x64`, which is included in the -Microsoft C/C++ Build Tools. This script is automatically called by the `x64 -Native Tools Command Prompt for VS ` variant of the vanilla `cmd.exe`. If -you've installed the build tools, this command prompt may be easily located by -searching for `Native` from the Windows Start Menu search. - -You can ensure that the MSVC compiler is accessible from your command line by -running: - -``` -cl -``` - -If everything is set up correctly, you should have output very similar to the -following: - -``` -Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30151 for x64 -Copyright (C) Microsoft Corporation. All rights reserved. - -usage: cl [ option... ] filename... [ /link linkoption... ] -``` - -### 3. Building - -Within this terminal, `cd` to the root directory of the codebase, and just run -the `build.bat` script: - -``` -build -``` - -You should see the following output: - -``` -[debug mode] -[msvc compile] -[default mode, assuming `raddbg` build] -metagen_main.c -searching C:\devel\raddebugger/src... 299 files found -parsing metadesk... 12 metadesk files parsed -gathering tables... 37 tables found -generating layer code... -raddbg.cpp -``` - -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`. - -## Short-To-Medium-Term Roadmap - -### The Initial Alpha Battle-Testing Phase - -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 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 - -The next priority for the project is to take the rock solid x64 Windows -debugging experience, and port all of the relevant pieces to support local x64 -Linux debugging also. - -The debugger has been written to abstract over the parts that need to differ on -either Linux or Windows, and this is mainly going to be a task in building out -different backends for those abstraction layers. - -The major parts of this phase are: - -- Porting the `src/demon` layer to implement the Demon local process control -abstraction API. -- Implementing an x64 ELF Linux unwinder in the `src/ctrl` layer. -- Creating a DWARF-to-RDI converter (in the same way that we've built a -PDB-to-RDI converter). A partial implementation of this is in -`src/rdi_from_dwarf`. -- Porting the `src/render` layer to implement all of the rendering features the -frontend needs on a Linux-compatible API (the backend used on Windows is D3D11). -- Porting the `src/font_provider` layer to a Linux-compatible font -rasterization backend, like FreeType (the backend used on Windows is -DirectWrite). -- Porting the `src/os` layers to Linux. This includes core operating system -abstraction (virtual memory allocation, threading and synchronization -primitives, and so on), and graphical operating system abstraction (windows, -input events, and so on). - -Once the above list is complete, and once every part is rock solid, the Windows -debugging experience we'll have worked diligently to create will also be -available natively on Linux machines. - -### And Beyond! - -There are several directions we might take after these two major phases, -like remote debugging, porting to different architectures, further improving -the debugger's features (like improving the visualization engine), and so on. -But for now, we're mostly focused on those first two phases. - -## Top-Level Directory Descriptions - -- `data`: Small binary files which are used when building, either to embed - within build artifacts, or to package with them. -- `src`: All source code. - -After setting up the codebase and building, the following directories will -also exist: - -- `build`: All build artifacts. Not checked in to version control. -- `local`: Local files, used for local build configuration input files. - -## 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 -pull everything in the codebase into a build. Layers correspond with folders -inside of the `src` directory. Sometimes, one folder inside of the `src` -directory will include multiple sub-layers, but the structure is intended to be -fairly flat. - -Layers correspond roughly 1-to-1 with *namespaces*. The term "namespaces" in -this context does not refer to specific namespace language features, but rather -a naming convention for C-style namespaces, which are written in the codebase as -a short prefix, usually 1-3 characters, followed by an underscore. These -namespaces are used such that the layer to which certain code belongs may be -quickly understood by glancing at code. The namespaces are generally quite short -to ensure that they aren't much of a hassle to write. Sometimes, multiple sub- -layers will share a namespace. A few layers do not have a namespace, but most -do. Namespaces are either all-caps or lowercase depending on the context in -which they're used. For types, enum values, and some macros, they are -capitalized. For functions and global variables, they are lowercase. - -Layers depend on other layers, but circular dependencies would break the -separability and isolation utility of layers (in effect, forming one big layer), -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_format`. - -A list of the layers in the codebase and their associated namespaces is below: -- `base` (no namespace): Universal, codebase-wide constructs. Strings, math, - 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. -- `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` (`DASM_`): An asynchronous disassembly decoder and cache. Users ask for - disassembly for a particular virtual address range in a process, and threads - implemented in this layer decode and cache the disassembly for that range. -- `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. -- `demon` (`DEMON_`): 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`. -- `df/core` (`DF_`): The debugger's non-graphical frontend. Implements a - debugger "entity cache" (where "entities" include processes, threads, modules, - breakpoints, source files, targets, and so on). Implements a command loop - for driving process control, which is used to implement stepping commands and - user breakpoints. Implements extractors and caches for various entity-related - data, like full thread unwinds and local variable maps. Also implements core - building blocks for evaluation and evaluation visualization. -- `df/gfx` (`DF_`): The debugger's graphical frontend. Builds on top of - `df/core` to provide all graphical features, including windows, panels, all - of the various debugger interfaces, and evaluation visualization. -- `draw` (`D_`): 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. -- `eval` (`EVAL_`): Implements a compiler for an expression language built for - evaluation of variables, registers, and so on from debugger-attached processes - and/or debug info. Broken into several phases mostly corresponding to - traditional compiler phases - lexer, parser, type-checker, IR generation, and - IR evaluation. -- `font_cache` (`F_`): 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. -- `geo_cache` (`GEO_`): Implements an asynchronously-filled cache for GPU - geometry data, filled by data sourced in the `hash_store` layer's cache. Used - for asynchronously preparing data for memory visualization in the debugger. -- `hash_store` (`HS_`): Implements a cache for general data blobs, keyed by a - 128-bit hash of the data. Used as a general data store by other layers. -- `lib_raddbg_markup` (`RADDBG_`): Standalone library for marking up user - programs to work with various features in the `raddbg` debugger. 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. -- `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. -- `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 - does not analyze the codebase's hand-written C code, but in principle this is - possible. This allows easier & less-error-prone management of large data - 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. - 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. -- `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 - functionality from the operating system under an abstract API, which is - implemented per-target-operating-system. -- `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. -- `os/socket` (`OS_`): An abstraction layer, building on `os/core`, providing - networking operating system features under an abstract API, which is - implemented per-target-operating-system. -- `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` (no namespace): The layer which ties everything together for the main - graphical debugger. Not much "meat", just drives `df`, implements command line - options, and so on. -- `rdi_from_pdb` (`P2R_`): Our implementation of PDB-to-RDI conversion. -- `rdi_from_dwarf` (`D2R_`): Our in-progress implementation of DWARF-to-RDI - conversion. -- `rdi_dump` (no namespace): A dumper utility program for dumping - textualizations of RDI debug info files. -- `regs` (`REGS_`): Types, helper functions, and metadata for registers on - supported architectures. Used in reading/writing registers in `demon`, or in - looking up register metadata. -- `render` (`R_`): An abstraction layer providing an abstract API for rendering - using various GPU APIs under a common interface. Does not implement a high - level drawing API - this layer is strictly for minimally abstracting on an - as-needed basis. Higher level drawing features are implemented in the `draw` - layer. -- `scratch` (no namespace): Scratch space for small and transient test or sample - programs. -- `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 memory visualization in the debugger. -- `txti` (`TXTI_`): Machinery for asynchronously-loaded, asynchronously hot- - reloaded, asynchronously parsed, and asynchronously mutated source code files. - Used by the debugger to visualize source code files. Users ask for text lines, - tokens, and metadata, and it is prepared on background threads. -- `type_graph` (`TG_`): Code for analyzing and navigating type structures from - RDI debug info files, with the additional capability of constructing - synthetic types *not* found in debug info. Used in `eval` and for various - visualization features. -- `ui` (`UI_`): Machinery for building graphical user interfaces. Provides a - core immediate mode hierarchical user interface data structure building - API, and has helper layers for building some higher-level widgets. +# The RAD Debugger Project + +_**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._ + +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 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. + +You can download pre-built binaries for the debugger +[here](https://github.com/EpicGames/raddebugger/releases). + +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_format` folder. The other relevant folders for working with the +format are: + +- `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. + +## Development Setup Instructions + +**Note: Currently, only x64 Windows development is supported.** + +### 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 +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 + +Building the codebase can be done in a terminal which is equipped with the +ability to call either MSVC or Clang from command line. + +This is generally done by calling `vcvarsall.bat x64`, which is included in the +Microsoft C/C++ Build Tools. This script is automatically called by the `x64 +Native Tools Command Prompt for VS ` variant of the vanilla `cmd.exe`. If +you've installed the build tools, this command prompt may be easily located by +searching for `Native` from the Windows Start Menu search. + +You can ensure that the MSVC compiler is accessible from your command line by +running: + +``` +cl +``` + +If everything is set up correctly, you should have output very similar to the +following: + +``` +Microsoft (R) C/C++ Optimizing Compiler Version 19.29.30151 for x64 +Copyright (C) Microsoft Corporation. All rights reserved. + +usage: cl [ option... ] filename... [ /link linkoption... ] +``` + +### 3. Building + +Within this terminal, `cd` to the root directory of the codebase, and just run +the `build.bat` script: + +``` +build +``` + +You should see the following output: + +``` +[debug mode] +[msvc compile] +[default mode, assuming `raddbg` build] +metagen_main.c +searching C:\devel\raddebugger/src... 299 files found +parsing metadesk... 12 metadesk files parsed +gathering tables... 37 tables found +generating layer code... +raddbg.cpp +``` + +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`. + +## Short-To-Medium-Term Roadmap + +### The Initial Alpha Battle-Testing Phase + +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 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 + +The next priority for the project is to take the rock solid x64 Windows +debugging experience, and port all of the relevant pieces to support local x64 +Linux debugging also. + +The debugger has been written to abstract over the parts that need to differ on +either Linux or Windows, and this is mainly going to be a task in building out +different backends for those abstraction layers. + +The major parts of this phase are: + +- Porting the `src/demon` layer to implement the Demon local process control +abstraction API. +- Implementing an x64 ELF Linux unwinder in the `src/ctrl` layer. +- Creating a DWARF-to-RDI converter (in the same way that we've built a +PDB-to-RDI converter). A partial implementation of this is in +`src/rdi_from_dwarf`. +- Porting the `src/render` layer to implement all of the rendering features the +frontend needs on a Linux-compatible API (the backend used on Windows is D3D11). +- Porting the `src/font_provider` layer to a Linux-compatible font +rasterization backend, like FreeType (the backend used on Windows is +DirectWrite). +- Porting the `src/os` layers to Linux. This includes core operating system +abstraction (virtual memory allocation, threading and synchronization +primitives, and so on), and graphical operating system abstraction (windows, +input events, and so on). + +Once the above list is complete, and once every part is rock solid, the Windows +debugging experience we'll have worked diligently to create will also be +available natively on Linux machines. + +### And Beyond! + +There are several directions we might take after these two major phases, +like remote debugging, porting to different architectures, further improving +the debugger's features (like improving the visualization engine), and so on. +But for now, we're mostly focused on those first two phases. + +## Top-Level Directory Descriptions + +- `data`: Small binary files which are used when building, either to embed + within build artifacts, or to package with them. +- `src`: All source code. + +After setting up the codebase and building, the following directories will +also exist: + +- `build`: All build artifacts. Not checked in to version control. +- `local`: Local files, used for local build configuration input files. + +## 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 +pull everything in the codebase into a build. Layers correspond with folders +inside of the `src` directory. Sometimes, one folder inside of the `src` +directory will include multiple sub-layers, but the structure is intended to be +fairly flat. + +Layers correspond roughly 1-to-1 with *namespaces*. The term "namespaces" in +this context does not refer to specific namespace language features, but rather +a naming convention for C-style namespaces, which are written in the codebase as +a short prefix, usually 1-3 characters, followed by an underscore. These +namespaces are used such that the layer to which certain code belongs may be +quickly understood by glancing at code. The namespaces are generally quite short +to ensure that they aren't much of a hassle to write. Sometimes, multiple sub- +layers will share a namespace. A few layers do not have a namespace, but most +do. Namespaces are either all-caps or lowercase depending on the context in +which they're used. For types, enum values, and some macros, they are +capitalized. For functions and global variables, they are lowercase. + +Layers depend on other layers, but circular dependencies would break the +separability and isolation utility of layers (in effect, forming one big layer), +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_format`. + +A list of the layers in the codebase and their associated namespaces is below: +- `base` (no namespace): Universal, codebase-wide constructs. Strings, math, + 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. +- `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` (`DASM_`): An asynchronous disassembly decoder and cache. Users ask for + disassembly for a particular virtual address range in a process, and threads + implemented in this layer decode and cache the disassembly for that range. +- `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. +- `demon` (`DEMON_`): 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`. +- `df/core` (`DF_`): The debugger's non-graphical frontend. Implements a + debugger "entity cache" (where "entities" include processes, threads, modules, + breakpoints, source files, targets, and so on). Implements a command loop + for driving process control, which is used to implement stepping commands and + user breakpoints. Implements extractors and caches for various entity-related + data, like full thread unwinds and local variable maps. Also implements core + building blocks for evaluation and evaluation visualization. +- `df/gfx` (`DF_`): The debugger's graphical frontend. Builds on top of + `df/core` to provide all graphical features, including windows, panels, all + of the various debugger interfaces, and evaluation visualization. +- `draw` (`D_`): 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. +- `eval` (`EVAL_`): Implements a compiler for an expression language built for + evaluation of variables, registers, and so on from debugger-attached processes + and/or debug info. Broken into several phases mostly corresponding to + traditional compiler phases - lexer, parser, type-checker, IR generation, and + IR evaluation. +- `font_cache` (`F_`): 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. +- `geo_cache` (`GEO_`): Implements an asynchronously-filled cache for GPU + geometry data, filled by data sourced in the `hash_store` layer's cache. Used + for asynchronously preparing data for memory visualization in the debugger. +- `hash_store` (`HS_`): Implements a cache for general data blobs, keyed by a + 128-bit hash of the data. Used as a general data store by other layers. +- `lib_raddbg_markup` (`RADDBG_`): Standalone library for marking up user + programs to work with various features in the `raddbg` debugger. 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. +- `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. +- `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 + does not analyze the codebase's hand-written C code, but in principle this is + possible. This allows easier & less-error-prone management of large data + 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. + 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. +- `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 + functionality from the operating system under an abstract API, which is + implemented per-target-operating-system. +- `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. +- `os/socket` (`OS_`): An abstraction layer, building on `os/core`, providing + networking operating system features under an abstract API, which is + implemented per-target-operating-system. +- `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` (no namespace): The layer which ties everything together for the main + graphical debugger. Not much "meat", just drives `df`, implements command line + options, and so on. +- `rdi_from_pdb` (`P2R_`): Our implementation of PDB-to-RDI conversion. +- `rdi_from_dwarf` (`D2R_`): Our in-progress implementation of DWARF-to-RDI + conversion. +- `rdi_dump` (no namespace): A dumper utility program for dumping + textualizations of RDI debug info files. +- `regs` (`REGS_`): Types, helper functions, and metadata for registers on + supported architectures. Used in reading/writing registers in `demon`, or in + looking up register metadata. +- `render` (`R_`): An abstraction layer providing an abstract API for rendering + using various GPU APIs under a common interface. Does not implement a high + level drawing API - this layer is strictly for minimally abstracting on an + as-needed basis. Higher level drawing features are implemented in the `draw` + layer. +- `scratch` (no namespace): Scratch space for small and transient test or sample + programs. +- `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 memory visualization in the debugger. +- `txti` (`TXTI_`): Machinery for asynchronously-loaded, asynchronously hot- + reloaded, asynchronously parsed, and asynchronously mutated source code files. + Used by the debugger to visualize source code files. Users ask for text lines, + tokens, and metadata, and it is prepared on background threads. +- `type_graph` (`TG_`): Code for analyzing and navigating type structures from + RDI debug info files, with the additional capability of constructing + synthetic types *not* found in debug info. Used in `eval` and for various + visualization features. +- `ui` (`UI_`): Machinery for building graphical user interfaces. Provides a + core immediate mode hierarchical user interface data structure building + API, and has helper layers for building some higher-level widgets. diff --git a/src/base/base_command_line.c b/src/base/base_command_line.c index 2c761583..ecf23745 100644 --- a/src/base/base_command_line.c +++ b/src/base/base_command_line.c @@ -1,232 +1,232 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ 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) -{ - CmdLineOpt **slot = 0; - if(cmd_line->option_table_size != 0) - { - U64 hash = cmd_line_hash_from_string(string); - U64 bucket = hash % cmd_line->option_table_size; - slot = &cmd_line->option_table[bucket]; - } - return slot; -} - -internal CmdLineOpt * -cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string) -{ - CmdLineOpt *result = 0; - for(CmdLineOpt *var = *slot; var; var = var->hash_next) - { - if(str8_match(string, var->string, 0)) - { - result = var; - break; - } - } - return result; -} - -internal void -cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var) -{ - SLLQueuePush(list->first, list->last, var); - list->count += 1; -} - -internal CmdLineOpt * -cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values) -{ - CmdLineOpt *var = 0; - CmdLineOpt **slot = cmd_line_slot_from_string(cmd_line, string); - CmdLineOpt *existing_var = cmd_line_opt_from_slot(slot, string); - if(existing_var != 0) - { - var = existing_var; - } - else - { - var = push_array(arena, CmdLineOpt, 1); - var->hash_next = *slot; - var->hash = cmd_line_hash_from_string(string); - var->string = push_str8_copy(arena, string); - var->value_strings = values; - StringJoin join = {0}; - join.pre = str8_lit(""); - join.sep = str8_lit(","); - join.post = str8_lit(""); - var->value_string = str8_list_join(arena, &var->value_strings, &join); - *slot = var; - cmd_line_push_opt(&cmd_line->options, var); - } - return var; -} - -internal CmdLine -cmd_line_from_string_list(Arena *arena, String8List command_line) -{ - CmdLine parsed = {0}; - parsed.exe_name = command_line.first->string; - - // 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; - String8 option_name = node->string; - - // NOTE(rjf): Look at -- or - at the start of an argument to determine if it's - // a flag option. All arguments after a single "--" (with no trailing string - // on the command line will be considered as input files. - B32 is_option = 1; - if(after_passthrough_option == 0) - { - if(str8_match(node->string, str8_lit("--"), 0)) - { - after_passthrough_option = 1; - is_option = 0; - } - else if(str8_match(str8_prefix(node->string, 2), str8_lit("--"), 0)) - { - option_name = str8_skip(option_name, 2); - } - else if(str8_match(str8_prefix(node->string, 1), str8_lit("-"), 0)) - { - option_name = str8_skip(option_name, 1); - } - else - { - is_option = 0; - } - } - else - { - is_option = 0; - } - - // NOTE(rjf): This string is an option. - if(is_option) - { - 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_arguments = 1; - } - option_name = str8_prefix(option_name, arg_signifier_position); - - 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 = arg_portion_this_string; - } - - U8 splits[] = { ',' }; - 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, &arguments, sub_arg->string); - } - if(!str8_match(str8_postfix(n->string, 1), str8_lit(","), 0) && - (n != node || arg_portion_this_string.size != 0)) - { - break; - } - } - } - - // NOTE(rjf): Register config variable. - cmd_line_insert_opt(arena, &parsed, option_name, arguments); - } - - // 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; - } - } - - return parsed; -} - -internal CmdLineOpt * -cmd_line_opt_from_string(CmdLine *cmd_line, String8 name) -{ - return cmd_line_opt_from_slot(cmd_line_slot_from_string(cmd_line, name), name); -} - -internal String8List -cmd_line_strings(CmdLine *cmd_line, String8 name) -{ - String8List result = {0}; - CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); - if(var != 0) - { - result = var->value_strings; - } - return result; -} - -internal String8 -cmd_line_string(CmdLine *cmd_line, String8 name) -{ - String8 result = {0}; - CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); - if(var != 0) - { - result = var->value_string; - } - return result; -} - -internal B32 -cmd_line_has_flag(CmdLine *cmd_line, String8 name) -{ - CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); - 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); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ 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) +{ + CmdLineOpt **slot = 0; + if(cmd_line->option_table_size != 0) + { + U64 hash = cmd_line_hash_from_string(string); + U64 bucket = hash % cmd_line->option_table_size; + slot = &cmd_line->option_table[bucket]; + } + return slot; +} + +internal CmdLineOpt * +cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string) +{ + CmdLineOpt *result = 0; + for(CmdLineOpt *var = *slot; var; var = var->hash_next) + { + if(str8_match(string, var->string, 0)) + { + result = var; + break; + } + } + return result; +} + +internal void +cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var) +{ + SLLQueuePush(list->first, list->last, var); + list->count += 1; +} + +internal CmdLineOpt * +cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values) +{ + CmdLineOpt *var = 0; + CmdLineOpt **slot = cmd_line_slot_from_string(cmd_line, string); + CmdLineOpt *existing_var = cmd_line_opt_from_slot(slot, string); + if(existing_var != 0) + { + var = existing_var; + } + else + { + var = push_array(arena, CmdLineOpt, 1); + var->hash_next = *slot; + var->hash = cmd_line_hash_from_string(string); + var->string = push_str8_copy(arena, string); + var->value_strings = values; + StringJoin join = {0}; + join.pre = str8_lit(""); + join.sep = str8_lit(","); + join.post = str8_lit(""); + var->value_string = str8_list_join(arena, &var->value_strings, &join); + *slot = var; + cmd_line_push_opt(&cmd_line->options, var); + } + return var; +} + +internal CmdLine +cmd_line_from_string_list(Arena *arena, String8List command_line) +{ + CmdLine parsed = {0}; + parsed.exe_name = command_line.first->string; + + // 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; + String8 option_name = node->string; + + // NOTE(rjf): Look at -- or - at the start of an argument to determine if it's + // a flag option. All arguments after a single "--" (with no trailing string + // on the command line will be considered as input files. + B32 is_option = 1; + if(after_passthrough_option == 0) + { + if(str8_match(node->string, str8_lit("--"), 0)) + { + after_passthrough_option = 1; + is_option = 0; + } + else if(str8_match(str8_prefix(node->string, 2), str8_lit("--"), 0)) + { + option_name = str8_skip(option_name, 2); + } + else if(str8_match(str8_prefix(node->string, 1), str8_lit("-"), 0)) + { + option_name = str8_skip(option_name, 1); + } + else + { + is_option = 0; + } + } + else + { + is_option = 0; + } + + // NOTE(rjf): This string is an option. + if(is_option) + { + 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_arguments = 1; + } + option_name = str8_prefix(option_name, arg_signifier_position); + + 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 = arg_portion_this_string; + } + + U8 splits[] = { ',' }; + 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, &arguments, sub_arg->string); + } + if(!str8_match(str8_postfix(n->string, 1), str8_lit(","), 0) && + (n != node || arg_portion_this_string.size != 0)) + { + break; + } + } + } + + // NOTE(rjf): Register config variable. + cmd_line_insert_opt(arena, &parsed, option_name, arguments); + } + + // 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; + } + } + + return parsed; +} + +internal CmdLineOpt * +cmd_line_opt_from_string(CmdLine *cmd_line, String8 name) +{ + return cmd_line_opt_from_slot(cmd_line_slot_from_string(cmd_line, name), name); +} + +internal String8List +cmd_line_strings(CmdLine *cmd_line, String8 name) +{ + String8List result = {0}; + CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); + if(var != 0) + { + result = var->value_strings; + } + return result; +} + +internal String8 +cmd_line_string(CmdLine *cmd_line, String8 name) +{ + String8 result = {0}; + CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); + if(var != 0) + { + result = var->value_string; + } + return result; +} + +internal B32 +cmd_line_has_flag(CmdLine *cmd_line, String8 name) +{ + CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name); + 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); +} diff --git a/src/base/base_command_line.h b/src/base/base_command_line.h index b18fc874..c37f91dc 100644 --- a/src/base/base_command_line.h +++ b/src/base/base_command_line.h @@ -1,54 +1,54 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_COMMAND_LINE_H -#define BASE_COMMAND_LINE_H - -//////////////////////////////// -//~ rjf: Parsed Command Line Types - -typedef struct CmdLineOpt CmdLineOpt; -struct CmdLineOpt -{ - CmdLineOpt *next; - CmdLineOpt *hash_next; - U64 hash; - String8 string; - String8List value_strings; - String8 value_string; -}; - -typedef struct CmdLineOptList CmdLineOptList; -struct CmdLineOptList -{ - U64 count; - CmdLineOpt *first; - CmdLineOpt *last; -}; - -typedef struct CmdLine CmdLine; -struct CmdLine -{ - String8 exe_name; - CmdLineOptList options; - String8List inputs; - U64 option_table_size; - CmdLineOpt **option_table; -}; - -//////////////////////////////// -//~ 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); -internal CmdLineOpt* cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values); -internal CmdLine cmd_line_from_string_list(Arena *arena, String8List arguments); -internal CmdLineOpt* cmd_line_opt_from_string(CmdLine *cmd_line, String8 name); -internal String8List cmd_line_strings(CmdLine *cmd_line, String8 name); -internal String8 cmd_line_string(CmdLine *cmd_line, String8 name); -internal B32 cmd_line_has_flag(CmdLine *cmd_line, String8 name); -internal B32 cmd_line_has_argument(CmdLine *cmd_line, String8 name); - -#endif // BASE_COMMAND_LINE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_COMMAND_LINE_H +#define BASE_COMMAND_LINE_H + +//////////////////////////////// +//~ rjf: Parsed Command Line Types + +typedef struct CmdLineOpt CmdLineOpt; +struct CmdLineOpt +{ + CmdLineOpt *next; + CmdLineOpt *hash_next; + U64 hash; + String8 string; + String8List value_strings; + String8 value_string; +}; + +typedef struct CmdLineOptList CmdLineOptList; +struct CmdLineOptList +{ + U64 count; + CmdLineOpt *first; + CmdLineOpt *last; +}; + +typedef struct CmdLine CmdLine; +struct CmdLine +{ + String8 exe_name; + CmdLineOptList options; + String8List inputs; + U64 option_table_size; + CmdLineOpt **option_table; +}; + +//////////////////////////////// +//~ 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); +internal CmdLineOpt* cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values); +internal CmdLine cmd_line_from_string_list(Arena *arena, String8List arguments); +internal CmdLineOpt* cmd_line_opt_from_string(CmdLine *cmd_line, String8 name); +internal String8List cmd_line_strings(CmdLine *cmd_line, String8 name); +internal String8 cmd_line_string(CmdLine *cmd_line, String8 name); +internal B32 cmd_line_has_flag(CmdLine *cmd_line, String8 name); +internal B32 cmd_line_has_argument(CmdLine *cmd_line, String8 name); + +#endif // BASE_COMMAND_LINE_H diff --git a/src/base/base_context_cracking.h b/src/base/base_context_cracking.h index 9df1dc5e..040c0004 100644 --- a/src/base/base_context_cracking.h +++ b/src/base/base_context_cracking.h @@ -1,247 +1,247 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_CONTEXT_CRACKING_H -#define BASE_CONTEXT_CRACKING_H - -//////////////////////////////// -//~ rjf: Clang OS/Arch Cracking - -#if defined(__clang__) - -# define COMPILER_CLANG 1 - -# if defined(_WIN32) -# define OS_WINDOWS 1 -# elif defined(__gnu_linux__) || defined(__linux__) -# define OS_LINUX 1 -# elif defined(__APPLE__) && defined(__MACH__) -# define OS_MAC 1 -# else -# error This compiler/OS combo is not supported. -# endif - -# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) -# define ARCH_X64 1 -# elif defined(i386) || defined(__i386) || defined(__i386__) -# define ARCH_X86 1 -# elif defined(__aarch64__) -# define ARCH_ARM64 1 -# elif defined(__arm__) -# define ARCH_ARM32 1 -# else -# error Architecture not supported. -# endif - -//////////////////////////////// -//~ rjf: MSVC OS/Arch Cracking - -#elif defined(_MSC_VER) - -# define COMPILER_MSVC 1 - -# if _MSC_VER >= 1920 -# define COMPILER_MSVC_YEAR 2019 -# elif _MSC_VER >= 1910 -# define COMPILER_MSVC_YEAR 2017 -# elif _MSC_VER >= 1900 -# define COMPILER_MSVC_YEAR 2015 -# elif _MSC_VER >= 1800 -# define COMPILER_MSVC_YEAR 2013 -# elif _MSC_VER >= 1700 -# define COMPILER_MSVC_YEAR 2012 -# elif _MSC_VER >= 1600 -# define COMPILER_MSVC_YEAR 2010 -# elif _MSC_VER >= 1500 -# define COMPILER_MSVC_YEAR 2008 -# elif _MSC_VER >= 1400 -# define COMPILER_MSVC_YEAR 2005 -# else -# define COMPILER_MSVC_YEAR 0 -# endif - -# if defined(_WIN32) -# define OS_WINDOWS 1 -# else -# error This compiler/OS combo is not supported. -# endif - -# if defined(_M_AMD64) -# define ARCH_X64 1 -# elif defined(_M_IX86) -# define ARCH_X86 1 -# elif defined(_M_ARM64) -# define ARCH_ARM64 1 -# elif defined(_M_ARM) -# define ARCH_ARM32 1 -# else -# error Architecture not supported. -# endif - -//////////////////////////////// -//~ rjf: GCC OS/Arch Cracking - -#elif defined(__GNUC__) || defined(__GNUG__) - -# define COMPILER_GCC 1 - -# if defined(__gnu_linux__) || defined(__linux__) -# define OS_LINUX 1 -# else -# error This compiler/OS combo is not supported. -# endif - -# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) -# define ARCH_X64 1 -# elif defined(i386) || defined(__i386) || defined(__i386__) -# define ARCH_X86 1 -# elif defined(__aarch64__) -# define ARCH_ARM64 1 -# elif defined(__arm__) -# define ARCH_ARM32 1 -# else -# error Architecture not supported. -# endif - -#else -# error Compiler not supported. -#endif - -//////////////////////////////// -//~ rjf: Arch Cracking - -#if defined(ARCH_X64) -# define ARCH_64BIT 1 -#elif defined(ARCH_X86) -# define ARCH_32BIT 1 -#endif - -#if ARCH_ARM32 || ARCH_ARM64 || ARCH_X64 || ARCH_X86 -# define ARCH_LITTLE_ENDIAN 1 -#else -# error Endianness of this architecture not understood by context cracker. -#endif - -//////////////////////////////// -//~ rjf: Language Cracking - -#if defined(__cplusplus) -# define LANG_CPP 1 -#else -# define LANG_C 1 -#endif - -//////////////////////////////// -//~ rjf: Build Option Cracking - -#if !defined(BUILD_DEBUG) -# define BUILD_DEBUG 1 -#endif - -#if !defined(BUILD_SUPPLEMENTARY_UNIT) -# define BUILD_SUPPLEMENTARY_UNIT 0 -#endif - -#if !defined(BUILD_ENTRY_DEFINING_UNIT) -# define BUILD_ENTRY_DEFINING_UNIT 1 -#endif - -#if !defined(BUILD_CONSOLE_INTERFACE) -# define BUILD_CONSOLE_INTERFACE 0 -#endif - -#if !defined(BUILD_VERSION_MAJOR) -# define BUILD_VERSION_MAJOR 0 -#endif - -#if !defined(BUILD_VERSION_MINOR) -# define BUILD_VERSION_MINOR 0 -#endif - -#if !defined(BUILD_VERSION_PATCH) -# define BUILD_VERSION_PATCH 0 -#endif - -#define BUILD_VERSION_STRING_LITERAL Stringify(BUILD_VERSION_MAJOR) "." Stringify(BUILD_VERSION_MINOR) "." Stringify(BUILD_VERSION_PATCH) -#if BUILD_DEBUG -# define BUILD_MODE_STRING_LITERAL_APPEND " [Debug]" -#else -# define BUILD_MODE_STRING_LITERAL_APPEND "" -#endif -#if defined(BUILD_GIT_HASH) -# define BUILD_GIT_HASH_STRING_LITERAL_APPEND " [" BUILD_GIT_HASH "]" -#else -# define BUILD_GIT_HASH_STRING_LITERAL_APPEND "" -#endif - -#if !defined(BUILD_TITLE) -# define BUILD_TITLE "Untitled" -#endif - -#if !defined(BUILD_RELEASE_PHASE_STRING_LITERAL) -# define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA" -#endif - -#if !defined(BUILD_ISSUES_LINK_STRING_LITERAL) -# define BUILD_ISSUES_LINK_STRING_LITERAL "https://github.com/EpicGames/raddebugger/issues" -#endif - -#define BUILD_TITLE_STRING_LITERAL BUILD_TITLE " (" BUILD_VERSION_STRING_LITERAL " " BUILD_RELEASE_PHASE_STRING_LITERAL ") - " __DATE__ "" BUILD_GIT_HASH_STRING_LITERAL_APPEND BUILD_MODE_STRING_LITERAL_APPEND - -//////////////////////////////// -//~ rjf: Zero All Undefined Options - -#if !defined(ARCH_32BIT) -# define ARCH_32BIT 0 -#endif -#if !defined(ARCH_64BIT) -# define ARCH_64BIT 0 -#endif -#if !defined(ARCH_X64) -# define ARCH_X64 0 -#endif -#if !defined(ARCH_X86) -# define ARCH_X86 0 -#endif -#if !defined(ARCH_ARM64) -# define ARCH_ARM64 0 -#endif -#if !defined(ARCH_ARM32) -# define ARCH_ARM32 0 -#endif -#if !defined(COMPILER_MSVC) -# define COMPILER_MSVC 0 -#endif -#if !defined(COMPILER_GCC) -# define COMPILER_GCC 0 -#endif -#if !defined(COMPILER_CLANG) -# define COMPILER_CLANG 0 -#endif -#if !defined(OS_WINDOWS) -# define OS_WINDOWS 0 -#endif -#if !defined(OS_LINUX) -# define OS_LINUX 0 -#endif -#if !defined(OS_MAC) -# define OS_MAC 0 -#endif -#if !defined(LANG_CPP) -# define LANG_CPP 0 -#endif -#if !defined(LANG_C) -# define LANG_C 0 -#endif - -//////////////////////////////// -//~ rjf: Unsupported Errors - -#if ARCH_X86 -# error You tried to build in x86 (32 bit) mode, but currently, only building in x64 (64 bit) mode is supported. -#endif -#if !ARCH_X64 -# error You tried to build with an unsupported architecture. Currently, only building in x64 mode is supported. -#endif - -#endif // BASE_CONTEXT_CRACKING_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_CONTEXT_CRACKING_H +#define BASE_CONTEXT_CRACKING_H + +//////////////////////////////// +//~ rjf: Clang OS/Arch Cracking + +#if defined(__clang__) + +# define COMPILER_CLANG 1 + +# if defined(_WIN32) +# define OS_WINDOWS 1 +# elif defined(__gnu_linux__) || defined(__linux__) +# define OS_LINUX 1 +# elif defined(__APPLE__) && defined(__MACH__) +# define OS_MAC 1 +# else +# error This compiler/OS combo is not supported. +# endif + +# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) +# define ARCH_X64 1 +# elif defined(i386) || defined(__i386) || defined(__i386__) +# define ARCH_X86 1 +# elif defined(__aarch64__) +# define ARCH_ARM64 1 +# elif defined(__arm__) +# define ARCH_ARM32 1 +# else +# error Architecture not supported. +# endif + +//////////////////////////////// +//~ rjf: MSVC OS/Arch Cracking + +#elif defined(_MSC_VER) + +# define COMPILER_MSVC 1 + +# if _MSC_VER >= 1920 +# define COMPILER_MSVC_YEAR 2019 +# elif _MSC_VER >= 1910 +# define COMPILER_MSVC_YEAR 2017 +# elif _MSC_VER >= 1900 +# define COMPILER_MSVC_YEAR 2015 +# elif _MSC_VER >= 1800 +# define COMPILER_MSVC_YEAR 2013 +# elif _MSC_VER >= 1700 +# define COMPILER_MSVC_YEAR 2012 +# elif _MSC_VER >= 1600 +# define COMPILER_MSVC_YEAR 2010 +# elif _MSC_VER >= 1500 +# define COMPILER_MSVC_YEAR 2008 +# elif _MSC_VER >= 1400 +# define COMPILER_MSVC_YEAR 2005 +# else +# define COMPILER_MSVC_YEAR 0 +# endif + +# if defined(_WIN32) +# define OS_WINDOWS 1 +# else +# error This compiler/OS combo is not supported. +# endif + +# if defined(_M_AMD64) +# define ARCH_X64 1 +# elif defined(_M_IX86) +# define ARCH_X86 1 +# elif defined(_M_ARM64) +# define ARCH_ARM64 1 +# elif defined(_M_ARM) +# define ARCH_ARM32 1 +# else +# error Architecture not supported. +# endif + +//////////////////////////////// +//~ rjf: GCC OS/Arch Cracking + +#elif defined(__GNUC__) || defined(__GNUG__) + +# define COMPILER_GCC 1 + +# if defined(__gnu_linux__) || defined(__linux__) +# define OS_LINUX 1 +# else +# error This compiler/OS combo is not supported. +# endif + +# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) +# define ARCH_X64 1 +# elif defined(i386) || defined(__i386) || defined(__i386__) +# define ARCH_X86 1 +# elif defined(__aarch64__) +# define ARCH_ARM64 1 +# elif defined(__arm__) +# define ARCH_ARM32 1 +# else +# error Architecture not supported. +# endif + +#else +# error Compiler not supported. +#endif + +//////////////////////////////// +//~ rjf: Arch Cracking + +#if defined(ARCH_X64) +# define ARCH_64BIT 1 +#elif defined(ARCH_X86) +# define ARCH_32BIT 1 +#endif + +#if ARCH_ARM32 || ARCH_ARM64 || ARCH_X64 || ARCH_X86 +# define ARCH_LITTLE_ENDIAN 1 +#else +# error Endianness of this architecture not understood by context cracker. +#endif + +//////////////////////////////// +//~ rjf: Language Cracking + +#if defined(__cplusplus) +# define LANG_CPP 1 +#else +# define LANG_C 1 +#endif + +//////////////////////////////// +//~ rjf: Build Option Cracking + +#if !defined(BUILD_DEBUG) +# define BUILD_DEBUG 1 +#endif + +#if !defined(BUILD_SUPPLEMENTARY_UNIT) +# define BUILD_SUPPLEMENTARY_UNIT 0 +#endif + +#if !defined(BUILD_ENTRY_DEFINING_UNIT) +# define BUILD_ENTRY_DEFINING_UNIT 1 +#endif + +#if !defined(BUILD_CONSOLE_INTERFACE) +# define BUILD_CONSOLE_INTERFACE 0 +#endif + +#if !defined(BUILD_VERSION_MAJOR) +# define BUILD_VERSION_MAJOR 0 +#endif + +#if !defined(BUILD_VERSION_MINOR) +# define BUILD_VERSION_MINOR 0 +#endif + +#if !defined(BUILD_VERSION_PATCH) +# define BUILD_VERSION_PATCH 0 +#endif + +#define BUILD_VERSION_STRING_LITERAL Stringify(BUILD_VERSION_MAJOR) "." Stringify(BUILD_VERSION_MINOR) "." Stringify(BUILD_VERSION_PATCH) +#if BUILD_DEBUG +# define BUILD_MODE_STRING_LITERAL_APPEND " [Debug]" +#else +# define BUILD_MODE_STRING_LITERAL_APPEND "" +#endif +#if defined(BUILD_GIT_HASH) +# define BUILD_GIT_HASH_STRING_LITERAL_APPEND " [" BUILD_GIT_HASH "]" +#else +# define BUILD_GIT_HASH_STRING_LITERAL_APPEND "" +#endif + +#if !defined(BUILD_TITLE) +# define BUILD_TITLE "Untitled" +#endif + +#if !defined(BUILD_RELEASE_PHASE_STRING_LITERAL) +# define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA" +#endif + +#if !defined(BUILD_ISSUES_LINK_STRING_LITERAL) +# define BUILD_ISSUES_LINK_STRING_LITERAL "https://github.com/EpicGames/raddebugger/issues" +#endif + +#define BUILD_TITLE_STRING_LITERAL BUILD_TITLE " (" BUILD_VERSION_STRING_LITERAL " " BUILD_RELEASE_PHASE_STRING_LITERAL ") - " __DATE__ "" BUILD_GIT_HASH_STRING_LITERAL_APPEND BUILD_MODE_STRING_LITERAL_APPEND + +//////////////////////////////// +//~ rjf: Zero All Undefined Options + +#if !defined(ARCH_32BIT) +# define ARCH_32BIT 0 +#endif +#if !defined(ARCH_64BIT) +# define ARCH_64BIT 0 +#endif +#if !defined(ARCH_X64) +# define ARCH_X64 0 +#endif +#if !defined(ARCH_X86) +# define ARCH_X86 0 +#endif +#if !defined(ARCH_ARM64) +# define ARCH_ARM64 0 +#endif +#if !defined(ARCH_ARM32) +# define ARCH_ARM32 0 +#endif +#if !defined(COMPILER_MSVC) +# define COMPILER_MSVC 0 +#endif +#if !defined(COMPILER_GCC) +# define COMPILER_GCC 0 +#endif +#if !defined(COMPILER_CLANG) +# define COMPILER_CLANG 0 +#endif +#if !defined(OS_WINDOWS) +# define OS_WINDOWS 0 +#endif +#if !defined(OS_LINUX) +# define OS_LINUX 0 +#endif +#if !defined(OS_MAC) +# define OS_MAC 0 +#endif +#if !defined(LANG_CPP) +# define LANG_CPP 0 +#endif +#if !defined(LANG_C) +# define LANG_C 0 +#endif + +//////////////////////////////// +//~ rjf: Unsupported Errors + +#if ARCH_X86 +# error You tried to build in x86 (32 bit) mode, but currently, only building in x64 (64 bit) mode is supported. +#endif +#if !ARCH_X64 +# error You tried to build with an unsupported architecture. Currently, only building in x64 mode is supported. +#endif + +#endif // BASE_CONTEXT_CRACKING_H diff --git a/src/base/base_core.c b/src/base/base_core.c index 2d40fe34..877dcb4e 100644 --- a/src/base/base_core.c +++ b/src/base/base_core.c @@ -1,562 +1,562 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Safe Casts - -internal U16 -safe_cast_u16(U32 x) -{ - AssertAlways(x <= max_U16); - U16 result = (U16)x; - return result; -} - -internal U32 -safe_cast_u32(U64 x) -{ - AssertAlways(x <= max_U32); - U32 result = (U32)x; - return result; -} - -internal S32 -safe_cast_s32(S64 x) -{ - AssertAlways(x <= max_S32); - S32 result = (S32)x; - return result; -} - -//////////////////////////////// -//~ rjf: Large Base Type Functions - -internal U128 -u128_zero(void) -{ - U128 v = {0}; - return v; -} - -internal U128 -u128_make(U64 v0, U64 v1) -{ - U128 v = {v0, v1}; - return v; -} - -internal B32 -u128_match(U128 a, U128 b) -{ - return MemoryMatchStruct(&a, &b); -} - -//////////////////////////////// -//~ rjf: Bit Patterns - -internal U32 -u32_from_u64_saturate(U64 x){ - U32 x32 = (x > max_U32)?max_U32:(U32)x; - return(x32); -} - -internal U64 -u64_up_to_pow2(U64 x){ - if (x == 0){ - x = 1; - } - else{ - x -= 1; - x |= (x >> 1); - x |= (x >> 2); - x |= (x >> 4); - x |= (x >> 8); - x |= (x >> 16); - x |= (x >> 32); - x += 1; - } - return(x); -} - -internal S32 -extend_sign32(U32 x, U32 size){ - U32 high_bit = size * 8; - U32 shift = 32 - high_bit; - S32 result = ((S32)x << shift) >> shift; - return result; -} - -internal S64 -extend_sign64(U64 x, U64 size){ - U64 high_bit = size * 8; - U64 shift = 64 - high_bit; - S64 result = ((S64)x << shift) >> shift; - return result; -} - -internal F32 -inf32(void){ - union { U32 u; F32 f; } x; - x.u = exponent32; - return(x.f); -} - -internal F32 -neg_inf32(void){ - union { U32 u; F32 f; } x; - x.u = sign32 | exponent32; - return(x.f); -} - -internal U16 -bswap_u16(U16 x) -{ - U16 result = (((x & 0xFF00) >> 8) | - ((x & 0x00FF) << 8)); - return result; -} - -internal U32 -bswap_u32(U32 x) -{ - U32 result = (((x & 0xFF000000) >> 24) | - ((x & 0x00FF0000) >> 8) | - ((x & 0x0000FF00) << 8) | - ((x & 0x000000FF) << 24)); - return result; -} - -internal U64 -bswap_u64(U64 x) -{ - // TODO(nick): naive bswap, replace with something that is faster like an intrinsic - U64 result = (((x & 0xFF00000000000000ULL) >> 56) | - ((x & 0x00FF000000000000ULL) >> 40) | - ((x & 0x0000FF0000000000ULL) >> 24) | - ((x & 0x000000FF00000000ULL) >> 8) | - ((x & 0x00000000FF000000ULL) << 8) | - ((x & 0x0000000000FF0000ULL) << 24) | - ((x & 0x000000000000FF00ULL) << 40) | - ((x & 0x00000000000000FFULL) << 56)); - return result; -} - -#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS) - -internal U64 -count_bits_set16(U16 val) -{ - return __popcnt16(val); -} - -internal U64 -count_bits_set32(U32 val) -{ - return __popcnt(val); -} - -internal U64 -count_bits_set64(U64 val) -{ - return __popcnt64(val); -} - -internal U64 -ctz32(U32 mask) -{ - unsigned long idx; - _BitScanForward(&idx, mask); - return idx; -} - -internal U64 -ctz64(U64 mask) -{ - unsigned long idx; - _BitScanForward64(&idx, mask); - return idx; -} - -internal U64 -clz32(U32 mask) -{ - unsigned long idx; - _BitScanReverse(&idx, mask); - return 31 - idx; -} - -internal U64 -clz64(U64 mask) -{ - unsigned long idx; - _BitScanReverse64(&idx, mask); - return 63 - idx; -} - -#elif COMPILER_CLANG || COMPILER_GCC - -internal U64 -count_bits_set16(U16 val) -{ - NotImplemented; - return 0; -} - -internal U64 -count_bits_set32(U32 val) -{ - NotImplemented; - return 0; -} - -internal U64 -count_bits_set64(U64 val) -{ - NotImplemented; - return 0; -} - -internal U64 -ctz32(U32 val) -{ - NotImplemented; - return 0; -} - -internal U64 -clz32(U32 val) -{ - NotImplemented; - return 0; -} - -internal U64 -clz64(U64 val) -{ - NotImplemented; - return 0; -} - -#else -# error "Bit intrinsic functions not defined for this compiler." -#endif - -//////////////////////////////// -//~ rjf: Enum -> Sign - -internal S32 -sign_from_side_S32(Side side){ - return((side == Side_Min)?-1:1); -} - -internal F32 -sign_from_side_F32(Side side){ - return((side == Side_Min)?-1.f:1.f); -} - -//////////////////////////////// -//~ rjf: Memory Functions - -internal B32 -memory_is_zero(void *ptr, U64 size){ - B32 result = 1; - - // break down size - U64 extra = (size&0x7); - U64 count8 = (size >> 3); - - // check with 8-byte stride - U64 *p64 = (U64*)ptr; - if(result) - { - for (U64 i = 0; i < count8; i += 1, p64 += 1){ - if (*p64 != 0){ - result = 0; - goto done; - } - } - } - - // check extra - if(result) - { - U8 *p8 = (U8*)p64; - for (U64 i = 0; i < extra; i += 1, p8 += 1){ - if (*p8 != 0){ - result = 0; - goto done; - } - } - } - - done:; - return(result); -} - -//////////////////////////////// -//~ rjf: Text 2D Coordinate/Range Functions - -internal TxtPt -txt_pt(S64 line, S64 column) -{ - TxtPt p = {0}; - p.line = line; - p.column = column; - return p; -} - -internal B32 -txt_pt_match(TxtPt a, TxtPt b) -{ - return a.line == b.line && a.column == b.column; -} - -internal B32 -txt_pt_less_than(TxtPt a, TxtPt b) -{ - B32 result = 0; - if(a.line < b.line) - { - result = 1; - } - else if(a.line == b.line) - { - result = a.column < b.column; - } - return result; -} - -internal TxtPt -txt_pt_min(TxtPt a, TxtPt b) -{ - TxtPt result = b; - if(txt_pt_less_than(a, b)) - { - result = a; - } - return result; -} - -internal TxtPt -txt_pt_max(TxtPt a, TxtPt b) -{ - TxtPt result = a; - if(txt_pt_less_than(a, b)) - { - result = b; - } - return result; -} - -internal TxtRng -txt_rng(TxtPt min, TxtPt max) -{ - TxtRng range = {0}; - if(txt_pt_less_than(min, max)) - { - range.min = min; - range.max = max; - } - else - { - range.min = max; - range.max = min; - } - return range; -} - -internal TxtRng -txt_rng_intersect(TxtRng a, TxtRng b) -{ - TxtRng result = {0}; - result.min = txt_pt_max(a.min, b.min); - result.max = txt_pt_min(a.max, b.max); - if(txt_pt_less_than(result.max, result.min)) - { - MemoryZeroStruct(&result); - } - return result; -} - -internal TxtRng -txt_rng_union(TxtRng a, TxtRng b) -{ - TxtRng result = {0}; - result.min = txt_pt_min(a.min, b.min); - result.max = txt_pt_max(a.max, b.max); - return result; -} - -internal B32 -txt_rng_contains(TxtRng r, TxtPt pt) -{ - B32 result = ((txt_pt_less_than(r.min, pt) || txt_pt_match(r.min, pt)) && - txt_pt_less_than(pt, r.max)); - return result; -} - -//////////////////////////////// -//~ rjf: Toolchain/Environment Enum Functions - -internal U64 -bit_size_from_arch(Architecture arch) -{ - // TODO(rjf): metacode - U64 arch_bitsize = 0; - switch(arch) - { - case Architecture_x64: arch_bitsize = 64; break; - case Architecture_x86: arch_bitsize = 32; break; - case Architecture_arm64: arch_bitsize = 64; break; - case Architecture_arm32: arch_bitsize = 32; break; - default: break; - } - return arch_bitsize; -} - -internal U64 -max_instruction_size_from_arch(Architecture arch) -{ - // TODO(rjf): make this real - return 64; -} - -internal OperatingSystem -operating_system_from_context(void){ - OperatingSystem os = OperatingSystem_Null; -#if OS_WINDOWS - os = OperatingSystem_Windows; -#elif OS_LINUX - os = OperatingSystem_Linux; -#elif OS_MAC - os = OperatingSystem_Mac; -#endif - return os; -} - -internal Architecture -architecture_from_context(void){ - Architecture arch = Architecture_Null; -#if ARCH_X64 - arch = Architecture_x64; -#elif ARCH_X86 - arch = Architecture_x86; -#elif ARCH_ARM64 - arch = Architecture_arm64; -#elif ARCH_ARM32 - arch = Architecture_arm32; -#endif - return arch; -} - -internal Compiler -compiler_from_context(void){ - Compiler compiler = Compiler_Null; -#if COMPILER_MSVC - compiler = Compiler_msvc; -#elif COMPILER_GCC - compiler = Compiler_gcc; -#elif COMPILER_CLANG - compiler = Compiler_clang; -#endif - return compiler; -} - -//////////////////////////////// -//~ rjf: Time Functions - -internal DenseTime -dense_time_from_date_time(DateTime date_time){ - DenseTime result = 0; - result += date_time.year; - result *= 12; - result += date_time.mon; - result *= 31; - result += date_time.day; - result *= 24; - result += date_time.hour; - result *= 60; - result += date_time.min; - result *= 61; - result += date_time.sec; - result *= 1000; - result += date_time.msec; - return(result); -} - -internal DateTime -date_time_from_dense_time(DenseTime time){ - DateTime result = {0}; - result.msec = time%1000; - time /= 1000; - result.sec = time%61; - time /= 61; - result.min = time%60; - time /= 60; - result.hour = time%24; - time /= 24; - result.day = time%31; - time /= 31; - result.mon = time%12; - time /= 12; - Assert(time <= max_U32); - result.year = (U32)time; - return(result); -} - -internal DateTime -date_time_from_micro_seconds(U64 time){ - DateTime result = {0}; - result.micro_sec = time%1000; - time /= 1000; - result.msec = time%1000; - time /= 1000; - result.sec = time%60; - time /= 60; - result.min = time%60; - time /= 60; - result.hour = time%24; - time /= 24; - result.day = time%31; - time /= 31; - result.mon = time%12; - time /= 12; - Assert(time <= max_U32); - result.year = (U32)time; - return(result); -} - -//////////////////////////////// -//~ rjf: Non-Fancy Ring Buffer Reads/Writes - -internal U64 -ring_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_data_size) -{ - Assert(src_data_size <= ring_size); - { - U64 ring_off = ring_pos%ring_size; - U64 bytes_before_split = ring_size-ring_off; - U64 pre_split_bytes = Min(bytes_before_split, src_data_size); - U64 pst_split_bytes = src_data_size-pre_split_bytes; - void *pre_split_data = src_data; - void *pst_split_data = ((U8 *)src_data + pre_split_bytes); - MemoryCopy(ring_base+ring_off, pre_split_data, pre_split_bytes); - MemoryCopy(ring_base+0, pst_split_data, pst_split_bytes); - } - return src_data_size; -} - -internal U64 -ring_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_data, U64 read_size) -{ - Assert(read_size <= ring_size); - { - U64 ring_off = ring_pos%ring_size; - U64 bytes_before_split = ring_size-ring_off; - U64 pre_split_bytes = Min(bytes_before_split, read_size); - U64 pst_split_bytes = read_size-pre_split_bytes; - MemoryCopy(dst_data, ring_base+ring_off, pre_split_bytes); - MemoryCopy((U8 *)dst_data + pre_split_bytes, ring_base+0, pst_split_bytes); - } - return read_size; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Safe Casts + +internal U16 +safe_cast_u16(U32 x) +{ + AssertAlways(x <= max_U16); + U16 result = (U16)x; + return result; +} + +internal U32 +safe_cast_u32(U64 x) +{ + AssertAlways(x <= max_U32); + U32 result = (U32)x; + return result; +} + +internal S32 +safe_cast_s32(S64 x) +{ + AssertAlways(x <= max_S32); + S32 result = (S32)x; + return result; +} + +//////////////////////////////// +//~ rjf: Large Base Type Functions + +internal U128 +u128_zero(void) +{ + U128 v = {0}; + return v; +} + +internal U128 +u128_make(U64 v0, U64 v1) +{ + U128 v = {v0, v1}; + return v; +} + +internal B32 +u128_match(U128 a, U128 b) +{ + return MemoryMatchStruct(&a, &b); +} + +//////////////////////////////// +//~ rjf: Bit Patterns + +internal U32 +u32_from_u64_saturate(U64 x){ + U32 x32 = (x > max_U32)?max_U32:(U32)x; + return(x32); +} + +internal U64 +u64_up_to_pow2(U64 x){ + if (x == 0){ + x = 1; + } + else{ + x -= 1; + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + x |= (x >> 32); + x += 1; + } + return(x); +} + +internal S32 +extend_sign32(U32 x, U32 size){ + U32 high_bit = size * 8; + U32 shift = 32 - high_bit; + S32 result = ((S32)x << shift) >> shift; + return result; +} + +internal S64 +extend_sign64(U64 x, U64 size){ + U64 high_bit = size * 8; + U64 shift = 64 - high_bit; + S64 result = ((S64)x << shift) >> shift; + return result; +} + +internal F32 +inf32(void){ + union { U32 u; F32 f; } x; + x.u = exponent32; + return(x.f); +} + +internal F32 +neg_inf32(void){ + union { U32 u; F32 f; } x; + x.u = sign32 | exponent32; + return(x.f); +} + +internal U16 +bswap_u16(U16 x) +{ + U16 result = (((x & 0xFF00) >> 8) | + ((x & 0x00FF) << 8)); + return result; +} + +internal U32 +bswap_u32(U32 x) +{ + U32 result = (((x & 0xFF000000) >> 24) | + ((x & 0x00FF0000) >> 8) | + ((x & 0x0000FF00) << 8) | + ((x & 0x000000FF) << 24)); + return result; +} + +internal U64 +bswap_u64(U64 x) +{ + // TODO(nick): naive bswap, replace with something that is faster like an intrinsic + U64 result = (((x & 0xFF00000000000000ULL) >> 56) | + ((x & 0x00FF000000000000ULL) >> 40) | + ((x & 0x0000FF0000000000ULL) >> 24) | + ((x & 0x000000FF00000000ULL) >> 8) | + ((x & 0x00000000FF000000ULL) << 8) | + ((x & 0x0000000000FF0000ULL) << 24) | + ((x & 0x000000000000FF00ULL) << 40) | + ((x & 0x00000000000000FFULL) << 56)); + return result; +} + +#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS) + +internal U64 +count_bits_set16(U16 val) +{ + return __popcnt16(val); +} + +internal U64 +count_bits_set32(U32 val) +{ + return __popcnt(val); +} + +internal U64 +count_bits_set64(U64 val) +{ + return __popcnt64(val); +} + +internal U64 +ctz32(U32 mask) +{ + unsigned long idx; + _BitScanForward(&idx, mask); + return idx; +} + +internal U64 +ctz64(U64 mask) +{ + unsigned long idx; + _BitScanForward64(&idx, mask); + return idx; +} + +internal U64 +clz32(U32 mask) +{ + unsigned long idx; + _BitScanReverse(&idx, mask); + return 31 - idx; +} + +internal U64 +clz64(U64 mask) +{ + unsigned long idx; + _BitScanReverse64(&idx, mask); + return 63 - idx; +} + +#elif COMPILER_CLANG || COMPILER_GCC + +internal U64 +count_bits_set16(U16 val) +{ + NotImplemented; + return 0; +} + +internal U64 +count_bits_set32(U32 val) +{ + NotImplemented; + return 0; +} + +internal U64 +count_bits_set64(U64 val) +{ + NotImplemented; + return 0; +} + +internal U64 +ctz32(U32 val) +{ + NotImplemented; + return 0; +} + +internal U64 +clz32(U32 val) +{ + NotImplemented; + return 0; +} + +internal U64 +clz64(U64 val) +{ + NotImplemented; + return 0; +} + +#else +# error "Bit intrinsic functions not defined for this compiler." +#endif + +//////////////////////////////// +//~ rjf: Enum -> Sign + +internal S32 +sign_from_side_S32(Side side){ + return((side == Side_Min)?-1:1); +} + +internal F32 +sign_from_side_F32(Side side){ + return((side == Side_Min)?-1.f:1.f); +} + +//////////////////////////////// +//~ rjf: Memory Functions + +internal B32 +memory_is_zero(void *ptr, U64 size){ + B32 result = 1; + + // break down size + U64 extra = (size&0x7); + U64 count8 = (size >> 3); + + // check with 8-byte stride + U64 *p64 = (U64*)ptr; + if(result) + { + for (U64 i = 0; i < count8; i += 1, p64 += 1){ + if (*p64 != 0){ + result = 0; + goto done; + } + } + } + + // check extra + if(result) + { + U8 *p8 = (U8*)p64; + for (U64 i = 0; i < extra; i += 1, p8 += 1){ + if (*p8 != 0){ + result = 0; + goto done; + } + } + } + + done:; + return(result); +} + +//////////////////////////////// +//~ rjf: Text 2D Coordinate/Range Functions + +internal TxtPt +txt_pt(S64 line, S64 column) +{ + TxtPt p = {0}; + p.line = line; + p.column = column; + return p; +} + +internal B32 +txt_pt_match(TxtPt a, TxtPt b) +{ + return a.line == b.line && a.column == b.column; +} + +internal B32 +txt_pt_less_than(TxtPt a, TxtPt b) +{ + B32 result = 0; + if(a.line < b.line) + { + result = 1; + } + else if(a.line == b.line) + { + result = a.column < b.column; + } + return result; +} + +internal TxtPt +txt_pt_min(TxtPt a, TxtPt b) +{ + TxtPt result = b; + if(txt_pt_less_than(a, b)) + { + result = a; + } + return result; +} + +internal TxtPt +txt_pt_max(TxtPt a, TxtPt b) +{ + TxtPt result = a; + if(txt_pt_less_than(a, b)) + { + result = b; + } + return result; +} + +internal TxtRng +txt_rng(TxtPt min, TxtPt max) +{ + TxtRng range = {0}; + if(txt_pt_less_than(min, max)) + { + range.min = min; + range.max = max; + } + else + { + range.min = max; + range.max = min; + } + return range; +} + +internal TxtRng +txt_rng_intersect(TxtRng a, TxtRng b) +{ + TxtRng result = {0}; + result.min = txt_pt_max(a.min, b.min); + result.max = txt_pt_min(a.max, b.max); + if(txt_pt_less_than(result.max, result.min)) + { + MemoryZeroStruct(&result); + } + return result; +} + +internal TxtRng +txt_rng_union(TxtRng a, TxtRng b) +{ + TxtRng result = {0}; + result.min = txt_pt_min(a.min, b.min); + result.max = txt_pt_max(a.max, b.max); + return result; +} + +internal B32 +txt_rng_contains(TxtRng r, TxtPt pt) +{ + B32 result = ((txt_pt_less_than(r.min, pt) || txt_pt_match(r.min, pt)) && + txt_pt_less_than(pt, r.max)); + return result; +} + +//////////////////////////////// +//~ rjf: Toolchain/Environment Enum Functions + +internal U64 +bit_size_from_arch(Architecture arch) +{ + // TODO(rjf): metacode + U64 arch_bitsize = 0; + switch(arch) + { + case Architecture_x64: arch_bitsize = 64; break; + case Architecture_x86: arch_bitsize = 32; break; + case Architecture_arm64: arch_bitsize = 64; break; + case Architecture_arm32: arch_bitsize = 32; break; + default: break; + } + return arch_bitsize; +} + +internal U64 +max_instruction_size_from_arch(Architecture arch) +{ + // TODO(rjf): make this real + return 64; +} + +internal OperatingSystem +operating_system_from_context(void){ + OperatingSystem os = OperatingSystem_Null; +#if OS_WINDOWS + os = OperatingSystem_Windows; +#elif OS_LINUX + os = OperatingSystem_Linux; +#elif OS_MAC + os = OperatingSystem_Mac; +#endif + return os; +} + +internal Architecture +architecture_from_context(void){ + Architecture arch = Architecture_Null; +#if ARCH_X64 + arch = Architecture_x64; +#elif ARCH_X86 + arch = Architecture_x86; +#elif ARCH_ARM64 + arch = Architecture_arm64; +#elif ARCH_ARM32 + arch = Architecture_arm32; +#endif + return arch; +} + +internal Compiler +compiler_from_context(void){ + Compiler compiler = Compiler_Null; +#if COMPILER_MSVC + compiler = Compiler_msvc; +#elif COMPILER_GCC + compiler = Compiler_gcc; +#elif COMPILER_CLANG + compiler = Compiler_clang; +#endif + return compiler; +} + +//////////////////////////////// +//~ rjf: Time Functions + +internal DenseTime +dense_time_from_date_time(DateTime date_time){ + DenseTime result = 0; + result += date_time.year; + result *= 12; + result += date_time.mon; + result *= 31; + result += date_time.day; + result *= 24; + result += date_time.hour; + result *= 60; + result += date_time.min; + result *= 61; + result += date_time.sec; + result *= 1000; + result += date_time.msec; + return(result); +} + +internal DateTime +date_time_from_dense_time(DenseTime time){ + DateTime result = {0}; + result.msec = time%1000; + time /= 1000; + result.sec = time%61; + time /= 61; + result.min = time%60; + time /= 60; + result.hour = time%24; + time /= 24; + result.day = time%31; + time /= 31; + result.mon = time%12; + time /= 12; + Assert(time <= max_U32); + result.year = (U32)time; + return(result); +} + +internal DateTime +date_time_from_micro_seconds(U64 time){ + DateTime result = {0}; + result.micro_sec = time%1000; + time /= 1000; + result.msec = time%1000; + time /= 1000; + result.sec = time%60; + time /= 60; + result.min = time%60; + time /= 60; + result.hour = time%24; + time /= 24; + result.day = time%31; + time /= 31; + result.mon = time%12; + time /= 12; + Assert(time <= max_U32); + result.year = (U32)time; + return(result); +} + +//////////////////////////////// +//~ rjf: Non-Fancy Ring Buffer Reads/Writes + +internal U64 +ring_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_data_size) +{ + Assert(src_data_size <= ring_size); + { + U64 ring_off = ring_pos%ring_size; + U64 bytes_before_split = ring_size-ring_off; + U64 pre_split_bytes = Min(bytes_before_split, src_data_size); + U64 pst_split_bytes = src_data_size-pre_split_bytes; + void *pre_split_data = src_data; + void *pst_split_data = ((U8 *)src_data + pre_split_bytes); + MemoryCopy(ring_base+ring_off, pre_split_data, pre_split_bytes); + MemoryCopy(ring_base+0, pst_split_data, pst_split_bytes); + } + return src_data_size; +} + +internal U64 +ring_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_data, U64 read_size) +{ + Assert(read_size <= ring_size); + { + U64 ring_off = ring_pos%ring_size; + U64 bytes_before_split = ring_size-ring_off; + U64 pre_split_bytes = Min(bytes_before_split, read_size); + U64 pst_split_bytes = read_size-pre_split_bytes; + MemoryCopy(dst_data, ring_base+ring_off, pre_split_bytes); + MemoryCopy((U8 *)dst_data + pre_split_bytes, ring_base+0, pst_split_bytes); + } + return read_size; +} diff --git a/src/base/base_entry_point.h b/src/base/base_entry_point.h index fd6c0d92..560bdcc7 100644 --- a/src/base/base_entry_point.h +++ b/src/base/base_entry_point.h @@ -1,10 +1,10 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_ENTRY_POINT_H -#define BASE_ENTRY_POINT_H - -internal void main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **arguments, U64 arguments_count); -internal void supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params); - -#endif // BASE_ENTRY_POINT_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_ENTRY_POINT_H +#define BASE_ENTRY_POINT_H + +internal void main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **arguments, U64 arguments_count); +internal void supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params); + +#endif // BASE_ENTRY_POINT_H diff --git a/src/base/base_inc.c b/src/base/base_inc.c index dec3ee53..b1a30708 100644 --- a/src/base/base_inc.c +++ b/src/base/base_inc.c @@ -1,19 +1,19 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Base Includes - -#undef RADDBG_LAYER_COLOR -#define RADDBG_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_thread_context.c" -#include "base_command_line.c" -#include "base_markup.c" -#include "base_log.c" -#include "base_entry_point.c" +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Base Includes + +#undef RADDBG_LAYER_COLOR +#define RADDBG_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_thread_context.c" +#include "base_command_line.c" +#include "base_markup.c" +#include "base_log.c" +#include "base_entry_point.c" diff --git a/src/base/base_inc.h b/src/base/base_inc.h index 40927620..76437b09 100644 --- a/src/base/base_inc.h +++ b/src/base/base_inc.h @@ -1,23 +1,23 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_INC_H -#define BASE_INC_H - -//////////////////////////////// -//~ rjf: Base Includes - -#include "base_context_cracking.h" - -#include "base_core.h" -#include "base_profile.h" -#include "base_arena.h" -#include "base_math.h" -#include "base_strings.h" -#include "base_thread_context.h" -#include "base_command_line.h" -#include "base_markup.h" -#include "base_log.h" -#include "base_entry_point.h" - -#endif // BASE_INC_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_INC_H +#define BASE_INC_H + +//////////////////////////////// +//~ rjf: Base Includes + +#include "base_context_cracking.h" + +#include "base_core.h" +#include "base_profile.h" +#include "base_arena.h" +#include "base_math.h" +#include "base_strings.h" +#include "base_thread_context.h" +#include "base_command_line.h" +#include "base_markup.h" +#include "base_log.h" +#include "base_entry_point.h" + +#endif // BASE_INC_H diff --git a/src/base/base_log.c b/src/base/base_log.c index a5a917ac..418b29ff 100644 --- a/src/base/base_log.c +++ b/src/base/base_log.c @@ -1,103 +1,103 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Globals/Thread-Locals - -C_LINKAGE thread_static Log *log_active; -#if !BUILD_SUPPLEMENTARY_UNIT -C_LINKAGE thread_static Log *log_active = 0; -#endif - -//////////////////////////////// -//~ rjf: Log Creation/Selection - -internal Log * -log_alloc(void) -{ - Arena *arena = arena_alloc(); - Log *log = push_array(arena, Log, 1); - log->arena = arena; - return log; -} - -internal void -log_release(Log *log) -{ - arena_release(log->arena); -} - -internal void -log_select(Log *log) -{ - log_active = log; -} - -//////////////////////////////// -//~ rjf: Log Building/Clearing - -internal void -log_msg(LogMsgKind kind, String8 string) -{ - if(log_active != 0 && log_active->top_scope != 0) - { - String8 string_copy = push_str8_copy(log_active->arena, string); - str8_list_push(log_active->arena, &log_active->top_scope->strings[kind], string_copy); - } -} - -internal void -log_msgf(LogMsgKind kind, char *fmt, ...) -{ - if(log_active != 0) - { - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - log_msg(kind, string); - va_end(args); - scratch_end(scratch); - } -} - -//////////////////////////////// -//~ rjf: Log Scopes - -internal void -log_scope_begin(void) -{ - if(log_active != 0) - { - U64 pos = arena_pos(log_active->arena); - LogScope *scope = push_array(log_active->arena, LogScope, 1); - scope->pos = pos; - SLLStackPush(log_active->top_scope, scope); - } -} - -internal LogScopeResult -log_scope_end(Arena *arena) -{ - LogScopeResult result = {0}; - if(log_active != 0) - { - LogScope *scope = log_active->top_scope; - if(scope != 0) - { - SLLStackPop(log_active->top_scope); - if(arena != 0) - { - for(EachEnumVal(LogMsgKind, kind)) - { - Temp scratch = scratch_begin(&arena, 1); - String8 result_unindented = str8_list_join(scratch.arena, &scope->strings[kind], 0); - result.strings[kind] = indented_from_string(arena, result_unindented); - scratch_end(scratch); - } - } - arena_pop_to(log_active->arena, scope->pos); - } - } - return result; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Globals/Thread-Locals + +C_LINKAGE thread_static Log *log_active; +#if !BUILD_SUPPLEMENTARY_UNIT +C_LINKAGE thread_static Log *log_active = 0; +#endif + +//////////////////////////////// +//~ rjf: Log Creation/Selection + +internal Log * +log_alloc(void) +{ + Arena *arena = arena_alloc(); + Log *log = push_array(arena, Log, 1); + log->arena = arena; + return log; +} + +internal void +log_release(Log *log) +{ + arena_release(log->arena); +} + +internal void +log_select(Log *log) +{ + log_active = log; +} + +//////////////////////////////// +//~ rjf: Log Building/Clearing + +internal void +log_msg(LogMsgKind kind, String8 string) +{ + if(log_active != 0 && log_active->top_scope != 0) + { + String8 string_copy = push_str8_copy(log_active->arena, string); + str8_list_push(log_active->arena, &log_active->top_scope->strings[kind], string_copy); + } +} + +internal void +log_msgf(LogMsgKind kind, char *fmt, ...) +{ + if(log_active != 0) + { + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + log_msg(kind, string); + va_end(args); + scratch_end(scratch); + } +} + +//////////////////////////////// +//~ rjf: Log Scopes + +internal void +log_scope_begin(void) +{ + if(log_active != 0) + { + U64 pos = arena_pos(log_active->arena); + LogScope *scope = push_array(log_active->arena, LogScope, 1); + scope->pos = pos; + SLLStackPush(log_active->top_scope, scope); + } +} + +internal LogScopeResult +log_scope_end(Arena *arena) +{ + LogScopeResult result = {0}; + if(log_active != 0) + { + LogScope *scope = log_active->top_scope; + if(scope != 0) + { + SLLStackPop(log_active->top_scope); + if(arena != 0) + { + for(EachEnumVal(LogMsgKind, kind)) + { + Temp scratch = scratch_begin(&arena, 1); + String8 result_unindented = str8_list_join(scratch.arena, &scope->strings[kind], 0); + result.strings[kind] = indented_from_string(arena, result_unindented); + scratch_end(scratch); + } + } + arena_pop_to(log_active->arena, scope->pos); + } + } + return result; +} diff --git a/src/base/base_markup.c b/src/base/base_markup.c index 033fb49a..5ad47ef5 100644 --- a/src/base/base_markup.c +++ b/src/base/base_markup.c @@ -1,21 +1,21 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -internal void -set_thread_name(String8 string) -{ - ProfThreadName("%.*s", str8_varg(string)); - os_set_thread_name(string); -} - -internal void -set_thread_namef(char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - set_thread_name(string); - va_end(args); - scratch_end(scratch); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +internal void +set_thread_name(String8 string) +{ + ProfThreadName("%.*s", str8_varg(string)); + os_set_thread_name(string); +} + +internal void +set_thread_namef(char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + set_thread_name(string); + va_end(args); + scratch_end(scratch); +} diff --git a/src/base/base_markup.h b/src/base/base_markup.h index 0f255efc..fd291375 100644 --- a/src/base/base_markup.h +++ b/src/base/base_markup.h @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_MARKUP_H -#define BASE_MARKUP_H - -internal void set_thread_name(String8 string); -internal void set_thread_namef(char *fmt, ...); -#define ThreadNameF(...) (set_thread_namef(__VA_ARGS__)) -#define ThreadName(str) (set_thread_name(str)) - -#endif // BASE_MARKUP_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_MARKUP_H +#define BASE_MARKUP_H + +internal void set_thread_name(String8 string); +internal void set_thread_namef(char *fmt, ...); +#define ThreadNameF(...) (set_thread_namef(__VA_ARGS__)) +#define ThreadName(str) (set_thread_name(str)) + +#endif // BASE_MARKUP_H diff --git a/src/base/base_math.c b/src/base/base_math.c index 5585443b..465d342d 100644 --- a/src/base/base_math.c +++ b/src/base/base_math.c @@ -1,616 +1,616 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Scalar Ops - -internal F32 -mix_1f32(F32 a, F32 b, F32 t) -{ - F32 c = (a + (b-a) * Clamp(0.f, t, 1.f)); - return c; -} - -internal F64 -mix_1f64(F64 a, F64 b, F64 t) -{ - F64 c = (a + (b-a) * Clamp(0.0, t, 1.0)); - return c; -} - -//////////////////////////////// -//~ rjf: Vector Ops - -internal Vec2F32 vec_2f32(F32 x, F32 y) {Vec2F32 v = {x, y}; return v;} -internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x+b.x, a.y+b.y}; return c;} -internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x-b.x, a.y-b.y}; return c;} -internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x*b.x, a.y*b.y}; return c;} -internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x/b.x, a.y/b.y}; return c;} -internal Vec2F32 scale_2f32(Vec2F32 v, F32 s) {Vec2F32 c = {v.x*s, v.y*s}; return c;} -internal F32 dot_2f32(Vec2F32 a, Vec2F32 b) {F32 c = a.x*b.x + a.y*b.y; return c;} -internal F32 length_squared_2f32(Vec2F32 v) {F32 c = v.x*v.x + v.y*v.y; return c;} -internal F32 length_2f32(Vec2F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y); return c;} -internal Vec2F32 normalize_2f32(Vec2F32 v) {v = scale_2f32(v, 1.f/length_2f32(v)); return v;} -internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t) {Vec2F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t)}; return c;} - -internal Vec2S64 vec_2s64(S64 x, S64 y) {Vec2S64 v = {x, y}; return v;} -internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x+b.x, a.y+b.y}; return c;} -internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x-b.x, a.y-b.y}; return c;} -internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x*b.x, a.y*b.y}; return c;} -internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x/b.x, a.y/b.y}; return c;} -internal Vec2S64 scale_2s64(Vec2S64 v, S64 s) {Vec2S64 c = {v.x*s, v.y*s}; return c;} -internal S64 dot_2s64(Vec2S64 a, Vec2S64 b) {S64 c = a.x*b.x + a.y*b.y; return c;} -internal S64 length_squared_2s64(Vec2S64 v) {S64 c = v.x*v.x + v.y*v.y; return c;} -internal S64 length_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;} -internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x+b.x, a.y+b.y}; return c;} -internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x-b.x, a.y-b.y}; return c;} -internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x*b.x, a.y*b.y}; return c;} -internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x/b.x, a.y/b.y}; return c;} -internal Vec2S32 scale_2s32(Vec2S32 v, S32 s) {Vec2S32 c = {v.x*s, v.y*s}; return c;} -internal S32 dot_2s32(Vec2S32 a, Vec2S32 b) {S32 c = a.x*b.x + a.y*b.y; return c;} -internal S32 length_squared_2s32(Vec2S32 v) {S32 c = v.x*v.x + v.y*v.y; return c;} -internal S32 length_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;} -internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x+b.x), (S16)(a.y+b.y)}; return c;} -internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x-b.x), (S16)(a.y-b.y)}; return c;} -internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x*b.x), (S16)(a.y*b.y)}; return c;} -internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x/b.x), (S16)(a.y/b.y)}; return c;} -internal Vec2S16 scale_2s16(Vec2S16 v, S16 s) {Vec2S16 c = {(S16)(v.x*s), (S16)(v.y*s)}; return c;} -internal S16 dot_2s16(Vec2S16 a, Vec2S16 b) {S16 c = a.x*b.x + a.y*b.y; return c;} -internal S16 length_squared_2s16(Vec2S16 v) {S16 c = v.x*v.x + v.y*v.y; return c;} -internal S16 length_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;} -internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;} -internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x-b.x, a.y-b.y, a.z-b.z}; return c;} -internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x*b.x, a.y*b.y, a.z*b.z}; return c;} -internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x/b.x, a.y/b.y, a.z/b.z}; return c;} -internal Vec3F32 scale_3f32(Vec3F32 v, F32 s) {Vec3F32 c = {v.x*s, v.y*s, v.z*s}; return c;} -internal F32 dot_3f32(Vec3F32 a, Vec3F32 b) {F32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;} -internal F32 length_squared_3f32(Vec3F32 v) {F32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;} -internal F32 length_3f32(Vec3F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y + v.z*v.z); return c;} -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 Vec3S32 vec_3s32(S32 x, S32 y, S32 z) {Vec3S32 v = {x, y, z}; return v;} -internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;} -internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x-b.x, a.y-b.y, a.z-b.z}; return c;} -internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x*b.x, a.y*b.y, a.z*b.z}; return c;} -internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x/b.x, a.y/b.y, a.z/b.z}; return c;} -internal Vec3S32 scale_3s32(Vec3S32 v, S32 s) {Vec3S32 c = {v.x*s, v.y*s, v.z*s}; return c;} -internal S32 dot_3s32(Vec3S32 a, Vec3S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;} -internal S32 length_squared_3s32(Vec3S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;} -internal S32 length_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;} - -internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w) {Vec4F32 v = {x, y, z, w}; return v;} -internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; return c;} -internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w}; return c;} -internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return c;} -internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return c;} -internal Vec4F32 scale_4f32(Vec4F32 v, F32 s) {Vec4F32 c = {v.x*s, v.y*s, v.z*s, v.w*s}; return c;} -internal F32 dot_4f32(Vec4F32 a, Vec4F32 b) {F32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;} -internal F32 length_squared_4f32(Vec4F32 v) {F32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;} -internal F32 length_4f32(Vec4F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w); return c;} -internal Vec4F32 normalize_4f32(Vec4F32 v) {v = scale_4f32(v, 1.f/length_4f32(v)); return v;} -internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t) {Vec4F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t), mix_1f32(a.z, b.z, t), mix_1f32(a.w, b.w, t)}; return c;} - -internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w) {Vec4S32 v = {x, y, z, w}; return v;} -internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; return c;} -internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w}; return c;} -internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return c;} -internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return c;} -internal Vec4S32 scale_4s32(Vec4S32 v, S32 s) {Vec4S32 c = {v.x*s, v.y*s, v.z*s, v.w*s}; return c;} -internal S32 dot_4s32(Vec4S32 a, Vec4S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;} -internal S32 length_squared_4s32(Vec4S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;} -internal S32 length_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;} - -//////////////////////////////// -//~ rjf: Matrix Ops - -internal Mat3x3F32 -mat_3x3f32(F32 diagonal) -{ - Mat3x3F32 result = {0}; - result.v[0][0] = diagonal; - result.v[1][1] = diagonal; - result.v[2][2] = diagonal; - return result; -} - -internal Mat3x3F32 -make_translate_3x3f32(Vec2F32 delta) -{ - Mat3x3F32 mat = mat_3x3f32(1.f); - mat.v[2][0] = delta.x; - mat.v[2][1] = delta.y; - return mat; -} - -internal Mat3x3F32 -make_scale_3x3f32(Vec2F32 scale) -{ - Mat3x3F32 mat = mat_3x3f32(1.f); - mat.v[0][0] = scale.x; - mat.v[1][1] = scale.y; - return mat; -} - -internal Mat3x3F32 -mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b) -{ - Mat3x3F32 c = {0}; - for(int j = 0; j < 3; j += 1) - { - for(int i = 0; i < 3; i += 1) - { - c.v[i][j] = (a.v[0][j]*b.v[i][0] + - a.v[1][j]*b.v[i][1] + - a.v[2][j]*b.v[i][2]); - } - } - return c; -} - -internal Mat4x4F32 -mat_4x4f32(F32 diagonal) -{ - Mat4x4F32 result = {0}; - result.v[0][0] = diagonal; - result.v[1][1] = diagonal; - result.v[2][2] = diagonal; - result.v[3][3] = diagonal; - return result; -} - -internal Mat4x4F32 -make_translate_4x4f32(Vec3F32 delta) -{ - Mat4x4F32 result = mat_4x4f32(1.f); - result.v[3][0] = delta.x; - result.v[3][1] = delta.y; - result.v[3][2] = delta.z; - return result; -} - -internal Mat4x4F32 -make_scale_4x4f32(Vec3F32 scale) -{ - Mat4x4F32 result = mat_4x4f32(1.f); - result.v[0][0] = scale.x; - result.v[1][1] = scale.y; - result.v[2][2] = scale.z; - return result; -} - -internal Mat4x4F32 -make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z) -{ - Mat4x4F32 result = mat_4x4f32(1.f); - F32 tan_theta_over_2 = tan_f32(fov / 2); - result.v[0][0] = 1.f / tan_theta_over_2; - result.v[1][1] = aspect_ratio / tan_theta_over_2; - result.v[2][3] = 1.f; - result.v[2][2] = -(near_z + far_z) / (near_z - far_z); - result.v[3][2] = (2.f * near_z * far_z) / (near_z - far_z); - result.v[3][3] = 0.f; - return result; -} - -internal Mat4x4F32 -make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z) -{ - Mat4x4F32 result = mat_4x4f32(1.f); - - result.v[0][0] = 2.f / (right - left); - result.v[1][1] = 2.f / (top - bottom); - result.v[2][2] = 2.f / (far_z - near_z); - result.v[3][3] = 1.f; - - result.v[3][0] = (left + right) / (left - right); - result.v[3][1] = (bottom + top) / (bottom - top); - result.v[3][2] = (near_z + far_z) / (near_z - far_z); - - return result; -} - -internal Mat4x4F32 -make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up) -{ - Mat4x4F32 result; - Vec3F32 f = normalize_3f32(sub_3f32(eye, center)); - Vec3F32 s = normalize_3f32(cross_3f32(f, up)); - Vec3F32 u = cross_3f32(s, f); - result.v[0][0] = s.x; - result.v[0][1] = u.x; - result.v[0][2] = -f.x; - result.v[0][3] = 0.0f; - result.v[1][0] = s.y; - result.v[1][1] = u.y; - result.v[1][2] = -f.y; - result.v[1][3] = 0.0f; - result.v[2][0] = s.z; - result.v[2][1] = u.z; - result.v[2][2] = -f.z; - result.v[2][3] = 0.0f; - result.v[3][0] = -dot_3f32(s, eye); - result.v[3][1] = -dot_3f32(u, eye); - result.v[3][2] = dot_3f32(f, eye); - result.v[3][3] = 1.0f; - return result; -} - -internal Mat4x4F32 -make_rotate_4x4f32(Vec3F32 axis, F32 turns) -{ - Mat4x4F32 result = mat_4x4f32(1.f); - axis = normalize_3f32(axis); - F32 sin_theta = sin_f32(turns); - F32 cos_theta = cos_f32(turns); - F32 cos_value = 1.f - cos_theta; - result.v[0][0] = (axis.x * axis.x * cos_value) + cos_theta; - result.v[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); - result.v[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); - result.v[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); - result.v[1][1] = (axis.y * axis.y * cos_value) + cos_theta; - result.v[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); - result.v[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); - result.v[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); - result.v[2][2] = (axis.z * axis.z * cos_value) + cos_theta; - return result; -} - -internal Mat4x4F32 -mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b) -{ - Mat4x4F32 c = {0}; - for(int j = 0; j < 4; j += 1) - { - for(int i = 0; i < 4; i += 1) - { - c.v[i][j] = (a.v[0][j]*b.v[i][0] + - a.v[1][j]*b.v[i][1] + - a.v[2][j]*b.v[i][2] + - a.v[3][j]*b.v[i][3]); - } - } - return c; -} - -internal Mat4x4F32 -scale_4x4f32(Mat4x4F32 m, F32 scale) -{ - for(int j = 0; j < 4; j += 1) - { - for(int i = 0; i < 4; i += 1) - { - m.v[i][j] *= scale; - } - } - return m; -} - -internal Mat4x4F32 -inverse_4x4f32(Mat4x4F32 m) -{ - F32 coef00 = m.v[2][2] * m.v[3][3] - m.v[3][2] * m.v[2][3]; - F32 coef02 = m.v[1][2] * m.v[3][3] - m.v[3][2] * m.v[1][3]; - F32 coef03 = m.v[1][2] * m.v[2][3] - m.v[2][2] * m.v[1][3]; - F32 coef04 = m.v[2][1] * m.v[3][3] - m.v[3][1] * m.v[2][3]; - F32 coef06 = m.v[1][1] * m.v[3][3] - m.v[3][1] * m.v[1][3]; - F32 coef07 = m.v[1][1] * m.v[2][3] - m.v[2][1] * m.v[1][3]; - F32 coef08 = m.v[2][1] * m.v[3][2] - m.v[3][1] * m.v[2][2]; - F32 coef10 = m.v[1][1] * m.v[3][2] - m.v[3][1] * m.v[1][2]; - F32 coef11 = m.v[1][1] * m.v[2][2] - m.v[2][1] * m.v[1][2]; - F32 coef12 = m.v[2][0] * m.v[3][3] - m.v[3][0] * m.v[2][3]; - F32 coef14 = m.v[1][0] * m.v[3][3] - m.v[3][0] * m.v[1][3]; - F32 coef15 = m.v[1][0] * m.v[2][3] - m.v[2][0] * m.v[1][3]; - F32 coef16 = m.v[2][0] * m.v[3][2] - m.v[3][0] * m.v[2][2]; - F32 coef18 = m.v[1][0] * m.v[3][2] - m.v[3][0] * m.v[1][2]; - F32 coef19 = m.v[1][0] * m.v[2][2] - m.v[2][0] * m.v[1][2]; - F32 coef20 = m.v[2][0] * m.v[3][1] - m.v[3][0] * m.v[2][1]; - F32 coef22 = m.v[1][0] * m.v[3][1] - m.v[3][0] * m.v[1][1]; - F32 coef23 = m.v[1][0] * m.v[2][1] - m.v[2][0] * m.v[1][1]; - - Vec4F32 fac0 = { coef00, coef00, coef02, coef03 }; - Vec4F32 fac1 = { coef04, coef04, coef06, coef07 }; - Vec4F32 fac2 = { coef08, coef08, coef10, coef11 }; - Vec4F32 fac3 = { coef12, coef12, coef14, coef15 }; - Vec4F32 fac4 = { coef16, coef16, coef18, coef19 }; - Vec4F32 fac5 = { coef20, coef20, coef22, coef23 }; - - Vec4F32 vec0 = { m.v[1][0], m.v[0][0], m.v[0][0], m.v[0][0] }; - Vec4F32 vec1 = { m.v[1][1], m.v[0][1], m.v[0][1], m.v[0][1] }; - Vec4F32 vec2 = { m.v[1][2], m.v[0][2], m.v[0][2], m.v[0][2] }; - Vec4F32 vec3 = { m.v[1][3], m.v[0][3], m.v[0][3], m.v[0][3] }; - - Vec4F32 inv0 = add_4f32(sub_4f32(mul_4f32(vec1, fac0), mul_4f32(vec2, fac1)), mul_4f32(vec3, fac2)); - Vec4F32 inv1 = add_4f32(sub_4f32(mul_4f32(vec0, fac0), mul_4f32(vec2, fac3)), mul_4f32(vec3, fac4)); - Vec4F32 inv2 = add_4f32(sub_4f32(mul_4f32(vec0, fac1), mul_4f32(vec1, fac3)), mul_4f32(vec3, fac5)); - Vec4F32 inv3 = add_4f32(sub_4f32(mul_4f32(vec0, fac2), mul_4f32(vec1, fac4)), mul_4f32(vec2, fac5)); - - Vec4F32 sign_a = { +1, -1, +1, -1 }; - Vec4F32 sign_b = { -1, +1, -1, +1 }; - - Mat4x4F32 inverse; - for(U32 i = 0; i < 4; i += 1) - { - inverse.v[0][i] = inv0.v[i] * sign_a.v[i]; - inverse.v[1][i] = inv1.v[i] * sign_b.v[i]; - inverse.v[2][i] = inv2.v[i] * sign_a.v[i]; - inverse.v[3][i] = inv3.v[i] * sign_b.v[i]; - } - - Vec4F32 row0 = { inverse.v[0][0], inverse.v[1][0], inverse.v[2][0], inverse.v[3][0] }; - Vec4F32 m0 = { m.v[0][0], m.v[0][1], m.v[0][2], m.v[0][3] }; - Vec4F32 dot0 = mul_4f32(m0, row0); - F32 dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w); - - F32 one_over_det = 1 / dot1; - - return scale_4x4f32(inverse, one_over_det); -} - -internal Mat4x4F32 -derotate_4x4f32(Mat4x4F32 mat) -{ - Vec3F32 scale = - { - length_3f32(v3f32(mat.v[0][0], mat.v[0][1], mat.v[0][2])), - length_3f32(v3f32(mat.v[1][0], mat.v[1][1], mat.v[1][2])), - length_3f32(v3f32(mat.v[2][0], mat.v[2][1], mat.v[2][2])), - }; - mat.v[0][0] = scale.x; - mat.v[1][0] = 0.f; - mat.v[2][0] = 0.f; - mat.v[0][1] = 0.f; - mat.v[1][1] = scale.y; - mat.v[2][1] = 0.f; - mat.v[0][2] = 0.f; - mat.v[1][2] = 0.f; - mat.v[2][2] = scale.z; - return mat; -} - -//////////////////////////////// -//~ rjf: Range Ops - -internal Rng1U32 rng_1u32(U32 min, U32 max) {Rng1U32 r = {min, max}; if(r.min > r.max) { Swap(U32, r.min, r.max); } return r;} -internal Rng1U32 shift_1u32(Rng1U32 r, U32 x) {r.min += x; r.max += x; return r;} -internal Rng1U32 pad_1u32(Rng1U32 r, U32 x) {r.min -= x; r.max += x; return r;} -internal U32 center_1u32(Rng1U32 r) {U32 c = (r.min+r.max)/2; return c;} -internal 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; 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;} - -internal Rng1S32 rng_1s32(S32 min, S32 max) {Rng1S32 r = {min, max}; if(r.min > r.max) { Swap(S32, r.min, r.max); } return r;} -internal Rng1S32 shift_1s32(Rng1S32 r, S32 x) {r.min += x; r.max += x; return r;} -internal Rng1S32 pad_1s32(Rng1S32 r, S32 x) {r.min -= x; r.max += x; return r;} -internal S32 center_1s32(Rng1S32 r) {S32 c = (r.min+r.max)/2; return c;} -internal 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; 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;} - -internal Rng1U64 rng_1u64(U64 min, U64 max) {Rng1U64 r = {min, max}; if(r.min > r.max) { Swap(U64, r.min, r.max); } return r;} -internal Rng1U64 shift_1u64(Rng1U64 r, U64 x) {r.min += x; r.max += x; return r;} -internal Rng1U64 pad_1u64(Rng1U64 r, U64 x) {r.min -= x; r.max += x; return r;} -internal U64 center_1u64(Rng1U64 r) {U64 c = (r.min+r.max)/2; return c;} -internal 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; 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;} - -internal Rng1S64 rng_1s64(S64 min, S64 max) {Rng1S64 r = {min, max}; if(r.min > r.max) { Swap(S64, r.min, r.max); } return r;} -internal Rng1S64 shift_1s64(Rng1S64 r, S64 x) {r.min += x; r.max += x; return r;} -internal Rng1S64 pad_1s64(Rng1S64 r, S64 x) {r.min -= x; r.max += x; return r;} -internal S64 center_1s64(Rng1S64 r) {S64 c = (r.min+r.max)/2; return c;} -internal 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; 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;} - -internal Rng1F32 rng_1f32(F32 min, F32 max) {Rng1F32 r = {min, max}; if(r.min > r.max) { Swap(F32, r.min, r.max); } return r;} -internal Rng1F32 shift_1f32(Rng1F32 r, F32 x) {r.min += x; r.max += x; return r;} -internal Rng1F32 pad_1f32(Rng1F32 r, F32 x) {r.min -= x; r.max += x; return r;} -internal F32 center_1f32(Rng1F32 r) {F32 c = (r.min+r.max)/2; return c;} -internal 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; 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;} - -internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max) {Rng2S16 r = {min, max}; return r;} -internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x) {r.min = add_2s16(r.min, x); r.max = add_2s16(r.max, x); return r;} -internal Rng2S16 pad_2s16(Rng2S16 r, S16 x) {Vec2S16 xv = {x, x}; r.min = sub_2s16(r.min, xv); r.max = add_2s16(r.max, xv); return r;} -internal Vec2S16 center_2s16(Rng2S16 r) {Vec2S16 c = {(S16)((r.min.x+r.max.x)/2), (S16)((r.min.y+r.max.y)/2)}; return c;} -internal 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), (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;} - -internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max) {Rng2S32 r = {min, max}; return r;} -internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x) {r.min = add_2s32(r.min, x); r.max = add_2s32(r.max, x); return r;} -internal Rng2S32 pad_2s32(Rng2S32 r, S32 x) {Vec2S32 xv = {x, x}; r.min = sub_2s32(r.min, xv); r.max = add_2s32(r.max, xv); return r;} -internal Vec2S32 center_2s32(Rng2S32 r) {Vec2S32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} -internal 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.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;} - -internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max) {Rng2S64 r = {min, max}; return r;} -internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x) {r.min = add_2s64(r.min, x); r.max = add_2s64(r.max, x); return r;} -internal Rng2S64 pad_2s64(Rng2S64 r, S64 x) {Vec2S64 xv = {x, x}; r.min = sub_2s64(r.min, xv); r.max = add_2s64(r.max, xv); return r;} -internal Vec2S64 center_2s64(Rng2S64 r) {Vec2S64 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} -internal 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.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;} - -internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max) {Rng2F32 r = {min, max}; return r;} -internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x) {r.min = add_2f32(r.min, x); r.max = add_2f32(r.max, x); return r;} -internal Rng2F32 pad_2f32(Rng2F32 r, F32 x) {Vec2F32 xv = {x, x}; r.min = sub_2f32(r.min, xv); r.max = add_2f32(r.max, xv); return r;} -internal Vec2F32 center_2f32(Rng2F32 r) {Vec2F32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} -internal 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.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: Miscellaneous Ops - -internal Vec3F32 -hsv_from_rgb(Vec3F32 rgb) -{ - F32 c_max = Max(rgb.x, Max(rgb.y, rgb.z)); - F32 c_min = Min(rgb.x, Min(rgb.y, rgb.z)); - F32 delta = c_max - c_min; - F32 h = ((delta == 0.f) ? 0.f : - (c_max == rgb.x) ? mod_f32((rgb.y - rgb.z)/delta + 6.f, 6.f) : - (c_max == rgb.y) ? (rgb.z - rgb.x)/delta + 2.f : - (c_max == rgb.z) ? (rgb.x - rgb.y)/delta + 4.f : - 0.f); - F32 s = (c_max == 0.f) ? 0.f : (delta/c_max); - F32 v = c_max; - Vec3F32 hsv = {h/6.f, s, v}; - return hsv; -} - -internal Vec3F32 -rgb_from_hsv(Vec3F32 hsv) -{ - F32 h = mod_f32(hsv.x * 360.f, 360.f); - F32 s = hsv.y; - F32 v = hsv.z; - - F32 c = v*s; - F32 x = c*(1.f - abs_f32(mod_f32(h/60.f, 2.f) - 1.f)); - F32 m = v - c; - - F32 r = 0; - F32 g = 0; - F32 b = 0; - - if ((h >= 0.f && h < 60.f) || (h >= 360.f && h < 420.f)){ - r = c; - g = x; - b = 0; - } - else if (h >= 60.f && h < 120.f){ - r = x; - g = c; - b = 0; - } - else if (h >= 120.f && h < 180.f){ - r = 0; - g = c; - b = x; - } - else if (h >= 180.f && h < 240.f){ - r = 0; - g = x; - b = c; - } - else if (h >= 240.f && h < 300.f){ - r = x; - g = 0; - b = c; - } - else if ((h >= 300.f && h <= 360.f) || (h >= -60.f && h <= 0.f)){ - r = c; - g = 0; - b = x; - } - - Vec3F32 rgb = {r + m, g + m, b + m}; - return(rgb); -} - -internal Vec4F32 -hsva_from_rgba(Vec4F32 rgba) -{ - Vec3F32 rgb = v3f32(rgba.x, rgba.y, rgba.z); - Vec3F32 hsv = hsv_from_rgb(rgb); - Vec4F32 hsva = v4f32(hsv.x, hsv.y, hsv.z, rgba.w); - return hsva; -} - -internal Vec4F32 -rgba_from_hsva(Vec4F32 hsva) -{ - Vec3F32 hsv = v3f32(hsva.x, hsva.y, hsva.z); - Vec3F32 rgb = rgb_from_hsv(hsv); - Vec4F32 rgba = v4f32(rgb.x, rgb.y, rgb.z, hsva.w); - return rgba; -} - -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; -} - -internal U32 -u32_from_rgba(Vec4F32 rgba) -{ - U32 result = 0; - result |= ((U32)((U8)(rgba.x*255.f))) << 24; - result |= ((U32)((U8)(rgba.y*255.f))) << 16; - result |= ((U32)((U8)(rgba.z*255.f))) << 8; - result |= ((U32)((U8)(rgba.w*255.f))) << 0; - return result; -} - -//////////////////////////////// -//~ rjf: List Type Functions - -internal void -rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng) -{ - Rng1S64Node *n = push_array(arena, Rng1S64Node, 1); - MemoryCopyStruct(&n->v, &rng); - SLLQueuePush(list->first, list->last, n); - list->count += 1; -} - -internal Rng1S64Array -rng1s64_array_from_list(Arena *arena, Rng1S64List *list) -{ - Rng1S64Array arr = {0}; - arr.count = list->count; - arr.v = push_array_no_zero(arena, Rng1S64, arr.count); - U64 idx = 0; - for(Rng1S64Node *n = list->first; n != 0; n = n->next) - { - arr.v[idx] = n->v; - idx += 1; - } - return arr; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Scalar Ops + +internal F32 +mix_1f32(F32 a, F32 b, F32 t) +{ + F32 c = (a + (b-a) * Clamp(0.f, t, 1.f)); + return c; +} + +internal F64 +mix_1f64(F64 a, F64 b, F64 t) +{ + F64 c = (a + (b-a) * Clamp(0.0, t, 1.0)); + return c; +} + +//////////////////////////////// +//~ rjf: Vector Ops + +internal Vec2F32 vec_2f32(F32 x, F32 y) {Vec2F32 v = {x, y}; return v;} +internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x+b.x, a.y+b.y}; return c;} +internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x-b.x, a.y-b.y}; return c;} +internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x*b.x, a.y*b.y}; return c;} +internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x/b.x, a.y/b.y}; return c;} +internal Vec2F32 scale_2f32(Vec2F32 v, F32 s) {Vec2F32 c = {v.x*s, v.y*s}; return c;} +internal F32 dot_2f32(Vec2F32 a, Vec2F32 b) {F32 c = a.x*b.x + a.y*b.y; return c;} +internal F32 length_squared_2f32(Vec2F32 v) {F32 c = v.x*v.x + v.y*v.y; return c;} +internal F32 length_2f32(Vec2F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y); return c;} +internal Vec2F32 normalize_2f32(Vec2F32 v) {v = scale_2f32(v, 1.f/length_2f32(v)); return v;} +internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t) {Vec2F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t)}; return c;} + +internal Vec2S64 vec_2s64(S64 x, S64 y) {Vec2S64 v = {x, y}; return v;} +internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x+b.x, a.y+b.y}; return c;} +internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x-b.x, a.y-b.y}; return c;} +internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x*b.x, a.y*b.y}; return c;} +internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x/b.x, a.y/b.y}; return c;} +internal Vec2S64 scale_2s64(Vec2S64 v, S64 s) {Vec2S64 c = {v.x*s, v.y*s}; return c;} +internal S64 dot_2s64(Vec2S64 a, Vec2S64 b) {S64 c = a.x*b.x + a.y*b.y; return c;} +internal S64 length_squared_2s64(Vec2S64 v) {S64 c = v.x*v.x + v.y*v.y; return c;} +internal S64 length_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;} +internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x+b.x, a.y+b.y}; return c;} +internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x-b.x, a.y-b.y}; return c;} +internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x*b.x, a.y*b.y}; return c;} +internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x/b.x, a.y/b.y}; return c;} +internal Vec2S32 scale_2s32(Vec2S32 v, S32 s) {Vec2S32 c = {v.x*s, v.y*s}; return c;} +internal S32 dot_2s32(Vec2S32 a, Vec2S32 b) {S32 c = a.x*b.x + a.y*b.y; return c;} +internal S32 length_squared_2s32(Vec2S32 v) {S32 c = v.x*v.x + v.y*v.y; return c;} +internal S32 length_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;} +internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x+b.x), (S16)(a.y+b.y)}; return c;} +internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x-b.x), (S16)(a.y-b.y)}; return c;} +internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x*b.x), (S16)(a.y*b.y)}; return c;} +internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x/b.x), (S16)(a.y/b.y)}; return c;} +internal Vec2S16 scale_2s16(Vec2S16 v, S16 s) {Vec2S16 c = {(S16)(v.x*s), (S16)(v.y*s)}; return c;} +internal S16 dot_2s16(Vec2S16 a, Vec2S16 b) {S16 c = a.x*b.x + a.y*b.y; return c;} +internal S16 length_squared_2s16(Vec2S16 v) {S16 c = v.x*v.x + v.y*v.y; return c;} +internal S16 length_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;} +internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;} +internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x-b.x, a.y-b.y, a.z-b.z}; return c;} +internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x*b.x, a.y*b.y, a.z*b.z}; return c;} +internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x/b.x, a.y/b.y, a.z/b.z}; return c;} +internal Vec3F32 scale_3f32(Vec3F32 v, F32 s) {Vec3F32 c = {v.x*s, v.y*s, v.z*s}; return c;} +internal F32 dot_3f32(Vec3F32 a, Vec3F32 b) {F32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;} +internal F32 length_squared_3f32(Vec3F32 v) {F32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;} +internal F32 length_3f32(Vec3F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y + v.z*v.z); return c;} +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 Vec3S32 vec_3s32(S32 x, S32 y, S32 z) {Vec3S32 v = {x, y, z}; return v;} +internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;} +internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x-b.x, a.y-b.y, a.z-b.z}; return c;} +internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x*b.x, a.y*b.y, a.z*b.z}; return c;} +internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x/b.x, a.y/b.y, a.z/b.z}; return c;} +internal Vec3S32 scale_3s32(Vec3S32 v, S32 s) {Vec3S32 c = {v.x*s, v.y*s, v.z*s}; return c;} +internal S32 dot_3s32(Vec3S32 a, Vec3S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;} +internal S32 length_squared_3s32(Vec3S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;} +internal S32 length_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;} + +internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w) {Vec4F32 v = {x, y, z, w}; return v;} +internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; return c;} +internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w}; return c;} +internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return c;} +internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return c;} +internal Vec4F32 scale_4f32(Vec4F32 v, F32 s) {Vec4F32 c = {v.x*s, v.y*s, v.z*s, v.w*s}; return c;} +internal F32 dot_4f32(Vec4F32 a, Vec4F32 b) {F32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;} +internal F32 length_squared_4f32(Vec4F32 v) {F32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;} +internal F32 length_4f32(Vec4F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w); return c;} +internal Vec4F32 normalize_4f32(Vec4F32 v) {v = scale_4f32(v, 1.f/length_4f32(v)); return v;} +internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t) {Vec4F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t), mix_1f32(a.z, b.z, t), mix_1f32(a.w, b.w, t)}; return c;} + +internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w) {Vec4S32 v = {x, y, z, w}; return v;} +internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; return c;} +internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w}; return c;} +internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return c;} +internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return c;} +internal Vec4S32 scale_4s32(Vec4S32 v, S32 s) {Vec4S32 c = {v.x*s, v.y*s, v.z*s, v.w*s}; return c;} +internal S32 dot_4s32(Vec4S32 a, Vec4S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;} +internal S32 length_squared_4s32(Vec4S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;} +internal S32 length_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;} + +//////////////////////////////// +//~ rjf: Matrix Ops + +internal Mat3x3F32 +mat_3x3f32(F32 diagonal) +{ + Mat3x3F32 result = {0}; + result.v[0][0] = diagonal; + result.v[1][1] = diagonal; + result.v[2][2] = diagonal; + return result; +} + +internal Mat3x3F32 +make_translate_3x3f32(Vec2F32 delta) +{ + Mat3x3F32 mat = mat_3x3f32(1.f); + mat.v[2][0] = delta.x; + mat.v[2][1] = delta.y; + return mat; +} + +internal Mat3x3F32 +make_scale_3x3f32(Vec2F32 scale) +{ + Mat3x3F32 mat = mat_3x3f32(1.f); + mat.v[0][0] = scale.x; + mat.v[1][1] = scale.y; + return mat; +} + +internal Mat3x3F32 +mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b) +{ + Mat3x3F32 c = {0}; + for(int j = 0; j < 3; j += 1) + { + for(int i = 0; i < 3; i += 1) + { + c.v[i][j] = (a.v[0][j]*b.v[i][0] + + a.v[1][j]*b.v[i][1] + + a.v[2][j]*b.v[i][2]); + } + } + return c; +} + +internal Mat4x4F32 +mat_4x4f32(F32 diagonal) +{ + Mat4x4F32 result = {0}; + result.v[0][0] = diagonal; + result.v[1][1] = diagonal; + result.v[2][2] = diagonal; + result.v[3][3] = diagonal; + return result; +} + +internal Mat4x4F32 +make_translate_4x4f32(Vec3F32 delta) +{ + Mat4x4F32 result = mat_4x4f32(1.f); + result.v[3][0] = delta.x; + result.v[3][1] = delta.y; + result.v[3][2] = delta.z; + return result; +} + +internal Mat4x4F32 +make_scale_4x4f32(Vec3F32 scale) +{ + Mat4x4F32 result = mat_4x4f32(1.f); + result.v[0][0] = scale.x; + result.v[1][1] = scale.y; + result.v[2][2] = scale.z; + return result; +} + +internal Mat4x4F32 +make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z) +{ + Mat4x4F32 result = mat_4x4f32(1.f); + F32 tan_theta_over_2 = tan_f32(fov / 2); + result.v[0][0] = 1.f / tan_theta_over_2; + result.v[1][1] = aspect_ratio / tan_theta_over_2; + result.v[2][3] = 1.f; + result.v[2][2] = -(near_z + far_z) / (near_z - far_z); + result.v[3][2] = (2.f * near_z * far_z) / (near_z - far_z); + result.v[3][3] = 0.f; + return result; +} + +internal Mat4x4F32 +make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z) +{ + Mat4x4F32 result = mat_4x4f32(1.f); + + result.v[0][0] = 2.f / (right - left); + result.v[1][1] = 2.f / (top - bottom); + result.v[2][2] = 2.f / (far_z - near_z); + result.v[3][3] = 1.f; + + result.v[3][0] = (left + right) / (left - right); + result.v[3][1] = (bottom + top) / (bottom - top); + result.v[3][2] = (near_z + far_z) / (near_z - far_z); + + return result; +} + +internal Mat4x4F32 +make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up) +{ + Mat4x4F32 result; + Vec3F32 f = normalize_3f32(sub_3f32(eye, center)); + Vec3F32 s = normalize_3f32(cross_3f32(f, up)); + Vec3F32 u = cross_3f32(s, f); + result.v[0][0] = s.x; + result.v[0][1] = u.x; + result.v[0][2] = -f.x; + result.v[0][3] = 0.0f; + result.v[1][0] = s.y; + result.v[1][1] = u.y; + result.v[1][2] = -f.y; + result.v[1][3] = 0.0f; + result.v[2][0] = s.z; + result.v[2][1] = u.z; + result.v[2][2] = -f.z; + result.v[2][3] = 0.0f; + result.v[3][0] = -dot_3f32(s, eye); + result.v[3][1] = -dot_3f32(u, eye); + result.v[3][2] = dot_3f32(f, eye); + result.v[3][3] = 1.0f; + return result; +} + +internal Mat4x4F32 +make_rotate_4x4f32(Vec3F32 axis, F32 turns) +{ + Mat4x4F32 result = mat_4x4f32(1.f); + axis = normalize_3f32(axis); + F32 sin_theta = sin_f32(turns); + F32 cos_theta = cos_f32(turns); + F32 cos_value = 1.f - cos_theta; + result.v[0][0] = (axis.x * axis.x * cos_value) + cos_theta; + result.v[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta); + result.v[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta); + result.v[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta); + result.v[1][1] = (axis.y * axis.y * cos_value) + cos_theta; + result.v[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta); + result.v[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta); + result.v[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta); + result.v[2][2] = (axis.z * axis.z * cos_value) + cos_theta; + return result; +} + +internal Mat4x4F32 +mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b) +{ + Mat4x4F32 c = {0}; + for(int j = 0; j < 4; j += 1) + { + for(int i = 0; i < 4; i += 1) + { + c.v[i][j] = (a.v[0][j]*b.v[i][0] + + a.v[1][j]*b.v[i][1] + + a.v[2][j]*b.v[i][2] + + a.v[3][j]*b.v[i][3]); + } + } + return c; +} + +internal Mat4x4F32 +scale_4x4f32(Mat4x4F32 m, F32 scale) +{ + for(int j = 0; j < 4; j += 1) + { + for(int i = 0; i < 4; i += 1) + { + m.v[i][j] *= scale; + } + } + return m; +} + +internal Mat4x4F32 +inverse_4x4f32(Mat4x4F32 m) +{ + F32 coef00 = m.v[2][2] * m.v[3][3] - m.v[3][2] * m.v[2][3]; + F32 coef02 = m.v[1][2] * m.v[3][3] - m.v[3][2] * m.v[1][3]; + F32 coef03 = m.v[1][2] * m.v[2][3] - m.v[2][2] * m.v[1][3]; + F32 coef04 = m.v[2][1] * m.v[3][3] - m.v[3][1] * m.v[2][3]; + F32 coef06 = m.v[1][1] * m.v[3][3] - m.v[3][1] * m.v[1][3]; + F32 coef07 = m.v[1][1] * m.v[2][3] - m.v[2][1] * m.v[1][3]; + F32 coef08 = m.v[2][1] * m.v[3][2] - m.v[3][1] * m.v[2][2]; + F32 coef10 = m.v[1][1] * m.v[3][2] - m.v[3][1] * m.v[1][2]; + F32 coef11 = m.v[1][1] * m.v[2][2] - m.v[2][1] * m.v[1][2]; + F32 coef12 = m.v[2][0] * m.v[3][3] - m.v[3][0] * m.v[2][3]; + F32 coef14 = m.v[1][0] * m.v[3][3] - m.v[3][0] * m.v[1][3]; + F32 coef15 = m.v[1][0] * m.v[2][3] - m.v[2][0] * m.v[1][3]; + F32 coef16 = m.v[2][0] * m.v[3][2] - m.v[3][0] * m.v[2][2]; + F32 coef18 = m.v[1][0] * m.v[3][2] - m.v[3][0] * m.v[1][2]; + F32 coef19 = m.v[1][0] * m.v[2][2] - m.v[2][0] * m.v[1][2]; + F32 coef20 = m.v[2][0] * m.v[3][1] - m.v[3][0] * m.v[2][1]; + F32 coef22 = m.v[1][0] * m.v[3][1] - m.v[3][0] * m.v[1][1]; + F32 coef23 = m.v[1][0] * m.v[2][1] - m.v[2][0] * m.v[1][1]; + + Vec4F32 fac0 = { coef00, coef00, coef02, coef03 }; + Vec4F32 fac1 = { coef04, coef04, coef06, coef07 }; + Vec4F32 fac2 = { coef08, coef08, coef10, coef11 }; + Vec4F32 fac3 = { coef12, coef12, coef14, coef15 }; + Vec4F32 fac4 = { coef16, coef16, coef18, coef19 }; + Vec4F32 fac5 = { coef20, coef20, coef22, coef23 }; + + Vec4F32 vec0 = { m.v[1][0], m.v[0][0], m.v[0][0], m.v[0][0] }; + Vec4F32 vec1 = { m.v[1][1], m.v[0][1], m.v[0][1], m.v[0][1] }; + Vec4F32 vec2 = { m.v[1][2], m.v[0][2], m.v[0][2], m.v[0][2] }; + Vec4F32 vec3 = { m.v[1][3], m.v[0][3], m.v[0][3], m.v[0][3] }; + + Vec4F32 inv0 = add_4f32(sub_4f32(mul_4f32(vec1, fac0), mul_4f32(vec2, fac1)), mul_4f32(vec3, fac2)); + Vec4F32 inv1 = add_4f32(sub_4f32(mul_4f32(vec0, fac0), mul_4f32(vec2, fac3)), mul_4f32(vec3, fac4)); + Vec4F32 inv2 = add_4f32(sub_4f32(mul_4f32(vec0, fac1), mul_4f32(vec1, fac3)), mul_4f32(vec3, fac5)); + Vec4F32 inv3 = add_4f32(sub_4f32(mul_4f32(vec0, fac2), mul_4f32(vec1, fac4)), mul_4f32(vec2, fac5)); + + Vec4F32 sign_a = { +1, -1, +1, -1 }; + Vec4F32 sign_b = { -1, +1, -1, +1 }; + + Mat4x4F32 inverse; + for(U32 i = 0; i < 4; i += 1) + { + inverse.v[0][i] = inv0.v[i] * sign_a.v[i]; + inverse.v[1][i] = inv1.v[i] * sign_b.v[i]; + inverse.v[2][i] = inv2.v[i] * sign_a.v[i]; + inverse.v[3][i] = inv3.v[i] * sign_b.v[i]; + } + + Vec4F32 row0 = { inverse.v[0][0], inverse.v[1][0], inverse.v[2][0], inverse.v[3][0] }; + Vec4F32 m0 = { m.v[0][0], m.v[0][1], m.v[0][2], m.v[0][3] }; + Vec4F32 dot0 = mul_4f32(m0, row0); + F32 dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w); + + F32 one_over_det = 1 / dot1; + + return scale_4x4f32(inverse, one_over_det); +} + +internal Mat4x4F32 +derotate_4x4f32(Mat4x4F32 mat) +{ + Vec3F32 scale = + { + length_3f32(v3f32(mat.v[0][0], mat.v[0][1], mat.v[0][2])), + length_3f32(v3f32(mat.v[1][0], mat.v[1][1], mat.v[1][2])), + length_3f32(v3f32(mat.v[2][0], mat.v[2][1], mat.v[2][2])), + }; + mat.v[0][0] = scale.x; + mat.v[1][0] = 0.f; + mat.v[2][0] = 0.f; + mat.v[0][1] = 0.f; + mat.v[1][1] = scale.y; + mat.v[2][1] = 0.f; + mat.v[0][2] = 0.f; + mat.v[1][2] = 0.f; + mat.v[2][2] = scale.z; + return mat; +} + +//////////////////////////////// +//~ rjf: Range Ops + +internal Rng1U32 rng_1u32(U32 min, U32 max) {Rng1U32 r = {min, max}; if(r.min > r.max) { Swap(U32, r.min, r.max); } return r;} +internal Rng1U32 shift_1u32(Rng1U32 r, U32 x) {r.min += x; r.max += x; return r;} +internal Rng1U32 pad_1u32(Rng1U32 r, U32 x) {r.min -= x; r.max += x; return r;} +internal U32 center_1u32(Rng1U32 r) {U32 c = (r.min+r.max)/2; return c;} +internal 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; 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;} + +internal Rng1S32 rng_1s32(S32 min, S32 max) {Rng1S32 r = {min, max}; if(r.min > r.max) { Swap(S32, r.min, r.max); } return r;} +internal Rng1S32 shift_1s32(Rng1S32 r, S32 x) {r.min += x; r.max += x; return r;} +internal Rng1S32 pad_1s32(Rng1S32 r, S32 x) {r.min -= x; r.max += x; return r;} +internal S32 center_1s32(Rng1S32 r) {S32 c = (r.min+r.max)/2; return c;} +internal 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; 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;} + +internal Rng1U64 rng_1u64(U64 min, U64 max) {Rng1U64 r = {min, max}; if(r.min > r.max) { Swap(U64, r.min, r.max); } return r;} +internal Rng1U64 shift_1u64(Rng1U64 r, U64 x) {r.min += x; r.max += x; return r;} +internal Rng1U64 pad_1u64(Rng1U64 r, U64 x) {r.min -= x; r.max += x; return r;} +internal U64 center_1u64(Rng1U64 r) {U64 c = (r.min+r.max)/2; return c;} +internal 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; 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;} + +internal Rng1S64 rng_1s64(S64 min, S64 max) {Rng1S64 r = {min, max}; if(r.min > r.max) { Swap(S64, r.min, r.max); } return r;} +internal Rng1S64 shift_1s64(Rng1S64 r, S64 x) {r.min += x; r.max += x; return r;} +internal Rng1S64 pad_1s64(Rng1S64 r, S64 x) {r.min -= x; r.max += x; return r;} +internal S64 center_1s64(Rng1S64 r) {S64 c = (r.min+r.max)/2; return c;} +internal 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; 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;} + +internal Rng1F32 rng_1f32(F32 min, F32 max) {Rng1F32 r = {min, max}; if(r.min > r.max) { Swap(F32, r.min, r.max); } return r;} +internal Rng1F32 shift_1f32(Rng1F32 r, F32 x) {r.min += x; r.max += x; return r;} +internal Rng1F32 pad_1f32(Rng1F32 r, F32 x) {r.min -= x; r.max += x; return r;} +internal F32 center_1f32(Rng1F32 r) {F32 c = (r.min+r.max)/2; return c;} +internal 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; 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;} + +internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max) {Rng2S16 r = {min, max}; return r;} +internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x) {r.min = add_2s16(r.min, x); r.max = add_2s16(r.max, x); return r;} +internal Rng2S16 pad_2s16(Rng2S16 r, S16 x) {Vec2S16 xv = {x, x}; r.min = sub_2s16(r.min, xv); r.max = add_2s16(r.max, xv); return r;} +internal Vec2S16 center_2s16(Rng2S16 r) {Vec2S16 c = {(S16)((r.min.x+r.max.x)/2), (S16)((r.min.y+r.max.y)/2)}; return c;} +internal 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), (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;} + +internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max) {Rng2S32 r = {min, max}; return r;} +internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x) {r.min = add_2s32(r.min, x); r.max = add_2s32(r.max, x); return r;} +internal Rng2S32 pad_2s32(Rng2S32 r, S32 x) {Vec2S32 xv = {x, x}; r.min = sub_2s32(r.min, xv); r.max = add_2s32(r.max, xv); return r;} +internal Vec2S32 center_2s32(Rng2S32 r) {Vec2S32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} +internal 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.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;} + +internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max) {Rng2S64 r = {min, max}; return r;} +internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x) {r.min = add_2s64(r.min, x); r.max = add_2s64(r.max, x); return r;} +internal Rng2S64 pad_2s64(Rng2S64 r, S64 x) {Vec2S64 xv = {x, x}; r.min = sub_2s64(r.min, xv); r.max = add_2s64(r.max, xv); return r;} +internal Vec2S64 center_2s64(Rng2S64 r) {Vec2S64 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} +internal 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.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;} + +internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max) {Rng2F32 r = {min, max}; return r;} +internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x) {r.min = add_2f32(r.min, x); r.max = add_2f32(r.max, x); return r;} +internal Rng2F32 pad_2f32(Rng2F32 r, F32 x) {Vec2F32 xv = {x, x}; r.min = sub_2f32(r.min, xv); r.max = add_2f32(r.max, xv); return r;} +internal Vec2F32 center_2f32(Rng2F32 r) {Vec2F32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;} +internal 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.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: Miscellaneous Ops + +internal Vec3F32 +hsv_from_rgb(Vec3F32 rgb) +{ + F32 c_max = Max(rgb.x, Max(rgb.y, rgb.z)); + F32 c_min = Min(rgb.x, Min(rgb.y, rgb.z)); + F32 delta = c_max - c_min; + F32 h = ((delta == 0.f) ? 0.f : + (c_max == rgb.x) ? mod_f32((rgb.y - rgb.z)/delta + 6.f, 6.f) : + (c_max == rgb.y) ? (rgb.z - rgb.x)/delta + 2.f : + (c_max == rgb.z) ? (rgb.x - rgb.y)/delta + 4.f : + 0.f); + F32 s = (c_max == 0.f) ? 0.f : (delta/c_max); + F32 v = c_max; + Vec3F32 hsv = {h/6.f, s, v}; + return hsv; +} + +internal Vec3F32 +rgb_from_hsv(Vec3F32 hsv) +{ + F32 h = mod_f32(hsv.x * 360.f, 360.f); + F32 s = hsv.y; + F32 v = hsv.z; + + F32 c = v*s; + F32 x = c*(1.f - abs_f32(mod_f32(h/60.f, 2.f) - 1.f)); + F32 m = v - c; + + F32 r = 0; + F32 g = 0; + F32 b = 0; + + if ((h >= 0.f && h < 60.f) || (h >= 360.f && h < 420.f)){ + r = c; + g = x; + b = 0; + } + else if (h >= 60.f && h < 120.f){ + r = x; + g = c; + b = 0; + } + else if (h >= 120.f && h < 180.f){ + r = 0; + g = c; + b = x; + } + else if (h >= 180.f && h < 240.f){ + r = 0; + g = x; + b = c; + } + else if (h >= 240.f && h < 300.f){ + r = x; + g = 0; + b = c; + } + else if ((h >= 300.f && h <= 360.f) || (h >= -60.f && h <= 0.f)){ + r = c; + g = 0; + b = x; + } + + Vec3F32 rgb = {r + m, g + m, b + m}; + return(rgb); +} + +internal Vec4F32 +hsva_from_rgba(Vec4F32 rgba) +{ + Vec3F32 rgb = v3f32(rgba.x, rgba.y, rgba.z); + Vec3F32 hsv = hsv_from_rgb(rgb); + Vec4F32 hsva = v4f32(hsv.x, hsv.y, hsv.z, rgba.w); + return hsva; +} + +internal Vec4F32 +rgba_from_hsva(Vec4F32 hsva) +{ + Vec3F32 hsv = v3f32(hsva.x, hsva.y, hsva.z); + Vec3F32 rgb = rgb_from_hsv(hsv); + Vec4F32 rgba = v4f32(rgb.x, rgb.y, rgb.z, hsva.w); + return rgba; +} + +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; +} + +internal U32 +u32_from_rgba(Vec4F32 rgba) +{ + U32 result = 0; + result |= ((U32)((U8)(rgba.x*255.f))) << 24; + result |= ((U32)((U8)(rgba.y*255.f))) << 16; + result |= ((U32)((U8)(rgba.z*255.f))) << 8; + result |= ((U32)((U8)(rgba.w*255.f))) << 0; + return result; +} + +//////////////////////////////// +//~ rjf: List Type Functions + +internal void +rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng) +{ + Rng1S64Node *n = push_array(arena, Rng1S64Node, 1); + MemoryCopyStruct(&n->v, &rng); + SLLQueuePush(list->first, list->last, n); + list->count += 1; +} + +internal Rng1S64Array +rng1s64_array_from_list(Arena *arena, Rng1S64List *list) +{ + Rng1S64Array arr = {0}; + arr.count = list->count; + arr.v = push_array_no_zero(arena, Rng1S64, arr.count); + U64 idx = 0; + for(Rng1S64Node *n = list->first; n != 0; n = n->next) + { + arr.v[idx] = n->v; + idx += 1; + } + return arr; +} diff --git a/src/base/base_math.h b/src/base/base_math.h index 3406e6a9..645a6d31 100644 --- a/src/base/base_math.h +++ b/src/base/base_math.h @@ -1,649 +1,649 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_MATH_H -#define BASE_MATH_H - -//////////////////////////////// -//~ rjf: Vector Types - -//- rjf: 2-vectors - -typedef union Vec2F32 Vec2F32; -union Vec2F32 -{ - struct - { - F32 x; - F32 y; - }; - F32 v[2]; -}; - -typedef union Vec2S64 Vec2S64; -union Vec2S64 -{ - struct - { - S64 x; - S64 y; - }; - S64 v[2]; -}; - -typedef union Vec2S32 Vec2S32; -union Vec2S32 -{ - struct - { - S32 x; - S32 y; - }; - S32 v[2]; -}; - -typedef union Vec2S16 Vec2S16; -union Vec2S16 -{ - struct - { - S16 x; - S16 y; - }; - S16 v[2]; -}; - -//- rjf: 3-vectors - -typedef union Vec3F32 Vec3F32; -union Vec3F32 -{ - struct - { - F32 x; - F32 y; - F32 z; - }; - struct - { - Vec2F32 xy; - F32 _z0; - }; - struct - { - F32 _x0; - Vec2F32 yz; - }; - F32 v[3]; -}; - -typedef union Vec3S32 Vec3S32; -union Vec3S32 -{ - struct - { - S32 x; - S32 y; - S32 z; - }; - struct - { - Vec2S32 xy; - S32 _z0; - }; - struct - { - S32 _x0; - Vec2S32 yz; - }; - S32 v[3]; -}; - -//- rjf: 4-vectors - -typedef union Vec4F32 Vec4F32; -union Vec4F32 -{ - struct - { - F32 x; - F32 y; - F32 z; - F32 w; - }; - struct - { - Vec2F32 xy; - Vec2F32 zw; - }; - struct - { - Vec3F32 xyz; - F32 _z0; - }; - struct - { - F32 _x0; - Vec3F32 yzw; - }; - F32 v[4]; -}; - -typedef union Vec4S32 Vec4S32; -union Vec4S32 -{ - struct - { - S32 x; - S32 y; - S32 z; - S32 w; - }; - struct - { - Vec2S32 xy; - Vec2S32 zw; - }; - struct - { - Vec3S32 xyz; - S32 _z0; - }; - struct - { - S32 _x0; - Vec3S32 yzw; - }; - S32 v[4]; -}; - -//////////////////////////////// -//~ rjf: Matrix Types - -typedef struct Mat3x3F32 Mat3x3F32; -struct Mat3x3F32 -{ - F32 v[3][3]; -}; - -typedef struct Mat4x4F32 Mat4x4F32; -struct Mat4x4F32 -{ - F32 v[4][4]; -}; - -//////////////////////////////// -//~ rjf: Range Types - -//- rjf: 1-range - -typedef union Rng1U32 Rng1U32; -union Rng1U32 -{ - struct - { - U32 min; - U32 max; - }; - U32 v[2]; -}; - -typedef union Rng1S32 Rng1S32; -union Rng1S32 -{ - struct - { - S32 min; - S32 max; - }; - S32 v[2]; -}; - -typedef union Rng1U64 Rng1U64; -union Rng1U64 -{ - struct - { - U64 min; - U64 max; - }; - U64 v[2]; -}; - -typedef union Rng1S64 Rng1S64; -union Rng1S64 -{ - struct - { - S64 min; - S64 max; - }; - S64 v[2]; -}; - -typedef union Rng1F32 Rng1F32; -union Rng1F32 -{ - struct - { - F32 min; - F32 max; - }; - F32 v[2]; -}; - -//- rjf: 2-range (rectangles) - -typedef union Rng2S16 Rng2S16; -union Rng2S16 -{ - struct - { - Vec2S16 min; - Vec2S16 max; - }; - struct - { - Vec2S16 p0; - Vec2S16 p1; - }; - struct - { - S16 x0; - S16 y0; - S16 x1; - S16 y1; - }; - Vec2S16 v[2]; -}; - -typedef union Rng2S32 Rng2S32; -union Rng2S32 -{ - struct - { - Vec2S32 min; - Vec2S32 max; - }; - struct - { - Vec2S32 p0; - Vec2S32 p1; - }; - struct - { - S32 x0; - S32 y0; - S32 x1; - S32 y1; - }; - Vec2S32 v[2]; -}; - -typedef union Rng2F32 Rng2F32; -union Rng2F32 -{ - struct - { - Vec2F32 min; - Vec2F32 max; - }; - struct - { - Vec2F32 p0; - Vec2F32 p1; - }; - struct - { - F32 x0; - F32 y0; - F32 x1; - F32 y1; - }; - Vec2F32 v[2]; -}; - -typedef union Rng2S64 Rng2S64; -union Rng2S64 -{ - struct - { - Vec2S64 min; - Vec2S64 max; - }; - struct - { - Vec2S64 p0; - Vec2S64 p1; - }; - struct - { - S64 x0; - S64 y0; - S64 x1; - S64 y1; - }; - Vec2S64 v[2]; -}; - -//////////////////////////////// -//~ rjf: List Types - -typedef struct Rng1S64Node Rng1S64Node; -struct Rng1S64Node -{ - Rng1S64Node *next; - Rng1S64 v; -}; - -typedef struct Rng1S64List Rng1S64List; -struct Rng1S64List -{ - Rng1S64Node *first; - Rng1S64Node *last; - U64 count; -}; - -typedef struct Rng1S64Array Rng1S64Array; -struct Rng1S64Array -{ - Rng1S64 *v; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Scalar Ops - -#define abs_s64(v) (S64)llabs(v) - -#define sqrt_f32(v) sqrtf(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 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))) -#define radians_from_degrees_f32(v) (radians_from_turns_f32(turns_from_degrees_f32(v))) -#define sin_f32(v) sinf(radians_from_turns_f32(v)) -#define cos_f32(v) cosf(radians_from_turns_f32(v)) -#define tan_f32(v) tanf(radians_from_turns_f32(v)) - -#define sqrt_f64(v) sqrt(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 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))) -#define radians_from_degrees_f64(v) (radians_from_turns_f64(turns_from_degrees_f64(v))) -#define sin_f64(v) sin(radians_from_turns_f64(v)) -#define cos_f64(v) cos(radians_from_turns_f64(v)) -#define tan_f64(v) tan(radians_from_turns_f64(v)) - -internal F32 mix_1f32(F32 a, F32 b, F32 t); -internal F64 mix_1f64(F64 a, F64 b, F64 t); - -//////////////////////////////// -//~ rjf: Vector Ops - -#define v2f32(x, y) vec_2f32((x), (y)) -internal Vec2F32 vec_2f32(F32 x, F32 y); -internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b); -internal Vec2F32 scale_2f32(Vec2F32 v, F32 s); -internal F32 dot_2f32(Vec2F32 a, Vec2F32 b); -internal F32 length_squared_2f32(Vec2F32 v); -internal F32 length_2f32(Vec2F32 v); -internal Vec2F32 normalize_2f32(Vec2F32 v); -internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t); - -#define v2s64(x, y) vec_2s64((x), (y)) -internal Vec2S64 vec_2s64(S64 x, S64 y); -internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b); -internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b); -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)) -internal Vec2S32 vec_2s32(S32 x, S32 y); -internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b); -internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b); -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)) -internal Vec2S16 vec_2s16(S16 x, S16 y); -internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b); -internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b); -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)) -internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z); -internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b); -internal Vec3F32 scale_3f32(Vec3F32 v, F32 s); -internal F32 dot_3f32(Vec3F32 a, Vec3F32 b); -internal F32 length_squared_3f32(Vec3F32 v); -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); - -#define v3s32(x, y, z) vec_3s32((x), (y), (z)) -internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z); -internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b); -internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b); -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); - -#define v4f32(x, y, z, w) vec_4f32((x), (y), (z), (w)) -internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w); -internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b); -internal Vec4F32 scale_4f32(Vec4F32 v, F32 s); -internal F32 dot_4f32(Vec4F32 a, Vec4F32 b); -internal F32 length_squared_4f32(Vec4F32 v); -internal F32 length_4f32(Vec4F32 v); -internal Vec4F32 normalize_4f32(Vec4F32 v); -internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t); - -#define v4s32(x, y, z, w) vec_4s32((x), (y), (z), (w)) -internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w); -internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b); -internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b); -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); - -//////////////////////////////// -//~ rjf: Matrix Ops - -internal Mat3x3F32 mat_3x3f32(F32 diagonal); -internal Mat3x3F32 make_translate_3x3f32(Vec2F32 delta); -internal Mat3x3F32 make_scale_3x3f32(Vec2F32 scale); -internal Mat3x3F32 mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b); - -internal Mat4x4F32 mat_4x4f32(F32 diagonal); -internal Mat4x4F32 make_translate_4x4f32(Vec3F32 delta); -internal Mat4x4F32 make_scale_4x4f32(Vec3F32 scale); -internal Mat4x4F32 make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z); -internal Mat4x4F32 make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z); -internal Mat4x4F32 make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up); -internal Mat4x4F32 make_rotate_4x4f32(Vec3F32 axis, F32 turns); -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); - -//////////////////////////////// -//~ rjf: Range Ops - -#define r1u32(min, max) rng_1u32((min), (max)) -internal Rng1U32 rng_1u32(U32 min, U32 max); -internal Rng1U32 shift_1u32(Rng1U32 r, U32 x); -internal Rng1U32 pad_1u32(Rng1U32 r, U32 x); -internal U32 center_1u32(Rng1U32 r); -internal B32 contains_1u32(Rng1U32 r, U32 x); -internal U32 dim_1u32(Rng1U32 r); -internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b); -internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b); -internal U32 clamp_1u32(Rng1U32 r, U32 v); - -#define r1s32(min, max) rng_1s32((min), (max)) -internal Rng1S32 rng_1s32(S32 min, S32 max); -internal Rng1S32 shift_1s32(Rng1S32 r, S32 x); -internal Rng1S32 pad_1s32(Rng1S32 r, S32 x); -internal S32 center_1s32(Rng1S32 r); -internal B32 contains_1s32(Rng1S32 r, S32 x); -internal S32 dim_1s32(Rng1S32 r); -internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b); -internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b); -internal S32 clamp_1s32(Rng1S32 r, S32 v); - -#define r1u64(min, max) rng_1u64((min), (max)) -internal Rng1U64 rng_1u64(U64 min, U64 max); -internal Rng1U64 shift_1u64(Rng1U64 r, U64 x); -internal Rng1U64 pad_1u64(Rng1U64 r, U64 x); -internal U64 center_1u64(Rng1U64 r); -internal B32 contains_1u64(Rng1U64 r, U64 x); -internal U64 dim_1u64(Rng1U64 r); -internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b); -internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b); -internal U64 clamp_1u64(Rng1U64 r, U64 v); - -#define r1s64(min, max) rng_1s64((min), (max)) -internal Rng1S64 rng_1s64(S64 min, S64 max); -internal Rng1S64 shift_1s64(Rng1S64 r, S64 x); -internal Rng1S64 pad_1s64(Rng1S64 r, S64 x); -internal S64 center_1s64(Rng1S64 r); -internal B32 contains_1s64(Rng1S64 r, S64 x); -internal S64 dim_1s64(Rng1S64 r); -internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b); -internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b); -internal S64 clamp_1s64(Rng1S64 r, S64 v); - -#define r1f32(min, max) rng_1f32((min), (max)) -internal Rng1F32 rng_1f32(F32 min, F32 max); -internal Rng1F32 shift_1f32(Rng1F32 r, F32 x); -internal Rng1F32 pad_1f32(Rng1F32 r, F32 x); -internal F32 center_1f32(Rng1F32 r); -internal B32 contains_1f32(Rng1F32 r, F32 x); -internal F32 dim_1f32(Rng1F32 r); -internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b); -internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b); -internal F32 clamp_1f32(Rng1F32 r, F32 v); - -#define r2s16(min, max) rng_2s16((min), (max)) -#define r2s16p(x, y, z, w) r2s16(v2s16((x), (y)), v2s16((z), (w))) -internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max); -internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x); -internal Rng2S16 pad_2s16(Rng2S16 r, S16 x); -internal Vec2S16 center_2s16(Rng2S16 r); -internal B32 contains_2s16(Rng2S16 r, Vec2S16 x); -internal Vec2S16 dim_2s16(Rng2S16 r); -internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b); -internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b); -internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v); - -#define r2s32(min, max) rng_2s32((min), (max)) -#define r2s32p(x, y, z, w) r2s32(v2s32((x), (y)), v2s32((z), (w))) -internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max); -internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x); -internal Rng2S32 pad_2s32(Rng2S32 r, S32 x); -internal Vec2S32 center_2s32(Rng2S32 r); -internal B32 contains_2s32(Rng2S32 r, Vec2S32 x); -internal Vec2S32 dim_2s32(Rng2S32 r); -internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b); -internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b); -internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v); - -#define r2s64(min, max) rng_2s64((min), (max)) -#define r2s64p(x, y, z, w) r2s64(v2s64((x), (y)), v2s64((z), (w))) -internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max); -internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x); -internal Rng2S64 pad_2s64(Rng2S64 r, S64 x); -internal Vec2S64 center_2s64(Rng2S64 r); -internal B32 contains_2s64(Rng2S64 r, Vec2S64 x); -internal Vec2S64 dim_2s64(Rng2S64 r); -internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b); -internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b); -internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v); - -#define r2f32(min, max) rng_2f32((min), (max)) -#define r2f32p(x, y, z, w) r2f32(v2f32((x), (y)), v2f32((z), (w))) -internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max); -internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x); -internal Rng2F32 pad_2f32(Rng2F32 r, F32 x); -internal Vec2F32 center_2f32(Rng2F32 r); -internal B32 contains_2f32(Rng2F32 r, Vec2F32 x); -internal Vec2F32 dim_2f32(Rng2F32 r); -internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b); -internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b); -internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v); - -//////////////////////////////// -//~ rjf: Miscellaneous Ops - -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); -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 rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng); -internal Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list); - -#endif // BASE_MATH_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_MATH_H +#define BASE_MATH_H + +//////////////////////////////// +//~ rjf: Vector Types + +//- rjf: 2-vectors + +typedef union Vec2F32 Vec2F32; +union Vec2F32 +{ + struct + { + F32 x; + F32 y; + }; + F32 v[2]; +}; + +typedef union Vec2S64 Vec2S64; +union Vec2S64 +{ + struct + { + S64 x; + S64 y; + }; + S64 v[2]; +}; + +typedef union Vec2S32 Vec2S32; +union Vec2S32 +{ + struct + { + S32 x; + S32 y; + }; + S32 v[2]; +}; + +typedef union Vec2S16 Vec2S16; +union Vec2S16 +{ + struct + { + S16 x; + S16 y; + }; + S16 v[2]; +}; + +//- rjf: 3-vectors + +typedef union Vec3F32 Vec3F32; +union Vec3F32 +{ + struct + { + F32 x; + F32 y; + F32 z; + }; + struct + { + Vec2F32 xy; + F32 _z0; + }; + struct + { + F32 _x0; + Vec2F32 yz; + }; + F32 v[3]; +}; + +typedef union Vec3S32 Vec3S32; +union Vec3S32 +{ + struct + { + S32 x; + S32 y; + S32 z; + }; + struct + { + Vec2S32 xy; + S32 _z0; + }; + struct + { + S32 _x0; + Vec2S32 yz; + }; + S32 v[3]; +}; + +//- rjf: 4-vectors + +typedef union Vec4F32 Vec4F32; +union Vec4F32 +{ + struct + { + F32 x; + F32 y; + F32 z; + F32 w; + }; + struct + { + Vec2F32 xy; + Vec2F32 zw; + }; + struct + { + Vec3F32 xyz; + F32 _z0; + }; + struct + { + F32 _x0; + Vec3F32 yzw; + }; + F32 v[4]; +}; + +typedef union Vec4S32 Vec4S32; +union Vec4S32 +{ + struct + { + S32 x; + S32 y; + S32 z; + S32 w; + }; + struct + { + Vec2S32 xy; + Vec2S32 zw; + }; + struct + { + Vec3S32 xyz; + S32 _z0; + }; + struct + { + S32 _x0; + Vec3S32 yzw; + }; + S32 v[4]; +}; + +//////////////////////////////// +//~ rjf: Matrix Types + +typedef struct Mat3x3F32 Mat3x3F32; +struct Mat3x3F32 +{ + F32 v[3][3]; +}; + +typedef struct Mat4x4F32 Mat4x4F32; +struct Mat4x4F32 +{ + F32 v[4][4]; +}; + +//////////////////////////////// +//~ rjf: Range Types + +//- rjf: 1-range + +typedef union Rng1U32 Rng1U32; +union Rng1U32 +{ + struct + { + U32 min; + U32 max; + }; + U32 v[2]; +}; + +typedef union Rng1S32 Rng1S32; +union Rng1S32 +{ + struct + { + S32 min; + S32 max; + }; + S32 v[2]; +}; + +typedef union Rng1U64 Rng1U64; +union Rng1U64 +{ + struct + { + U64 min; + U64 max; + }; + U64 v[2]; +}; + +typedef union Rng1S64 Rng1S64; +union Rng1S64 +{ + struct + { + S64 min; + S64 max; + }; + S64 v[2]; +}; + +typedef union Rng1F32 Rng1F32; +union Rng1F32 +{ + struct + { + F32 min; + F32 max; + }; + F32 v[2]; +}; + +//- rjf: 2-range (rectangles) + +typedef union Rng2S16 Rng2S16; +union Rng2S16 +{ + struct + { + Vec2S16 min; + Vec2S16 max; + }; + struct + { + Vec2S16 p0; + Vec2S16 p1; + }; + struct + { + S16 x0; + S16 y0; + S16 x1; + S16 y1; + }; + Vec2S16 v[2]; +}; + +typedef union Rng2S32 Rng2S32; +union Rng2S32 +{ + struct + { + Vec2S32 min; + Vec2S32 max; + }; + struct + { + Vec2S32 p0; + Vec2S32 p1; + }; + struct + { + S32 x0; + S32 y0; + S32 x1; + S32 y1; + }; + Vec2S32 v[2]; +}; + +typedef union Rng2F32 Rng2F32; +union Rng2F32 +{ + struct + { + Vec2F32 min; + Vec2F32 max; + }; + struct + { + Vec2F32 p0; + Vec2F32 p1; + }; + struct + { + F32 x0; + F32 y0; + F32 x1; + F32 y1; + }; + Vec2F32 v[2]; +}; + +typedef union Rng2S64 Rng2S64; +union Rng2S64 +{ + struct + { + Vec2S64 min; + Vec2S64 max; + }; + struct + { + Vec2S64 p0; + Vec2S64 p1; + }; + struct + { + S64 x0; + S64 y0; + S64 x1; + S64 y1; + }; + Vec2S64 v[2]; +}; + +//////////////////////////////// +//~ rjf: List Types + +typedef struct Rng1S64Node Rng1S64Node; +struct Rng1S64Node +{ + Rng1S64Node *next; + Rng1S64 v; +}; + +typedef struct Rng1S64List Rng1S64List; +struct Rng1S64List +{ + Rng1S64Node *first; + Rng1S64Node *last; + U64 count; +}; + +typedef struct Rng1S64Array Rng1S64Array; +struct Rng1S64Array +{ + Rng1S64 *v; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Scalar Ops + +#define abs_s64(v) (S64)llabs(v) + +#define sqrt_f32(v) sqrtf(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 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))) +#define radians_from_degrees_f32(v) (radians_from_turns_f32(turns_from_degrees_f32(v))) +#define sin_f32(v) sinf(radians_from_turns_f32(v)) +#define cos_f32(v) cosf(radians_from_turns_f32(v)) +#define tan_f32(v) tanf(radians_from_turns_f32(v)) + +#define sqrt_f64(v) sqrt(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 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))) +#define radians_from_degrees_f64(v) (radians_from_turns_f64(turns_from_degrees_f64(v))) +#define sin_f64(v) sin(radians_from_turns_f64(v)) +#define cos_f64(v) cos(radians_from_turns_f64(v)) +#define tan_f64(v) tan(radians_from_turns_f64(v)) + +internal F32 mix_1f32(F32 a, F32 b, F32 t); +internal F64 mix_1f64(F64 a, F64 b, F64 t); + +//////////////////////////////// +//~ rjf: Vector Ops + +#define v2f32(x, y) vec_2f32((x), (y)) +internal Vec2F32 vec_2f32(F32 x, F32 y); +internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b); +internal Vec2F32 scale_2f32(Vec2F32 v, F32 s); +internal F32 dot_2f32(Vec2F32 a, Vec2F32 b); +internal F32 length_squared_2f32(Vec2F32 v); +internal F32 length_2f32(Vec2F32 v); +internal Vec2F32 normalize_2f32(Vec2F32 v); +internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t); + +#define v2s64(x, y) vec_2s64((x), (y)) +internal Vec2S64 vec_2s64(S64 x, S64 y); +internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b); +internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b); +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)) +internal Vec2S32 vec_2s32(S32 x, S32 y); +internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b); +internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b); +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)) +internal Vec2S16 vec_2s16(S16 x, S16 y); +internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b); +internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b); +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)) +internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z); +internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b); +internal Vec3F32 scale_3f32(Vec3F32 v, F32 s); +internal F32 dot_3f32(Vec3F32 a, Vec3F32 b); +internal F32 length_squared_3f32(Vec3F32 v); +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); + +#define v3s32(x, y, z) vec_3s32((x), (y), (z)) +internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z); +internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b); +internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b); +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); + +#define v4f32(x, y, z, w) vec_4f32((x), (y), (z), (w)) +internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w); +internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b); +internal Vec4F32 scale_4f32(Vec4F32 v, F32 s); +internal F32 dot_4f32(Vec4F32 a, Vec4F32 b); +internal F32 length_squared_4f32(Vec4F32 v); +internal F32 length_4f32(Vec4F32 v); +internal Vec4F32 normalize_4f32(Vec4F32 v); +internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t); + +#define v4s32(x, y, z, w) vec_4s32((x), (y), (z), (w)) +internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w); +internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b); +internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b); +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); + +//////////////////////////////// +//~ rjf: Matrix Ops + +internal Mat3x3F32 mat_3x3f32(F32 diagonal); +internal Mat3x3F32 make_translate_3x3f32(Vec2F32 delta); +internal Mat3x3F32 make_scale_3x3f32(Vec2F32 scale); +internal Mat3x3F32 mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b); + +internal Mat4x4F32 mat_4x4f32(F32 diagonal); +internal Mat4x4F32 make_translate_4x4f32(Vec3F32 delta); +internal Mat4x4F32 make_scale_4x4f32(Vec3F32 scale); +internal Mat4x4F32 make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z); +internal Mat4x4F32 make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z); +internal Mat4x4F32 make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up); +internal Mat4x4F32 make_rotate_4x4f32(Vec3F32 axis, F32 turns); +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); + +//////////////////////////////// +//~ rjf: Range Ops + +#define r1u32(min, max) rng_1u32((min), (max)) +internal Rng1U32 rng_1u32(U32 min, U32 max); +internal Rng1U32 shift_1u32(Rng1U32 r, U32 x); +internal Rng1U32 pad_1u32(Rng1U32 r, U32 x); +internal U32 center_1u32(Rng1U32 r); +internal B32 contains_1u32(Rng1U32 r, U32 x); +internal U32 dim_1u32(Rng1U32 r); +internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b); +internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b); +internal U32 clamp_1u32(Rng1U32 r, U32 v); + +#define r1s32(min, max) rng_1s32((min), (max)) +internal Rng1S32 rng_1s32(S32 min, S32 max); +internal Rng1S32 shift_1s32(Rng1S32 r, S32 x); +internal Rng1S32 pad_1s32(Rng1S32 r, S32 x); +internal S32 center_1s32(Rng1S32 r); +internal B32 contains_1s32(Rng1S32 r, S32 x); +internal S32 dim_1s32(Rng1S32 r); +internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b); +internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b); +internal S32 clamp_1s32(Rng1S32 r, S32 v); + +#define r1u64(min, max) rng_1u64((min), (max)) +internal Rng1U64 rng_1u64(U64 min, U64 max); +internal Rng1U64 shift_1u64(Rng1U64 r, U64 x); +internal Rng1U64 pad_1u64(Rng1U64 r, U64 x); +internal U64 center_1u64(Rng1U64 r); +internal B32 contains_1u64(Rng1U64 r, U64 x); +internal U64 dim_1u64(Rng1U64 r); +internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b); +internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b); +internal U64 clamp_1u64(Rng1U64 r, U64 v); + +#define r1s64(min, max) rng_1s64((min), (max)) +internal Rng1S64 rng_1s64(S64 min, S64 max); +internal Rng1S64 shift_1s64(Rng1S64 r, S64 x); +internal Rng1S64 pad_1s64(Rng1S64 r, S64 x); +internal S64 center_1s64(Rng1S64 r); +internal B32 contains_1s64(Rng1S64 r, S64 x); +internal S64 dim_1s64(Rng1S64 r); +internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b); +internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b); +internal S64 clamp_1s64(Rng1S64 r, S64 v); + +#define r1f32(min, max) rng_1f32((min), (max)) +internal Rng1F32 rng_1f32(F32 min, F32 max); +internal Rng1F32 shift_1f32(Rng1F32 r, F32 x); +internal Rng1F32 pad_1f32(Rng1F32 r, F32 x); +internal F32 center_1f32(Rng1F32 r); +internal B32 contains_1f32(Rng1F32 r, F32 x); +internal F32 dim_1f32(Rng1F32 r); +internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b); +internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b); +internal F32 clamp_1f32(Rng1F32 r, F32 v); + +#define r2s16(min, max) rng_2s16((min), (max)) +#define r2s16p(x, y, z, w) r2s16(v2s16((x), (y)), v2s16((z), (w))) +internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max); +internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x); +internal Rng2S16 pad_2s16(Rng2S16 r, S16 x); +internal Vec2S16 center_2s16(Rng2S16 r); +internal B32 contains_2s16(Rng2S16 r, Vec2S16 x); +internal Vec2S16 dim_2s16(Rng2S16 r); +internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b); +internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b); +internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v); + +#define r2s32(min, max) rng_2s32((min), (max)) +#define r2s32p(x, y, z, w) r2s32(v2s32((x), (y)), v2s32((z), (w))) +internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max); +internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x); +internal Rng2S32 pad_2s32(Rng2S32 r, S32 x); +internal Vec2S32 center_2s32(Rng2S32 r); +internal B32 contains_2s32(Rng2S32 r, Vec2S32 x); +internal Vec2S32 dim_2s32(Rng2S32 r); +internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b); +internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b); +internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v); + +#define r2s64(min, max) rng_2s64((min), (max)) +#define r2s64p(x, y, z, w) r2s64(v2s64((x), (y)), v2s64((z), (w))) +internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max); +internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x); +internal Rng2S64 pad_2s64(Rng2S64 r, S64 x); +internal Vec2S64 center_2s64(Rng2S64 r); +internal B32 contains_2s64(Rng2S64 r, Vec2S64 x); +internal Vec2S64 dim_2s64(Rng2S64 r); +internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b); +internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b); +internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v); + +#define r2f32(min, max) rng_2f32((min), (max)) +#define r2f32p(x, y, z, w) r2f32(v2f32((x), (y)), v2f32((z), (w))) +internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max); +internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x); +internal Rng2F32 pad_2f32(Rng2F32 r, F32 x); +internal Vec2F32 center_2f32(Rng2F32 r); +internal B32 contains_2f32(Rng2F32 r, Vec2F32 x); +internal Vec2F32 dim_2f32(Rng2F32 r); +internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b); +internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b); +internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v); + +//////////////////////////////// +//~ rjf: Miscellaneous Ops + +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); +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 rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng); +internal Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list); + +#endif // BASE_MATH_H diff --git a/src/base/base_profile.c b/src/base/base_profile.c index 7ea8904c..8ac6bce5 100644 --- a/src/base/base_profile.c +++ b/src/base/base_profile.c @@ -1,2 +1,2 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) diff --git a/src/base/base_profile.h b/src/base/base_profile.h index 0809f207..fa67b823 100644 --- a/src/base/base_profile.h +++ b/src/base/base_profile.h @@ -1,74 +1,74 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef BASE_PROFILE_H -#define BASE_PROFILE_H - -//////////////////////////////// -//~ rjf: Zero Settings - -#if !defined(PROFILE_TELEMETRY) -# define PROFILE_TELEMETRY 0 -#endif - -#if !defined(MARKUP_LAYER_COLOR) -# define MARKUP_LAYER_COLOR 1.00f, 0.00f, 1.00f -#endif - -//////////////////////////////// -//~ rjf: Third Party Includes - -#if PROFILE_TELEMETRY -# include "rad_tm.h" -# if OS_WINDOWS -# pragma comment(lib, "rad_tm_win64.lib") -# endif -#endif - -//////////////////////////////// -//~ rjf: Telemetry Profile Defines - -#if PROFILE_TELEMETRY -# define ProfBegin(...) tmEnter(0, 0, __VA_ARGS__) -# define ProfBeginDynamic(...) (TM_API_PTR ? TM_API_PTR->_tmEnterZoneV_Core(0, 0, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0) -# define ProfEnd(...) (TM_API_PTR ? TM_API_PTR->_tmLeaveZone(0) : (void)0) -# define ProfTick(...) tmTick(0) -# define ProfIsCapturing(...) tmRunning() -# define ProfBeginCapture(...) tmOpen(0, __VA_ARGS__, __DATE__, "localhost", TMCT_TCP, TELEMETRY_DEFAULT_PORT, TMOF_INIT_NETWORKING|TMOF_CAPTURE_CONTEXT_SWITCHES, 100) -# define ProfEndCapture(...) tmClose(0) -# define ProfThreadName(...) (TM_API_PTR ? TM_API_PTR->_tmThreadName(0, 0, __VA_ARGS__) : (void)0) -# define ProfMsg(...) (TM_API_PTR ? TM_API_PTR->_tmMessageV_Core(0, TMMF_ICON_NOTE, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0) -# define ProfBeginLockWait(...) tmStartWaitForLock(0, 0, __VA_ARGS__) -# define ProfEndLockWait(...) tmEndWaitForLock(0) -# define ProfLockTake(...) tmAcquiredLock(0, 0, __VA_ARGS__) -# define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__) -# define ProfColor(color) tmZoneColorSticky(color) -#endif - -//////////////////////////////// -//~ rjf: Zeroify Undefined Defines - -#if !defined(ProfBegin) -# define ProfBegin(...) (0) -# define ProfBeginDynamic(...) (0) -# define ProfEnd(...) (0) -# define ProfTick(...) (0) -# define ProfIsCapturing(...) (0) -# define ProfBeginCapture(...) (0) -# define ProfEndCapture(...) (0) -# define ProfThreadName(...) (0) -# define ProfMsg(...) (0) -# define ProfBeginLockWait(...) (0) -# define ProfEndLockWait(...) (0) -# define ProfLockTake(...) (0) -# define ProfLockDrop(...) (0) -# define ProfColor(...) (0) -#endif - -//////////////////////////////// -//~ rjf: Helper Wrappers - -#define ProfBeginFunction(...) ProfBegin(this_function_name) -#define ProfScope(...) DeferLoop(ProfBeginDynamic(__VA_ARGS__), ProfEnd()) - -#endif // BASE_PROFILE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef BASE_PROFILE_H +#define BASE_PROFILE_H + +//////////////////////////////// +//~ rjf: Zero Settings + +#if !defined(PROFILE_TELEMETRY) +# define PROFILE_TELEMETRY 0 +#endif + +#if !defined(MARKUP_LAYER_COLOR) +# define MARKUP_LAYER_COLOR 1.00f, 0.00f, 1.00f +#endif + +//////////////////////////////// +//~ rjf: Third Party Includes + +#if PROFILE_TELEMETRY +# include "rad_tm.h" +# if OS_WINDOWS +# pragma comment(lib, "rad_tm_win64.lib") +# endif +#endif + +//////////////////////////////// +//~ rjf: Telemetry Profile Defines + +#if PROFILE_TELEMETRY +# define ProfBegin(...) tmEnter(0, 0, __VA_ARGS__) +# define ProfBeginDynamic(...) (TM_API_PTR ? TM_API_PTR->_tmEnterZoneV_Core(0, 0, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0) +# define ProfEnd(...) (TM_API_PTR ? TM_API_PTR->_tmLeaveZone(0) : (void)0) +# define ProfTick(...) tmTick(0) +# define ProfIsCapturing(...) tmRunning() +# define ProfBeginCapture(...) tmOpen(0, __VA_ARGS__, __DATE__, "localhost", TMCT_TCP, TELEMETRY_DEFAULT_PORT, TMOF_INIT_NETWORKING|TMOF_CAPTURE_CONTEXT_SWITCHES, 100) +# define ProfEndCapture(...) tmClose(0) +# define ProfThreadName(...) (TM_API_PTR ? TM_API_PTR->_tmThreadName(0, 0, __VA_ARGS__) : (void)0) +# define ProfMsg(...) (TM_API_PTR ? TM_API_PTR->_tmMessageV_Core(0, TMMF_ICON_NOTE, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0) +# define ProfBeginLockWait(...) tmStartWaitForLock(0, 0, __VA_ARGS__) +# define ProfEndLockWait(...) tmEndWaitForLock(0) +# define ProfLockTake(...) tmAcquiredLock(0, 0, __VA_ARGS__) +# define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__) +# define ProfColor(color) tmZoneColorSticky(color) +#endif + +//////////////////////////////// +//~ rjf: Zeroify Undefined Defines + +#if !defined(ProfBegin) +# define ProfBegin(...) (0) +# define ProfBeginDynamic(...) (0) +# define ProfEnd(...) (0) +# define ProfTick(...) (0) +# define ProfIsCapturing(...) (0) +# define ProfBeginCapture(...) (0) +# define ProfEndCapture(...) (0) +# define ProfThreadName(...) (0) +# define ProfMsg(...) (0) +# define ProfBeginLockWait(...) (0) +# define ProfEndLockWait(...) (0) +# define ProfLockTake(...) (0) +# define ProfLockDrop(...) (0) +# define ProfColor(...) (0) +#endif + +//////////////////////////////// +//~ rjf: Helper Wrappers + +#define ProfBeginFunction(...) ProfBegin(this_function_name) +#define ProfScope(...) DeferLoop(ProfBeginDynamic(__VA_ARGS__), ProfEnd()) + +#endif // BASE_PROFILE_H diff --git a/src/base/base_strings.h b/src/base/base_strings.h index c42b3508..c68bdff6 100644 --- a/src/base/base_strings.h +++ b/src/base/base_strings.h @@ -1,381 +1,381 @@ -// 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 - -typedef struct String8 String8; -struct String8 -{ - U8 *str; - U64 size; -}; - -typedef struct String16 String16; -struct String16 -{ - U16 *str; - U64 size; -}; - -typedef struct String32 String32; -struct String32 -{ - U32 *str; - U64 size; -}; - -//////////////////////////////// -//~ rjf: String List & Array Types - -typedef struct String8Node String8Node; -struct String8Node -{ - String8Node *next; - String8 string; -}; - -typedef struct String8MetaNode String8MetaNode; -struct String8MetaNode -{ - String8MetaNode *next; - String8Node *node; -}; - -typedef struct String8List String8List; -struct String8List -{ - String8Node *first; - String8Node *last; - U64 node_count; - U64 total_size; -}; - -typedef struct String8Array String8Array; -struct String8Array -{ - String8 *v; - U64 count; -}; - -//////////////////////////////// -//~ rjf: String Matching, Splitting, & Joining Types - -typedef U32 StringMatchFlags; -enum -{ - StringMatchFlag_CaseInsensitive = (1 << 0), - StringMatchFlag_RightSideSloppy = (1 << 1), - StringMatchFlag_SlashInsensitive = (1 << 2), -}; - -typedef U32 StringSplitFlags; -enum -{ - StringSplitFlag_KeepEmpties = (1 << 0), -}; - -typedef enum PathStyle -{ - PathStyle_Relative, - PathStyle_WindowsAbsolute, - PathStyle_UnixAbsolute, - -#if OS_WINDOWS - PathStyle_SystemAbsolute = PathStyle_WindowsAbsolute -#elif OS_LINUX - PathStyle_SystemAbsolute = PathStyle_UnixAbsolute -#else -# error "absolute path style is undefined for this OS" -#endif -} -PathStyle; - -typedef struct StringJoin StringJoin; -struct StringJoin -{ - String8 pre; - String8 sep; - String8 post; -}; - -//////////////////////////////// -//~ rjf: String Pair Types - -typedef struct String8TxtPtPair String8TxtPtPair; -struct String8TxtPtPair -{ - String8 string; - TxtPt pt; -}; - -//////////////////////////////// -//~ rjf: UTF Decoding Types - -typedef struct UnicodeDecode UnicodeDecode; -struct UnicodeDecode -{ - U32 inc; - U32 codepoint; -}; - -//////////////////////////////// -//~ rjf: String Fuzzy Matching Types - -typedef struct FuzzyMatchRangeNode FuzzyMatchRangeNode; -struct FuzzyMatchRangeNode -{ - FuzzyMatchRangeNode *next; - Rng1U64 range; -}; - -typedef struct FuzzyMatchRangeList FuzzyMatchRangeList; -struct FuzzyMatchRangeList -{ - FuzzyMatchRangeNode *first; - FuzzyMatchRangeNode *last; - U64 count; - U64 needle_part_count; - U64 total_dim; -}; - -//////////////////////////////// -//~ rjf: Character Classification & Conversion Functions - -internal B32 char_is_space(U8 c); -internal B32 char_is_upper(U8 c); -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 char_to_lower(U8 c); -internal U8 char_to_upper(U8 c); -internal U8 char_to_correct_slash(U8 c); - -//////////////////////////////// -//~ rjf: C-String Measurement - -internal U64 cstring8_length(U8 *c); -internal U64 cstring16_length(U16 *c); -internal U64 cstring32_length(U32 *c); - -//////////////////////////////// -//~ rjf: String Constructors - -#define str8_lit(S) str8((U8*)(S), sizeof(S) - 1) -#define str8_lit_comp(S) {(U8*)(S), sizeof(S) - 1,} -#define str8_varg(S) (int)((S).size), ((S).str) - -#define str8_array(S,C) str8((U8*)(S), sizeof(*(S))*(C)) -#define str8_array_fixed(S) str8((U8*)(S), sizeof(S)) -#define str8_struct(S) str8((U8*)(S), sizeof(*(S))) - -internal String8 str8(U8 *str, U64 size); -internal String8 str8_range(U8 *first, U8 *one_past_last); -internal String8 str8_zero(void); -internal String16 str16(U16 *str, U64 size); -internal String16 str16_range(U16 *first, U16 *one_past_last); -internal String16 str16_zero(void); -internal String32 str32(U32 *str, U64 size); -internal String32 str32_range(U32 *first, U32 *one_past_last); -internal String32 str32_zero(void); -internal String8 str8_cstring(char *c); -internal String16 str16_cstring(U16 *c); -internal String32 str32_cstring(U32 *c); -internal String8 str8_cstring_capped(void *cstr, void *cap); - -//////////////////////////////// -//~ rjf: String Stylization - -internal String8 upper_from_str8(Arena *arena, String8 string); -internal String8 lower_from_str8(Arena *arena, String8 string); -internal String8 backslashed_from_str8(Arena *arena, String8 string); - -//////////////////////////////// -//~ rjf: String Matching - -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 B32 str8_ends_with(String8 string, String8 end, StringMatchFlags flags); - -//////////////////////////////// -//~ rjf: String Slicing - -internal String8 str8_substr(String8 str, Rng1U64 range); -internal String8 str8_prefix(String8 str, U64 size); -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); - -//////////////////////////////// -//~ rjf: String Formatting & Copying - -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 - -//- rjf: string -> integer -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 B32 try_u64_from_str8_c_rules(String8 string, U64 *x); -internal B32 try_s64_from_str8_c_rules(String8 string, S64 *x); - -//- rjf: integer -> string -internal String8 str8_from_memory_size(Arena *arena, U64 z); -internal String8 str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator); -internal String8 str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator); - -//////////////////////////////// -//~ rjf: String <=> Float Conversions - -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 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, ...); -internal String8Node* str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...); -internal String8List str8_list_copy(Arena *arena, String8List *list); -#define str8_list_first(list) ((list)->first ? (list)->first->string : str8_zero()) - -//////////////////////////////// -//~ rjf: String Splitting & Joining - -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); -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_from_list(Arena *arena, String8List *list); -internal String8Array str8_array_reserve(Arena *arena, U64 count); - -//////////////////////////////// -//~ rjf: String Path Helpers - -internal String8 str8_chop_last_slash(String8 string); -internal String8 str8_skip_last_slash(String8 string); -internal String8 str8_chop_last_dot(String8 string); -internal String8 str8_skip_last_dot(String8 string); - -internal PathStyle path_style_from_str8(String8 string); -internal String8List str8_split_path(Arena *arena, String8 string); -internal void str8_path_list_resolve_dots_in_place(String8List *path, PathStyle style); -internal String8 str8_path_list_join_by_style(Arena *arena, String8List *path, PathStyle style); - -internal String8TxtPtPair str8_txt_pt_pair_from_string(String8 string); - -//////////////////////////////// -//~ rjf: UTF-8 & UTF-16 Decoding/Encoding - -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 - -internal String8 str8_from_16(Arena *arena, String16 in); -internal String16 str16_from_8(Arena *arena, String8 in); -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 - -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_architecture(Architecture arch); - -//////////////////////////////// -//~ rjf: Time Types -> String - -internal String8 string_from_week_day(WeekDay week_day); -internal String8 string_from_month(Month month); -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: Basic Text Indentation - -internal String8 indented_from_string(Arena *arena, String8 string); - -//////////////////////////////// -//~ rjf: Text Wrapping - -internal String8List wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent); - -//////////////////////////////// -//~ rjf: String <-> Color - -internal String8 hex_string_from_rgba_4f32(Arena *arena, Vec4F32 rgba); -internal Vec4F32 rgba_from_hex_string_4f32(String8 hex_string); - -//////////////////////////////// -//~ rjf: String Fuzzy Matching - -internal FuzzyMatchRangeList fuzzy_match_find(Arena *arena, String8 needle, String8 haystack); -internal FuzzyMatchRangeList fuzzy_match_range_list_copy(Arena *arena, FuzzyMatchRangeList *src); - -//////////////////////////////// -//~ NOTE(allen): Serialization Helpers - -internal void str8_serial_begin(Arena *arena, String8List *srl); -internal String8 str8_serial_end(Arena *arena, String8List *srl); -internal void str8_serial_write_to_dst(String8List *srl, void *out); -internal U64 str8_serial_push_align(Arena *arena, String8List *srl, U64 align); -internal void * str8_serial_push_size(Arena *arena, String8List *srl, U64 size); -internal void * str8_serial_push_data(Arena *arena, String8List *srl, void *data, U64 size); -internal void str8_serial_push_data_list(Arena *arena, String8List *srl, String8Node *first); -internal void str8_serial_push_u64(Arena *arena, String8List *srl, U64 x); -internal void str8_serial_push_u32(Arena *arena, String8List *srl, U32 x); -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); -#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) - -//////////////////////////////// -//~ rjf: Deserialization Helpers - -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 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((string), (off), (ptr), sizeof(*(ptr)), sizeof(*(ptr))) - -#endif // BASE_STRINGS_H +// 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 + +typedef struct String8 String8; +struct String8 +{ + U8 *str; + U64 size; +}; + +typedef struct String16 String16; +struct String16 +{ + U16 *str; + U64 size; +}; + +typedef struct String32 String32; +struct String32 +{ + U32 *str; + U64 size; +}; + +//////////////////////////////// +//~ rjf: String List & Array Types + +typedef struct String8Node String8Node; +struct String8Node +{ + String8Node *next; + String8 string; +}; + +typedef struct String8MetaNode String8MetaNode; +struct String8MetaNode +{ + String8MetaNode *next; + String8Node *node; +}; + +typedef struct String8List String8List; +struct String8List +{ + String8Node *first; + String8Node *last; + U64 node_count; + U64 total_size; +}; + +typedef struct String8Array String8Array; +struct String8Array +{ + String8 *v; + U64 count; +}; + +//////////////////////////////// +//~ rjf: String Matching, Splitting, & Joining Types + +typedef U32 StringMatchFlags; +enum +{ + StringMatchFlag_CaseInsensitive = (1 << 0), + StringMatchFlag_RightSideSloppy = (1 << 1), + StringMatchFlag_SlashInsensitive = (1 << 2), +}; + +typedef U32 StringSplitFlags; +enum +{ + StringSplitFlag_KeepEmpties = (1 << 0), +}; + +typedef enum PathStyle +{ + PathStyle_Relative, + PathStyle_WindowsAbsolute, + PathStyle_UnixAbsolute, + +#if OS_WINDOWS + PathStyle_SystemAbsolute = PathStyle_WindowsAbsolute +#elif OS_LINUX + PathStyle_SystemAbsolute = PathStyle_UnixAbsolute +#else +# error "absolute path style is undefined for this OS" +#endif +} +PathStyle; + +typedef struct StringJoin StringJoin; +struct StringJoin +{ + String8 pre; + String8 sep; + String8 post; +}; + +//////////////////////////////// +//~ rjf: String Pair Types + +typedef struct String8TxtPtPair String8TxtPtPair; +struct String8TxtPtPair +{ + String8 string; + TxtPt pt; +}; + +//////////////////////////////// +//~ rjf: UTF Decoding Types + +typedef struct UnicodeDecode UnicodeDecode; +struct UnicodeDecode +{ + U32 inc; + U32 codepoint; +}; + +//////////////////////////////// +//~ rjf: String Fuzzy Matching Types + +typedef struct FuzzyMatchRangeNode FuzzyMatchRangeNode; +struct FuzzyMatchRangeNode +{ + FuzzyMatchRangeNode *next; + Rng1U64 range; +}; + +typedef struct FuzzyMatchRangeList FuzzyMatchRangeList; +struct FuzzyMatchRangeList +{ + FuzzyMatchRangeNode *first; + FuzzyMatchRangeNode *last; + U64 count; + U64 needle_part_count; + U64 total_dim; +}; + +//////////////////////////////// +//~ rjf: Character Classification & Conversion Functions + +internal B32 char_is_space(U8 c); +internal B32 char_is_upper(U8 c); +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 char_to_lower(U8 c); +internal U8 char_to_upper(U8 c); +internal U8 char_to_correct_slash(U8 c); + +//////////////////////////////// +//~ rjf: C-String Measurement + +internal U64 cstring8_length(U8 *c); +internal U64 cstring16_length(U16 *c); +internal U64 cstring32_length(U32 *c); + +//////////////////////////////// +//~ rjf: String Constructors + +#define str8_lit(S) str8((U8*)(S), sizeof(S) - 1) +#define str8_lit_comp(S) {(U8*)(S), sizeof(S) - 1,} +#define str8_varg(S) (int)((S).size), ((S).str) + +#define str8_array(S,C) str8((U8*)(S), sizeof(*(S))*(C)) +#define str8_array_fixed(S) str8((U8*)(S), sizeof(S)) +#define str8_struct(S) str8((U8*)(S), sizeof(*(S))) + +internal String8 str8(U8 *str, U64 size); +internal String8 str8_range(U8 *first, U8 *one_past_last); +internal String8 str8_zero(void); +internal String16 str16(U16 *str, U64 size); +internal String16 str16_range(U16 *first, U16 *one_past_last); +internal String16 str16_zero(void); +internal String32 str32(U32 *str, U64 size); +internal String32 str32_range(U32 *first, U32 *one_past_last); +internal String32 str32_zero(void); +internal String8 str8_cstring(char *c); +internal String16 str16_cstring(U16 *c); +internal String32 str32_cstring(U32 *c); +internal String8 str8_cstring_capped(void *cstr, void *cap); + +//////////////////////////////// +//~ rjf: String Stylization + +internal String8 upper_from_str8(Arena *arena, String8 string); +internal String8 lower_from_str8(Arena *arena, String8 string); +internal String8 backslashed_from_str8(Arena *arena, String8 string); + +//////////////////////////////// +//~ rjf: String Matching + +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 B32 str8_ends_with(String8 string, String8 end, StringMatchFlags flags); + +//////////////////////////////// +//~ rjf: String Slicing + +internal String8 str8_substr(String8 str, Rng1U64 range); +internal String8 str8_prefix(String8 str, U64 size); +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); + +//////////////////////////////// +//~ rjf: String Formatting & Copying + +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 + +//- rjf: string -> integer +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 B32 try_u64_from_str8_c_rules(String8 string, U64 *x); +internal B32 try_s64_from_str8_c_rules(String8 string, S64 *x); + +//- rjf: integer -> string +internal String8 str8_from_memory_size(Arena *arena, U64 z); +internal String8 str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator); +internal String8 str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator); + +//////////////////////////////// +//~ rjf: String <=> Float Conversions + +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 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, ...); +internal String8Node* str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...); +internal String8List str8_list_copy(Arena *arena, String8List *list); +#define str8_list_first(list) ((list)->first ? (list)->first->string : str8_zero()) + +//////////////////////////////// +//~ rjf: String Splitting & Joining + +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); +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_from_list(Arena *arena, String8List *list); +internal String8Array str8_array_reserve(Arena *arena, U64 count); + +//////////////////////////////// +//~ rjf: String Path Helpers + +internal String8 str8_chop_last_slash(String8 string); +internal String8 str8_skip_last_slash(String8 string); +internal String8 str8_chop_last_dot(String8 string); +internal String8 str8_skip_last_dot(String8 string); + +internal PathStyle path_style_from_str8(String8 string); +internal String8List str8_split_path(Arena *arena, String8 string); +internal void str8_path_list_resolve_dots_in_place(String8List *path, PathStyle style); +internal String8 str8_path_list_join_by_style(Arena *arena, String8List *path, PathStyle style); + +internal String8TxtPtPair str8_txt_pt_pair_from_string(String8 string); + +//////////////////////////////// +//~ rjf: UTF-8 & UTF-16 Decoding/Encoding + +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 + +internal String8 str8_from_16(Arena *arena, String16 in); +internal String16 str16_from_8(Arena *arena, String8 in); +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 + +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_architecture(Architecture arch); + +//////////////////////////////// +//~ rjf: Time Types -> String + +internal String8 string_from_week_day(WeekDay week_day); +internal String8 string_from_month(Month month); +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: Basic Text Indentation + +internal String8 indented_from_string(Arena *arena, String8 string); + +//////////////////////////////// +//~ rjf: Text Wrapping + +internal String8List wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent); + +//////////////////////////////// +//~ rjf: String <-> Color + +internal String8 hex_string_from_rgba_4f32(Arena *arena, Vec4F32 rgba); +internal Vec4F32 rgba_from_hex_string_4f32(String8 hex_string); + +//////////////////////////////// +//~ rjf: String Fuzzy Matching + +internal FuzzyMatchRangeList fuzzy_match_find(Arena *arena, String8 needle, String8 haystack); +internal FuzzyMatchRangeList fuzzy_match_range_list_copy(Arena *arena, FuzzyMatchRangeList *src); + +//////////////////////////////// +//~ NOTE(allen): Serialization Helpers + +internal void str8_serial_begin(Arena *arena, String8List *srl); +internal String8 str8_serial_end(Arena *arena, String8List *srl); +internal void str8_serial_write_to_dst(String8List *srl, void *out); +internal U64 str8_serial_push_align(Arena *arena, String8List *srl, U64 align); +internal void * str8_serial_push_size(Arena *arena, String8List *srl, U64 size); +internal void * str8_serial_push_data(Arena *arena, String8List *srl, void *data, U64 size); +internal void str8_serial_push_data_list(Arena *arena, String8List *srl, String8Node *first); +internal void str8_serial_push_u64(Arena *arena, String8List *srl, U64 x); +internal void str8_serial_push_u32(Arena *arena, String8List *srl, U32 x); +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); +#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) + +//////////////////////////////// +//~ rjf: Deserialization Helpers + +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 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((string), (off), (ptr), sizeof(*(ptr)), sizeof(*(ptr))) + +#endif // BASE_STRINGS_H diff --git a/src/base/base_thread_context.c b/src/base/base_thread_context.c index 45ab7afe..1a3f84ed 100644 --- a/src/base/base_thread_context.c +++ b/src/base/base_thread_context.c @@ -1,87 +1,87 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -// NOTE(allen): Thread Context Functions - -C_LINKAGE thread_static TCTX* tctx_thread_local; -#if !BUILD_SUPPLEMENTARY_UNIT -C_LINKAGE thread_static TCTX* tctx_thread_local = 0; -#endif - -internal void -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 void -tctx_release(void) -{ - for(U64 i = 0; i < ArrayCount(tctx_thread_local->arenas); i += 1) - { - arena_release(tctx_thread_local->arenas[i]); - } -} - -internal TCTX* -tctx_get_equipped(void){ - return(tctx_thread_local); -} - -internal Arena* -tctx_get_scratch(Arena **conflicts, U64 count){ - TCTX *tctx = tctx_get_equipped(); - - Arena *result = 0; - Arena **arena_ptr = tctx->arenas; - 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){ - has_conflict = 1; - break; - } - } - if (!has_conflict){ - result = *arena_ptr; - break; - } - } - - return(result); -} - -internal void -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_get_equipped(); - String8 result = str8(tctx->thread_name, tctx->thread_name_size); - return(result); -} - -internal void -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_get_equipped(); - *file_name = tctx->file_name; - *line_number = tctx->line_number; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +// NOTE(allen): Thread Context Functions + +C_LINKAGE thread_static TCTX* tctx_thread_local; +#if !BUILD_SUPPLEMENTARY_UNIT +C_LINKAGE thread_static TCTX* tctx_thread_local = 0; +#endif + +internal void +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 void +tctx_release(void) +{ + for(U64 i = 0; i < ArrayCount(tctx_thread_local->arenas); i += 1) + { + arena_release(tctx_thread_local->arenas[i]); + } +} + +internal TCTX* +tctx_get_equipped(void){ + return(tctx_thread_local); +} + +internal Arena* +tctx_get_scratch(Arena **conflicts, U64 count){ + TCTX *tctx = tctx_get_equipped(); + + Arena *result = 0; + Arena **arena_ptr = tctx->arenas; + 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){ + has_conflict = 1; + break; + } + } + if (!has_conflict){ + result = *arena_ptr; + break; + } + } + + return(result); +} + +internal void +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_get_equipped(); + String8 result = str8(tctx->thread_name, tctx->thread_name_size); + return(result); +} + +internal void +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_get_equipped(); + *file_name = tctx->file_name; + *line_number = tctx->line_number; +} diff --git a/src/codeview/codeview.mdesk b/src/codeview/codeview.mdesk index e004557c..235ef75b 100644 --- a/src/codeview/codeview.mdesk +++ b/src/codeview/codeview.mdesk @@ -1,622 +1,622 @@ -//////////////////////////////// -//~ rjf: CV Numerics - -@table(name val) -CV_NumericKindTable: -{ - {CHAR 0x8000} - {SHORT 0x8001} - {USHORT 0x8002} - {LONG 0x8003} - {ULONG 0x8004} - {FLOAT32 0x8005} - {FLOAT64 0x8006} - {FLOAT80 0x8007} - {FLOAT128 0x8008} - {QUADWORD 0x8009} - {UQUADWORD 0x800a} - {FLOAT48 0x800b} - {COMPLEX32 0x800c} - {COMPLEX64 0x800d} - {COMPLEX80 0x800e} - {COMPLEX128 0x800f} - {VARSTRING 0x8010} - {OCTWORD 0x8017} - {UOCTWORD 0x8018} - {DECIMAL 0x8019} - {DATE 0x801a} - {UTF8STRING 0x801b} - {FLOAT16 0x801c} -} - -@enum(U16) CV_NumericKind: -{ - @expand(CV_NumericKindTable a) `$(a.name) = $(a.val)` -} - -@enum2string_switch(CV_NumericKind) -cv_string_from_numeric_kind: -{ - @expand(CV_NumericKindTable a) `case CV_NumericKind_$(a.name):{result = str8_lit("$(a.name)");}break`; -} - -//////////////////////////////// -//~ rjf: CV Architectures - -@table(name val) -CV_ArchTable: -{ - {8080 0x00} - {8086 0x01} - {80286 0x02} - {80386 0x03} - {80486 0x04} - {PENTIUM 0x05} - {PENTIUMII 0x06} - {PENTIUMIII 0x07} - {MIPS 0x10} - {MIPS16 0x11} - {MIPS32 0x12} - {MIPS64 0x13} - {MIPSI 0x14} - {MIPSII 0x15} - {MIPSIII 0x16} - {MIPSIV 0x17} - {MIPSV 0x18} - {M68000 0x20} - {M68010 0x21} - {M68020 0x22} - {M68030 0x23} - {M68040 0x24} - {ALPHA 0x30} - {ALPHA_21164 0x31} - {ALPHA_21164A 0x32} - {ALPHA_21264 0x33} - {ALPHA_21364 0x34} - {PPC601 0x40} - {PPC603 0x41} - {PPC604 0x42} - {PPC620 0x43} - {PPCFP 0x44} - {PPCBE 0x45} - {SH3 0x50} - {SH3E 0x51} - {SH3DSP 0x52} - {SH4 0x53} - {SHMEDIA 0x54} - {ARM3 0x60} - {ARM4 0x61} - {ARM4T 0x62} - {ARM5 0x63} - {ARM5T 0x64} - {ARM6 0x65} - {ARM_XMAC 0x66} - {ARM_WMMX 0x67} - {ARM7 0x68} - {OMNI 0x70} - {IA64_1 0x80} - {IA64_2 0x81} - {CEE 0x90} - {AM33 0xA0} - {M32R 0xB0} - {TRICORE 0xC0} - {X64 0xD0} - {EBC 0xE0} - {THUMB 0xF0} - {ARMNT 0xF4} - {ARM64 0xF6} - {D3D11_SHADER 0x100} -} - -@enum(U16) CV_Arch: -{ - @expand(CV_ArchTable a) `$(a.name) = $(a.val)`, - `IA64 = CV_Arch_IA64_1`, - `PENTIUMPRO = CV_Arch_PENTIUMII`, - `MIPSR4000 = CV_Arch_MIPS`, - `ALPHA_21064 = CV_Arch_ALPHA`, - `AMD64 = CV_Arch_X64`, -} - -@enum2string_switch(CV_Arch) -cv_string_from_arch: -{ - @expand(CV_ArchTable a) `case CV_Arch_$(a.name):{result = str8_lit("$(a.name)");}break`; -} - -//////////////////////////////// -//~ rjf: CV Registers - -@table(name val) CV_AllRegTable: -{ - {ERR 30000} - {TEB 30001} - {TIMER 30002} - {EFAD1 30003} - {EFAD2 30004} - {EFAD3 30005} - {VFRAME 30006} - {HANDLE 30007} - {PARAMS 30008} - {LOCALS 30009} - {TID 30010} - {ENV 30011} - {CMDLN 30012} -} - -@enum(U16) CV_AllReg: -{ - @expand(CV_AllRegTable a) `$(a.name) = $(a.val)` -} - -//////////////////////////////// -//~ rjf: CV Sym Kinds - -@table(name header_type_name val) CV_SymKindTable: -{ - {COMPILE Compile 0x0001} - {REGISTER_16t - 0x0002} - {CONSTANT_16t - 0x0003} - {UDT_16t - 0x0004} - {SSEARCH StartSearch 0x0005} - {END - 0x0006} - {SKIP - 0x0007} - {CVRESERVE - 0x0008} - {OBJNAME_ST - 0x0009} - {ENDARG - 0x000a} - {COBOLUDT_16t - 0x000b} - {MANYREG_16t - 0x000c} - {RETURN Return 0x000d} - {ENTRYTHIS - 0x000e} - {BPREL16 - 0x0100} - {LDATA16 - 0x0101} - {GDATA16 - 0x0102} - {PUB16 - 0x0103} - {LPROC16 - 0x0104} - {GPROC16 - 0x0105} - {THUNK16 - 0x0106} - {BLOCK16 - 0x0107} - {WITH16 - 0x0108} - {LABEL16 - 0x0109} - {CEXMODEL16 - 0x010a} - {VFTABLE16 - 0x010b} - {REGREL16 - 0x010c} - {BPREL32_16t - 0x0200} - {LDATA32_16t - 0x0201} - {GDATA32_16t - 0x0202} - {PUB32_16t - 0x0203} - {LPROC32_16t - 0x0204} - {GPROC32_16t - 0x0205} - {THUNK32_ST - 0x0206} - {BLOCK32_ST - 0x0207} - {WITH32_ST - 0x0208} - {LABEL32_ST - 0x0209} - {CEXMODEL32 - 0x020a} - {VFTABLE32_16t - 0x020b} - {REGREL32_16t - 0x020c} - {LTHREAD32_16t - 0x020d} - {GTHREAD32_16t - 0x020e} - {SLINK32 SLink32 0x020f} - {LPROCMIPS_16t - 0x0300} - {GPROCMIPS_16t - 0x0301} - {PROCREF_ST - 0x0400} - {DATAREF_ST - 0x0401} - {ALIGN - 0x0402} - {LPROCREF_ST - 0x0403} - {OEM OEM 0x0404} - {TI16_MAX - 0x1000} - {CONSTANT_ST - 0x1002} - {UDT_ST - 0x1003} - {COBOLUDT_ST - 0x1004} - {MANYREG_ST - 0x1005} - {BPREL32_ST - 0x1006} - {LDATA32_ST - 0x1007} - {GDATA32_ST - 0x1008} - {PUB32_ST - 0x1009} - {LPROC32_ST - 0x100a} - {GPROC32_ST - 0x100b} - {VFTABLE32 VPath32 0x100c} - {REGREL32_ST - 0x100d} - {LTHREAD32_ST - 0x100e} - {GTHREAD32_ST - 0x100f} - {LPROCMIPS_ST - 0x1010} - {GPROCMIPS_ST - 0x1011} - {FRAMEPROC Frameproc 0x1012} - {COMPILE2_ST - 0x1013} - {MANYREG2_ST - 0x1014} - {LPROCIA64_ST - 0x1015} - {GPROCIA64_ST - 0x1016} - {LOCALSLOT_ST - 0x1017} - {PARAMSLOT_ST - 0x1018} - {ANNOTATION Annotation 0x1019} - {GMANPROC_ST - 0x101a} - {LMANPROC_ST - 0x101b} - {RESERVED1 - 0x101c} - {RESERVED2 - 0x101d} - {RESERVED3 - 0x101e} - {RESERVED4 - 0x101f} - {LMANDATA_ST - 0x1020} - {GMANDATA_ST - 0x1021} - {MANFRAMEREL_ST - 0x1022} - {MANREGISTER_ST - 0x1023} - {MANSLOT_ST - 0x1024} - {MANMANYREG_ST - 0x1025} - {MANREGREL_ST - 0x1026} - {MANMANYREG2_ST - 0x1027} - {MANTYPREF - 0x1028} - {UNAMESPACE_ST - 0x1029} - {ST_MAX - 0x1100} - {OBJNAME Objname 0x1101} - {THUNK32 Thunk32 0x1102} - {BLOCK32 Block32 0x1103} - {WITH32 - 0x1104} - {LABEL32 Label32 0x1105} - {REGISTER Register 0x1106} - {CONSTANT Constant 0x1107} - {UDT UDT 0x1108} - {COBOLUDT - 0x1109} - {MANYREG Manyreg 0x110a} - {BPREL32 BPRel32 0x110b} - {LDATA32 Data32 0x110c} - {GDATA32 Data32 0x110d} - {PUB32 Pub32 0x110e} - {LPROC32 Proc32 0x110f} - {GPROC32 Proc32 0x1110} - {REGREL32 Regrel32 0x1111} - {LTHREAD32 Thread32 0x1112} - {GTHREAD32 Thread32 0x1113} - {LPROCMIPS - 0x1114} - {GPROCMIPS - 0x1115} - {COMPILE2 Compile2 0x1116} - {MANYREG2 Manyreg2 0x1117} - {LPROCIA64 - 0x1118} - {GPROCIA64 - 0x1119} - {LOCALSLOT Slot 0x111a} - {PARAMSLOT - 0x111b} - {LMANDATA - 0x111c} - {GMANDATA - 0x111d} - {MANFRAMEREL AttrFrameRel 0x111e} - {MANREGISTER AttrReg 0x111f} - {MANSLOT - 0x1120} - {MANMANYREG AttrManyReg 0x1121} - {MANREGREL AttrRegRel 0x1122} - {MANMANYREG2 - 0x1123} - {UNAMESPACE UNamespace 0x1124} - {PROCREF Ref2 0x1125} - {DATAREF Ref2 0x1126} - {LPROCREF Ref2 0x1127} - {ANNOTATIONREF - 0x1128} - {TOKENREF - 0x1129} - {GMANPROC - 0x112a} - {LMANPROC - 0x112b} - {TRAMPOLINE Trampoline 0x112c} - {MANCONSTANT - 0x112d} - {ATTR_FRAMEREL AttrFrameRel 0x112e} - {ATTR_REGISTER AttrReg 0x112f} - {ATTR_REGREL AttrRegRel 0x1130} - {ATTR_MANYREG AttrManyReg 0x1131} - {SEPCODE Sepcode 0x1132} - {DEFRANGE_2005 - 0x1134} - {DEFRANGE2_2005 - 0x1135} - {SECTION Section 0x1136} - {COFFGROUP CoffGroup 0x1137} - {EXPORT Export 0x1138} - {CALLSITEINFO CallSiteInfo 0x1139} - {FRAMECOOKIE FrameCookie 0x113a} - {DISCARDED Discarded 0x113b} - {COMPILE3 Compile3 0x113c} - {ENVBLOCK EnvBlock 0x113d} - {LOCAL Local 0x113e} - {DEFRANGE - 0x113f} - {DEFRANGE_SUBFIELD DefrangeSubfield 0x1140} - {DEFRANGE_REGISTER DefrangeRegister 0x1141} - {DEFRANGE_FRAMEPOINTER_REL DefrangeFramepointerRel 0x1142} - {DEFRANGE_SUBFIELD_REGISTER DefrangeSubfieldRegister 0x1143} - {DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE DefrangeFramepointerRelFullScope 0x1144} - {DEFRANGE_REGISTER_REL DefrangeRegisterRel 0x1145} - {LPROC32_ID - 0x1146} - {GPROC32_ID - 0x1147} - {LPROCMIPS_ID - 0x1148} - {GPROCMIPS_ID - 0x1149} - {LPROCIA64_ID - 0x114a} - {GPROCIA64_ID - 0x114b} - {BUILDINFO BuildInfo 0x114c} - {INLINESITE InlineSite 0x114d} - {INLINESITE_END - 0x114e} - {PROC_ID_END - 0x114f} - {DEFRANGE_HLSL - 0x1150} - {GDATA_HLSL - 0x1151} - {LDATA_HLSL - 0x1152} - {FILESTATIC FileStatic 0x1153} - {LPROC32_DPC - 0x1155} - {LPROC32_DPC_ID - 0x1156} - {DEFRANGE_DPC_PTR_TAG - 0x1157} - {DPC_SYM_TAG_MAP - 0x1158} - {ARMSWITCHTABLE - 0x1159} - {CALLEES FunctionList 0x115a} - {CALLERS FunctionList 0x115b} - {POGODATA PogoInfo 0x115c} - {INLINESITE2 InlineSite2 0x115d} - {HEAPALLOCSITE HeapAllocSite 0x115e} - {MOD_TYPEREF ModTypeRef 0x115f} - {REF_MINIPDB RefMiniPdb 0x1160} - {PDBMAP - 0x1161} - {GDATA_HLSL32 - 0x1162} - {LDATA_HLSL32 - 0x1163} - {GDATA_HLSL32_EX - 0x1164} - {LDATA_HLSL32_EX - 0x1165} - {FASTLINK FastLink 0x1167} - {INLINEES Inlinees 0x1168} -} - -@enum(U16) CV_SymKind: -{ - @expand(CV_SymKindTable a) `$(a.name) = $(a.val)` -} - -@enum2string_switch(CV_SymKind) -cv_string_from_sym_kind: -{ - @expand(CV_SymKindTable a) `case CV_SymKind_$(a.name):{result = str8_lit("$(a.name)");}break`; -} - -@gen(functions) -{ - `internal U64 cv_header_struct_size_from_sym_kind(CV_SymKind v);`; -} - -@gen(functions) @c_file -{ - `internal U64`; - `cv_header_struct_size_from_sym_kind(CV_SymKind v)`; - `{`; - `U64 result = 0;`; - `switch(v)`; - `{`; - `default:{}break;`; - @expand(CV_SymKindTable a) `$(a.header_type_name != "-" -> "case CV_SymKind_"..a.name..":{result = sizeof(CV_Sym"..a.header_type_name..");}break;")`; - `}`; - `return result;`; - `}`; -} - -//////////////////////////////// -//~ rjf: CV Basic Types - -@table(name val type_name) -CV_BasicTypeTable: -{ - {NOTYPE 0x00 "" } - {ABS 0x01 "" } - {SEGMENT 0x02 "" } - {VOID 0x03 "void" } - {CURRENCY 0x04 "" } - {NBASICSTR 0x05 "" } - {FBASICSTR 0x06 "" } - {NOTTRANS 0x07 "" } - {HRESULT 0x08 "HRESULT" } - {CHAR 0x10 "char" } - {SHORT 0x11 "S16" } - {LONG 0x12 "S32" } - {QUAD 0x13 "S64" } - {OCT 0x14 "S128" } - {UCHAR 0x20 "UCHAR" } - {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 "B32FF" } - {INT8 0x68 "S8" } - {UINT8 0x69 "U8" } - {RCHAR 0x70 "char" } - {WCHAR 0x71 "WCHAR" } - {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" } -} - -@enum(U8) CV_BasicType: -{ - @expand(CV_BasicTypeTable a) `$(a.name) = $(a.val)` -} - -@enum2string_switch(CV_BasicType) cv_string_from_basic_type: -{ - @expand(CV_BasicTypeTable a) `case CV_BasicType_$(a.name):{result = str8_lit("$(a.name)");}break` -} - -@enum2string_switch(CV_BasicType) cv_type_name_from_basic_type: -{ - @expand(CV_BasicTypeTable a) `case CV_BasicType_$(a.name):{result = str8_lit("$(a.type_name)");}break` -} - -//////////////////////////////// -//~ rjf: CV Leaf Kinds - -@table(name header_type_name val) -CV_LeafKindTable: -{ - {MODIFIER_16t - 0x0001} - {POINTER_16t - 0x0002} - {ARRAY_16t - 0x0003} - {CLASS_16t - 0x0004} - {STRUCTURE_16t - 0x0005} - {UNION_16t - 0x0006} - {ENUM_16t - 0x0007} - {PROCEDURE_16t - 0x0008} - {MFUNCTION_16t - 0x0009} - {VTSHAPE VTShape 0x000a} - {COBOL0_16t - 0x000b} - {COBOL1 - 0x000c} - {BARRAY_16t - 0x000d} - {LABEL Label 0x000e} - {NULL - 0x000f} - {NOTTRAN - 0x0010} - {DIMARRAY_16t - 0x0011} - {VFTPATH_16t - 0x0012} - {PRECOMP_16t - 0x0013} - {ENDPRECOMP - 0x0014} - {OEM_16t - 0x0015} - {TYPESERVER_ST - 0x0016} - {SKIP_16t - 0x0200} - {ARGLIST_16t - 0x0201} - {DEFARG_16t - 0x0202} - {LIST - 0x0203} - {FIELDLIST_16t - 0x0204} - {DERIVED_16t - 0x0205} - {BITFIELD_16t - 0x0206} - {METHODLIST_16t - 0x0207} - {DIMCONU_16t - 0x0208} - {DIMCONLU_16t - 0x0209} - {DIMVARU_16t - 0x020a} - {DIMVARLU_16t - 0x020b} - {REFSYM - 0x020c} - {BCLASS_16t - 0x0400} - {VBCLASS_16t - 0x0401} - {IVBCLASS_16t - 0x0402} - {ENUMERATE_ST - 0x0403} - {FRIENDFCN_16t - 0x0404} - {INDEX_16t - 0x0405} - {MEMBER_16t - 0x0406} - {STMEMBER_16t - 0x0407} - {METHOD_16t - 0x0408} - {NESTTYPE_16t - 0x0409} - {VFUNCTAB_16t - 0x040a} - {FRIENDCLS_16t - 0x040b} - {ONEMETHOD_16t - 0x040c} - {VFUNCOFF_16t - 0x040d} - {TI16_MAX - 0x1000} - {MODIFIER Modifier 0x1001} - {POINTER Pointer 0x1002} - {ARRAY_ST - 0x1003} - {CLASS_ST - 0x1004} - {STRUCTURE_ST - 0x1005} - {UNION_ST - 0x1006} - {ENUM_ST - 0x1007} - {PROCEDURE Procedure 0x1008} - {MFUNCTION MFunction 0x1009} - {COBOL0 - 0x100a} - {BARRAY - 0x100b} - {DIMARRAY_ST - 0x100c} - {VFTPATH VFPath 0x100d} - {PRECOMP_ST - 0x100e} - {OEM - 0x100f} - {ALIAS_ST - 0x1010} - {OEM2 - 0x1011} - {SKIP Skip 0x1200} - {ARGLIST ArgList 0x1201} - {DEFARG_ST - 0x1202} - {FIELDLIST - 0x1203} - {DERIVED - 0x1204} - {BITFIELD BitField 0x1205} - {METHODLIST MethodListMember 0x1206} - {DIMCONU - 0x1207} - {DIMCONLU - 0x1208} - {DIMVARU - 0x1209} - {DIMVARLU - 0x120a} - {BCLASS BClass 0x1400} - {VBCLASS VBClass 0x1401} - {IVBCLASS - 0x1402} - {FRIENDFCN_ST - 0x1403} - {INDEX Index 0x1404} - {MEMBER_ST - 0x1405} - {STMEMBER_ST - 0x1406} - {METHOD_ST - 0x1407} - {NESTTYPE_ST - 0x1408} - {VFUNCTAB VFuncTab 0x1409} - {FRIENDCLS - 0x140a} - {ONEMETHOD_ST - 0x140b} - {VFUNCOFF VFuncOff 0x140c} - {NESTTYPEEX_ST - 0x140d} - {MEMBERMODIFY_ST - 0x140e} - {MANAGED_ST - 0x140f} - {ST_MAX - 0x1500} - {TYPESERVER TypeServer 0x1501} - {ENUMERATE Enumerate 0x1502} - {ARRAY Array 0x1503} - {CLASS Struct 0x1504} - {STRUCTURE Struct 0x1505} - {UNION Union 0x1506} - {ENUM Enum 0x1507} - {DIMARRAY - 0x1508} - {PRECOMP PreComp 0x1509} - {ALIAS Alias 0x150a} - {DEFARG - 0x150b} - {FRIENDFCN - 0x150c} - {MEMBER Member 0x150d} - {STMEMBER StMember 0x150e} - {METHOD Method 0x150f} - {NESTTYPE NestType 0x1510} - {ONEMETHOD OneMethod 0x1511} - {NESTTYPEEX NestTypeEx 0x1512} - {MEMBERMODIFY - 0x1513} - {MANAGED - 0x1514} - {TYPESERVER2 TypeServer2 0x1515} - {STRIDED_ARRAY - 0x1516} - {HLSL - 0x1517} - {MODIFIER_EX - 0x1518} - {INTERFACE Struct 0x1519} - {BINTERFACE - 0x151a} - {VECTOR - 0x151b} - {MATRIX - 0x151c} - {VFTABLE - 0x151d} - {CLASS2 Struct2 0x1608} - {STRUCT2 Struct2 0x1609} -} - -@enum(U16) CV_LeafKind: -{ - @expand(CV_LeafKindTable a) `$(a.name) = $(a.val)`; -} - -@enum2string_switch(CV_LeafKind) -cv_string_from_leaf_kind: -{ - @expand(CV_LeafKindTable a) `case CV_LeafKind_$(a.name):{result = str8_lit("$(a.name)");}break`; -} - -@gen(functions) -{ - `internal U64 cv_header_struct_size_from_leaf_kind(CV_LeafKind v);`; -} - -@gen(functions) @c_file -{ - `internal U64`; - `cv_header_struct_size_from_leaf_kind(CV_LeafKind v)`; - `{`; - `U64 result = 0;`; - `switch(v)`; - `{`; - `default:{}break;`; - @expand(CV_LeafKindTable a) `$(a.header_type_name != "-" -> "case CV_LeafKind_"..a.name..":{result = sizeof(CV_Leaf"..a.header_type_name..");}break;")`; - `}`; - `return result;`; - `}`; -} +//////////////////////////////// +//~ rjf: CV Numerics + +@table(name val) +CV_NumericKindTable: +{ + {CHAR 0x8000} + {SHORT 0x8001} + {USHORT 0x8002} + {LONG 0x8003} + {ULONG 0x8004} + {FLOAT32 0x8005} + {FLOAT64 0x8006} + {FLOAT80 0x8007} + {FLOAT128 0x8008} + {QUADWORD 0x8009} + {UQUADWORD 0x800a} + {FLOAT48 0x800b} + {COMPLEX32 0x800c} + {COMPLEX64 0x800d} + {COMPLEX80 0x800e} + {COMPLEX128 0x800f} + {VARSTRING 0x8010} + {OCTWORD 0x8017} + {UOCTWORD 0x8018} + {DECIMAL 0x8019} + {DATE 0x801a} + {UTF8STRING 0x801b} + {FLOAT16 0x801c} +} + +@enum(U16) CV_NumericKind: +{ + @expand(CV_NumericKindTable a) `$(a.name) = $(a.val)` +} + +@enum2string_switch(CV_NumericKind) +cv_string_from_numeric_kind: +{ + @expand(CV_NumericKindTable a) `case CV_NumericKind_$(a.name):{result = str8_lit("$(a.name)");}break`; +} + +//////////////////////////////// +//~ rjf: CV Architectures + +@table(name val) +CV_ArchTable: +{ + {8080 0x00} + {8086 0x01} + {80286 0x02} + {80386 0x03} + {80486 0x04} + {PENTIUM 0x05} + {PENTIUMII 0x06} + {PENTIUMIII 0x07} + {MIPS 0x10} + {MIPS16 0x11} + {MIPS32 0x12} + {MIPS64 0x13} + {MIPSI 0x14} + {MIPSII 0x15} + {MIPSIII 0x16} + {MIPSIV 0x17} + {MIPSV 0x18} + {M68000 0x20} + {M68010 0x21} + {M68020 0x22} + {M68030 0x23} + {M68040 0x24} + {ALPHA 0x30} + {ALPHA_21164 0x31} + {ALPHA_21164A 0x32} + {ALPHA_21264 0x33} + {ALPHA_21364 0x34} + {PPC601 0x40} + {PPC603 0x41} + {PPC604 0x42} + {PPC620 0x43} + {PPCFP 0x44} + {PPCBE 0x45} + {SH3 0x50} + {SH3E 0x51} + {SH3DSP 0x52} + {SH4 0x53} + {SHMEDIA 0x54} + {ARM3 0x60} + {ARM4 0x61} + {ARM4T 0x62} + {ARM5 0x63} + {ARM5T 0x64} + {ARM6 0x65} + {ARM_XMAC 0x66} + {ARM_WMMX 0x67} + {ARM7 0x68} + {OMNI 0x70} + {IA64_1 0x80} + {IA64_2 0x81} + {CEE 0x90} + {AM33 0xA0} + {M32R 0xB0} + {TRICORE 0xC0} + {X64 0xD0} + {EBC 0xE0} + {THUMB 0xF0} + {ARMNT 0xF4} + {ARM64 0xF6} + {D3D11_SHADER 0x100} +} + +@enum(U16) CV_Arch: +{ + @expand(CV_ArchTable a) `$(a.name) = $(a.val)`, + `IA64 = CV_Arch_IA64_1`, + `PENTIUMPRO = CV_Arch_PENTIUMII`, + `MIPSR4000 = CV_Arch_MIPS`, + `ALPHA_21064 = CV_Arch_ALPHA`, + `AMD64 = CV_Arch_X64`, +} + +@enum2string_switch(CV_Arch) +cv_string_from_arch: +{ + @expand(CV_ArchTable a) `case CV_Arch_$(a.name):{result = str8_lit("$(a.name)");}break`; +} + +//////////////////////////////// +//~ rjf: CV Registers + +@table(name val) CV_AllRegTable: +{ + {ERR 30000} + {TEB 30001} + {TIMER 30002} + {EFAD1 30003} + {EFAD2 30004} + {EFAD3 30005} + {VFRAME 30006} + {HANDLE 30007} + {PARAMS 30008} + {LOCALS 30009} + {TID 30010} + {ENV 30011} + {CMDLN 30012} +} + +@enum(U16) CV_AllReg: +{ + @expand(CV_AllRegTable a) `$(a.name) = $(a.val)` +} + +//////////////////////////////// +//~ rjf: CV Sym Kinds + +@table(name header_type_name val) CV_SymKindTable: +{ + {COMPILE Compile 0x0001} + {REGISTER_16t - 0x0002} + {CONSTANT_16t - 0x0003} + {UDT_16t - 0x0004} + {SSEARCH StartSearch 0x0005} + {END - 0x0006} + {SKIP - 0x0007} + {CVRESERVE - 0x0008} + {OBJNAME_ST - 0x0009} + {ENDARG - 0x000a} + {COBOLUDT_16t - 0x000b} + {MANYREG_16t - 0x000c} + {RETURN Return 0x000d} + {ENTRYTHIS - 0x000e} + {BPREL16 - 0x0100} + {LDATA16 - 0x0101} + {GDATA16 - 0x0102} + {PUB16 - 0x0103} + {LPROC16 - 0x0104} + {GPROC16 - 0x0105} + {THUNK16 - 0x0106} + {BLOCK16 - 0x0107} + {WITH16 - 0x0108} + {LABEL16 - 0x0109} + {CEXMODEL16 - 0x010a} + {VFTABLE16 - 0x010b} + {REGREL16 - 0x010c} + {BPREL32_16t - 0x0200} + {LDATA32_16t - 0x0201} + {GDATA32_16t - 0x0202} + {PUB32_16t - 0x0203} + {LPROC32_16t - 0x0204} + {GPROC32_16t - 0x0205} + {THUNK32_ST - 0x0206} + {BLOCK32_ST - 0x0207} + {WITH32_ST - 0x0208} + {LABEL32_ST - 0x0209} + {CEXMODEL32 - 0x020a} + {VFTABLE32_16t - 0x020b} + {REGREL32_16t - 0x020c} + {LTHREAD32_16t - 0x020d} + {GTHREAD32_16t - 0x020e} + {SLINK32 SLink32 0x020f} + {LPROCMIPS_16t - 0x0300} + {GPROCMIPS_16t - 0x0301} + {PROCREF_ST - 0x0400} + {DATAREF_ST - 0x0401} + {ALIGN - 0x0402} + {LPROCREF_ST - 0x0403} + {OEM OEM 0x0404} + {TI16_MAX - 0x1000} + {CONSTANT_ST - 0x1002} + {UDT_ST - 0x1003} + {COBOLUDT_ST - 0x1004} + {MANYREG_ST - 0x1005} + {BPREL32_ST - 0x1006} + {LDATA32_ST - 0x1007} + {GDATA32_ST - 0x1008} + {PUB32_ST - 0x1009} + {LPROC32_ST - 0x100a} + {GPROC32_ST - 0x100b} + {VFTABLE32 VPath32 0x100c} + {REGREL32_ST - 0x100d} + {LTHREAD32_ST - 0x100e} + {GTHREAD32_ST - 0x100f} + {LPROCMIPS_ST - 0x1010} + {GPROCMIPS_ST - 0x1011} + {FRAMEPROC Frameproc 0x1012} + {COMPILE2_ST - 0x1013} + {MANYREG2_ST - 0x1014} + {LPROCIA64_ST - 0x1015} + {GPROCIA64_ST - 0x1016} + {LOCALSLOT_ST - 0x1017} + {PARAMSLOT_ST - 0x1018} + {ANNOTATION Annotation 0x1019} + {GMANPROC_ST - 0x101a} + {LMANPROC_ST - 0x101b} + {RESERVED1 - 0x101c} + {RESERVED2 - 0x101d} + {RESERVED3 - 0x101e} + {RESERVED4 - 0x101f} + {LMANDATA_ST - 0x1020} + {GMANDATA_ST - 0x1021} + {MANFRAMEREL_ST - 0x1022} + {MANREGISTER_ST - 0x1023} + {MANSLOT_ST - 0x1024} + {MANMANYREG_ST - 0x1025} + {MANREGREL_ST - 0x1026} + {MANMANYREG2_ST - 0x1027} + {MANTYPREF - 0x1028} + {UNAMESPACE_ST - 0x1029} + {ST_MAX - 0x1100} + {OBJNAME Objname 0x1101} + {THUNK32 Thunk32 0x1102} + {BLOCK32 Block32 0x1103} + {WITH32 - 0x1104} + {LABEL32 Label32 0x1105} + {REGISTER Register 0x1106} + {CONSTANT Constant 0x1107} + {UDT UDT 0x1108} + {COBOLUDT - 0x1109} + {MANYREG Manyreg 0x110a} + {BPREL32 BPRel32 0x110b} + {LDATA32 Data32 0x110c} + {GDATA32 Data32 0x110d} + {PUB32 Pub32 0x110e} + {LPROC32 Proc32 0x110f} + {GPROC32 Proc32 0x1110} + {REGREL32 Regrel32 0x1111} + {LTHREAD32 Thread32 0x1112} + {GTHREAD32 Thread32 0x1113} + {LPROCMIPS - 0x1114} + {GPROCMIPS - 0x1115} + {COMPILE2 Compile2 0x1116} + {MANYREG2 Manyreg2 0x1117} + {LPROCIA64 - 0x1118} + {GPROCIA64 - 0x1119} + {LOCALSLOT Slot 0x111a} + {PARAMSLOT - 0x111b} + {LMANDATA - 0x111c} + {GMANDATA - 0x111d} + {MANFRAMEREL AttrFrameRel 0x111e} + {MANREGISTER AttrReg 0x111f} + {MANSLOT - 0x1120} + {MANMANYREG AttrManyReg 0x1121} + {MANREGREL AttrRegRel 0x1122} + {MANMANYREG2 - 0x1123} + {UNAMESPACE UNamespace 0x1124} + {PROCREF Ref2 0x1125} + {DATAREF Ref2 0x1126} + {LPROCREF Ref2 0x1127} + {ANNOTATIONREF - 0x1128} + {TOKENREF - 0x1129} + {GMANPROC - 0x112a} + {LMANPROC - 0x112b} + {TRAMPOLINE Trampoline 0x112c} + {MANCONSTANT - 0x112d} + {ATTR_FRAMEREL AttrFrameRel 0x112e} + {ATTR_REGISTER AttrReg 0x112f} + {ATTR_REGREL AttrRegRel 0x1130} + {ATTR_MANYREG AttrManyReg 0x1131} + {SEPCODE Sepcode 0x1132} + {DEFRANGE_2005 - 0x1134} + {DEFRANGE2_2005 - 0x1135} + {SECTION Section 0x1136} + {COFFGROUP CoffGroup 0x1137} + {EXPORT Export 0x1138} + {CALLSITEINFO CallSiteInfo 0x1139} + {FRAMECOOKIE FrameCookie 0x113a} + {DISCARDED Discarded 0x113b} + {COMPILE3 Compile3 0x113c} + {ENVBLOCK EnvBlock 0x113d} + {LOCAL Local 0x113e} + {DEFRANGE - 0x113f} + {DEFRANGE_SUBFIELD DefrangeSubfield 0x1140} + {DEFRANGE_REGISTER DefrangeRegister 0x1141} + {DEFRANGE_FRAMEPOINTER_REL DefrangeFramepointerRel 0x1142} + {DEFRANGE_SUBFIELD_REGISTER DefrangeSubfieldRegister 0x1143} + {DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE DefrangeFramepointerRelFullScope 0x1144} + {DEFRANGE_REGISTER_REL DefrangeRegisterRel 0x1145} + {LPROC32_ID - 0x1146} + {GPROC32_ID - 0x1147} + {LPROCMIPS_ID - 0x1148} + {GPROCMIPS_ID - 0x1149} + {LPROCIA64_ID - 0x114a} + {GPROCIA64_ID - 0x114b} + {BUILDINFO BuildInfo 0x114c} + {INLINESITE InlineSite 0x114d} + {INLINESITE_END - 0x114e} + {PROC_ID_END - 0x114f} + {DEFRANGE_HLSL - 0x1150} + {GDATA_HLSL - 0x1151} + {LDATA_HLSL - 0x1152} + {FILESTATIC FileStatic 0x1153} + {LPROC32_DPC - 0x1155} + {LPROC32_DPC_ID - 0x1156} + {DEFRANGE_DPC_PTR_TAG - 0x1157} + {DPC_SYM_TAG_MAP - 0x1158} + {ARMSWITCHTABLE - 0x1159} + {CALLEES FunctionList 0x115a} + {CALLERS FunctionList 0x115b} + {POGODATA PogoInfo 0x115c} + {INLINESITE2 InlineSite2 0x115d} + {HEAPALLOCSITE HeapAllocSite 0x115e} + {MOD_TYPEREF ModTypeRef 0x115f} + {REF_MINIPDB RefMiniPdb 0x1160} + {PDBMAP - 0x1161} + {GDATA_HLSL32 - 0x1162} + {LDATA_HLSL32 - 0x1163} + {GDATA_HLSL32_EX - 0x1164} + {LDATA_HLSL32_EX - 0x1165} + {FASTLINK FastLink 0x1167} + {INLINEES Inlinees 0x1168} +} + +@enum(U16) CV_SymKind: +{ + @expand(CV_SymKindTable a) `$(a.name) = $(a.val)` +} + +@enum2string_switch(CV_SymKind) +cv_string_from_sym_kind: +{ + @expand(CV_SymKindTable a) `case CV_SymKind_$(a.name):{result = str8_lit("$(a.name)");}break`; +} + +@gen(functions) +{ + `internal U64 cv_header_struct_size_from_sym_kind(CV_SymKind v);`; +} + +@gen(functions) @c_file +{ + `internal U64`; + `cv_header_struct_size_from_sym_kind(CV_SymKind v)`; + `{`; + `U64 result = 0;`; + `switch(v)`; + `{`; + `default:{}break;`; + @expand(CV_SymKindTable a) `$(a.header_type_name != "-" -> "case CV_SymKind_"..a.name..":{result = sizeof(CV_Sym"..a.header_type_name..");}break;")`; + `}`; + `return result;`; + `}`; +} + +//////////////////////////////// +//~ rjf: CV Basic Types + +@table(name val type_name) +CV_BasicTypeTable: +{ + {NOTYPE 0x00 "" } + {ABS 0x01 "" } + {SEGMENT 0x02 "" } + {VOID 0x03 "void" } + {CURRENCY 0x04 "" } + {NBASICSTR 0x05 "" } + {FBASICSTR 0x06 "" } + {NOTTRANS 0x07 "" } + {HRESULT 0x08 "HRESULT" } + {CHAR 0x10 "char" } + {SHORT 0x11 "S16" } + {LONG 0x12 "S32" } + {QUAD 0x13 "S64" } + {OCT 0x14 "S128" } + {UCHAR 0x20 "UCHAR" } + {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 "B32FF" } + {INT8 0x68 "S8" } + {UINT8 0x69 "U8" } + {RCHAR 0x70 "char" } + {WCHAR 0x71 "WCHAR" } + {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" } +} + +@enum(U8) CV_BasicType: +{ + @expand(CV_BasicTypeTable a) `$(a.name) = $(a.val)` +} + +@enum2string_switch(CV_BasicType) cv_string_from_basic_type: +{ + @expand(CV_BasicTypeTable a) `case CV_BasicType_$(a.name):{result = str8_lit("$(a.name)");}break` +} + +@enum2string_switch(CV_BasicType) cv_type_name_from_basic_type: +{ + @expand(CV_BasicTypeTable a) `case CV_BasicType_$(a.name):{result = str8_lit("$(a.type_name)");}break` +} + +//////////////////////////////// +//~ rjf: CV Leaf Kinds + +@table(name header_type_name val) +CV_LeafKindTable: +{ + {MODIFIER_16t - 0x0001} + {POINTER_16t - 0x0002} + {ARRAY_16t - 0x0003} + {CLASS_16t - 0x0004} + {STRUCTURE_16t - 0x0005} + {UNION_16t - 0x0006} + {ENUM_16t - 0x0007} + {PROCEDURE_16t - 0x0008} + {MFUNCTION_16t - 0x0009} + {VTSHAPE VTShape 0x000a} + {COBOL0_16t - 0x000b} + {COBOL1 - 0x000c} + {BARRAY_16t - 0x000d} + {LABEL Label 0x000e} + {NULL - 0x000f} + {NOTTRAN - 0x0010} + {DIMARRAY_16t - 0x0011} + {VFTPATH_16t - 0x0012} + {PRECOMP_16t - 0x0013} + {ENDPRECOMP - 0x0014} + {OEM_16t - 0x0015} + {TYPESERVER_ST - 0x0016} + {SKIP_16t - 0x0200} + {ARGLIST_16t - 0x0201} + {DEFARG_16t - 0x0202} + {LIST - 0x0203} + {FIELDLIST_16t - 0x0204} + {DERIVED_16t - 0x0205} + {BITFIELD_16t - 0x0206} + {METHODLIST_16t - 0x0207} + {DIMCONU_16t - 0x0208} + {DIMCONLU_16t - 0x0209} + {DIMVARU_16t - 0x020a} + {DIMVARLU_16t - 0x020b} + {REFSYM - 0x020c} + {BCLASS_16t - 0x0400} + {VBCLASS_16t - 0x0401} + {IVBCLASS_16t - 0x0402} + {ENUMERATE_ST - 0x0403} + {FRIENDFCN_16t - 0x0404} + {INDEX_16t - 0x0405} + {MEMBER_16t - 0x0406} + {STMEMBER_16t - 0x0407} + {METHOD_16t - 0x0408} + {NESTTYPE_16t - 0x0409} + {VFUNCTAB_16t - 0x040a} + {FRIENDCLS_16t - 0x040b} + {ONEMETHOD_16t - 0x040c} + {VFUNCOFF_16t - 0x040d} + {TI16_MAX - 0x1000} + {MODIFIER Modifier 0x1001} + {POINTER Pointer 0x1002} + {ARRAY_ST - 0x1003} + {CLASS_ST - 0x1004} + {STRUCTURE_ST - 0x1005} + {UNION_ST - 0x1006} + {ENUM_ST - 0x1007} + {PROCEDURE Procedure 0x1008} + {MFUNCTION MFunction 0x1009} + {COBOL0 - 0x100a} + {BARRAY - 0x100b} + {DIMARRAY_ST - 0x100c} + {VFTPATH VFPath 0x100d} + {PRECOMP_ST - 0x100e} + {OEM - 0x100f} + {ALIAS_ST - 0x1010} + {OEM2 - 0x1011} + {SKIP Skip 0x1200} + {ARGLIST ArgList 0x1201} + {DEFARG_ST - 0x1202} + {FIELDLIST - 0x1203} + {DERIVED - 0x1204} + {BITFIELD BitField 0x1205} + {METHODLIST MethodListMember 0x1206} + {DIMCONU - 0x1207} + {DIMCONLU - 0x1208} + {DIMVARU - 0x1209} + {DIMVARLU - 0x120a} + {BCLASS BClass 0x1400} + {VBCLASS VBClass 0x1401} + {IVBCLASS - 0x1402} + {FRIENDFCN_ST - 0x1403} + {INDEX Index 0x1404} + {MEMBER_ST - 0x1405} + {STMEMBER_ST - 0x1406} + {METHOD_ST - 0x1407} + {NESTTYPE_ST - 0x1408} + {VFUNCTAB VFuncTab 0x1409} + {FRIENDCLS - 0x140a} + {ONEMETHOD_ST - 0x140b} + {VFUNCOFF VFuncOff 0x140c} + {NESTTYPEEX_ST - 0x140d} + {MEMBERMODIFY_ST - 0x140e} + {MANAGED_ST - 0x140f} + {ST_MAX - 0x1500} + {TYPESERVER TypeServer 0x1501} + {ENUMERATE Enumerate 0x1502} + {ARRAY Array 0x1503} + {CLASS Struct 0x1504} + {STRUCTURE Struct 0x1505} + {UNION Union 0x1506} + {ENUM Enum 0x1507} + {DIMARRAY - 0x1508} + {PRECOMP PreComp 0x1509} + {ALIAS Alias 0x150a} + {DEFARG - 0x150b} + {FRIENDFCN - 0x150c} + {MEMBER Member 0x150d} + {STMEMBER StMember 0x150e} + {METHOD Method 0x150f} + {NESTTYPE NestType 0x1510} + {ONEMETHOD OneMethod 0x1511} + {NESTTYPEEX NestTypeEx 0x1512} + {MEMBERMODIFY - 0x1513} + {MANAGED - 0x1514} + {TYPESERVER2 TypeServer2 0x1515} + {STRIDED_ARRAY - 0x1516} + {HLSL - 0x1517} + {MODIFIER_EX - 0x1518} + {INTERFACE Struct 0x1519} + {BINTERFACE - 0x151a} + {VECTOR - 0x151b} + {MATRIX - 0x151c} + {VFTABLE - 0x151d} + {CLASS2 Struct2 0x1608} + {STRUCT2 Struct2 0x1609} +} + +@enum(U16) CV_LeafKind: +{ + @expand(CV_LeafKindTable a) `$(a.name) = $(a.val)`; +} + +@enum2string_switch(CV_LeafKind) +cv_string_from_leaf_kind: +{ + @expand(CV_LeafKindTable a) `case CV_LeafKind_$(a.name):{result = str8_lit("$(a.name)");}break`; +} + +@gen(functions) +{ + `internal U64 cv_header_struct_size_from_leaf_kind(CV_LeafKind v);`; +} + +@gen(functions) @c_file +{ + `internal U64`; + `cv_header_struct_size_from_leaf_kind(CV_LeafKind v)`; + `{`; + `U64 result = 0;`; + `switch(v)`; + `{`; + `default:{}break;`; + @expand(CV_LeafKindTable a) `$(a.header_type_name != "-" -> "case CV_LeafKind_"..a.name..":{result = sizeof(CV_Leaf"..a.header_type_name..");}break;")`; + `}`; + `return result;`; + `}`; +} diff --git a/src/codeview/codeview_stringize.c b/src/codeview/codeview_stringize.c index 8539ab96..d017d155 100644 --- a/src/codeview/codeview_stringize.c +++ b/src/codeview/codeview_stringize.c @@ -1,2330 +1,2330 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ CodeView Common Stringize Functions - -internal void -cv_stringize_numeric(Arena *arena, String8List *out, CV_NumericParsed *num){ - String8 numeric_kind_str = cv_string_from_numeric_kind(num->kind); - str8_list_pushf(arena, out, "(%.*s)", str8_varg(numeric_kind_str)); - - if (cv_numeric_fits_in_u64(num)){ - U64 n = cv_u64_from_numeric(num); - str8_list_pushf(arena, out, "(%llu)", n); - } - else if (cv_numeric_fits_in_s64(num)){ - S64 n = cv_s64_from_numeric(num); - str8_list_pushf(arena, out, "(%lld)", n); - } - else if (cv_numeric_fits_in_f64(num)){ - F64 n = cv_f64_from_numeric(num); - str8_list_pushf(arena, out, "(%f)", n); - } -} - -internal void -cv_stringize_lvar_addr_range(Arena *arena, String8List *out, CV_LvarAddrRange *range){ - str8_list_pushf(arena, out, "{off=%x, sec=%u, len=%u}", range->off, range->sec, range->len); -} - -internal void -cv_stringize_lvar_addr_gap(Arena *arena, String8List *out, CV_LvarAddrGap *gap){ - str8_list_pushf(arena, out, "{off=%x, len=%u}", gap->off, gap->len); -} - -internal void -cv_stringize_lvar_addr_gap_list(Arena *arena, String8List *out, void *first, void *opl){ - U64 gap_count = ((U8*)first - (U8*)opl)/sizeof(CV_LvarAddrGap); - if (gap_count > 0){ - str8_list_push(arena, out, str8_lit(" gaps=\n")); - CV_LvarAddrGap *gap = (CV_LvarAddrGap*)first; - CV_LvarAddrGap *opl = gap + gap_count; - for (;gap < opl; gap += 1){ - str8_list_push(arena, out, str8_lit(" ")); - cv_stringize_lvar_addr_gap(arena, out, gap); - str8_list_push(arena, out, str8_lit("\n")); - } - } -} - -internal String8 -cv_string_from_c13_sub_section_kind(CV_C13SubSectionKind kind){ - String8 result = str8_lit("UNRECOGNIZED_C13_SUB_SECTION_KIND"); - switch (kind){ - case 0: str8_lit("PARSE_ERROR"); break; -#define X(N,c) case CV_C13SubSectionKind_##N: result = str8_lit(#N); break; - CV_C13SubSectionKindXList(X) -#undef X - } - return(result); -} - -internal String8 -cv_string_from_reg(CV_Arch arch, CV_Reg reg){ - String8 result = {0}; - switch (arch){ - default: result = str8_lit(""); break; - - case CV_Arch_8086: - { - switch (reg){ -#define X(CVN,C,RDN,BP,BZ) case CV_Regx86_##CVN: result = str8_lit(#CVN); break; - CV_Reg_X86_XList(X) -#undef X - } - }break; - - case CV_Arch_X64: - { - switch (reg){ -#define X(CVN,C,RDN,BP,BZ) case CV_Regx64_##CVN: result = str8_lit(#CVN); break; - CV_Reg_X64_XList(X) -#undef X - } - }break; - } - return(result); -} - -internal String8 -cv_string_from_pointer_kind(CV_PointerKind ptr_kind){ - String8 result = {0}; - switch (ptr_kind){ - default: result = str8_lit(""); break; - case CV_PointerKind_Near: result = str8_lit("Near"); break; - case CV_PointerKind_Far: result = str8_lit("Far"); break; - case CV_PointerKind_Huge: result = str8_lit("Huge"); break; - case CV_PointerKind_BaseSeg: result = str8_lit("BaseSeg"); break; - case CV_PointerKind_BaseVal: result = str8_lit("BaseVal"); break; - case CV_PointerKind_BaseSegVal: result = str8_lit("BaseSegVal"); break; - case CV_PointerKind_BaseAddr: result = str8_lit("BaseAddr"); break; - case CV_PointerKind_BaseSegAddr: result = str8_lit("BaseSegAddr"); break; - case CV_PointerKind_BaseType: result = str8_lit("BaseType"); break; - case CV_PointerKind_BaseSelf: result = str8_lit("BaseSelf"); break; - case CV_PointerKind_Near32: result = str8_lit("Near32"); break; - case CV_PointerKind_Far32: result = str8_lit("Far32"); break; - case CV_PointerKind_64: result = str8_lit("64"); break; - } - return(result); -} - -internal String8 -cv_string_from_pointer_mode(CV_PointerMode ptr_mode){ - String8 result = {0}; - switch (ptr_mode){ - default: result = str8_lit(""); break; - case CV_PointerMode_Ptr: result = str8_lit("Ptr"); break; - case CV_PointerMode_LRef: result = str8_lit("LRef"); break; - case CV_PointerMode_PtrMem: result = str8_lit("PtrMem"); break; - case CV_PointerMode_PtrMethod: result = str8_lit("PtrMethod"); break; - case CV_PointerMode_RRef: result = str8_lit("RRef"); break; - } - return(result); -} - -internal String8 -cv_string_from_hfa_kind(CV_HFAKind hfa_kind){ - String8 result = {0}; - switch (hfa_kind){ - default: result = str8_lit(""); break; - case CV_HFAKind_None: result = str8_lit("None"); break; - case CV_HFAKind_Float: result = str8_lit("Float"); break; - case CV_HFAKind_Double: result = str8_lit("Double"); break; - case CV_HFAKind_Other: result = str8_lit("Other"); break; - } - return(result); -} - -internal String8 -cv_string_from_mo_com_udt_kind(CV_MoComUDTKind mo_com_udt_kind){ - String8 result = {0}; - switch (mo_com_udt_kind){ - default: result = str8_lit(""); break; - case CV_MoComUDTKind_None: result = str8_lit("None"); break; - case CV_MoComUDTKind_Ref: result = str8_lit("Ref"); break; - case CV_MoComUDTKind_Value: result = str8_lit("Value"); break; - case CV_MoComUDTKind_Interface: result = str8_lit("Interface"); break; - } - return(result); -} - -//////////////////////////////// -//~ CodeView Flags Stringize Functions - -global char cv_stringize_spaces[] = " "; - -#define SPACES cv_stringize_spaces - -internal void -cv_stringize_modifier_flags(Arena *arena, String8List *out, - U32 indent, CV_ModifierFlags flags){ - if (flags & CV_ModifierFlag_Const){ - str8_list_pushf(arena, out, "%.*sConst\n", indent, SPACES); - } - if (flags & CV_ModifierFlag_Volatile){ - str8_list_pushf(arena, out, "%.*sVolatile\n", indent, SPACES); - } - if (flags & CV_ModifierFlag_Unaligned){ - str8_list_pushf(arena, out, "%.*sUnaligned\n", indent, SPACES); - } -} - -internal void -cv_stringize_type_props(Arena *arena, String8List *out, - U32 indent, CV_TypeProps props){ - if (props & CV_TypeProp_Packed){ - str8_list_pushf(arena, out, "%.*sPacked\n", indent, SPACES); - } - if (props & CV_TypeProp_HasConstructorsDestructors){ - str8_list_pushf(arena, out, "%.*sHasConstructorsDesctructors\n", indent, SPACES); - } - if (props & CV_TypeProp_OverloadedOperators){ - str8_list_pushf(arena, out, "%.*sOverloadedOperators\n", indent, SPACES); - } - if (props & CV_TypeProp_IsNested){ - str8_list_pushf(arena, out, "%.*sIsNested\n", indent, SPACES); - } - if (props & CV_TypeProp_ContainsNested){ - str8_list_pushf(arena, out, "%.*sContainsNested\n", indent, SPACES); - } - if (props & CV_TypeProp_OverloadedAssignment){ - str8_list_pushf(arena, out, "%.*sOverloadedAssignment\n", indent, SPACES); - } - if (props & CV_TypeProp_OverloadedCasting){ - str8_list_pushf(arena, out, "%.*sOverloadedCasting\n", indent, SPACES); - } - if (props & CV_TypeProp_FwdRef){ - str8_list_pushf(arena, out, "%.*sFwdRef\n", indent, SPACES); - } - if (props & CV_TypeProp_Scoped){ - str8_list_pushf(arena, out, "%.*sScoped\n", indent, SPACES); - } - if (props & CV_TypeProp_HasUniqueName){ - str8_list_pushf(arena, out, "%.*sHasUniqueName\n", indent, SPACES); - } - if (props & CV_TypeProp_Sealed){ - str8_list_pushf(arena, out, "%.*sSealed\n", indent, SPACES); - } - if (props & CV_TypeProp_Intrinsic){ - str8_list_pushf(arena, out, "%.*sIntrinsic\n", indent, SPACES); - } - - CV_HFAKind hfa = CV_TypeProps_ExtractHFA(props); - { - String8 hfa_str = cv_string_from_hfa_kind(hfa); - str8_list_pushf(arena, out, "%.*shfa=%.*s\n", - indent, SPACES, str8_varg(hfa_str)); - } - - CV_MoComUDTKind mo_com = CV_TypeProps_ExtractMOCOM(props); - { - String8 mo_com_str = cv_string_from_mo_com_udt_kind(mo_com); - str8_list_pushf(arena, out, "%.*smocom=%.*s\n", - indent, SPACES, str8_varg(mo_com_str)); - } -} - -internal void -cv_stringize_pointer_attribs(Arena *arena, String8List *out, - U32 indent, CV_PointerAttribs attribs){ - if (attribs & CV_PointerAttrib_IsFlat){ - str8_list_pushf(arena, out, "%.*sIsFlat\n", indent, SPACES); - } - if (attribs & CV_PointerAttrib_Volatile){ - str8_list_pushf(arena, out, "%.*sVolatile\n", indent, SPACES); - } - if (attribs & CV_PointerAttrib_Const){ - str8_list_pushf(arena, out, "%.*sConst\n", indent, SPACES); - } - if (attribs & CV_PointerAttrib_Unaligned){ - str8_list_pushf(arena, out, "%.*sUnaligned\n", indent, SPACES); - } - if (attribs & CV_PointerAttrib_Restricted){ - str8_list_pushf(arena, out, "%.*sRestricted\n", indent, SPACES); - } - if (attribs & CV_PointerAttrib_MOCOM){ - str8_list_pushf(arena, out, "%.*sMOCOM\n", indent, SPACES); - } - if (attribs & CV_PointerAttrib_LRef){ - str8_list_pushf(arena, out, "%.*sLRef\n", indent, SPACES); - } - if (attribs & CV_PointerAttrib_RRef){ - str8_list_pushf(arena, out, "%.*sRRef\n", indent, SPACES); - } - - CV_PointerKind kind = CV_PointerAttribs_ExtractKind(attribs); - { - String8 kind_str = cv_string_from_pointer_kind(kind); - str8_list_pushf(arena, out, "%.*skind=%.*s\n", - indent, SPACES, str8_varg(kind_str)); - } - - CV_PointerMode mode = CV_PointerAttribs_ExtractMode(attribs); - { - String8 mode_str = cv_string_from_pointer_mode(mode); - str8_list_pushf(arena, out, "%.*smode=%.*s\n", - indent, SPACES, str8_varg(mode_str)); - } - - U32 size = CV_PointerAttribs_ExtractSize(attribs); - str8_list_pushf(arena, out, "%.*ssize=%u\n", - indent, SPACES, size); -} - -internal void -cv_stringize_local_flags(Arena *arena, String8List *out, - U32 indent, CV_LocalFlags flags){ - if (flags & CV_LocalFlag_Param){ - str8_list_pushf(arena, out, "%.*sParam\n", indent, SPACES); - } - if (flags & CV_LocalFlag_AddrTaken){ - str8_list_pushf(arena, out, "%.*sAddrTaken\n", indent, SPACES); - } - if (flags & CV_LocalFlag_Compgen){ - str8_list_pushf(arena, out, "%.*sCompgen\n", indent, SPACES); - } - if (flags & CV_LocalFlag_Aggregate){ - str8_list_pushf(arena, out, "%.*sAggregate\n", indent, SPACES); - } - if (flags & CV_LocalFlag_PartOfAggregate){ - str8_list_pushf(arena, out, "%.*sPartOfAggregate\n", indent, SPACES); - } - if (flags & CV_LocalFlag_Aliased){ - str8_list_pushf(arena, out, "%.*sAliased\n", indent, SPACES); - } - if (flags & CV_LocalFlag_Alias){ - str8_list_pushf(arena, out, "%.*sAlias\n", indent, SPACES); - } - if (flags & CV_LocalFlag_Retval){ - str8_list_pushf(arena, out, "%.*sRetval\n", indent, SPACES); - } - if (flags & CV_LocalFlag_OptOut){ - str8_list_pushf(arena, out, "%.*sOptOut\n", indent, SPACES); - } - if (flags & CV_LocalFlag_Global){ - str8_list_pushf(arena, out, "%.*sGlobal\n", indent, SPACES); - } - if (flags & CV_LocalFlag_Static){ - str8_list_pushf(arena, out, "%.*sStatic\n", indent, SPACES); - } -} - - -#undef SPACES - -//////////////////////////////// -//~ CodeView Sym Stringize Functions - -internal void -cv_stringize_sym_parsed(Arena *arena, String8List *out, CV_SymParsed *sym){ - CV_StringizeSymParams params = {0}; - params.arch = sym->info.arch; - - cv_stringize_sym_array(arena, out, &sym->sym_ranges, sym->data, ¶ms); -} - -internal void -cv_stringize_sym_range(Arena *arena, String8List *out, - CV_RecRange *range, String8 data, - CV_StringizeSymParams *p){ - U64 opl_off = range->off + range->hdr.size; - if (opl_off > data.size){ - str8_list_push(arena, out, str8_lit("bad symbol range\n")); - } - - if (opl_off <= data.size){ - // [off]: kind - { - String8 kind_str = cv_string_from_sym_kind(range->hdr.kind); - str8_list_pushf(arena, out, "[%06x]: %.*s\n", - range->off + 2, str8_varg(kind_str)); - } - - // details - U8 *first = data.str + range->off + 2; - U64 cap = range->hdr.size - 2; - - switch (range->hdr.kind){ - default:break; - - case CV_SymKind_COMPILE: - { - if (sizeof(CV_SymCompile) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymCompile *compile = (CV_SymCompile*)first; - - // machine - String8 machine = cv_string_from_arch(compile->machine); - str8_list_pushf(arena, out, " machine=%.*s\n", - str8_varg(machine)); - - // flags - // TODO(allen): better flags path - str8_list_pushf(arena, out, " flags=%x\n", compile->flags); - - // ver_str - String8 ver_str = str8_cstring_capped((char*)(compile + 1), first + cap); - str8_list_pushf(arena, out, " ver_str='%.*s'\n", str8_varg(ver_str)); - } - }break; - - case CV_SymKind_END: - { - // no contents - }break; - - case CV_SymKind_FRAMEPROC: - { - if (sizeof(CV_SymFrameproc) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymFrameproc *frameproc = (CV_SymFrameproc*)first; - - // frame sizes and offsets - str8_list_pushf(arena, out, " frame_size=%u\n", - frameproc->frame_size); - str8_list_pushf(arena, out, " pad_size=%u\n", - frameproc->pad_size); - str8_list_pushf(arena, out, " pad_off=%u\n", - frameproc->pad_off); - str8_list_pushf(arena, out, " save_reg_size=%u\n", - frameproc->save_reg_size); - str8_list_pushf(arena, out, " eh_off=%x\n", - frameproc->eh_off); - - // eh section - str8_list_pushf(arena, out, " eh_sec=%u\n", - frameproc->eh_sec); - - // flags - // TODO(allen): better flags path - str8_list_pushf(arena, out, " flags=%x\n", frameproc->flags); - } - }break; - - case CV_SymKind_OBJNAME: - { - if (sizeof(CV_SymObjname) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymObjname *objname = (CV_SymObjname*)first; - - // sig - str8_list_pushf(arena, out, " sig=%u\n", objname->sig); - - // name - String8 name = str8_cstring_capped((char*)(objname + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_THUNK32: - { - if (sizeof(CV_SymThunk32) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymThunk32 *thunk32 = (CV_SymThunk32*)first; - - // members - str8_list_pushf(arena, out, " parent=%x\n", thunk32->parent); - str8_list_pushf(arena, out, " end=%x\n", thunk32->end); - str8_list_pushf(arena, out, " next=%x\n", thunk32->next); - str8_list_pushf(arena, out, " off=%u\n", thunk32->off); - str8_list_pushf(arena, out, " sec=%u\n", thunk32->sec); - str8_list_pushf(arena, out, " len=%u\n", thunk32->len); - - // ord - // TODO(allen): better ord path - str8_list_pushf(arena, out, " ord=%u\n", thunk32->ord); - - // name - String8 name = str8_cstring_capped((char*)(thunk32 + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - - // variant - String8 variant = str8_cstring_capped(name.str + name.size + 1, first + cap); - str8_list_pushf(arena, out, " variant='%.*s'\n", str8_varg(variant)); - } - }break; - - case CV_SymKind_BLOCK32: - { - if (sizeof(CV_SymBlock32) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymBlock32 *block32 = (CV_SymBlock32*)first; - - // block attributes - str8_list_pushf(arena, out, " parent=%x\n", block32->parent); - str8_list_pushf(arena, out, " end=%x\n", block32->end); - str8_list_pushf(arena, out, " len=%u\n", block32->len); - str8_list_pushf(arena, out, " off=%x\n", block32->off); - str8_list_pushf(arena, out, " sec=%u\n", block32->sec); - - // name - String8 name = str8_cstring_capped((char*)(block32 + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", - str8_varg(name)); - } - }break; - - case CV_SymKind_LABEL32: - { - if (sizeof(CV_SymLabel32) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymLabel32 *label32 = (CV_SymLabel32*)first; - - // label attributes - str8_list_pushf(arena, out, " off=%x\n", label32->off); - str8_list_pushf(arena, out, " sec=%u\n", label32->sec); - - // flags - // TODO(allen): better flags path - str8_list_pushf(arena, out, " flags=%x\n", label32->flags); - - // name - String8 name = str8_cstring_capped((char*)(label32 + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", - str8_varg(name)); - } - }break; - - case CV_SymKind_CONSTANT: - { - if (sizeof(CV_SymConstant) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymConstant *constant = (CV_SymConstant*)first; - - // itype - str8_list_pushf(arena, out, " itype=%u\n", constant->itype); - - // num - U8 *numeric_ptr = (U8*)(constant + 1); - CV_NumericParsed numeric = cv_numeric_from_data_range(numeric_ptr, first + cap); - str8_list_push(arena, out, str8_lit(" num=")); - cv_stringize_numeric(arena, out, &numeric); - str8_list_push(arena, out, str8_lit("\n")); - - // name - U8 *name_ptr = numeric_ptr + numeric.encoded_size; - String8 name = str8_cstring_capped((char*)(name_ptr), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_UDT: - { - if (sizeof(CV_SymUDT) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymUDT *udt = (CV_SymUDT*)first; - - // itype - str8_list_pushf(arena, out, " itype=%u\n", udt->itype); - - // name - String8 name = str8_cstring_capped((char*)(udt + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_LDATA32: - case CV_SymKind_GDATA32: - { - if (sizeof(CV_SymData32) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymData32 *data32 = (CV_SymData32*)first; - - // itype, off & sec - str8_list_pushf(arena, out, " itype=%u\n off=%x\n sec=%u\n", - data32->itype, data32->off, data32->sec); - - // name - String8 name = str8_cstring_capped((char*)(data32 + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_PUB32: - { - if (sizeof(CV_SymPub32) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymPub32 *pub32 = (CV_SymPub32*)first; - - // flags - CV_PubFlags flags = pub32->flags; - str8_list_push(arena, out, str8_lit(" flags=")); - if (flags == 0){ - str8_list_push(arena, out, str8_lit("0|")); - } - else{ - if (flags&CV_PubFlag_Code){ - str8_list_push(arena, out, str8_lit("Code|")); - } - if (flags&CV_PubFlag_Function){ - str8_list_push(arena, out, str8_lit("Function|")); - } - if (flags&CV_PubFlag_ManagedCode){ - str8_list_push(arena, out, str8_lit("ManagedCode|")); - } - if (flags&CV_PubFlag_MSIL){ - str8_list_push(arena, out, str8_lit("MSIL|")); - } - } - str8_list_push(arena, out, str8_lit("\n")); - - // off & sec - str8_list_pushf(arena, out, " off=%x\n sec=%u\n", pub32->off, pub32->sec); - - // name - String8 name = str8_cstring_capped((char*)(pub32 + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_LPROC32: - case CV_SymKind_GPROC32: - { - if (sizeof(CV_SymProc32) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymProc32 *proc32 = (CV_SymProc32*)first; - - // proc attributes - str8_list_pushf(arena, out, " parent=%x\n", proc32->parent); - str8_list_pushf(arena, out, " end=%x\n", proc32->end); - str8_list_pushf(arena, out, " next=%x\n", proc32->next); - str8_list_pushf(arena, out, " len=%u\n", proc32->len); - str8_list_pushf(arena, out, " dbg_start=%x\n", proc32->dbg_start); - str8_list_pushf(arena, out, " dbg_end=%x\n", proc32->dbg_end); - str8_list_pushf(arena, out, " itype=%u\n", proc32->itype); - str8_list_pushf(arena, out, " off=%x\n", proc32->off); - str8_list_pushf(arena, out, " sec=%u\n", proc32->sec); - - // flags - // TODO(allen): better flags path - str8_list_pushf(arena, out, " flags=%x\n", proc32->flags); - - // name - String8 name = str8_cstring_capped((char*)(proc32 + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_REGREL32: - { - if (sizeof(CV_SymRegrel32) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymRegrel32 *regrel32 = (CV_SymRegrel32*)first; - - // regrel attributes - str8_list_pushf(arena, out, " reg_off=%u\n", regrel32->reg_off); - str8_list_pushf(arena, out, " itype=%u\n", regrel32->itype); - - // reg - String8 reg = cv_string_from_reg(p->arch, regrel32->reg); - str8_list_pushf(arena, out, " reg=%.*s\n", str8_varg(reg)); - - // name - String8 name = str8_cstring_capped((char*)(regrel32 + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_LTHREAD32: - case CV_SymKind_GTHREAD32: - { - if (sizeof(CV_SymThread32) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymThread32 *thread32 = (CV_SymThread32*)first; - - // itype, tls_off, tls_seg - str8_list_pushf(arena, out, " itype=%u\n tls_off=%x\n tls_seg=%u\n", - thread32->itype, thread32->tls_off, thread32->tls_seg); - - // name - String8 name = str8_cstring_capped((char*)(thread32 + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_COMPILE2: - { - if (sizeof(CV_SymCompile2) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymCompile2 *compile2 = (CV_SymCompile2*)first; - - // flags - // TODO(allen): better flags path - str8_list_pushf(arena, out, " flags=%x\n", compile2->flags); - - // machine - String8 machine = cv_string_from_arch(compile2->machine); - str8_list_pushf(arena, out, " machine=%.*s\n", - str8_varg(machine)); - - // ver - str8_list_pushf(arena, out, - " ver_fe_major=%u\n ver_fe_minor=%u\n ver_fe_build=%u\n" - " ver_major=%u\n ver_minor=%u\n ver_build=%u\n", - compile2->ver_fe_major, compile2->ver_fe_minor, compile2->ver_fe_build, - compile2->ver_major, compile2->ver_minor, compile2->ver_build); - - // ver_str - String8 ver_str = str8_cstring_capped((char*)(compile2 + 1), first + cap); - str8_list_pushf(arena, out, " ver_str='%.*s'\n", str8_varg(ver_str)); - } - }break; - - case CV_SymKind_UNAMESPACE: - { - CV_SymUNamespace *unamespace = (CV_SymUNamespace*)first; - - // name - String8 name = str8_cstring_capped((char*)(unamespace), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - }break; - - case CV_SymKind_PROCREF: - case CV_SymKind_DATAREF: - case CV_SymKind_LPROCREF: - { - if (sizeof(CV_SymRef2) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymRef2 *ref2 = (CV_SymRef2*)first; - - // suc_name, sym_off & imod - str8_list_pushf(arena, out, " suc_name=%u\n sym_off=%x\n imod=%u\n", - ref2->suc_name, ref2->sym_off, ref2->imod); - - // name - String8 name = str8_cstring_capped((char*)(ref2 + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_TRAMPOLINE: - { - if (sizeof(CV_SymTrampoline) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymTrampoline *trampoline = (CV_SymTrampoline*)first; - - // kind - // TODO(allen): better kind path - str8_list_pushf(arena, out, " kind=%u\n", trampoline->kind); - - // members - str8_list_pushf(arena, out, " thunk_size=%u\n", trampoline->thunk_size); - str8_list_pushf(arena, out, " thunk_sec_off=%x\n", trampoline->thunk_sec_off); - str8_list_pushf(arena, out, " target_sec_off=%x\n", trampoline->target_sec_off); - str8_list_pushf(arena, out, " thunk_sec=%u\n", trampoline->thunk_sec); - str8_list_pushf(arena, out, " target_sec=%u\n", trampoline->target_sec); - } - }break; - - case CV_SymKind_SECTION: - { - if (sizeof(CV_SymSection) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymSection *section = (CV_SymSection*)first; - - // members - str8_list_pushf(arena, out, " sec_index=%u\n", section->sec_index); - str8_list_pushf(arena, out, " align=%u\n", section->align); - str8_list_pushf(arena, out, " pad=%u\n", section->pad); - str8_list_pushf(arena, out, " rva=%x\n", section->rva); - str8_list_pushf(arena, out, " size=%u\n", section->size); - str8_list_pushf(arena, out, " characteristics=%x\n", section->characteristics); - - // name - String8 name = str8_cstring_capped((char*)(section + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_COFFGROUP: - { - if (sizeof(CV_SymCoffGroup) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymCoffGroup *coff_group = (CV_SymCoffGroup*)first; - - // members - str8_list_pushf(arena, out, " size=%u\n", coff_group->size); - str8_list_pushf(arena, out, " characteristics=%x\n", coff_group->characteristics); - str8_list_pushf(arena, out, " off=%x\n", coff_group->off); - str8_list_pushf(arena, out, " sec=%u\n", coff_group->sec); - - // name - String8 name = str8_cstring_capped((char*)(coff_group + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_CALLSITEINFO: - { - if (sizeof(CV_SymCallSiteInfo) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymCallSiteInfo *callsiteinfo = (CV_SymCallSiteInfo*)first; - - // callsite info attributes - str8_list_pushf(arena, out, " off=%x\n", callsiteinfo->off); - str8_list_pushf(arena, out, " sec=%u\n", callsiteinfo->sec); - str8_list_pushf(arena, out, " pad=%u\n", callsiteinfo->pad); - str8_list_pushf(arena, out, " itype=%u\n", callsiteinfo->itype); - } - }break; - - case CV_SymKind_FRAMECOOKIE: - { - if (sizeof(CV_SymFrameCookie) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymFrameCookie *framecookie = (CV_SymFrameCookie*)first; - - // off - str8_list_pushf(arena, out, " off=%x\n", framecookie->off); - - // reg - String8 reg = cv_string_from_reg(p->arch, framecookie->reg); - str8_list_pushf(arena, out, " reg=%.*s\n", - str8_varg(reg)); - - // kind - // TODO(allen): better kind path - str8_list_pushf(arena, out, " kind=%x\n", framecookie->kind); - - // flags - // TODO(allen): better flags path - str8_list_pushf(arena, out, " flags=%x\n", framecookie->flags); - } - }break; - - case CV_SymKind_COMPILE3: - { - if (sizeof(CV_SymCompile3) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymCompile3 *compile3 = (CV_SymCompile3*)first; - - // flags - // TODO(allen): better flags path - str8_list_pushf(arena, out, " flags=%x\n", compile3->flags); - - // machine - String8 machine = cv_string_from_arch(compile3->machine); - str8_list_pushf(arena, out, " machine=%.*s\n", - str8_varg(machine)); - - // ver - str8_list_pushf(arena, out, - " ver_fe_major=%u\n ver_fe_minor=%u\n ver_fe_build=%u\n" - " ver_major=%u\n ver_minor=%u\n ver_build=%u\n" - " ver_qfe=%u\n", - compile3->ver_fe_major, compile3->ver_fe_minor, compile3->ver_fe_build, - compile3->ver_major, compile3->ver_minor, compile3->ver_build); - // ver_str - String8 ver_str = str8_cstring_capped((char*)(compile3 + 1), first + cap); - str8_list_pushf(arena, out, " ver_str='%.*s'\n", str8_varg(ver_str)); - } - }break; - - case CV_SymKind_ENVBLOCK: - { - if (sizeof(CV_SymEnvBlock) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymEnvBlock *envblock = (CV_SymEnvBlock*)first; - - // flags - str8_list_pushf(arena, out, " flags=%x\n", envblock->flags); - - // name - str8_list_pushf(arena, out, " rgsz=\n"); - char *name_ptr = (char*)(envblock + 1); - for (;;){ - String8 name = str8_cstring_capped(name_ptr, first + cap); - if (name.size == 0){ - break; - } - str8_list_pushf(arena, out, " '%.*s'\n", str8_varg(name)); - name_ptr += name.size + 1; - } - } - }break; - - case CV_SymKind_LOCAL: - { - if (sizeof(CV_SymLocal) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymLocal *slocal = (CV_SymLocal*)first; - - // itype - str8_list_pushf(arena, out, " itype=%u\n", slocal->itype); - - // flags - str8_list_pushf(arena, out, " flags={\n"); - cv_stringize_local_flags(arena, out, 2, slocal->flags); - str8_list_pushf(arena, out, " }\n"); - - // name - String8 name = str8_cstring_capped((char*)(slocal + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_DEFRANGE_REGISTER: - { - if (sizeof(CV_SymDefrangeRegister) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymDefrangeRegister *defrange_register = (CV_SymDefrangeRegister*)first; - - // reg - String8 reg = cv_string_from_reg(p->arch, defrange_register->reg); - str8_list_pushf(arena, out, " reg=%.*s\n", str8_varg(reg)); - - // range attribs - // TODO(allen): better range attribs - str8_list_pushf(arena, out, " attribs=%x\n", defrange_register->attribs); - - // addr range - str8_list_push(arena, out, str8_lit(" range=")); - cv_stringize_lvar_addr_range(arena, out, &defrange_register->range); - str8_list_push(arena, out, str8_lit("\n")); - - // gaps - cv_stringize_lvar_addr_gap_list(arena, out, defrange_register + 1, first + cap); - } - }break; - - case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL: - { - if (sizeof(CV_SymDefrangeFramepointerRel) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymDefrangeFramepointerRel *defrange_fprel = (CV_SymDefrangeFramepointerRel*)first; - - // off - str8_list_pushf(arena, out, " off=%u\n", defrange_fprel->off); - - // addr range - str8_list_push(arena, out, str8_lit(" range=")); - cv_stringize_lvar_addr_range(arena, out, &defrange_fprel->range); - str8_list_push(arena, out, str8_lit("\n")); - - // gaps - cv_stringize_lvar_addr_gap_list(arena, out, defrange_fprel + 1, first + cap); - } - }break; - - case CV_SymKind_DEFRANGE_SUBFIELD_REGISTER: - { - if (sizeof(CV_SymDefrangeSubfieldRegister) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymDefrangeSubfieldRegister *defrange_subfield_register = (CV_SymDefrangeSubfieldRegister*)first; - - // reg - String8 reg = cv_string_from_reg(p->arch, defrange_subfield_register->reg); - str8_list_pushf(arena, out, " reg=%.*s\n", str8_varg(reg)); - - // range attribs - // TODO(allen): better range attribs - str8_list_pushf(arena, out, " attribs=%x\n", defrange_subfield_register->attribs); - - // offset - str8_list_pushf(arena, out, " field_offset=%u\n", - defrange_subfield_register->field_offset); - - // addr range - str8_list_push(arena, out, str8_lit(" range=")); - cv_stringize_lvar_addr_range(arena, out, &defrange_subfield_register->range); - str8_list_push(arena, out, str8_lit("\n")); - - // gaps - cv_stringize_lvar_addr_gap_list(arena, out, defrange_subfield_register + 1, first + cap); - } - }break; - - case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: - { - if (sizeof(CV_SymDefrangeFramepointerRelFullScope) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymDefrangeFramepointerRelFullScope *defrange_fprel_full_scope = - (CV_SymDefrangeFramepointerRelFullScope*)first; - - // off - str8_list_pushf(arena, out, " off=%u\n", defrange_fprel_full_scope->off); - } - }break; - - case CV_SymKind_DEFRANGE_REGISTER_REL: - { - if (sizeof(CV_SymDefrangeRegisterRel) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymDefrangeRegisterRel *defrange_register_rel = (CV_SymDefrangeRegisterRel*)first; - - // reg - String8 reg = cv_string_from_reg(p->arch, defrange_register_rel->reg); - str8_list_pushf(arena, out, " reg=%.*s\n", str8_varg(reg)); - - // flags - // TODO(allen): better flags path - str8_list_pushf(arena, out, " flags=%x\n", defrange_register_rel->flags); - - // reg off - str8_list_pushf(arena, out, " reg_off=%u\n", defrange_register_rel->reg_off); - - // addr range - str8_list_push(arena, out, str8_lit(" range=")); - cv_stringize_lvar_addr_range(arena, out, &defrange_register_rel->range); - str8_list_push(arena, out, str8_lit("\n")); - - // gaps - cv_stringize_lvar_addr_gap_list(arena, out, defrange_register_rel + 1, first + cap); - } - }break; - - case CV_SymKind_BUILDINFO: - { - if (sizeof(CV_SymBuildInfo) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymBuildInfo *buildinfo = (CV_SymBuildInfo*)first; - - // item id - str8_list_pushf(arena, out, " id=%u\n", buildinfo->id); - } - }break; - - case CV_SymKind_INLINESITE: - { - if (sizeof(CV_SymInlineSite) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymInlineSite *inlinesite = (CV_SymInlineSite*)first; - - // members - str8_list_pushf(arena, out, " parent=%x\n", inlinesite->parent); - str8_list_pushf(arena, out, " end=%x\n", inlinesite->end); - str8_list_pushf(arena, out, " inlinee=%u\n", inlinesite->inlinee); - - // binary annotation - // TODO(allen): - } - }break; - - case CV_SymKind_INLINESITE_END: - { - // no contents - }break; - - case CV_SymKind_FILESTATIC: - { - if (sizeof(CV_SymFileStatic) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymFileStatic *file_static = (CV_SymFileStatic*)first; - - // members - str8_list_pushf(arena, out, " itype=%u\n", file_static->itype); - str8_list_pushf(arena, out, " mod_offset=%x\n", file_static->mod_offset); - // TODO(allen): better flags path - str8_list_pushf(arena, out, " flags=%x\n", file_static->flags); - - // name - String8 name = str8_cstring_capped((char*)(file_static + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_SymKind_CALLEES: - case CV_SymKind_CALLERS: - { - if (sizeof(CV_SymFunctionList) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymFunctionList *functions = (CV_SymFunctionList*)first; - - // count - str8_list_pushf(arena, out, " count=%u\n", functions->count); - - // functions - U32 function_count_max = (cap - sizeof(*functions))/sizeof(CV_TypeId); - U32 function_count = ClampTop(functions->count, function_count_max); - if (function_count > 0){ - str8_list_push(arena, out, str8_lit(" functions=\n")); - CV_TypeId *func = (CV_TypeId*)(functions + 1); - CV_TypeId *opl = func + function_count; - for (;func < opl; func += 1){ - str8_list_pushf(arena, out, " %u\n", *func); - } - } - - // invocations - U32 invocation_count_max = (cap - sizeof(*functions) - function_count*sizeof(CV_TypeId))/sizeof(U32); - U32 invocation_count = ClampTop(functions->count, invocation_count_max); - if (invocation_count > 0){ - str8_list_push(arena, out, str8_lit(" invocations=\n")); - U32 *inv = (CV_TypeId*)(functions + 1); - U32 *opl = inv + invocation_count; - for (;inv < opl; inv += 1){ - str8_list_pushf(arena, out, " %u\n", *inv); - } - } - } - }break; - - case CV_SymKind_HEAPALLOCSITE: - { - if (sizeof(CV_SymHeapAllocSite) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymHeapAllocSite *heap_alloc_site = (CV_SymHeapAllocSite*)first; - - // members - str8_list_pushf(arena, out, " off=%x\n", heap_alloc_site->off); - str8_list_pushf(arena, out, " sec=%u\n", heap_alloc_site->sec); - str8_list_pushf(arena, out, " call_inst_len=%u\n", heap_alloc_site->call_inst_len); - str8_list_pushf(arena, out, " itype=%u\n", heap_alloc_site->itype); - } - }break; - - case CV_SymKind_INLINEES: - { - if (sizeof(CV_SymInlinees) > cap){ - str8_list_push(arena, out, str8_lit(" bad symbol range\n")); - } - else{ - CV_SymInlinees *inlinees = (CV_SymInlinees*)first; - - // count - str8_list_pushf(arena, out, " count=%u\n", inlinees->count); - - // desc - U32 desc_count = (cap - sizeof(*inlinees))/sizeof(U32); - if (desc_count > 0){ - str8_list_pushf(arena, out, " desc=\n"); - U32 *desc = (U32*)(inlinees + 1); - U32 *desc_opl = desc + desc_count; - for (;desc < desc_opl; desc += 1){ - str8_list_pushf(arena, out, " %u\n", *desc); - } - } - } - }break; - - case CV_SymKind_REGISTER_16t: - case CV_SymKind_CONSTANT_16t: - case CV_SymKind_UDT_16t: - case CV_SymKind_SSEARCH: - case CV_SymKind_SKIP: - case CV_SymKind_CVRESERVE: - case CV_SymKind_OBJNAME_ST: - case CV_SymKind_ENDARG: - case CV_SymKind_COBOLUDT_16t: - case CV_SymKind_MANYREG_16t: - case CV_SymKind_RETURN: - case CV_SymKind_ENTRYTHIS: - case CV_SymKind_BPREL16: - case CV_SymKind_LDATA16: - case CV_SymKind_GDATA16: - case CV_SymKind_PUB16: - case CV_SymKind_LPROC16: - case CV_SymKind_GPROC16: - case CV_SymKind_THUNK16: - case CV_SymKind_BLOCK16: - case CV_SymKind_WITH16: - case CV_SymKind_LABEL16: - case CV_SymKind_CEXMODEL16: - case CV_SymKind_VFTABLE16: - case CV_SymKind_REGREL16: - case CV_SymKind_BPREL32_16t: - case CV_SymKind_LDATA32_16t: - case CV_SymKind_GDATA32_16t: - case CV_SymKind_PUB32_16t: - case CV_SymKind_LPROC32_16t: - case CV_SymKind_GPROC32_16t: - case CV_SymKind_THUNK32_ST: - case CV_SymKind_BLOCK32_ST: - case CV_SymKind_WITH32_ST: - case CV_SymKind_LABEL32_ST: - case CV_SymKind_CEXMODEL32: - case CV_SymKind_VFTABLE32_16t: - case CV_SymKind_REGREL32_16t: - case CV_SymKind_LTHREAD32_16t: - case CV_SymKind_GTHREAD32_16t: - case CV_SymKind_SLINK32: - case CV_SymKind_LPROCMIPS_16t: - case CV_SymKind_GPROCMIPS_16t: - case CV_SymKind_PROCREF_ST: - case CV_SymKind_DATAREF_ST: - case CV_SymKind_ALIGN: - case CV_SymKind_LPROCREF_ST: - case CV_SymKind_OEM: - case CV_SymKind_TI16_MAX: - case CV_SymKind_CONSTANT_ST: - case CV_SymKind_UDT_ST: - case CV_SymKind_COBOLUDT_ST: - case CV_SymKind_MANYREG_ST: - case CV_SymKind_BPREL32_ST: - case CV_SymKind_LDATA32_ST: - case CV_SymKind_GDATA32_ST: - case CV_SymKind_PUB32_ST: - case CV_SymKind_LPROC32_ST: - case CV_SymKind_GPROC32_ST: - case CV_SymKind_VFTABLE32: - case CV_SymKind_REGREL32_ST: - case CV_SymKind_LTHREAD32_ST: - case CV_SymKind_GTHREAD32_ST: - case CV_SymKind_LPROCMIPS_ST: - case CV_SymKind_GPROCMIPS_ST: - case CV_SymKind_COMPILE2_ST: - case CV_SymKind_MANYREG2_ST: - case CV_SymKind_LPROCIA64_ST: - case CV_SymKind_GPROCIA64_ST: - case CV_SymKind_LOCALSLOT_ST: - case CV_SymKind_PARAMSLOT_ST: - case CV_SymKind_ANNOTATION: - case CV_SymKind_GMANPROC_ST: - case CV_SymKind_LMANPROC_ST: - case CV_SymKind_RESERVED1: - case CV_SymKind_RESERVED2: - case CV_SymKind_RESERVED3: - case CV_SymKind_RESERVED4: - case CV_SymKind_LMANDATA_ST: - case CV_SymKind_GMANDATA_ST: - case CV_SymKind_MANFRAMEREL_ST: - case CV_SymKind_MANREGISTER_ST: - case CV_SymKind_MANSLOT_ST: - case CV_SymKind_MANMANYREG_ST: - case CV_SymKind_MANREGREL_ST: - case CV_SymKind_MANMANYREG2_ST: - case CV_SymKind_MANTYPREF: - case CV_SymKind_UNAMESPACE_ST: - case CV_SymKind_ST_MAX: - case CV_SymKind_WITH32: - case CV_SymKind_REGISTER: - case CV_SymKind_COBOLUDT: - case CV_SymKind_MANYREG: - case CV_SymKind_BPREL32: - case CV_SymKind_LPROCMIPS: - case CV_SymKind_GPROCMIPS: - case CV_SymKind_MANYREG2: - case CV_SymKind_LPROCIA64: - case CV_SymKind_GPROCIA64: - case CV_SymKind_LOCALSLOT: - case CV_SymKind_PARAMSLOT: - case CV_SymKind_LMANDATA: - case CV_SymKind_GMANDATA: - case CV_SymKind_MANFRAMEREL: - case CV_SymKind_MANREGISTER: - case CV_SymKind_MANSLOT: - case CV_SymKind_MANMANYREG: - case CV_SymKind_MANREGREL: - case CV_SymKind_MANMANYREG2: - case CV_SymKind_ANNOTATIONREF: - case CV_SymKind_TOKENREF: - case CV_SymKind_GMANPROC: - case CV_SymKind_LMANPROC: - case CV_SymKind_MANCONSTANT: - case CV_SymKind_ATTR_FRAMEREL: - case CV_SymKind_ATTR_REGISTER: - case CV_SymKind_ATTR_REGREL: - case CV_SymKind_ATTR_MANYREG: - case CV_SymKind_SEPCODE: - case CV_SymKind_DEFRANGE_2005: - case CV_SymKind_DEFRANGE2_2005: - case CV_SymKind_EXPORT: - case CV_SymKind_DISCARDED: - case CV_SymKind_DEFRANGE: - case CV_SymKind_DEFRANGE_SUBFIELD: - case CV_SymKind_LPROC32_ID: - case CV_SymKind_GPROC32_ID: - case CV_SymKind_LPROCMIPS_ID: - case CV_SymKind_GPROCMIPS_ID: - case CV_SymKind_LPROCIA64_ID: - case CV_SymKind_GPROCIA64_ID: - case CV_SymKind_PROC_ID_END: - case CV_SymKind_DEFRANGE_HLSL: - case CV_SymKind_GDATA_HLSL: - case CV_SymKind_LDATA_HLSL: - case CV_SymKind_LPROC32_DPC: - case CV_SymKind_LPROC32_DPC_ID: - case CV_SymKind_DEFRANGE_DPC_PTR_TAG: - case CV_SymKind_DPC_SYM_TAG_MAP: - case CV_SymKind_ARMSWITCHTABLE: - case CV_SymKind_POGODATA: - case CV_SymKind_INLINESITE2: - case CV_SymKind_MOD_TYPEREF: - case CV_SymKind_REF_MINIPDB: - case CV_SymKind_PDBMAP: - case CV_SymKind_GDATA_HLSL32: - case CV_SymKind_LDATA_HLSL32: - case CV_SymKind_GDATA_HLSL32_EX: - case CV_SymKind_LDATA_HLSL32_EX: - case CV_SymKind_FASTLINK: - { - str8_list_push(arena, out, str8_lit(" no stringizer path\n")); - }break; - } - } -} - -internal void -cv_stringize_sym_array(Arena *arena, String8List *out, - CV_RecRangeArray *ranges, String8 data, - CV_StringizeSymParams *p){ - CV_RecRange *ptr = ranges->ranges; - CV_RecRange *opl = ranges->ranges + ranges->count; - for (;ptr < opl; ptr += 1){ - cv_stringize_sym_range(arena, out, ptr, data, p); - str8_list_push(arena, out, str8_lit("\n")); - } -} - -//////////////////////////////// -//~ CodeView Leaf Stringize Functions - -internal void -cv_stringize_leaf_parsed(Arena *arena, String8List *out, CV_LeafParsed *leaf){ - CV_StringizeLeafParams params = {0}; - - cv_stringize_leaf_array(arena, out, &leaf->leaf_ranges, leaf->itype_first, - leaf->data, ¶ms); -} - -internal void -cv_stringize_leaf_range(Arena *arena, String8List *out, - CV_RecRange *range, CV_TypeId itype, String8 data, - CV_StringizeLeafParams *p){ - U64 opl_off = range->off + range->hdr.size; - if (opl_off > data.size){ - str8_list_push(arena, out, str8_lit("bad leaf range\n")); - } - - if (opl_off <= data.size){ - // [off] (itype): kind - { - String8 kind_str = cv_string_from_leaf_kind(range->hdr.kind); - str8_list_pushf(arena, out, "[%06x] (%u): %.*s\n", - range->off + 2, itype, str8_varg(kind_str)); - } - - // details - U8 *first = data.str + range->off + 2; - U64 cap = range->hdr.size - 2; - - switch (range->hdr.kind){ - case CV_LeafKind_VTSHAPE: - { - if (sizeof(CV_LeafVTShape) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafVTShape *vtshape = (CV_LeafVTShape*)first; - - str8_list_pushf(arena, out, " count=%u\n", vtshape->count); - - str8_list_push(arena, out, str8_lit(" shapes=\n")); - U8 *shapes = (U8*)(vtshape + 1); - U32 max_count = (cap - sizeof(*vtshape))*2; - U32 clamped_count = ClampTop(vtshape->count, max_count); - for (U32 i = 0; i < clamped_count; i += 1){ - U32 j = (i >> 1); - U8 s = shapes[j]; - if (j & 1){ - s >>= 4; - } - CV_VirtualTableShape shape = (s & 0xF); - // TODO(allen): better shape path - str8_list_pushf(arena, out, " %u\n", shape); - } - } - }break; - - case CV_LeafKind_LABEL: - { - if (sizeof(CV_LeafLabel) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafLabel *label = (CV_LeafLabel*)first; - - // TODO(allen): better LabelKind path - str8_list_pushf(arena, out, " kind=%x\n", label->kind); - } - }break; - - case CV_LeafKind_MODIFIER: - { - if (sizeof(CV_LeafModifier) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafModifier *modifier = (CV_LeafModifier*)first; - - str8_list_pushf(arena, out, " itype=%u\n", modifier->itype); - str8_list_pushf(arena, out, " flags={\n"); - cv_stringize_modifier_flags(arena, out, 2, modifier->flags); - str8_list_pushf(arena, out, " }\n"); - } - }break; - - case CV_LeafKind_POINTER: - { - if (sizeof(CV_LeafPointer) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafPointer *pointer = (CV_LeafPointer*)first; - - str8_list_pushf(arena, out, " itype=%u\n", pointer->itype); - str8_list_pushf(arena, out, " attribs={\n"); - cv_stringize_pointer_attribs(arena, out, 2, pointer->attribs); - str8_list_pushf(arena, out, " }\n"); - } - }break; - - case CV_LeafKind_PROCEDURE: - { - if (sizeof(CV_LeafProcedure) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafProcedure *procedure = (CV_LeafProcedure*)first; - - str8_list_pushf(arena, out, " ret_itype=%u\n", procedure->ret_itype); - // TODO(allen): better CallKind path - str8_list_pushf(arena, out, " call_kind=%u\n", procedure->call_kind); - // TODO(allen): better flags path - str8_list_pushf(arena, out, " attribs=%x\n", procedure->attribs); - str8_list_pushf(arena, out, " arg_count=%u\n", procedure->arg_count); - str8_list_pushf(arena, out, " arg_itype=%u\n", procedure->arg_itype); - } - }break; - - case CV_LeafKind_MFUNCTION: - { - if (sizeof(CV_LeafMFunction) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafMFunction *mfunction = (CV_LeafMFunction*)first; - - str8_list_pushf(arena, out, " ret_itype=%u\n", mfunction->ret_itype); - str8_list_pushf(arena, out, " class_itype=%u\n", mfunction->class_itype); - str8_list_pushf(arena, out, " this_itype=%u\n", mfunction->this_itype); - // TODO(allen): better CallKind path - str8_list_pushf(arena, out, " call_kind=%u\n", mfunction->call_kind); - // TODO(allen): better flags path - str8_list_pushf(arena, out, " attribs=%x\n", mfunction->attribs); - str8_list_pushf(arena, out, " arg_count=%u\n", mfunction->arg_count); - str8_list_pushf(arena, out, " arg_itype=%u\n", mfunction->arg_itype); - str8_list_pushf(arena, out, " this_adjust=%d\n", mfunction->this_adjust); - } - }break; - - case CV_LeafKind_ARGLIST: - { - if (sizeof(CV_LeafArgList) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafArgList *arg_list = (CV_LeafArgList*)first; - - str8_list_pushf(arena, out, " count=%u\n", arg_list->count); - str8_list_push(arena, out, str8_lit(" itypes=\n")); - - CV_TypeId *itypes = (CV_TypeId*)(arg_list + 1); - U32 max_count = (cap - sizeof(*arg_list))/sizeof(U32); - U32 clamped_count = ClampTop(arg_list->count, max_count); - for (U32 i = 0; i < clamped_count; i += 1){ - str8_list_pushf(arena, out, " %u\n", itypes[i]); - } - } - }break; - - case CV_LeafKind_FIELDLIST: - { - U64 cursor = 0; - for (;cursor + sizeof(CV_LeafKind) <= cap;){ - CV_LeafKind field_kind = *(CV_LeafKind*)(first + cursor); - String8 field_kind_str = cv_string_from_leaf_kind(field_kind); - - str8_list_pushf(arena, out, " field kind: %.*s\n", - str8_varg(field_kind_str)); - - U64 list_item_off = cursor + 2; - - // if we hit an error or forget to set next cursor for a case - // default to exiting the loop - U64 list_item_opl_off = cap; - - switch (field_kind){ - default: - { - str8_list_push(arena, out, str8_lit(" unexpected field kind\n")); - }break; - - case CV_LeafKind_MEMBER: - { - if (list_item_off + sizeof(CV_LeafMember) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafMember *member = (CV_LeafMember*)(first + list_item_off); - - U64 num_off = list_item_off + sizeof(*member); - CV_NumericParsed num = cv_numeric_from_data_range(first + num_off, first + cap); - - U64 name_off = num_off + num.encoded_size; - String8 name = str8_cstring_capped(first + name_off, first + cap); - - list_item_opl_off = name_off + name.size + 1; - - // print data - // TODO(allen): better flags path - str8_list_pushf(arena, out, " attribs=%x\n", member->attribs); - str8_list_pushf(arena, out, " itype=%u\n", member->itype); - str8_list_push(arena, out, str8_lit(" offset=")); - cv_stringize_numeric(arena, out, &num); - str8_list_push(arena, out, str8_lit("\n")); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_LeafKind_STMEMBER: - { - if (list_item_off + sizeof(CV_LeafStMember) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafStMember *stmember = (CV_LeafStMember*)(first + list_item_off); - - U64 name_off = list_item_off + sizeof(*stmember); - String8 name = str8_cstring_capped(first + name_off, first + cap); - - list_item_opl_off = name_off + name.size + 1; - - // print data - // TODO(allen): better flags path - str8_list_pushf(arena, out, " attribs=%x\n", stmember->attribs); - str8_list_pushf(arena, out, " itype=%u\n", stmember->itype); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_LeafKind_METHOD: - { - if (list_item_off + sizeof(CV_LeafMethod) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafMethod *method = (CV_LeafMethod*)(first + list_item_off); - - U64 name_off = list_item_off + sizeof(*method); - String8 name = str8_cstring_capped(first + name_off, first + cap); - - list_item_opl_off = name_off + name.size + 1; - - // print data - str8_list_pushf(arena, out, " count=%u\n", method->count); - str8_list_pushf(arena, out, " list_itype=%u\n", method->list_itype); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_LeafKind_ONEMETHOD: - { - if (list_item_off + sizeof(CV_LeafOneMethod) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafOneMethod *one_method = (CV_LeafOneMethod*)(first + list_item_off); - - U64 vbaseoff_off = list_item_off + sizeof(*one_method); - U64 vbaseoff_opl_off = vbaseoff_off; - U32 vbaseoff = 0; - { - CV_MethodProp prop = CV_FieldAttribs_ExtractMethodProp(one_method->attribs); - if (prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro){ - vbaseoff = *(U32*)(first + vbaseoff_off); - vbaseoff_opl_off += sizeof(vbaseoff); - } - } - - U64 name_off = vbaseoff_opl_off; - String8 name = str8_cstring_capped(first + name_off, first + cap); - - list_item_opl_off = name_off + name.size + 1; - - // print data - // TODO(allen): better flags path - str8_list_pushf(arena, out, " attribs=%x\n", one_method->attribs); - str8_list_pushf(arena, out, " itype=%u\n", one_method->itype); - str8_list_pushf(arena, out, " vbaseoff=%u\n", vbaseoff); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_LeafKind_ENUMERATE: - { - if (list_item_off + sizeof(CV_LeafEnumerate) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafEnumerate *enumerate = (CV_LeafEnumerate*)(first + list_item_off); - - U64 num_off = list_item_off + sizeof(*enumerate); - CV_NumericParsed num = cv_numeric_from_data_range(first + num_off, first + cap); - - U64 name_off = num_off + num.encoded_size; - String8 name = str8_cstring_capped(first + name_off, first + cap); - - list_item_opl_off = name_off + name.size + 1; - - // print data - // TODO(allen): better flags path - str8_list_pushf(arena, out, " attribs=%x\n", enumerate->attribs); - str8_list_push(arena, out, str8_lit(" val=")); - cv_stringize_numeric(arena, out, &num); - str8_list_push(arena, out, str8_lit("\n")); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_LeafKind_NESTTYPE: - { - if (list_item_off + sizeof(CV_LeafNestType) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafNestType *nest_type = (CV_LeafNestType*)(first + list_item_off); - - U64 name_off = list_item_off + sizeof(*nest_type); - String8 name = str8_cstring_capped(first + name_off, first + cap); - - list_item_opl_off = name_off + name.size + 1; - - // print data - str8_list_pushf(arena, out, " itype=%u\n", nest_type->itype); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_LeafKind_NESTTYPEEX: - { - if (list_item_off + sizeof(CV_LeafNestTypeEx) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafNestTypeEx *nest_type = (CV_LeafNestTypeEx*)(first + list_item_off); - - U64 name_off = list_item_off + sizeof(*nest_type); - String8 name = str8_cstring_capped(first + name_off, first + cap); - - list_item_opl_off = name_off + name.size + 1; - - // print data - // TODO(allen): better flags printing - str8_list_pushf(arena, out, " attribs=%x\n", nest_type->attribs); - str8_list_pushf(arena, out, " itype=%u\n", nest_type->itype); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_LeafKind_BCLASS: - { - if (list_item_off + sizeof(CV_LeafBClass) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafBClass *bclass = (CV_LeafBClass*)(first + list_item_off); - - U64 num_off = list_item_off + sizeof(*bclass); - CV_NumericParsed num = cv_numeric_from_data_range(first + num_off, first + cap); - - list_item_opl_off = num_off + num.encoded_size; - - // print data - // TODO(allen): better flags printing - str8_list_pushf(arena, out, " attribs=%x\n", bclass->attribs); - str8_list_pushf(arena, out, " itype=%u\n", bclass->itype); - str8_list_push(arena, out, str8_lit(" offset=")); - cv_stringize_numeric(arena, out, &num); - str8_list_push(arena, out, str8_lit("\n")); - } - }break; - - case CV_LeafKind_VBCLASS: - case CV_LeafKind_IVBCLASS: - { - if (list_item_off + sizeof(CV_LeafVBClass) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafVBClass *vbclass = (CV_LeafVBClass*)(first + list_item_off); - - U64 num1_off = list_item_off + sizeof(*vbclass); - CV_NumericParsed num1 = cv_numeric_from_data_range(first + num1_off, first + cap); - - U64 num2_off = num1_off + num1.encoded_size; - CV_NumericParsed num2 = cv_numeric_from_data_range(first + num2_off, first + cap); - - list_item_opl_off = num2_off + num2.encoded_size; - - // print data - // TODO(allen): better flags printing - str8_list_pushf(arena, out, " attribs=%x\n", vbclass->attribs); - str8_list_pushf(arena, out, " itype=%u\n", vbclass->itype); - str8_list_pushf(arena, out, " vbptr_itype=%u\n", vbclass->vbptr_itype); - str8_list_push(arena, out, str8_lit(" vbptr_off=")); - cv_stringize_numeric(arena, out, &num1); - str8_list_push(arena, out, str8_lit("\n")); - str8_list_push(arena, out, str8_lit(" vtable_off=")); - cv_stringize_numeric(arena, out, &num2); - str8_list_push(arena, out, str8_lit("\n")); - } - }break; - - case CV_LeafKind_INDEX: - { - if (list_item_off + sizeof(CV_LeafIndex) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafIndex *index = (CV_LeafIndex*)(first + list_item_off); - - list_item_opl_off = list_item_off + sizeof(*index); - - // print data - str8_list_pushf(arena, out, " itype=%u\n", index->itype); - } - }break; - - case CV_LeafKind_VFUNCTAB: - { - if (list_item_off + sizeof(CV_LeafVFuncTab) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafVFuncTab *vfunctab = (CV_LeafVFuncTab*)(first + list_item_off); - - list_item_opl_off = list_item_off + sizeof(*vfunctab); - - // print data - str8_list_pushf(arena, out, " itype=%u\n", vfunctab->itype); - } - }break; - - case CV_LeafKind_VFUNCOFF: - { - if (list_item_off + sizeof(CV_LeafVFuncOff) > cap){ - str8_list_push(arena, out, str8_lit(" bad field list range\n")); - } - else{ - // compute whole layout - CV_LeafVFuncOff *vfuncoff = (CV_LeafVFuncOff*)(first + list_item_off); - - list_item_opl_off = list_item_off + sizeof(*vfuncoff); - - // print data - str8_list_pushf(arena, out, " itype=%u\n", vfuncoff->itype); - str8_list_pushf(arena, out, " off=%u\n", vfuncoff->off); - } - }break; - } - - // update cursor - U64 next_cursor = AlignPow2(list_item_opl_off, 4); - cursor = next_cursor; - } - }break; - - case CV_LeafKind_BITFIELD: - { - if (sizeof(CV_LeafBitField) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafBitField *bit_field = (CV_LeafBitField*)first; - - str8_list_pushf(arena, out, " itype=%u\n", bit_field->itype); - str8_list_pushf(arena, out, " len=%u\n", bit_field->len); - str8_list_pushf(arena, out, " pos=%u\n", bit_field->pos); - } - }break; - - case CV_LeafKind_METHODLIST: - { - U64 cursor = 0; - for (;cursor + sizeof(CV_LeafMethodListMember) <= cap;){ - CV_LeafMethodListMember *method = (CV_LeafMethodListMember*)(first + cursor); - - // extract vbaseoff - U64 next_cursor = cursor + sizeof(*method); - U32 vbaseoff = 0; - { - CV_MethodProp prop = CV_FieldAttribs_ExtractMethodProp(method->attribs); - if (prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro){ - if (cursor + sizeof(*method) + 4 <= cap){ - vbaseoff = *(U32*)(method + 1); - } - next_cursor += 4; - } - } - - // print - // TODO(allen): better flags path - str8_list_pushf(arena, out, " method\n", method->attribs); - str8_list_pushf(arena, out, " attribs=%x\n", method->attribs); - str8_list_pushf(arena, out, " itype=%u\n", method->itype); - str8_list_pushf(arena, out, " vbaseoff=%u\n", vbaseoff); - - // update cursor - cursor = next_cursor; - } - }break; - - case CV_LeafKind_ARRAY: - { - if (sizeof(CV_LeafArray) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafArray *array = (CV_LeafArray*)first; - - str8_list_pushf(arena, out, " entry_itype=%u\n", array->entry_itype); - str8_list_pushf(arena, out, " index_itype=%u\n", array->index_itype); - - // count - U8 *numeric_ptr = (U8*)(array + 1); - CV_NumericParsed array_count = cv_numeric_from_data_range(numeric_ptr, first + cap); - str8_list_pushf(arena, out, " count="); - cv_stringize_numeric(arena, out, &array_count); - str8_list_push(arena, out, str8_lit("\n")); - } - }break; - - case CV_LeafKind_CLASS: - case CV_LeafKind_STRUCTURE: - { - if (sizeof(CV_LeafStruct) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafStruct *lf_struct = (CV_LeafStruct*)first; - - str8_list_pushf(arena, out, " count=%u\n", lf_struct->count); - str8_list_pushf(arena, out, " props=%x (\n", lf_struct->props); - cv_stringize_type_props(arena, out, 2, lf_struct->props); - str8_list_pushf(arena, out, " )\n"); - str8_list_pushf(arena, out, " field_itype=%u\n", lf_struct->field_itype); - str8_list_pushf(arena, out, " derived_itype=%u\n", lf_struct->derived_itype); - str8_list_pushf(arena, out, " vshape_itype=%u\n", lf_struct->vshape_itype); - - U8 *numeric_ptr = (U8*)(lf_struct + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); - str8_list_pushf(arena, out, " size="); - cv_stringize_numeric(arena, out, &size); - str8_list_push(arena, out, str8_lit("\n")); - - String8 name = str8_cstring_capped((U8*)(numeric_ptr + size.encoded_size), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - - String8 unique_name = str8_cstring_capped(name.str + name.size + 1, first + cap); - str8_list_pushf(arena, out, " unique_name='%.*s'\n", str8_varg(unique_name)); - } - }break; - - case CV_LeafKind_UNION: - { - if (sizeof(CV_LeafUnion) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafUnion *lf_union = (CV_LeafUnion*)first; - - str8_list_pushf(arena, out, " count=%u\n", lf_union->count); - str8_list_pushf(arena, out, " props=%x (\n", lf_union->props); - cv_stringize_type_props(arena, out, 2, lf_union->props); - str8_list_pushf(arena, out, " )\n"); - str8_list_pushf(arena, out, " field_itype=%u\n", lf_union->field_itype); - - U8 *numeric_ptr = (U8*)(lf_union + 1); - CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); - str8_list_pushf(arena, out, " size="); - cv_stringize_numeric(arena, out, &size); - str8_list_push(arena, out, str8_lit("\n")); - - String8 name = str8_cstring_capped((U8*)(numeric_ptr + size.encoded_size), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - - String8 unique_name = str8_cstring_capped(name.str + name.size + 1, first + cap); - str8_list_pushf(arena, out, " unique_name='%.*s'\n", str8_varg(unique_name)); - } - }break; - - case CV_LeafKind_ENUM: - { - if (sizeof(CV_LeafEnum) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafEnum *lf_enum = (CV_LeafEnum*)first; - - str8_list_pushf(arena, out, " count=%u\n", lf_enum->count); - str8_list_pushf(arena, out, " props=%x (\n", lf_enum->props); - cv_stringize_type_props(arena, out, 2, lf_enum->props); - str8_list_pushf(arena, out, " )\n"); - str8_list_pushf(arena, out, " base_itype=%u\n", lf_enum->base_itype); - str8_list_pushf(arena, out, " field_itype=%u\n", lf_enum->field_itype); - - String8 name = str8_cstring_capped((U8*)(lf_enum + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - - String8 unique_name = str8_cstring_capped(name.str + name.size + 1, first + cap); - str8_list_pushf(arena, out, " unique_name='%.*s'\n", str8_varg(unique_name)); - } - }break; - - case CV_LeafKind_VFTABLE: - { - if (sizeof(CV_LeafVFTable) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafVFTable *vftable = (CV_LeafVFTable*)first; - - str8_list_pushf(arena, out, " owner_itype=%u\n", vftable->owner_itype); - str8_list_pushf(arena, out, " base_table_itype=%u\n", vftable->base_table_itype); - str8_list_pushf(arena, out, " offset_in_object_layout=%u\n", - vftable->offset_in_object_layout); - str8_list_pushf(arena, out, " names_len=%u\n", vftable->names_len); - - U64 names_cap = Min(sizeof(*vftable) + vftable->names_len, cap); - - str8_list_push(arena, out, str8_lit(" names=\n")); - U8 *ptr = (U8*)(vftable + 1); - U8 *opl = first + names_cap; - for (;ptr < opl;){ - String8 name = str8_cstring_capped(ptr, opl); - str8_list_pushf(arena, out, " '%.*s'\n", str8_varg(name)); - ptr += name.size + 1; - } - } - }break; - - case CV_LeafKind_CLASS2: - case CV_LeafKind_STRUCT2: - { - if (sizeof(CV_LeafStruct2) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafStruct2 *struct2 = (CV_LeafStruct2*)first; - - str8_list_pushf(arena, out, " props=%x (\n", struct2->props); - cv_stringize_type_props(arena, out, 2, struct2->props); - str8_list_pushf(arena, out, " )\n"); - str8_list_pushf(arena, out, " unknown1=%u\n", struct2->unknown1); - str8_list_pushf(arena, out, " field_itype=%u\n", struct2->field_itype); - str8_list_pushf(arena, out, " derived_itype=%u\n", struct2->derived_itype); - str8_list_pushf(arena, out, " vshape_itype=%u\n", struct2->vshape_itype); - str8_list_pushf(arena, out, " unknown2=0x%x\n", struct2->unknown2); - //str8_list_pushf(arena, out, " size=%u\n", struct2->size); - - String8 name = str8_cstring_capped((U8*)(struct2 + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - - String8 unique_name = str8_cstring_capped(name.str + name.size + 1, first + cap); - str8_list_pushf(arena, out, " unique_name='%.*s'\n", str8_varg(unique_name)); - } - }break; - - case CV_LeafIDKind_FUNC_ID: - { - if (sizeof(CV_LeafFuncId) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafFuncId *func_id = (CV_LeafFuncId*)first; - - str8_list_pushf(arena, out, " scope_string_id=%u\n", func_id->scope_string_id); - str8_list_pushf(arena, out, " itype=%u\n", func_id->itype); - - String8 name = str8_cstring_capped((U8*)(func_id + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_LeafIDKind_MFUNC_ID: - { - if (sizeof(CV_LeafMFuncId) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafMFuncId *mfunc_id = (CV_LeafMFuncId*)first; - - str8_list_pushf(arena, out, " owner_itype=%u\n", mfunc_id->owner_itype); - str8_list_pushf(arena, out, " itype=%u\n", mfunc_id->itype); - - String8 name = str8_cstring_capped((U8*)(mfunc_id + 1), first + cap); - str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); - } - }break; - - case CV_LeafIDKind_BUILDINFO: - { - if (sizeof(CV_LeafBuildInfo) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafBuildInfo *build_info = (CV_LeafBuildInfo*)first; - - str8_list_pushf(arena, out, " count=%u\n", build_info->count); - - CV_ItemId *item_ids = (CV_ItemId*)(build_info + 1); - str8_list_pushf(arena, out, " items=\n"); - U32 max_count = (cap - sizeof(*build_info))/sizeof(CV_ItemId); - U32 clamped_count = ClampTop(build_info->count, max_count); - for (U32 i = 0; i < clamped_count; i += 1){ - CV_ItemId item_id = item_ids[i]; - str8_list_pushf(arena, out, " %u\n", item_id); - } - } - }break; - - case CV_LeafIDKind_SUBSTR_LIST: - { - if (sizeof(CV_LeafSubstrList) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafSubstrList *substr_list = (CV_LeafSubstrList*)first; - - str8_list_pushf(arena, out, " count=%u\n", substr_list->count); - - str8_list_pushf(arena, out, " items=\n"); - U32 max_count = (cap - sizeof(CV_LeafSubstrList))/sizeof(CV_ItemId); - CV_ItemId *item_ids = (CV_ItemId*)(substr_list + 1); - U32 clamped_count = ClampTop(substr_list->count, max_count); - for (U32 i = 0; i < clamped_count; i += 1){ - CV_ItemId item_id = item_ids[i]; - str8_list_pushf(arena, out, " %u\n", item_id); - } - } - }break; - - case CV_LeafIDKind_STRING_ID: - { - if (sizeof(CV_LeafStringId) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafStringId *string_id = (CV_LeafStringId*)first; - - str8_list_pushf(arena, out, " substr_list_id=%u\n", string_id->substr_list_id); - - String8 string = str8_cstring_capped((U8*)(string_id + 1), first + cap); - str8_list_pushf(arena, out, " string='%.*s'\n", str8_varg(string)); - } - }break; - - case CV_LeafIDKind_UDT_SRC_LINE: - { - if (sizeof(CV_LeafUDTSrcLine) > cap){ - str8_list_push(arena, out, str8_lit(" bad leaf range\n")); - } - else{ - CV_LeafUDTSrcLine *udt_src_line = (CV_LeafUDTSrcLine*)first; - - str8_list_pushf(arena, out, " udt_itype=%u\n", udt_src_line->udt_itype); - str8_list_pushf(arena, out, " src_string_id=%u\n", udt_src_line->src_string_id); - str8_list_pushf(arena, out, " line=%u\n", udt_src_line->line); - } - }break; - - - default: - - // Leaf Kinds - case CV_LeafKind_MODIFIER_16t: - case CV_LeafKind_POINTER_16t: - case CV_LeafKind_ARRAY_16t: - case CV_LeafKind_CLASS_16t: - case CV_LeafKind_STRUCTURE_16t: - case CV_LeafKind_UNION_16t: - case CV_LeafKind_ENUM_16t: - case CV_LeafKind_PROCEDURE_16t: - case CV_LeafKind_MFUNCTION_16t: - //case CV_LeafKind_VTSHAPE: - case CV_LeafKind_COBOL0_16t: - case CV_LeafKind_COBOL1: - case CV_LeafKind_BARRAY_16t: - //case CV_LeafKind_LABEL: - case CV_LeafKind_NULL: - case CV_LeafKind_NOTTRAN: - case CV_LeafKind_DIMARRAY_16t: - case CV_LeafKind_VFTPATH_16t: - case CV_LeafKind_PRECOMP_16t: - case CV_LeafKind_ENDPRECOMP: - case CV_LeafKind_OEM_16t: - case CV_LeafKind_TYPESERVER_ST: - case CV_LeafKind_SKIP_16t: - case CV_LeafKind_ARGLIST_16t: - case CV_LeafKind_DEFARG_16t: - case CV_LeafKind_LIST: - case CV_LeafKind_FIELDLIST_16t: - case CV_LeafKind_DERIVED_16t: - case CV_LeafKind_BITFIELD_16t: - case CV_LeafKind_METHODLIST_16t: - case CV_LeafKind_DIMCONU_16t: - case CV_LeafKind_DIMCONLU_16t: - case CV_LeafKind_DIMVARU_16t: - case CV_LeafKind_DIMVARLU_16t: - case CV_LeafKind_REFSYM: - case CV_LeafKind_BCLASS_16t: - case CV_LeafKind_VBCLASS_16t: - case CV_LeafKind_IVBCLASS_16t: - case CV_LeafKind_ENUMERATE_ST: - case CV_LeafKind_FRIENDFCN_16t: - case CV_LeafKind_INDEX_16t: - case CV_LeafKind_MEMBER_16t: - case CV_LeafKind_STMEMBER_16t: - case CV_LeafKind_METHOD_16t: - case CV_LeafKind_NESTTYPE_16t: - case CV_LeafKind_VFUNCTAB_16t: - case CV_LeafKind_FRIENDCLS_16t: - case CV_LeafKind_ONEMETHOD_16t: - case CV_LeafKind_VFUNCOFF_16t: - case CV_LeafKind_TI16_MAX: - //case CV_LeafKind_MODIFIER: - //case CV_LeafKind_POINTER: - case CV_LeafKind_ARRAY_ST: - case CV_LeafKind_CLASS_ST: - case CV_LeafKind_STRUCTURE_ST: - case CV_LeafKind_UNION_ST: - case CV_LeafKind_ENUM_ST: - //case CV_LeafKind_PROCEDURE: - //case CV_LeafKind_MFUNCTION: - case CV_LeafKind_COBOL0: - case CV_LeafKind_BARRAY: - case CV_LeafKind_DIMARRAY_ST: - case CV_LeafKind_VFTPATH: - case CV_LeafKind_PRECOMP_ST: - case CV_LeafKind_OEM: - case CV_LeafKind_ALIAS_ST: - case CV_LeafKind_OEM2: - case CV_LeafKind_SKIP: - //case CV_LeafKind_ARGLIST: - case CV_LeafKind_DEFARG_ST: - //case CV_LeafKind_FIELDLIST: - case CV_LeafKind_DERIVED: - //case CV_LeafKind_BITFIELD: - //case CV_LeafKind_METHODLIST: - case CV_LeafKind_DIMCONU: - case CV_LeafKind_DIMCONLU: - case CV_LeafKind_DIMVARU: - case CV_LeafKind_DIMVARLU: - case CV_LeafKind_BCLASS: - case CV_LeafKind_VBCLASS: - case CV_LeafKind_IVBCLASS: - case CV_LeafKind_FRIENDFCN_ST: - //case CV_LeafKind_INDEX: - case CV_LeafKind_MEMBER_ST: - case CV_LeafKind_STMEMBER_ST: - case CV_LeafKind_METHOD_ST: - case CV_LeafKind_NESTTYPE_ST: - case CV_LeafKind_VFUNCTAB: - case CV_LeafKind_FRIENDCLS: - case CV_LeafKind_ONEMETHOD_ST: - case CV_LeafKind_VFUNCOFF: - case CV_LeafKind_NESTTYPEEX_ST: - case CV_LeafKind_MEMBERMODIFY_ST: - case CV_LeafKind_MANAGED_ST: - case CV_LeafKind_ST_MAX: - case CV_LeafKind_TYPESERVER: - case CV_LeafKind_ENUMERATE: - //case CV_LeafKind_ARRAY: - //case CV_LeafKind_CLASS: - //case CV_LeafKind_STRUCTURE: - //case CV_LeafKind_UNION: - //case CV_LeafKind_ENUM: - case CV_LeafKind_DIMARRAY: - case CV_LeafKind_PRECOMP: - case CV_LeafKind_ALIAS: - case CV_LeafKind_DEFARG: - case CV_LeafKind_FRIENDFCN: - case CV_LeafKind_MEMBER: - case CV_LeafKind_STMEMBER: - case CV_LeafKind_METHOD: - case CV_LeafKind_NESTTYPE: - case CV_LeafKind_ONEMETHOD: - case CV_LeafKind_NESTTYPEEX: - case CV_LeafKind_MEMBERMODIFY: - case CV_LeafKind_MANAGED: - case CV_LeafKind_TYPESERVER2: - case CV_LeafKind_STRIDED_ARRAY: - case CV_LeafKind_HLSL: - case CV_LeafKind_MODIFIER_EX: - case CV_LeafKind_INTERFACE: - case CV_LeafKind_BINTERFACE: - case CV_LeafKind_VECTOR: - case CV_LeafKind_MATRIX: - //case CV_LeafKind_VFTABLE: - - // Leaf ID Kinds - //case CV_LeafIDKind_FUNC_ID: - //case CV_LeafIDKind_MFUNC_ID: - //case CV_LeafIDKind_BUILDINFO: - //case CV_LeafIDKind_SUBSTR_LIST: - //case CV_LeafIDKind_STRING_ID: - //case CV_LeafIDKind_UDT_SRC_LINE: - case CV_LeafIDKind_UDT_MOD_SRC_LINE: - - { - str8_list_push(arena, out, str8_lit(" no stringizer path\n")); - }break; - } - } -} - -internal void -cv_stringize_leaf_array(Arena *arena, String8List *out, - CV_RecRangeArray *ranges, CV_TypeId itype_first, String8 data, - CV_StringizeLeafParams *p){ - CV_RecRange *ptr = ranges->ranges; - CV_RecRange *opl = ranges->ranges + ranges->count; - CV_TypeId itype = itype_first; - for (;ptr < opl; ptr += 1, itype += 1){ - cv_stringize_leaf_range(arena, out, ptr, itype, data, p); - str8_list_push(arena, out, str8_lit("\n")); - } -} - -//////////////////////////////// -//~ CodeView C13 Stringize Functions - -internal void -cv_stringize_c13_parsed(Arena *arena, String8List *out, CV_C13Parsed *c13){ - for(CV_C13SubSectionNode *node = c13->first_sub_section; - node != 0; - node = node->next) - { - String8 kind_str = cv_string_from_c13_sub_section_kind(node->kind); - str8_list_pushf(arena, out, "C13 Sub Section [%llx] (%.*s):\n", - node->off, str8_varg(kind_str)); - - switch(node->kind) - { - case CV_C13SubSectionKind_Lines: - { - if (node->lines_first == 0) - { - str8_list_push(arena, out, str8_lit(" failed to extract info\n")); - } - else for(CV_C13LinesParsedNode *n = node->lines_first; n != 0; n = n->next) - { - CV_C13LinesParsed *lines = &n->v; - - str8_list_pushf(arena, out, " section: %u\n", lines->sec_idx); - str8_list_pushf(arena, out, " file off: %u\n", lines->file_off); - str8_list_pushf(arena, out, " file name: %.*s\n", str8_varg(lines->file_name)); - str8_list_pushf(arena, out, " line count: %u\n", lines->line_count); - - U64 base_off = lines->secrel_base_off; - U64 *line_offs = lines->voffs; - U32 *line_nums = lines->line_nums; - - U32 line_count = lines->line_count; - for (U32 i = 0; i < line_count; i += 1){ - str8_list_pushf(arena, out, " {secrel_off=%llx, line_num=%u}\n", - line_offs[i], line_nums[i]); - } - - str8_list_pushf(arena, out, " {secrel_off=%x, ender}\n", line_offs[line_count]); - } - }break; - - case CV_C13SubSectionKind_FileChksms: - { - str8_list_push(arena, out, str8_lit(" no stringizer path\n")); - }break; - - case CV_C13SubSectionKind_InlineeLines: - { - str8_list_push(arena, out, str8_lit(" no stringizer path\n")); - }break; - - default: - { - str8_list_push(arena, out, str8_lit(" no stringizer path\n")); - }break; - } - - str8_list_push(arena, out, str8_lit("\n")); - } -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ CodeView Common Stringize Functions + +internal void +cv_stringize_numeric(Arena *arena, String8List *out, CV_NumericParsed *num){ + String8 numeric_kind_str = cv_string_from_numeric_kind(num->kind); + str8_list_pushf(arena, out, "(%.*s)", str8_varg(numeric_kind_str)); + + if (cv_numeric_fits_in_u64(num)){ + U64 n = cv_u64_from_numeric(num); + str8_list_pushf(arena, out, "(%llu)", n); + } + else if (cv_numeric_fits_in_s64(num)){ + S64 n = cv_s64_from_numeric(num); + str8_list_pushf(arena, out, "(%lld)", n); + } + else if (cv_numeric_fits_in_f64(num)){ + F64 n = cv_f64_from_numeric(num); + str8_list_pushf(arena, out, "(%f)", n); + } +} + +internal void +cv_stringize_lvar_addr_range(Arena *arena, String8List *out, CV_LvarAddrRange *range){ + str8_list_pushf(arena, out, "{off=%x, sec=%u, len=%u}", range->off, range->sec, range->len); +} + +internal void +cv_stringize_lvar_addr_gap(Arena *arena, String8List *out, CV_LvarAddrGap *gap){ + str8_list_pushf(arena, out, "{off=%x, len=%u}", gap->off, gap->len); +} + +internal void +cv_stringize_lvar_addr_gap_list(Arena *arena, String8List *out, void *first, void *opl){ + U64 gap_count = ((U8*)first - (U8*)opl)/sizeof(CV_LvarAddrGap); + if (gap_count > 0){ + str8_list_push(arena, out, str8_lit(" gaps=\n")); + CV_LvarAddrGap *gap = (CV_LvarAddrGap*)first; + CV_LvarAddrGap *opl = gap + gap_count; + for (;gap < opl; gap += 1){ + str8_list_push(arena, out, str8_lit(" ")); + cv_stringize_lvar_addr_gap(arena, out, gap); + str8_list_push(arena, out, str8_lit("\n")); + } + } +} + +internal String8 +cv_string_from_c13_sub_section_kind(CV_C13SubSectionKind kind){ + String8 result = str8_lit("UNRECOGNIZED_C13_SUB_SECTION_KIND"); + switch (kind){ + case 0: str8_lit("PARSE_ERROR"); break; +#define X(N,c) case CV_C13SubSectionKind_##N: result = str8_lit(#N); break; + CV_C13SubSectionKindXList(X) +#undef X + } + return(result); +} + +internal String8 +cv_string_from_reg(CV_Arch arch, CV_Reg reg){ + String8 result = {0}; + switch (arch){ + default: result = str8_lit(""); break; + + case CV_Arch_8086: + { + switch (reg){ +#define X(CVN,C,RDN,BP,BZ) case CV_Regx86_##CVN: result = str8_lit(#CVN); break; + CV_Reg_X86_XList(X) +#undef X + } + }break; + + case CV_Arch_X64: + { + switch (reg){ +#define X(CVN,C,RDN,BP,BZ) case CV_Regx64_##CVN: result = str8_lit(#CVN); break; + CV_Reg_X64_XList(X) +#undef X + } + }break; + } + return(result); +} + +internal String8 +cv_string_from_pointer_kind(CV_PointerKind ptr_kind){ + String8 result = {0}; + switch (ptr_kind){ + default: result = str8_lit(""); break; + case CV_PointerKind_Near: result = str8_lit("Near"); break; + case CV_PointerKind_Far: result = str8_lit("Far"); break; + case CV_PointerKind_Huge: result = str8_lit("Huge"); break; + case CV_PointerKind_BaseSeg: result = str8_lit("BaseSeg"); break; + case CV_PointerKind_BaseVal: result = str8_lit("BaseVal"); break; + case CV_PointerKind_BaseSegVal: result = str8_lit("BaseSegVal"); break; + case CV_PointerKind_BaseAddr: result = str8_lit("BaseAddr"); break; + case CV_PointerKind_BaseSegAddr: result = str8_lit("BaseSegAddr"); break; + case CV_PointerKind_BaseType: result = str8_lit("BaseType"); break; + case CV_PointerKind_BaseSelf: result = str8_lit("BaseSelf"); break; + case CV_PointerKind_Near32: result = str8_lit("Near32"); break; + case CV_PointerKind_Far32: result = str8_lit("Far32"); break; + case CV_PointerKind_64: result = str8_lit("64"); break; + } + return(result); +} + +internal String8 +cv_string_from_pointer_mode(CV_PointerMode ptr_mode){ + String8 result = {0}; + switch (ptr_mode){ + default: result = str8_lit(""); break; + case CV_PointerMode_Ptr: result = str8_lit("Ptr"); break; + case CV_PointerMode_LRef: result = str8_lit("LRef"); break; + case CV_PointerMode_PtrMem: result = str8_lit("PtrMem"); break; + case CV_PointerMode_PtrMethod: result = str8_lit("PtrMethod"); break; + case CV_PointerMode_RRef: result = str8_lit("RRef"); break; + } + return(result); +} + +internal String8 +cv_string_from_hfa_kind(CV_HFAKind hfa_kind){ + String8 result = {0}; + switch (hfa_kind){ + default: result = str8_lit(""); break; + case CV_HFAKind_None: result = str8_lit("None"); break; + case CV_HFAKind_Float: result = str8_lit("Float"); break; + case CV_HFAKind_Double: result = str8_lit("Double"); break; + case CV_HFAKind_Other: result = str8_lit("Other"); break; + } + return(result); +} + +internal String8 +cv_string_from_mo_com_udt_kind(CV_MoComUDTKind mo_com_udt_kind){ + String8 result = {0}; + switch (mo_com_udt_kind){ + default: result = str8_lit(""); break; + case CV_MoComUDTKind_None: result = str8_lit("None"); break; + case CV_MoComUDTKind_Ref: result = str8_lit("Ref"); break; + case CV_MoComUDTKind_Value: result = str8_lit("Value"); break; + case CV_MoComUDTKind_Interface: result = str8_lit("Interface"); break; + } + return(result); +} + +//////////////////////////////// +//~ CodeView Flags Stringize Functions + +global char cv_stringize_spaces[] = " "; + +#define SPACES cv_stringize_spaces + +internal void +cv_stringize_modifier_flags(Arena *arena, String8List *out, + U32 indent, CV_ModifierFlags flags){ + if (flags & CV_ModifierFlag_Const){ + str8_list_pushf(arena, out, "%.*sConst\n", indent, SPACES); + } + if (flags & CV_ModifierFlag_Volatile){ + str8_list_pushf(arena, out, "%.*sVolatile\n", indent, SPACES); + } + if (flags & CV_ModifierFlag_Unaligned){ + str8_list_pushf(arena, out, "%.*sUnaligned\n", indent, SPACES); + } +} + +internal void +cv_stringize_type_props(Arena *arena, String8List *out, + U32 indent, CV_TypeProps props){ + if (props & CV_TypeProp_Packed){ + str8_list_pushf(arena, out, "%.*sPacked\n", indent, SPACES); + } + if (props & CV_TypeProp_HasConstructorsDestructors){ + str8_list_pushf(arena, out, "%.*sHasConstructorsDesctructors\n", indent, SPACES); + } + if (props & CV_TypeProp_OverloadedOperators){ + str8_list_pushf(arena, out, "%.*sOverloadedOperators\n", indent, SPACES); + } + if (props & CV_TypeProp_IsNested){ + str8_list_pushf(arena, out, "%.*sIsNested\n", indent, SPACES); + } + if (props & CV_TypeProp_ContainsNested){ + str8_list_pushf(arena, out, "%.*sContainsNested\n", indent, SPACES); + } + if (props & CV_TypeProp_OverloadedAssignment){ + str8_list_pushf(arena, out, "%.*sOverloadedAssignment\n", indent, SPACES); + } + if (props & CV_TypeProp_OverloadedCasting){ + str8_list_pushf(arena, out, "%.*sOverloadedCasting\n", indent, SPACES); + } + if (props & CV_TypeProp_FwdRef){ + str8_list_pushf(arena, out, "%.*sFwdRef\n", indent, SPACES); + } + if (props & CV_TypeProp_Scoped){ + str8_list_pushf(arena, out, "%.*sScoped\n", indent, SPACES); + } + if (props & CV_TypeProp_HasUniqueName){ + str8_list_pushf(arena, out, "%.*sHasUniqueName\n", indent, SPACES); + } + if (props & CV_TypeProp_Sealed){ + str8_list_pushf(arena, out, "%.*sSealed\n", indent, SPACES); + } + if (props & CV_TypeProp_Intrinsic){ + str8_list_pushf(arena, out, "%.*sIntrinsic\n", indent, SPACES); + } + + CV_HFAKind hfa = CV_TypeProps_ExtractHFA(props); + { + String8 hfa_str = cv_string_from_hfa_kind(hfa); + str8_list_pushf(arena, out, "%.*shfa=%.*s\n", + indent, SPACES, str8_varg(hfa_str)); + } + + CV_MoComUDTKind mo_com = CV_TypeProps_ExtractMOCOM(props); + { + String8 mo_com_str = cv_string_from_mo_com_udt_kind(mo_com); + str8_list_pushf(arena, out, "%.*smocom=%.*s\n", + indent, SPACES, str8_varg(mo_com_str)); + } +} + +internal void +cv_stringize_pointer_attribs(Arena *arena, String8List *out, + U32 indent, CV_PointerAttribs attribs){ + if (attribs & CV_PointerAttrib_IsFlat){ + str8_list_pushf(arena, out, "%.*sIsFlat\n", indent, SPACES); + } + if (attribs & CV_PointerAttrib_Volatile){ + str8_list_pushf(arena, out, "%.*sVolatile\n", indent, SPACES); + } + if (attribs & CV_PointerAttrib_Const){ + str8_list_pushf(arena, out, "%.*sConst\n", indent, SPACES); + } + if (attribs & CV_PointerAttrib_Unaligned){ + str8_list_pushf(arena, out, "%.*sUnaligned\n", indent, SPACES); + } + if (attribs & CV_PointerAttrib_Restricted){ + str8_list_pushf(arena, out, "%.*sRestricted\n", indent, SPACES); + } + if (attribs & CV_PointerAttrib_MOCOM){ + str8_list_pushf(arena, out, "%.*sMOCOM\n", indent, SPACES); + } + if (attribs & CV_PointerAttrib_LRef){ + str8_list_pushf(arena, out, "%.*sLRef\n", indent, SPACES); + } + if (attribs & CV_PointerAttrib_RRef){ + str8_list_pushf(arena, out, "%.*sRRef\n", indent, SPACES); + } + + CV_PointerKind kind = CV_PointerAttribs_ExtractKind(attribs); + { + String8 kind_str = cv_string_from_pointer_kind(kind); + str8_list_pushf(arena, out, "%.*skind=%.*s\n", + indent, SPACES, str8_varg(kind_str)); + } + + CV_PointerMode mode = CV_PointerAttribs_ExtractMode(attribs); + { + String8 mode_str = cv_string_from_pointer_mode(mode); + str8_list_pushf(arena, out, "%.*smode=%.*s\n", + indent, SPACES, str8_varg(mode_str)); + } + + U32 size = CV_PointerAttribs_ExtractSize(attribs); + str8_list_pushf(arena, out, "%.*ssize=%u\n", + indent, SPACES, size); +} + +internal void +cv_stringize_local_flags(Arena *arena, String8List *out, + U32 indent, CV_LocalFlags flags){ + if (flags & CV_LocalFlag_Param){ + str8_list_pushf(arena, out, "%.*sParam\n", indent, SPACES); + } + if (flags & CV_LocalFlag_AddrTaken){ + str8_list_pushf(arena, out, "%.*sAddrTaken\n", indent, SPACES); + } + if (flags & CV_LocalFlag_Compgen){ + str8_list_pushf(arena, out, "%.*sCompgen\n", indent, SPACES); + } + if (flags & CV_LocalFlag_Aggregate){ + str8_list_pushf(arena, out, "%.*sAggregate\n", indent, SPACES); + } + if (flags & CV_LocalFlag_PartOfAggregate){ + str8_list_pushf(arena, out, "%.*sPartOfAggregate\n", indent, SPACES); + } + if (flags & CV_LocalFlag_Aliased){ + str8_list_pushf(arena, out, "%.*sAliased\n", indent, SPACES); + } + if (flags & CV_LocalFlag_Alias){ + str8_list_pushf(arena, out, "%.*sAlias\n", indent, SPACES); + } + if (flags & CV_LocalFlag_Retval){ + str8_list_pushf(arena, out, "%.*sRetval\n", indent, SPACES); + } + if (flags & CV_LocalFlag_OptOut){ + str8_list_pushf(arena, out, "%.*sOptOut\n", indent, SPACES); + } + if (flags & CV_LocalFlag_Global){ + str8_list_pushf(arena, out, "%.*sGlobal\n", indent, SPACES); + } + if (flags & CV_LocalFlag_Static){ + str8_list_pushf(arena, out, "%.*sStatic\n", indent, SPACES); + } +} + + +#undef SPACES + +//////////////////////////////// +//~ CodeView Sym Stringize Functions + +internal void +cv_stringize_sym_parsed(Arena *arena, String8List *out, CV_SymParsed *sym){ + CV_StringizeSymParams params = {0}; + params.arch = sym->info.arch; + + cv_stringize_sym_array(arena, out, &sym->sym_ranges, sym->data, ¶ms); +} + +internal void +cv_stringize_sym_range(Arena *arena, String8List *out, + CV_RecRange *range, String8 data, + CV_StringizeSymParams *p){ + U64 opl_off = range->off + range->hdr.size; + if (opl_off > data.size){ + str8_list_push(arena, out, str8_lit("bad symbol range\n")); + } + + if (opl_off <= data.size){ + // [off]: kind + { + String8 kind_str = cv_string_from_sym_kind(range->hdr.kind); + str8_list_pushf(arena, out, "[%06x]: %.*s\n", + range->off + 2, str8_varg(kind_str)); + } + + // details + U8 *first = data.str + range->off + 2; + U64 cap = range->hdr.size - 2; + + switch (range->hdr.kind){ + default:break; + + case CV_SymKind_COMPILE: + { + if (sizeof(CV_SymCompile) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymCompile *compile = (CV_SymCompile*)first; + + // machine + String8 machine = cv_string_from_arch(compile->machine); + str8_list_pushf(arena, out, " machine=%.*s\n", + str8_varg(machine)); + + // flags + // TODO(allen): better flags path + str8_list_pushf(arena, out, " flags=%x\n", compile->flags); + + // ver_str + String8 ver_str = str8_cstring_capped((char*)(compile + 1), first + cap); + str8_list_pushf(arena, out, " ver_str='%.*s'\n", str8_varg(ver_str)); + } + }break; + + case CV_SymKind_END: + { + // no contents + }break; + + case CV_SymKind_FRAMEPROC: + { + if (sizeof(CV_SymFrameproc) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymFrameproc *frameproc = (CV_SymFrameproc*)first; + + // frame sizes and offsets + str8_list_pushf(arena, out, " frame_size=%u\n", + frameproc->frame_size); + str8_list_pushf(arena, out, " pad_size=%u\n", + frameproc->pad_size); + str8_list_pushf(arena, out, " pad_off=%u\n", + frameproc->pad_off); + str8_list_pushf(arena, out, " save_reg_size=%u\n", + frameproc->save_reg_size); + str8_list_pushf(arena, out, " eh_off=%x\n", + frameproc->eh_off); + + // eh section + str8_list_pushf(arena, out, " eh_sec=%u\n", + frameproc->eh_sec); + + // flags + // TODO(allen): better flags path + str8_list_pushf(arena, out, " flags=%x\n", frameproc->flags); + } + }break; + + case CV_SymKind_OBJNAME: + { + if (sizeof(CV_SymObjname) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymObjname *objname = (CV_SymObjname*)first; + + // sig + str8_list_pushf(arena, out, " sig=%u\n", objname->sig); + + // name + String8 name = str8_cstring_capped((char*)(objname + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_THUNK32: + { + if (sizeof(CV_SymThunk32) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymThunk32 *thunk32 = (CV_SymThunk32*)first; + + // members + str8_list_pushf(arena, out, " parent=%x\n", thunk32->parent); + str8_list_pushf(arena, out, " end=%x\n", thunk32->end); + str8_list_pushf(arena, out, " next=%x\n", thunk32->next); + str8_list_pushf(arena, out, " off=%u\n", thunk32->off); + str8_list_pushf(arena, out, " sec=%u\n", thunk32->sec); + str8_list_pushf(arena, out, " len=%u\n", thunk32->len); + + // ord + // TODO(allen): better ord path + str8_list_pushf(arena, out, " ord=%u\n", thunk32->ord); + + // name + String8 name = str8_cstring_capped((char*)(thunk32 + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + + // variant + String8 variant = str8_cstring_capped(name.str + name.size + 1, first + cap); + str8_list_pushf(arena, out, " variant='%.*s'\n", str8_varg(variant)); + } + }break; + + case CV_SymKind_BLOCK32: + { + if (sizeof(CV_SymBlock32) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymBlock32 *block32 = (CV_SymBlock32*)first; + + // block attributes + str8_list_pushf(arena, out, " parent=%x\n", block32->parent); + str8_list_pushf(arena, out, " end=%x\n", block32->end); + str8_list_pushf(arena, out, " len=%u\n", block32->len); + str8_list_pushf(arena, out, " off=%x\n", block32->off); + str8_list_pushf(arena, out, " sec=%u\n", block32->sec); + + // name + String8 name = str8_cstring_capped((char*)(block32 + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", + str8_varg(name)); + } + }break; + + case CV_SymKind_LABEL32: + { + if (sizeof(CV_SymLabel32) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymLabel32 *label32 = (CV_SymLabel32*)first; + + // label attributes + str8_list_pushf(arena, out, " off=%x\n", label32->off); + str8_list_pushf(arena, out, " sec=%u\n", label32->sec); + + // flags + // TODO(allen): better flags path + str8_list_pushf(arena, out, " flags=%x\n", label32->flags); + + // name + String8 name = str8_cstring_capped((char*)(label32 + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", + str8_varg(name)); + } + }break; + + case CV_SymKind_CONSTANT: + { + if (sizeof(CV_SymConstant) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymConstant *constant = (CV_SymConstant*)first; + + // itype + str8_list_pushf(arena, out, " itype=%u\n", constant->itype); + + // num + U8 *numeric_ptr = (U8*)(constant + 1); + CV_NumericParsed numeric = cv_numeric_from_data_range(numeric_ptr, first + cap); + str8_list_push(arena, out, str8_lit(" num=")); + cv_stringize_numeric(arena, out, &numeric); + str8_list_push(arena, out, str8_lit("\n")); + + // name + U8 *name_ptr = numeric_ptr + numeric.encoded_size; + String8 name = str8_cstring_capped((char*)(name_ptr), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_UDT: + { + if (sizeof(CV_SymUDT) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymUDT *udt = (CV_SymUDT*)first; + + // itype + str8_list_pushf(arena, out, " itype=%u\n", udt->itype); + + // name + String8 name = str8_cstring_capped((char*)(udt + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_LDATA32: + case CV_SymKind_GDATA32: + { + if (sizeof(CV_SymData32) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymData32 *data32 = (CV_SymData32*)first; + + // itype, off & sec + str8_list_pushf(arena, out, " itype=%u\n off=%x\n sec=%u\n", + data32->itype, data32->off, data32->sec); + + // name + String8 name = str8_cstring_capped((char*)(data32 + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_PUB32: + { + if (sizeof(CV_SymPub32) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymPub32 *pub32 = (CV_SymPub32*)first; + + // flags + CV_PubFlags flags = pub32->flags; + str8_list_push(arena, out, str8_lit(" flags=")); + if (flags == 0){ + str8_list_push(arena, out, str8_lit("0|")); + } + else{ + if (flags&CV_PubFlag_Code){ + str8_list_push(arena, out, str8_lit("Code|")); + } + if (flags&CV_PubFlag_Function){ + str8_list_push(arena, out, str8_lit("Function|")); + } + if (flags&CV_PubFlag_ManagedCode){ + str8_list_push(arena, out, str8_lit("ManagedCode|")); + } + if (flags&CV_PubFlag_MSIL){ + str8_list_push(arena, out, str8_lit("MSIL|")); + } + } + str8_list_push(arena, out, str8_lit("\n")); + + // off & sec + str8_list_pushf(arena, out, " off=%x\n sec=%u\n", pub32->off, pub32->sec); + + // name + String8 name = str8_cstring_capped((char*)(pub32 + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_LPROC32: + case CV_SymKind_GPROC32: + { + if (sizeof(CV_SymProc32) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymProc32 *proc32 = (CV_SymProc32*)first; + + // proc attributes + str8_list_pushf(arena, out, " parent=%x\n", proc32->parent); + str8_list_pushf(arena, out, " end=%x\n", proc32->end); + str8_list_pushf(arena, out, " next=%x\n", proc32->next); + str8_list_pushf(arena, out, " len=%u\n", proc32->len); + str8_list_pushf(arena, out, " dbg_start=%x\n", proc32->dbg_start); + str8_list_pushf(arena, out, " dbg_end=%x\n", proc32->dbg_end); + str8_list_pushf(arena, out, " itype=%u\n", proc32->itype); + str8_list_pushf(arena, out, " off=%x\n", proc32->off); + str8_list_pushf(arena, out, " sec=%u\n", proc32->sec); + + // flags + // TODO(allen): better flags path + str8_list_pushf(arena, out, " flags=%x\n", proc32->flags); + + // name + String8 name = str8_cstring_capped((char*)(proc32 + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_REGREL32: + { + if (sizeof(CV_SymRegrel32) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymRegrel32 *regrel32 = (CV_SymRegrel32*)first; + + // regrel attributes + str8_list_pushf(arena, out, " reg_off=%u\n", regrel32->reg_off); + str8_list_pushf(arena, out, " itype=%u\n", regrel32->itype); + + // reg + String8 reg = cv_string_from_reg(p->arch, regrel32->reg); + str8_list_pushf(arena, out, " reg=%.*s\n", str8_varg(reg)); + + // name + String8 name = str8_cstring_capped((char*)(regrel32 + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_LTHREAD32: + case CV_SymKind_GTHREAD32: + { + if (sizeof(CV_SymThread32) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymThread32 *thread32 = (CV_SymThread32*)first; + + // itype, tls_off, tls_seg + str8_list_pushf(arena, out, " itype=%u\n tls_off=%x\n tls_seg=%u\n", + thread32->itype, thread32->tls_off, thread32->tls_seg); + + // name + String8 name = str8_cstring_capped((char*)(thread32 + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_COMPILE2: + { + if (sizeof(CV_SymCompile2) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymCompile2 *compile2 = (CV_SymCompile2*)first; + + // flags + // TODO(allen): better flags path + str8_list_pushf(arena, out, " flags=%x\n", compile2->flags); + + // machine + String8 machine = cv_string_from_arch(compile2->machine); + str8_list_pushf(arena, out, " machine=%.*s\n", + str8_varg(machine)); + + // ver + str8_list_pushf(arena, out, + " ver_fe_major=%u\n ver_fe_minor=%u\n ver_fe_build=%u\n" + " ver_major=%u\n ver_minor=%u\n ver_build=%u\n", + compile2->ver_fe_major, compile2->ver_fe_minor, compile2->ver_fe_build, + compile2->ver_major, compile2->ver_minor, compile2->ver_build); + + // ver_str + String8 ver_str = str8_cstring_capped((char*)(compile2 + 1), first + cap); + str8_list_pushf(arena, out, " ver_str='%.*s'\n", str8_varg(ver_str)); + } + }break; + + case CV_SymKind_UNAMESPACE: + { + CV_SymUNamespace *unamespace = (CV_SymUNamespace*)first; + + // name + String8 name = str8_cstring_capped((char*)(unamespace), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + }break; + + case CV_SymKind_PROCREF: + case CV_SymKind_DATAREF: + case CV_SymKind_LPROCREF: + { + if (sizeof(CV_SymRef2) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymRef2 *ref2 = (CV_SymRef2*)first; + + // suc_name, sym_off & imod + str8_list_pushf(arena, out, " suc_name=%u\n sym_off=%x\n imod=%u\n", + ref2->suc_name, ref2->sym_off, ref2->imod); + + // name + String8 name = str8_cstring_capped((char*)(ref2 + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_TRAMPOLINE: + { + if (sizeof(CV_SymTrampoline) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymTrampoline *trampoline = (CV_SymTrampoline*)first; + + // kind + // TODO(allen): better kind path + str8_list_pushf(arena, out, " kind=%u\n", trampoline->kind); + + // members + str8_list_pushf(arena, out, " thunk_size=%u\n", trampoline->thunk_size); + str8_list_pushf(arena, out, " thunk_sec_off=%x\n", trampoline->thunk_sec_off); + str8_list_pushf(arena, out, " target_sec_off=%x\n", trampoline->target_sec_off); + str8_list_pushf(arena, out, " thunk_sec=%u\n", trampoline->thunk_sec); + str8_list_pushf(arena, out, " target_sec=%u\n", trampoline->target_sec); + } + }break; + + case CV_SymKind_SECTION: + { + if (sizeof(CV_SymSection) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymSection *section = (CV_SymSection*)first; + + // members + str8_list_pushf(arena, out, " sec_index=%u\n", section->sec_index); + str8_list_pushf(arena, out, " align=%u\n", section->align); + str8_list_pushf(arena, out, " pad=%u\n", section->pad); + str8_list_pushf(arena, out, " rva=%x\n", section->rva); + str8_list_pushf(arena, out, " size=%u\n", section->size); + str8_list_pushf(arena, out, " characteristics=%x\n", section->characteristics); + + // name + String8 name = str8_cstring_capped((char*)(section + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_COFFGROUP: + { + if (sizeof(CV_SymCoffGroup) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymCoffGroup *coff_group = (CV_SymCoffGroup*)first; + + // members + str8_list_pushf(arena, out, " size=%u\n", coff_group->size); + str8_list_pushf(arena, out, " characteristics=%x\n", coff_group->characteristics); + str8_list_pushf(arena, out, " off=%x\n", coff_group->off); + str8_list_pushf(arena, out, " sec=%u\n", coff_group->sec); + + // name + String8 name = str8_cstring_capped((char*)(coff_group + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_CALLSITEINFO: + { + if (sizeof(CV_SymCallSiteInfo) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymCallSiteInfo *callsiteinfo = (CV_SymCallSiteInfo*)first; + + // callsite info attributes + str8_list_pushf(arena, out, " off=%x\n", callsiteinfo->off); + str8_list_pushf(arena, out, " sec=%u\n", callsiteinfo->sec); + str8_list_pushf(arena, out, " pad=%u\n", callsiteinfo->pad); + str8_list_pushf(arena, out, " itype=%u\n", callsiteinfo->itype); + } + }break; + + case CV_SymKind_FRAMECOOKIE: + { + if (sizeof(CV_SymFrameCookie) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymFrameCookie *framecookie = (CV_SymFrameCookie*)first; + + // off + str8_list_pushf(arena, out, " off=%x\n", framecookie->off); + + // reg + String8 reg = cv_string_from_reg(p->arch, framecookie->reg); + str8_list_pushf(arena, out, " reg=%.*s\n", + str8_varg(reg)); + + // kind + // TODO(allen): better kind path + str8_list_pushf(arena, out, " kind=%x\n", framecookie->kind); + + // flags + // TODO(allen): better flags path + str8_list_pushf(arena, out, " flags=%x\n", framecookie->flags); + } + }break; + + case CV_SymKind_COMPILE3: + { + if (sizeof(CV_SymCompile3) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymCompile3 *compile3 = (CV_SymCompile3*)first; + + // flags + // TODO(allen): better flags path + str8_list_pushf(arena, out, " flags=%x\n", compile3->flags); + + // machine + String8 machine = cv_string_from_arch(compile3->machine); + str8_list_pushf(arena, out, " machine=%.*s\n", + str8_varg(machine)); + + // ver + str8_list_pushf(arena, out, + " ver_fe_major=%u\n ver_fe_minor=%u\n ver_fe_build=%u\n" + " ver_major=%u\n ver_minor=%u\n ver_build=%u\n" + " ver_qfe=%u\n", + compile3->ver_fe_major, compile3->ver_fe_minor, compile3->ver_fe_build, + compile3->ver_major, compile3->ver_minor, compile3->ver_build); + // ver_str + String8 ver_str = str8_cstring_capped((char*)(compile3 + 1), first + cap); + str8_list_pushf(arena, out, " ver_str='%.*s'\n", str8_varg(ver_str)); + } + }break; + + case CV_SymKind_ENVBLOCK: + { + if (sizeof(CV_SymEnvBlock) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymEnvBlock *envblock = (CV_SymEnvBlock*)first; + + // flags + str8_list_pushf(arena, out, " flags=%x\n", envblock->flags); + + // name + str8_list_pushf(arena, out, " rgsz=\n"); + char *name_ptr = (char*)(envblock + 1); + for (;;){ + String8 name = str8_cstring_capped(name_ptr, first + cap); + if (name.size == 0){ + break; + } + str8_list_pushf(arena, out, " '%.*s'\n", str8_varg(name)); + name_ptr += name.size + 1; + } + } + }break; + + case CV_SymKind_LOCAL: + { + if (sizeof(CV_SymLocal) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymLocal *slocal = (CV_SymLocal*)first; + + // itype + str8_list_pushf(arena, out, " itype=%u\n", slocal->itype); + + // flags + str8_list_pushf(arena, out, " flags={\n"); + cv_stringize_local_flags(arena, out, 2, slocal->flags); + str8_list_pushf(arena, out, " }\n"); + + // name + String8 name = str8_cstring_capped((char*)(slocal + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_DEFRANGE_REGISTER: + { + if (sizeof(CV_SymDefrangeRegister) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymDefrangeRegister *defrange_register = (CV_SymDefrangeRegister*)first; + + // reg + String8 reg = cv_string_from_reg(p->arch, defrange_register->reg); + str8_list_pushf(arena, out, " reg=%.*s\n", str8_varg(reg)); + + // range attribs + // TODO(allen): better range attribs + str8_list_pushf(arena, out, " attribs=%x\n", defrange_register->attribs); + + // addr range + str8_list_push(arena, out, str8_lit(" range=")); + cv_stringize_lvar_addr_range(arena, out, &defrange_register->range); + str8_list_push(arena, out, str8_lit("\n")); + + // gaps + cv_stringize_lvar_addr_gap_list(arena, out, defrange_register + 1, first + cap); + } + }break; + + case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL: + { + if (sizeof(CV_SymDefrangeFramepointerRel) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymDefrangeFramepointerRel *defrange_fprel = (CV_SymDefrangeFramepointerRel*)first; + + // off + str8_list_pushf(arena, out, " off=%u\n", defrange_fprel->off); + + // addr range + str8_list_push(arena, out, str8_lit(" range=")); + cv_stringize_lvar_addr_range(arena, out, &defrange_fprel->range); + str8_list_push(arena, out, str8_lit("\n")); + + // gaps + cv_stringize_lvar_addr_gap_list(arena, out, defrange_fprel + 1, first + cap); + } + }break; + + case CV_SymKind_DEFRANGE_SUBFIELD_REGISTER: + { + if (sizeof(CV_SymDefrangeSubfieldRegister) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymDefrangeSubfieldRegister *defrange_subfield_register = (CV_SymDefrangeSubfieldRegister*)first; + + // reg + String8 reg = cv_string_from_reg(p->arch, defrange_subfield_register->reg); + str8_list_pushf(arena, out, " reg=%.*s\n", str8_varg(reg)); + + // range attribs + // TODO(allen): better range attribs + str8_list_pushf(arena, out, " attribs=%x\n", defrange_subfield_register->attribs); + + // offset + str8_list_pushf(arena, out, " field_offset=%u\n", + defrange_subfield_register->field_offset); + + // addr range + str8_list_push(arena, out, str8_lit(" range=")); + cv_stringize_lvar_addr_range(arena, out, &defrange_subfield_register->range); + str8_list_push(arena, out, str8_lit("\n")); + + // gaps + cv_stringize_lvar_addr_gap_list(arena, out, defrange_subfield_register + 1, first + cap); + } + }break; + + case CV_SymKind_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE: + { + if (sizeof(CV_SymDefrangeFramepointerRelFullScope) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymDefrangeFramepointerRelFullScope *defrange_fprel_full_scope = + (CV_SymDefrangeFramepointerRelFullScope*)first; + + // off + str8_list_pushf(arena, out, " off=%u\n", defrange_fprel_full_scope->off); + } + }break; + + case CV_SymKind_DEFRANGE_REGISTER_REL: + { + if (sizeof(CV_SymDefrangeRegisterRel) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymDefrangeRegisterRel *defrange_register_rel = (CV_SymDefrangeRegisterRel*)first; + + // reg + String8 reg = cv_string_from_reg(p->arch, defrange_register_rel->reg); + str8_list_pushf(arena, out, " reg=%.*s\n", str8_varg(reg)); + + // flags + // TODO(allen): better flags path + str8_list_pushf(arena, out, " flags=%x\n", defrange_register_rel->flags); + + // reg off + str8_list_pushf(arena, out, " reg_off=%u\n", defrange_register_rel->reg_off); + + // addr range + str8_list_push(arena, out, str8_lit(" range=")); + cv_stringize_lvar_addr_range(arena, out, &defrange_register_rel->range); + str8_list_push(arena, out, str8_lit("\n")); + + // gaps + cv_stringize_lvar_addr_gap_list(arena, out, defrange_register_rel + 1, first + cap); + } + }break; + + case CV_SymKind_BUILDINFO: + { + if (sizeof(CV_SymBuildInfo) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymBuildInfo *buildinfo = (CV_SymBuildInfo*)first; + + // item id + str8_list_pushf(arena, out, " id=%u\n", buildinfo->id); + } + }break; + + case CV_SymKind_INLINESITE: + { + if (sizeof(CV_SymInlineSite) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymInlineSite *inlinesite = (CV_SymInlineSite*)first; + + // members + str8_list_pushf(arena, out, " parent=%x\n", inlinesite->parent); + str8_list_pushf(arena, out, " end=%x\n", inlinesite->end); + str8_list_pushf(arena, out, " inlinee=%u\n", inlinesite->inlinee); + + // binary annotation + // TODO(allen): + } + }break; + + case CV_SymKind_INLINESITE_END: + { + // no contents + }break; + + case CV_SymKind_FILESTATIC: + { + if (sizeof(CV_SymFileStatic) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymFileStatic *file_static = (CV_SymFileStatic*)first; + + // members + str8_list_pushf(arena, out, " itype=%u\n", file_static->itype); + str8_list_pushf(arena, out, " mod_offset=%x\n", file_static->mod_offset); + // TODO(allen): better flags path + str8_list_pushf(arena, out, " flags=%x\n", file_static->flags); + + // name + String8 name = str8_cstring_capped((char*)(file_static + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_SymKind_CALLEES: + case CV_SymKind_CALLERS: + { + if (sizeof(CV_SymFunctionList) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymFunctionList *functions = (CV_SymFunctionList*)first; + + // count + str8_list_pushf(arena, out, " count=%u\n", functions->count); + + // functions + U32 function_count_max = (cap - sizeof(*functions))/sizeof(CV_TypeId); + U32 function_count = ClampTop(functions->count, function_count_max); + if (function_count > 0){ + str8_list_push(arena, out, str8_lit(" functions=\n")); + CV_TypeId *func = (CV_TypeId*)(functions + 1); + CV_TypeId *opl = func + function_count; + for (;func < opl; func += 1){ + str8_list_pushf(arena, out, " %u\n", *func); + } + } + + // invocations + U32 invocation_count_max = (cap - sizeof(*functions) - function_count*sizeof(CV_TypeId))/sizeof(U32); + U32 invocation_count = ClampTop(functions->count, invocation_count_max); + if (invocation_count > 0){ + str8_list_push(arena, out, str8_lit(" invocations=\n")); + U32 *inv = (CV_TypeId*)(functions + 1); + U32 *opl = inv + invocation_count; + for (;inv < opl; inv += 1){ + str8_list_pushf(arena, out, " %u\n", *inv); + } + } + } + }break; + + case CV_SymKind_HEAPALLOCSITE: + { + if (sizeof(CV_SymHeapAllocSite) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymHeapAllocSite *heap_alloc_site = (CV_SymHeapAllocSite*)first; + + // members + str8_list_pushf(arena, out, " off=%x\n", heap_alloc_site->off); + str8_list_pushf(arena, out, " sec=%u\n", heap_alloc_site->sec); + str8_list_pushf(arena, out, " call_inst_len=%u\n", heap_alloc_site->call_inst_len); + str8_list_pushf(arena, out, " itype=%u\n", heap_alloc_site->itype); + } + }break; + + case CV_SymKind_INLINEES: + { + if (sizeof(CV_SymInlinees) > cap){ + str8_list_push(arena, out, str8_lit(" bad symbol range\n")); + } + else{ + CV_SymInlinees *inlinees = (CV_SymInlinees*)first; + + // count + str8_list_pushf(arena, out, " count=%u\n", inlinees->count); + + // desc + U32 desc_count = (cap - sizeof(*inlinees))/sizeof(U32); + if (desc_count > 0){ + str8_list_pushf(arena, out, " desc=\n"); + U32 *desc = (U32*)(inlinees + 1); + U32 *desc_opl = desc + desc_count; + for (;desc < desc_opl; desc += 1){ + str8_list_pushf(arena, out, " %u\n", *desc); + } + } + } + }break; + + case CV_SymKind_REGISTER_16t: + case CV_SymKind_CONSTANT_16t: + case CV_SymKind_UDT_16t: + case CV_SymKind_SSEARCH: + case CV_SymKind_SKIP: + case CV_SymKind_CVRESERVE: + case CV_SymKind_OBJNAME_ST: + case CV_SymKind_ENDARG: + case CV_SymKind_COBOLUDT_16t: + case CV_SymKind_MANYREG_16t: + case CV_SymKind_RETURN: + case CV_SymKind_ENTRYTHIS: + case CV_SymKind_BPREL16: + case CV_SymKind_LDATA16: + case CV_SymKind_GDATA16: + case CV_SymKind_PUB16: + case CV_SymKind_LPROC16: + case CV_SymKind_GPROC16: + case CV_SymKind_THUNK16: + case CV_SymKind_BLOCK16: + case CV_SymKind_WITH16: + case CV_SymKind_LABEL16: + case CV_SymKind_CEXMODEL16: + case CV_SymKind_VFTABLE16: + case CV_SymKind_REGREL16: + case CV_SymKind_BPREL32_16t: + case CV_SymKind_LDATA32_16t: + case CV_SymKind_GDATA32_16t: + case CV_SymKind_PUB32_16t: + case CV_SymKind_LPROC32_16t: + case CV_SymKind_GPROC32_16t: + case CV_SymKind_THUNK32_ST: + case CV_SymKind_BLOCK32_ST: + case CV_SymKind_WITH32_ST: + case CV_SymKind_LABEL32_ST: + case CV_SymKind_CEXMODEL32: + case CV_SymKind_VFTABLE32_16t: + case CV_SymKind_REGREL32_16t: + case CV_SymKind_LTHREAD32_16t: + case CV_SymKind_GTHREAD32_16t: + case CV_SymKind_SLINK32: + case CV_SymKind_LPROCMIPS_16t: + case CV_SymKind_GPROCMIPS_16t: + case CV_SymKind_PROCREF_ST: + case CV_SymKind_DATAREF_ST: + case CV_SymKind_ALIGN: + case CV_SymKind_LPROCREF_ST: + case CV_SymKind_OEM: + case CV_SymKind_TI16_MAX: + case CV_SymKind_CONSTANT_ST: + case CV_SymKind_UDT_ST: + case CV_SymKind_COBOLUDT_ST: + case CV_SymKind_MANYREG_ST: + case CV_SymKind_BPREL32_ST: + case CV_SymKind_LDATA32_ST: + case CV_SymKind_GDATA32_ST: + case CV_SymKind_PUB32_ST: + case CV_SymKind_LPROC32_ST: + case CV_SymKind_GPROC32_ST: + case CV_SymKind_VFTABLE32: + case CV_SymKind_REGREL32_ST: + case CV_SymKind_LTHREAD32_ST: + case CV_SymKind_GTHREAD32_ST: + case CV_SymKind_LPROCMIPS_ST: + case CV_SymKind_GPROCMIPS_ST: + case CV_SymKind_COMPILE2_ST: + case CV_SymKind_MANYREG2_ST: + case CV_SymKind_LPROCIA64_ST: + case CV_SymKind_GPROCIA64_ST: + case CV_SymKind_LOCALSLOT_ST: + case CV_SymKind_PARAMSLOT_ST: + case CV_SymKind_ANNOTATION: + case CV_SymKind_GMANPROC_ST: + case CV_SymKind_LMANPROC_ST: + case CV_SymKind_RESERVED1: + case CV_SymKind_RESERVED2: + case CV_SymKind_RESERVED3: + case CV_SymKind_RESERVED4: + case CV_SymKind_LMANDATA_ST: + case CV_SymKind_GMANDATA_ST: + case CV_SymKind_MANFRAMEREL_ST: + case CV_SymKind_MANREGISTER_ST: + case CV_SymKind_MANSLOT_ST: + case CV_SymKind_MANMANYREG_ST: + case CV_SymKind_MANREGREL_ST: + case CV_SymKind_MANMANYREG2_ST: + case CV_SymKind_MANTYPREF: + case CV_SymKind_UNAMESPACE_ST: + case CV_SymKind_ST_MAX: + case CV_SymKind_WITH32: + case CV_SymKind_REGISTER: + case CV_SymKind_COBOLUDT: + case CV_SymKind_MANYREG: + case CV_SymKind_BPREL32: + case CV_SymKind_LPROCMIPS: + case CV_SymKind_GPROCMIPS: + case CV_SymKind_MANYREG2: + case CV_SymKind_LPROCIA64: + case CV_SymKind_GPROCIA64: + case CV_SymKind_LOCALSLOT: + case CV_SymKind_PARAMSLOT: + case CV_SymKind_LMANDATA: + case CV_SymKind_GMANDATA: + case CV_SymKind_MANFRAMEREL: + case CV_SymKind_MANREGISTER: + case CV_SymKind_MANSLOT: + case CV_SymKind_MANMANYREG: + case CV_SymKind_MANREGREL: + case CV_SymKind_MANMANYREG2: + case CV_SymKind_ANNOTATIONREF: + case CV_SymKind_TOKENREF: + case CV_SymKind_GMANPROC: + case CV_SymKind_LMANPROC: + case CV_SymKind_MANCONSTANT: + case CV_SymKind_ATTR_FRAMEREL: + case CV_SymKind_ATTR_REGISTER: + case CV_SymKind_ATTR_REGREL: + case CV_SymKind_ATTR_MANYREG: + case CV_SymKind_SEPCODE: + case CV_SymKind_DEFRANGE_2005: + case CV_SymKind_DEFRANGE2_2005: + case CV_SymKind_EXPORT: + case CV_SymKind_DISCARDED: + case CV_SymKind_DEFRANGE: + case CV_SymKind_DEFRANGE_SUBFIELD: + case CV_SymKind_LPROC32_ID: + case CV_SymKind_GPROC32_ID: + case CV_SymKind_LPROCMIPS_ID: + case CV_SymKind_GPROCMIPS_ID: + case CV_SymKind_LPROCIA64_ID: + case CV_SymKind_GPROCIA64_ID: + case CV_SymKind_PROC_ID_END: + case CV_SymKind_DEFRANGE_HLSL: + case CV_SymKind_GDATA_HLSL: + case CV_SymKind_LDATA_HLSL: + case CV_SymKind_LPROC32_DPC: + case CV_SymKind_LPROC32_DPC_ID: + case CV_SymKind_DEFRANGE_DPC_PTR_TAG: + case CV_SymKind_DPC_SYM_TAG_MAP: + case CV_SymKind_ARMSWITCHTABLE: + case CV_SymKind_POGODATA: + case CV_SymKind_INLINESITE2: + case CV_SymKind_MOD_TYPEREF: + case CV_SymKind_REF_MINIPDB: + case CV_SymKind_PDBMAP: + case CV_SymKind_GDATA_HLSL32: + case CV_SymKind_LDATA_HLSL32: + case CV_SymKind_GDATA_HLSL32_EX: + case CV_SymKind_LDATA_HLSL32_EX: + case CV_SymKind_FASTLINK: + { + str8_list_push(arena, out, str8_lit(" no stringizer path\n")); + }break; + } + } +} + +internal void +cv_stringize_sym_array(Arena *arena, String8List *out, + CV_RecRangeArray *ranges, String8 data, + CV_StringizeSymParams *p){ + CV_RecRange *ptr = ranges->ranges; + CV_RecRange *opl = ranges->ranges + ranges->count; + for (;ptr < opl; ptr += 1){ + cv_stringize_sym_range(arena, out, ptr, data, p); + str8_list_push(arena, out, str8_lit("\n")); + } +} + +//////////////////////////////// +//~ CodeView Leaf Stringize Functions + +internal void +cv_stringize_leaf_parsed(Arena *arena, String8List *out, CV_LeafParsed *leaf){ + CV_StringizeLeafParams params = {0}; + + cv_stringize_leaf_array(arena, out, &leaf->leaf_ranges, leaf->itype_first, + leaf->data, ¶ms); +} + +internal void +cv_stringize_leaf_range(Arena *arena, String8List *out, + CV_RecRange *range, CV_TypeId itype, String8 data, + CV_StringizeLeafParams *p){ + U64 opl_off = range->off + range->hdr.size; + if (opl_off > data.size){ + str8_list_push(arena, out, str8_lit("bad leaf range\n")); + } + + if (opl_off <= data.size){ + // [off] (itype): kind + { + String8 kind_str = cv_string_from_leaf_kind(range->hdr.kind); + str8_list_pushf(arena, out, "[%06x] (%u): %.*s\n", + range->off + 2, itype, str8_varg(kind_str)); + } + + // details + U8 *first = data.str + range->off + 2; + U64 cap = range->hdr.size - 2; + + switch (range->hdr.kind){ + case CV_LeafKind_VTSHAPE: + { + if (sizeof(CV_LeafVTShape) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafVTShape *vtshape = (CV_LeafVTShape*)first; + + str8_list_pushf(arena, out, " count=%u\n", vtshape->count); + + str8_list_push(arena, out, str8_lit(" shapes=\n")); + U8 *shapes = (U8*)(vtshape + 1); + U32 max_count = (cap - sizeof(*vtshape))*2; + U32 clamped_count = ClampTop(vtshape->count, max_count); + for (U32 i = 0; i < clamped_count; i += 1){ + U32 j = (i >> 1); + U8 s = shapes[j]; + if (j & 1){ + s >>= 4; + } + CV_VirtualTableShape shape = (s & 0xF); + // TODO(allen): better shape path + str8_list_pushf(arena, out, " %u\n", shape); + } + } + }break; + + case CV_LeafKind_LABEL: + { + if (sizeof(CV_LeafLabel) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafLabel *label = (CV_LeafLabel*)first; + + // TODO(allen): better LabelKind path + str8_list_pushf(arena, out, " kind=%x\n", label->kind); + } + }break; + + case CV_LeafKind_MODIFIER: + { + if (sizeof(CV_LeafModifier) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafModifier *modifier = (CV_LeafModifier*)first; + + str8_list_pushf(arena, out, " itype=%u\n", modifier->itype); + str8_list_pushf(arena, out, " flags={\n"); + cv_stringize_modifier_flags(arena, out, 2, modifier->flags); + str8_list_pushf(arena, out, " }\n"); + } + }break; + + case CV_LeafKind_POINTER: + { + if (sizeof(CV_LeafPointer) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafPointer *pointer = (CV_LeafPointer*)first; + + str8_list_pushf(arena, out, " itype=%u\n", pointer->itype); + str8_list_pushf(arena, out, " attribs={\n"); + cv_stringize_pointer_attribs(arena, out, 2, pointer->attribs); + str8_list_pushf(arena, out, " }\n"); + } + }break; + + case CV_LeafKind_PROCEDURE: + { + if (sizeof(CV_LeafProcedure) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafProcedure *procedure = (CV_LeafProcedure*)first; + + str8_list_pushf(arena, out, " ret_itype=%u\n", procedure->ret_itype); + // TODO(allen): better CallKind path + str8_list_pushf(arena, out, " call_kind=%u\n", procedure->call_kind); + // TODO(allen): better flags path + str8_list_pushf(arena, out, " attribs=%x\n", procedure->attribs); + str8_list_pushf(arena, out, " arg_count=%u\n", procedure->arg_count); + str8_list_pushf(arena, out, " arg_itype=%u\n", procedure->arg_itype); + } + }break; + + case CV_LeafKind_MFUNCTION: + { + if (sizeof(CV_LeafMFunction) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafMFunction *mfunction = (CV_LeafMFunction*)first; + + str8_list_pushf(arena, out, " ret_itype=%u\n", mfunction->ret_itype); + str8_list_pushf(arena, out, " class_itype=%u\n", mfunction->class_itype); + str8_list_pushf(arena, out, " this_itype=%u\n", mfunction->this_itype); + // TODO(allen): better CallKind path + str8_list_pushf(arena, out, " call_kind=%u\n", mfunction->call_kind); + // TODO(allen): better flags path + str8_list_pushf(arena, out, " attribs=%x\n", mfunction->attribs); + str8_list_pushf(arena, out, " arg_count=%u\n", mfunction->arg_count); + str8_list_pushf(arena, out, " arg_itype=%u\n", mfunction->arg_itype); + str8_list_pushf(arena, out, " this_adjust=%d\n", mfunction->this_adjust); + } + }break; + + case CV_LeafKind_ARGLIST: + { + if (sizeof(CV_LeafArgList) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafArgList *arg_list = (CV_LeafArgList*)first; + + str8_list_pushf(arena, out, " count=%u\n", arg_list->count); + str8_list_push(arena, out, str8_lit(" itypes=\n")); + + CV_TypeId *itypes = (CV_TypeId*)(arg_list + 1); + U32 max_count = (cap - sizeof(*arg_list))/sizeof(U32); + U32 clamped_count = ClampTop(arg_list->count, max_count); + for (U32 i = 0; i < clamped_count; i += 1){ + str8_list_pushf(arena, out, " %u\n", itypes[i]); + } + } + }break; + + case CV_LeafKind_FIELDLIST: + { + U64 cursor = 0; + for (;cursor + sizeof(CV_LeafKind) <= cap;){ + CV_LeafKind field_kind = *(CV_LeafKind*)(first + cursor); + String8 field_kind_str = cv_string_from_leaf_kind(field_kind); + + str8_list_pushf(arena, out, " field kind: %.*s\n", + str8_varg(field_kind_str)); + + U64 list_item_off = cursor + 2; + + // if we hit an error or forget to set next cursor for a case + // default to exiting the loop + U64 list_item_opl_off = cap; + + switch (field_kind){ + default: + { + str8_list_push(arena, out, str8_lit(" unexpected field kind\n")); + }break; + + case CV_LeafKind_MEMBER: + { + if (list_item_off + sizeof(CV_LeafMember) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafMember *member = (CV_LeafMember*)(first + list_item_off); + + U64 num_off = list_item_off + sizeof(*member); + CV_NumericParsed num = cv_numeric_from_data_range(first + num_off, first + cap); + + U64 name_off = num_off + num.encoded_size; + String8 name = str8_cstring_capped(first + name_off, first + cap); + + list_item_opl_off = name_off + name.size + 1; + + // print data + // TODO(allen): better flags path + str8_list_pushf(arena, out, " attribs=%x\n", member->attribs); + str8_list_pushf(arena, out, " itype=%u\n", member->itype); + str8_list_push(arena, out, str8_lit(" offset=")); + cv_stringize_numeric(arena, out, &num); + str8_list_push(arena, out, str8_lit("\n")); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_LeafKind_STMEMBER: + { + if (list_item_off + sizeof(CV_LeafStMember) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafStMember *stmember = (CV_LeafStMember*)(first + list_item_off); + + U64 name_off = list_item_off + sizeof(*stmember); + String8 name = str8_cstring_capped(first + name_off, first + cap); + + list_item_opl_off = name_off + name.size + 1; + + // print data + // TODO(allen): better flags path + str8_list_pushf(arena, out, " attribs=%x\n", stmember->attribs); + str8_list_pushf(arena, out, " itype=%u\n", stmember->itype); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_LeafKind_METHOD: + { + if (list_item_off + sizeof(CV_LeafMethod) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafMethod *method = (CV_LeafMethod*)(first + list_item_off); + + U64 name_off = list_item_off + sizeof(*method); + String8 name = str8_cstring_capped(first + name_off, first + cap); + + list_item_opl_off = name_off + name.size + 1; + + // print data + str8_list_pushf(arena, out, " count=%u\n", method->count); + str8_list_pushf(arena, out, " list_itype=%u\n", method->list_itype); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_LeafKind_ONEMETHOD: + { + if (list_item_off + sizeof(CV_LeafOneMethod) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafOneMethod *one_method = (CV_LeafOneMethod*)(first + list_item_off); + + U64 vbaseoff_off = list_item_off + sizeof(*one_method); + U64 vbaseoff_opl_off = vbaseoff_off; + U32 vbaseoff = 0; + { + CV_MethodProp prop = CV_FieldAttribs_ExtractMethodProp(one_method->attribs); + if (prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro){ + vbaseoff = *(U32*)(first + vbaseoff_off); + vbaseoff_opl_off += sizeof(vbaseoff); + } + } + + U64 name_off = vbaseoff_opl_off; + String8 name = str8_cstring_capped(first + name_off, first + cap); + + list_item_opl_off = name_off + name.size + 1; + + // print data + // TODO(allen): better flags path + str8_list_pushf(arena, out, " attribs=%x\n", one_method->attribs); + str8_list_pushf(arena, out, " itype=%u\n", one_method->itype); + str8_list_pushf(arena, out, " vbaseoff=%u\n", vbaseoff); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_LeafKind_ENUMERATE: + { + if (list_item_off + sizeof(CV_LeafEnumerate) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafEnumerate *enumerate = (CV_LeafEnumerate*)(first + list_item_off); + + U64 num_off = list_item_off + sizeof(*enumerate); + CV_NumericParsed num = cv_numeric_from_data_range(first + num_off, first + cap); + + U64 name_off = num_off + num.encoded_size; + String8 name = str8_cstring_capped(first + name_off, first + cap); + + list_item_opl_off = name_off + name.size + 1; + + // print data + // TODO(allen): better flags path + str8_list_pushf(arena, out, " attribs=%x\n", enumerate->attribs); + str8_list_push(arena, out, str8_lit(" val=")); + cv_stringize_numeric(arena, out, &num); + str8_list_push(arena, out, str8_lit("\n")); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_LeafKind_NESTTYPE: + { + if (list_item_off + sizeof(CV_LeafNestType) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafNestType *nest_type = (CV_LeafNestType*)(first + list_item_off); + + U64 name_off = list_item_off + sizeof(*nest_type); + String8 name = str8_cstring_capped(first + name_off, first + cap); + + list_item_opl_off = name_off + name.size + 1; + + // print data + str8_list_pushf(arena, out, " itype=%u\n", nest_type->itype); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_LeafKind_NESTTYPEEX: + { + if (list_item_off + sizeof(CV_LeafNestTypeEx) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafNestTypeEx *nest_type = (CV_LeafNestTypeEx*)(first + list_item_off); + + U64 name_off = list_item_off + sizeof(*nest_type); + String8 name = str8_cstring_capped(first + name_off, first + cap); + + list_item_opl_off = name_off + name.size + 1; + + // print data + // TODO(allen): better flags printing + str8_list_pushf(arena, out, " attribs=%x\n", nest_type->attribs); + str8_list_pushf(arena, out, " itype=%u\n", nest_type->itype); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_LeafKind_BCLASS: + { + if (list_item_off + sizeof(CV_LeafBClass) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafBClass *bclass = (CV_LeafBClass*)(first + list_item_off); + + U64 num_off = list_item_off + sizeof(*bclass); + CV_NumericParsed num = cv_numeric_from_data_range(first + num_off, first + cap); + + list_item_opl_off = num_off + num.encoded_size; + + // print data + // TODO(allen): better flags printing + str8_list_pushf(arena, out, " attribs=%x\n", bclass->attribs); + str8_list_pushf(arena, out, " itype=%u\n", bclass->itype); + str8_list_push(arena, out, str8_lit(" offset=")); + cv_stringize_numeric(arena, out, &num); + str8_list_push(arena, out, str8_lit("\n")); + } + }break; + + case CV_LeafKind_VBCLASS: + case CV_LeafKind_IVBCLASS: + { + if (list_item_off + sizeof(CV_LeafVBClass) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafVBClass *vbclass = (CV_LeafVBClass*)(first + list_item_off); + + U64 num1_off = list_item_off + sizeof(*vbclass); + CV_NumericParsed num1 = cv_numeric_from_data_range(first + num1_off, first + cap); + + U64 num2_off = num1_off + num1.encoded_size; + CV_NumericParsed num2 = cv_numeric_from_data_range(first + num2_off, first + cap); + + list_item_opl_off = num2_off + num2.encoded_size; + + // print data + // TODO(allen): better flags printing + str8_list_pushf(arena, out, " attribs=%x\n", vbclass->attribs); + str8_list_pushf(arena, out, " itype=%u\n", vbclass->itype); + str8_list_pushf(arena, out, " vbptr_itype=%u\n", vbclass->vbptr_itype); + str8_list_push(arena, out, str8_lit(" vbptr_off=")); + cv_stringize_numeric(arena, out, &num1); + str8_list_push(arena, out, str8_lit("\n")); + str8_list_push(arena, out, str8_lit(" vtable_off=")); + cv_stringize_numeric(arena, out, &num2); + str8_list_push(arena, out, str8_lit("\n")); + } + }break; + + case CV_LeafKind_INDEX: + { + if (list_item_off + sizeof(CV_LeafIndex) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafIndex *index = (CV_LeafIndex*)(first + list_item_off); + + list_item_opl_off = list_item_off + sizeof(*index); + + // print data + str8_list_pushf(arena, out, " itype=%u\n", index->itype); + } + }break; + + case CV_LeafKind_VFUNCTAB: + { + if (list_item_off + sizeof(CV_LeafVFuncTab) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafVFuncTab *vfunctab = (CV_LeafVFuncTab*)(first + list_item_off); + + list_item_opl_off = list_item_off + sizeof(*vfunctab); + + // print data + str8_list_pushf(arena, out, " itype=%u\n", vfunctab->itype); + } + }break; + + case CV_LeafKind_VFUNCOFF: + { + if (list_item_off + sizeof(CV_LeafVFuncOff) > cap){ + str8_list_push(arena, out, str8_lit(" bad field list range\n")); + } + else{ + // compute whole layout + CV_LeafVFuncOff *vfuncoff = (CV_LeafVFuncOff*)(first + list_item_off); + + list_item_opl_off = list_item_off + sizeof(*vfuncoff); + + // print data + str8_list_pushf(arena, out, " itype=%u\n", vfuncoff->itype); + str8_list_pushf(arena, out, " off=%u\n", vfuncoff->off); + } + }break; + } + + // update cursor + U64 next_cursor = AlignPow2(list_item_opl_off, 4); + cursor = next_cursor; + } + }break; + + case CV_LeafKind_BITFIELD: + { + if (sizeof(CV_LeafBitField) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafBitField *bit_field = (CV_LeafBitField*)first; + + str8_list_pushf(arena, out, " itype=%u\n", bit_field->itype); + str8_list_pushf(arena, out, " len=%u\n", bit_field->len); + str8_list_pushf(arena, out, " pos=%u\n", bit_field->pos); + } + }break; + + case CV_LeafKind_METHODLIST: + { + U64 cursor = 0; + for (;cursor + sizeof(CV_LeafMethodListMember) <= cap;){ + CV_LeafMethodListMember *method = (CV_LeafMethodListMember*)(first + cursor); + + // extract vbaseoff + U64 next_cursor = cursor + sizeof(*method); + U32 vbaseoff = 0; + { + CV_MethodProp prop = CV_FieldAttribs_ExtractMethodProp(method->attribs); + if (prop == CV_MethodProp_Intro || prop == CV_MethodProp_PureIntro){ + if (cursor + sizeof(*method) + 4 <= cap){ + vbaseoff = *(U32*)(method + 1); + } + next_cursor += 4; + } + } + + // print + // TODO(allen): better flags path + str8_list_pushf(arena, out, " method\n", method->attribs); + str8_list_pushf(arena, out, " attribs=%x\n", method->attribs); + str8_list_pushf(arena, out, " itype=%u\n", method->itype); + str8_list_pushf(arena, out, " vbaseoff=%u\n", vbaseoff); + + // update cursor + cursor = next_cursor; + } + }break; + + case CV_LeafKind_ARRAY: + { + if (sizeof(CV_LeafArray) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafArray *array = (CV_LeafArray*)first; + + str8_list_pushf(arena, out, " entry_itype=%u\n", array->entry_itype); + str8_list_pushf(arena, out, " index_itype=%u\n", array->index_itype); + + // count + U8 *numeric_ptr = (U8*)(array + 1); + CV_NumericParsed array_count = cv_numeric_from_data_range(numeric_ptr, first + cap); + str8_list_pushf(arena, out, " count="); + cv_stringize_numeric(arena, out, &array_count); + str8_list_push(arena, out, str8_lit("\n")); + } + }break; + + case CV_LeafKind_CLASS: + case CV_LeafKind_STRUCTURE: + { + if (sizeof(CV_LeafStruct) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafStruct *lf_struct = (CV_LeafStruct*)first; + + str8_list_pushf(arena, out, " count=%u\n", lf_struct->count); + str8_list_pushf(arena, out, " props=%x (\n", lf_struct->props); + cv_stringize_type_props(arena, out, 2, lf_struct->props); + str8_list_pushf(arena, out, " )\n"); + str8_list_pushf(arena, out, " field_itype=%u\n", lf_struct->field_itype); + str8_list_pushf(arena, out, " derived_itype=%u\n", lf_struct->derived_itype); + str8_list_pushf(arena, out, " vshape_itype=%u\n", lf_struct->vshape_itype); + + U8 *numeric_ptr = (U8*)(lf_struct + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + str8_list_pushf(arena, out, " size="); + cv_stringize_numeric(arena, out, &size); + str8_list_push(arena, out, str8_lit("\n")); + + String8 name = str8_cstring_capped((U8*)(numeric_ptr + size.encoded_size), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + + String8 unique_name = str8_cstring_capped(name.str + name.size + 1, first + cap); + str8_list_pushf(arena, out, " unique_name='%.*s'\n", str8_varg(unique_name)); + } + }break; + + case CV_LeafKind_UNION: + { + if (sizeof(CV_LeafUnion) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafUnion *lf_union = (CV_LeafUnion*)first; + + str8_list_pushf(arena, out, " count=%u\n", lf_union->count); + str8_list_pushf(arena, out, " props=%x (\n", lf_union->props); + cv_stringize_type_props(arena, out, 2, lf_union->props); + str8_list_pushf(arena, out, " )\n"); + str8_list_pushf(arena, out, " field_itype=%u\n", lf_union->field_itype); + + U8 *numeric_ptr = (U8*)(lf_union + 1); + CV_NumericParsed size = cv_numeric_from_data_range(numeric_ptr, first + cap); + str8_list_pushf(arena, out, " size="); + cv_stringize_numeric(arena, out, &size); + str8_list_push(arena, out, str8_lit("\n")); + + String8 name = str8_cstring_capped((U8*)(numeric_ptr + size.encoded_size), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + + String8 unique_name = str8_cstring_capped(name.str + name.size + 1, first + cap); + str8_list_pushf(arena, out, " unique_name='%.*s'\n", str8_varg(unique_name)); + } + }break; + + case CV_LeafKind_ENUM: + { + if (sizeof(CV_LeafEnum) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafEnum *lf_enum = (CV_LeafEnum*)first; + + str8_list_pushf(arena, out, " count=%u\n", lf_enum->count); + str8_list_pushf(arena, out, " props=%x (\n", lf_enum->props); + cv_stringize_type_props(arena, out, 2, lf_enum->props); + str8_list_pushf(arena, out, " )\n"); + str8_list_pushf(arena, out, " base_itype=%u\n", lf_enum->base_itype); + str8_list_pushf(arena, out, " field_itype=%u\n", lf_enum->field_itype); + + String8 name = str8_cstring_capped((U8*)(lf_enum + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + + String8 unique_name = str8_cstring_capped(name.str + name.size + 1, first + cap); + str8_list_pushf(arena, out, " unique_name='%.*s'\n", str8_varg(unique_name)); + } + }break; + + case CV_LeafKind_VFTABLE: + { + if (sizeof(CV_LeafVFTable) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafVFTable *vftable = (CV_LeafVFTable*)first; + + str8_list_pushf(arena, out, " owner_itype=%u\n", vftable->owner_itype); + str8_list_pushf(arena, out, " base_table_itype=%u\n", vftable->base_table_itype); + str8_list_pushf(arena, out, " offset_in_object_layout=%u\n", + vftable->offset_in_object_layout); + str8_list_pushf(arena, out, " names_len=%u\n", vftable->names_len); + + U64 names_cap = Min(sizeof(*vftable) + vftable->names_len, cap); + + str8_list_push(arena, out, str8_lit(" names=\n")); + U8 *ptr = (U8*)(vftable + 1); + U8 *opl = first + names_cap; + for (;ptr < opl;){ + String8 name = str8_cstring_capped(ptr, opl); + str8_list_pushf(arena, out, " '%.*s'\n", str8_varg(name)); + ptr += name.size + 1; + } + } + }break; + + case CV_LeafKind_CLASS2: + case CV_LeafKind_STRUCT2: + { + if (sizeof(CV_LeafStruct2) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafStruct2 *struct2 = (CV_LeafStruct2*)first; + + str8_list_pushf(arena, out, " props=%x (\n", struct2->props); + cv_stringize_type_props(arena, out, 2, struct2->props); + str8_list_pushf(arena, out, " )\n"); + str8_list_pushf(arena, out, " unknown1=%u\n", struct2->unknown1); + str8_list_pushf(arena, out, " field_itype=%u\n", struct2->field_itype); + str8_list_pushf(arena, out, " derived_itype=%u\n", struct2->derived_itype); + str8_list_pushf(arena, out, " vshape_itype=%u\n", struct2->vshape_itype); + str8_list_pushf(arena, out, " unknown2=0x%x\n", struct2->unknown2); + //str8_list_pushf(arena, out, " size=%u\n", struct2->size); + + String8 name = str8_cstring_capped((U8*)(struct2 + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + + String8 unique_name = str8_cstring_capped(name.str + name.size + 1, first + cap); + str8_list_pushf(arena, out, " unique_name='%.*s'\n", str8_varg(unique_name)); + } + }break; + + case CV_LeafIDKind_FUNC_ID: + { + if (sizeof(CV_LeafFuncId) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafFuncId *func_id = (CV_LeafFuncId*)first; + + str8_list_pushf(arena, out, " scope_string_id=%u\n", func_id->scope_string_id); + str8_list_pushf(arena, out, " itype=%u\n", func_id->itype); + + String8 name = str8_cstring_capped((U8*)(func_id + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_LeafIDKind_MFUNC_ID: + { + if (sizeof(CV_LeafMFuncId) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafMFuncId *mfunc_id = (CV_LeafMFuncId*)first; + + str8_list_pushf(arena, out, " owner_itype=%u\n", mfunc_id->owner_itype); + str8_list_pushf(arena, out, " itype=%u\n", mfunc_id->itype); + + String8 name = str8_cstring_capped((U8*)(mfunc_id + 1), first + cap); + str8_list_pushf(arena, out, " name='%.*s'\n", str8_varg(name)); + } + }break; + + case CV_LeafIDKind_BUILDINFO: + { + if (sizeof(CV_LeafBuildInfo) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafBuildInfo *build_info = (CV_LeafBuildInfo*)first; + + str8_list_pushf(arena, out, " count=%u\n", build_info->count); + + CV_ItemId *item_ids = (CV_ItemId*)(build_info + 1); + str8_list_pushf(arena, out, " items=\n"); + U32 max_count = (cap - sizeof(*build_info))/sizeof(CV_ItemId); + U32 clamped_count = ClampTop(build_info->count, max_count); + for (U32 i = 0; i < clamped_count; i += 1){ + CV_ItemId item_id = item_ids[i]; + str8_list_pushf(arena, out, " %u\n", item_id); + } + } + }break; + + case CV_LeafIDKind_SUBSTR_LIST: + { + if (sizeof(CV_LeafSubstrList) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafSubstrList *substr_list = (CV_LeafSubstrList*)first; + + str8_list_pushf(arena, out, " count=%u\n", substr_list->count); + + str8_list_pushf(arena, out, " items=\n"); + U32 max_count = (cap - sizeof(CV_LeafSubstrList))/sizeof(CV_ItemId); + CV_ItemId *item_ids = (CV_ItemId*)(substr_list + 1); + U32 clamped_count = ClampTop(substr_list->count, max_count); + for (U32 i = 0; i < clamped_count; i += 1){ + CV_ItemId item_id = item_ids[i]; + str8_list_pushf(arena, out, " %u\n", item_id); + } + } + }break; + + case CV_LeafIDKind_STRING_ID: + { + if (sizeof(CV_LeafStringId) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafStringId *string_id = (CV_LeafStringId*)first; + + str8_list_pushf(arena, out, " substr_list_id=%u\n", string_id->substr_list_id); + + String8 string = str8_cstring_capped((U8*)(string_id + 1), first + cap); + str8_list_pushf(arena, out, " string='%.*s'\n", str8_varg(string)); + } + }break; + + case CV_LeafIDKind_UDT_SRC_LINE: + { + if (sizeof(CV_LeafUDTSrcLine) > cap){ + str8_list_push(arena, out, str8_lit(" bad leaf range\n")); + } + else{ + CV_LeafUDTSrcLine *udt_src_line = (CV_LeafUDTSrcLine*)first; + + str8_list_pushf(arena, out, " udt_itype=%u\n", udt_src_line->udt_itype); + str8_list_pushf(arena, out, " src_string_id=%u\n", udt_src_line->src_string_id); + str8_list_pushf(arena, out, " line=%u\n", udt_src_line->line); + } + }break; + + + default: + + // Leaf Kinds + case CV_LeafKind_MODIFIER_16t: + case CV_LeafKind_POINTER_16t: + case CV_LeafKind_ARRAY_16t: + case CV_LeafKind_CLASS_16t: + case CV_LeafKind_STRUCTURE_16t: + case CV_LeafKind_UNION_16t: + case CV_LeafKind_ENUM_16t: + case CV_LeafKind_PROCEDURE_16t: + case CV_LeafKind_MFUNCTION_16t: + //case CV_LeafKind_VTSHAPE: + case CV_LeafKind_COBOL0_16t: + case CV_LeafKind_COBOL1: + case CV_LeafKind_BARRAY_16t: + //case CV_LeafKind_LABEL: + case CV_LeafKind_NULL: + case CV_LeafKind_NOTTRAN: + case CV_LeafKind_DIMARRAY_16t: + case CV_LeafKind_VFTPATH_16t: + case CV_LeafKind_PRECOMP_16t: + case CV_LeafKind_ENDPRECOMP: + case CV_LeafKind_OEM_16t: + case CV_LeafKind_TYPESERVER_ST: + case CV_LeafKind_SKIP_16t: + case CV_LeafKind_ARGLIST_16t: + case CV_LeafKind_DEFARG_16t: + case CV_LeafKind_LIST: + case CV_LeafKind_FIELDLIST_16t: + case CV_LeafKind_DERIVED_16t: + case CV_LeafKind_BITFIELD_16t: + case CV_LeafKind_METHODLIST_16t: + case CV_LeafKind_DIMCONU_16t: + case CV_LeafKind_DIMCONLU_16t: + case CV_LeafKind_DIMVARU_16t: + case CV_LeafKind_DIMVARLU_16t: + case CV_LeafKind_REFSYM: + case CV_LeafKind_BCLASS_16t: + case CV_LeafKind_VBCLASS_16t: + case CV_LeafKind_IVBCLASS_16t: + case CV_LeafKind_ENUMERATE_ST: + case CV_LeafKind_FRIENDFCN_16t: + case CV_LeafKind_INDEX_16t: + case CV_LeafKind_MEMBER_16t: + case CV_LeafKind_STMEMBER_16t: + case CV_LeafKind_METHOD_16t: + case CV_LeafKind_NESTTYPE_16t: + case CV_LeafKind_VFUNCTAB_16t: + case CV_LeafKind_FRIENDCLS_16t: + case CV_LeafKind_ONEMETHOD_16t: + case CV_LeafKind_VFUNCOFF_16t: + case CV_LeafKind_TI16_MAX: + //case CV_LeafKind_MODIFIER: + //case CV_LeafKind_POINTER: + case CV_LeafKind_ARRAY_ST: + case CV_LeafKind_CLASS_ST: + case CV_LeafKind_STRUCTURE_ST: + case CV_LeafKind_UNION_ST: + case CV_LeafKind_ENUM_ST: + //case CV_LeafKind_PROCEDURE: + //case CV_LeafKind_MFUNCTION: + case CV_LeafKind_COBOL0: + case CV_LeafKind_BARRAY: + case CV_LeafKind_DIMARRAY_ST: + case CV_LeafKind_VFTPATH: + case CV_LeafKind_PRECOMP_ST: + case CV_LeafKind_OEM: + case CV_LeafKind_ALIAS_ST: + case CV_LeafKind_OEM2: + case CV_LeafKind_SKIP: + //case CV_LeafKind_ARGLIST: + case CV_LeafKind_DEFARG_ST: + //case CV_LeafKind_FIELDLIST: + case CV_LeafKind_DERIVED: + //case CV_LeafKind_BITFIELD: + //case CV_LeafKind_METHODLIST: + case CV_LeafKind_DIMCONU: + case CV_LeafKind_DIMCONLU: + case CV_LeafKind_DIMVARU: + case CV_LeafKind_DIMVARLU: + case CV_LeafKind_BCLASS: + case CV_LeafKind_VBCLASS: + case CV_LeafKind_IVBCLASS: + case CV_LeafKind_FRIENDFCN_ST: + //case CV_LeafKind_INDEX: + case CV_LeafKind_MEMBER_ST: + case CV_LeafKind_STMEMBER_ST: + case CV_LeafKind_METHOD_ST: + case CV_LeafKind_NESTTYPE_ST: + case CV_LeafKind_VFUNCTAB: + case CV_LeafKind_FRIENDCLS: + case CV_LeafKind_ONEMETHOD_ST: + case CV_LeafKind_VFUNCOFF: + case CV_LeafKind_NESTTYPEEX_ST: + case CV_LeafKind_MEMBERMODIFY_ST: + case CV_LeafKind_MANAGED_ST: + case CV_LeafKind_ST_MAX: + case CV_LeafKind_TYPESERVER: + case CV_LeafKind_ENUMERATE: + //case CV_LeafKind_ARRAY: + //case CV_LeafKind_CLASS: + //case CV_LeafKind_STRUCTURE: + //case CV_LeafKind_UNION: + //case CV_LeafKind_ENUM: + case CV_LeafKind_DIMARRAY: + case CV_LeafKind_PRECOMP: + case CV_LeafKind_ALIAS: + case CV_LeafKind_DEFARG: + case CV_LeafKind_FRIENDFCN: + case CV_LeafKind_MEMBER: + case CV_LeafKind_STMEMBER: + case CV_LeafKind_METHOD: + case CV_LeafKind_NESTTYPE: + case CV_LeafKind_ONEMETHOD: + case CV_LeafKind_NESTTYPEEX: + case CV_LeafKind_MEMBERMODIFY: + case CV_LeafKind_MANAGED: + case CV_LeafKind_TYPESERVER2: + case CV_LeafKind_STRIDED_ARRAY: + case CV_LeafKind_HLSL: + case CV_LeafKind_MODIFIER_EX: + case CV_LeafKind_INTERFACE: + case CV_LeafKind_BINTERFACE: + case CV_LeafKind_VECTOR: + case CV_LeafKind_MATRIX: + //case CV_LeafKind_VFTABLE: + + // Leaf ID Kinds + //case CV_LeafIDKind_FUNC_ID: + //case CV_LeafIDKind_MFUNC_ID: + //case CV_LeafIDKind_BUILDINFO: + //case CV_LeafIDKind_SUBSTR_LIST: + //case CV_LeafIDKind_STRING_ID: + //case CV_LeafIDKind_UDT_SRC_LINE: + case CV_LeafIDKind_UDT_MOD_SRC_LINE: + + { + str8_list_push(arena, out, str8_lit(" no stringizer path\n")); + }break; + } + } +} + +internal void +cv_stringize_leaf_array(Arena *arena, String8List *out, + CV_RecRangeArray *ranges, CV_TypeId itype_first, String8 data, + CV_StringizeLeafParams *p){ + CV_RecRange *ptr = ranges->ranges; + CV_RecRange *opl = ranges->ranges + ranges->count; + CV_TypeId itype = itype_first; + for (;ptr < opl; ptr += 1, itype += 1){ + cv_stringize_leaf_range(arena, out, ptr, itype, data, p); + str8_list_push(arena, out, str8_lit("\n")); + } +} + +//////////////////////////////// +//~ CodeView C13 Stringize Functions + +internal void +cv_stringize_c13_parsed(Arena *arena, String8List *out, CV_C13Parsed *c13){ + for(CV_C13SubSectionNode *node = c13->first_sub_section; + node != 0; + node = node->next) + { + String8 kind_str = cv_string_from_c13_sub_section_kind(node->kind); + str8_list_pushf(arena, out, "C13 Sub Section [%llx] (%.*s):\n", + node->off, str8_varg(kind_str)); + + switch(node->kind) + { + case CV_C13SubSectionKind_Lines: + { + if (node->lines_first == 0) + { + str8_list_push(arena, out, str8_lit(" failed to extract info\n")); + } + else for(CV_C13LinesParsedNode *n = node->lines_first; n != 0; n = n->next) + { + CV_C13LinesParsed *lines = &n->v; + + str8_list_pushf(arena, out, " section: %u\n", lines->sec_idx); + str8_list_pushf(arena, out, " file off: %u\n", lines->file_off); + str8_list_pushf(arena, out, " file name: %.*s\n", str8_varg(lines->file_name)); + str8_list_pushf(arena, out, " line count: %u\n", lines->line_count); + + U64 base_off = lines->secrel_base_off; + U64 *line_offs = lines->voffs; + U32 *line_nums = lines->line_nums; + + U32 line_count = lines->line_count; + for (U32 i = 0; i < line_count; i += 1){ + str8_list_pushf(arena, out, " {secrel_off=%llx, line_num=%u}\n", + line_offs[i], line_nums[i]); + } + + str8_list_pushf(arena, out, " {secrel_off=%x, ender}\n", line_offs[line_count]); + } + }break; + + case CV_C13SubSectionKind_FileChksms: + { + str8_list_push(arena, out, str8_lit(" no stringizer path\n")); + }break; + + case CV_C13SubSectionKind_InlineeLines: + { + str8_list_push(arena, out, str8_lit(" no stringizer path\n")); + }break; + + default: + { + str8_list_push(arena, out, str8_lit(" no stringizer path\n")); + }break; + } + + str8_list_push(arena, out, str8_lit("\n")); + } +} diff --git a/src/codeview/codeview_stringize.h b/src/codeview/codeview_stringize.h index 9a94d9c4..ed0b4f60 100644 --- a/src/codeview/codeview_stringize.h +++ b/src/codeview/codeview_stringize.h @@ -1,82 +1,82 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef CODEVIEW_STRINGIZE_H -#define CODEVIEW_STRINGIZE_H - -//////////////////////////////// -//~ CodeView Stringize Helper Types - -typedef struct CV_StringizeSymParams{ - CV_Arch arch; -} CV_StringizeSymParams; - -typedef struct CV_StringizeLeafParams{ - U32 dummy; -} CV_StringizeLeafParams; - -//////////////////////////////// -//~ CodeView Common Stringize Functions - -internal void cv_stringize_numeric(Arena *arena, String8List *out, CV_NumericParsed *num); - -internal void cv_stringize_lvar_addr_range(Arena *arena, String8List *out, - CV_LvarAddrRange *range); -internal void cv_stringize_lvar_addr_gap(Arena *arena, String8List *out, CV_LvarAddrGap *gap); -internal void cv_stringize_lvar_addr_gap_list(Arena *arena, String8List *out, - void *first, void *opl); - -internal String8 cv_string_from_basic_type(CV_BasicType basic_type); -internal String8 cv_string_from_c13_sub_section_kind(CV_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); - -#endif // CODEVIEW_STRINGIZE_H +// 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); + +#endif // CODEVIEW_STRINGIZE_H diff --git a/src/ctrl/ctrl.mdesk b/src/ctrl/ctrl.mdesk index af133ebc..24d589a0 100644 --- a/src/ctrl/ctrl.mdesk +++ b/src/ctrl/ctrl.mdesk @@ -1,81 +1,81 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Tables - -@table(name lower_name code default display_string) -CTRL_ExceptionCodeKindTable: -{ - {Win32CtrlC win32_ctrl_c 0x40010005 1 "(Win32) Control-C" } - {Win32CtrlBreak win32_ctrl_break 0x40010008 1 "(Win32) Control-Break" } - {Win32WinRTOriginateError win32_win_rt_originate_error 0x40080201 0 "(Win32) WinRT Originate Error" } - {Win32WinRTTransformError win32_win_rt_transform_error 0x40080202 0 "(Win32) WinRT Transform Error" } - {Win32RPCCallCancelled win32_rpc_call_cancelled 0x0000071a 0 "(Win32) RPC Call Cancelled" } - {Win32DatatypeMisalignment win32_datatype_misalignment 0x80000002 0 "(Win32) Data Type Misalignment" } - {Win32AccessViolation win32_access_violation 0xc0000005 1 "(Win32) Access Violation" } - {Win32InPageError win32_in_page_error 0xc0000006 0 "(Win32) In Page Error" } - {Win32InvalidHandle win32_invalid_handle 0xc0000008 1 "(Win32) Invalid Handle Specified" } - {Win32NotEnoughQuota win32_not_enough_quota 0xc0000017 0 "(Win32) Not Enough Quota" } - {Win32IllegalInstruction win32_illegal_instruction 0xc000001d 0 "(Win32) Illegal Instruction" } - {Win32CannotContinueException win32_cannot_continue_exception 0xc0000025 0 "(Win32) Cannot Continue From Exception" } - {Win32InvalidExceptionDisposition win32_invalid_exception_disposition 0xc0000026 0 "(Win32) Invalid Exception Disposition Returned By Handler" } - {Win32ArrayBoundsExceeded win32_array_bounds_exceeded 0xc000008c 0 "(Win32) Array Bounds Exceeded" } - {Win32FloatingPointDenormalOperand win32_floating_point_denormal_operand 0xc000008d 0 "(Win32) Floating-Point Denormal Operand" } - {Win32FloatingPointDivisionByZero win32_floating_point_division_by_zero 0xc000008e 0 "(Win32) Floating-Point Division By Zero" } - {Win32FloatingPointInexactResult win32_floating_point_inexact_result 0xc000008f 0 "(Win32) Floating-Point Inexact Result" } - {Win32FloatingPointInvalidOperation win32_floating_point_invalid_operation 0xc0000090 0 "(Win32) Floating-Point Invalid Operation" } - {Win32FloatingPointOverflow win32_floating_point_overflow 0xc0000091 0 "(Win32) Floating-Point Overflow" } - {Win32FloatingPointStackCheck win32_floating_point_stack_check 0xc0000092 0 "(Win32) Floating-Point Stack Check" } - {Win32FloatingPointUnderflow win32_floating_point_underflow 0xc0000093 0 "(Win32) Floating-Point Underflow" } - {Win32IntegerDivisionByZero win32_integer_division_by_zero 0xc0000094 0 "(Win32) Integer Division By Zero" } - {Win32IntegerOverflow win32_integer_overflow 0xc0000095 0 "(Win32) Integer Overflow" } - {Win32PrivilegedInstruction win32_privileged_instruction 0xc0000096 0 "(Win32) Privileged Instruction" } - {Win32StackOverflow win32_stack_overflow 0xc00000fd 0 "(Win32) Stack Overflow" } - {Win32UnableToLocateDLL win32_unable_to_locate_dll 0xc0000135 0 "(Win32) Unable To Locate DLL" } - {Win32OrdinalNotFound win32_ordinal_not_found 0xc0000138 0 "(Win32) Ordinal Not Found" } - {Win32EntryPointNotFound win32_entry_point_not_found 0xc0000139 0 "(Win32) Entry Point Not Found" } - {Win32DLLInitializationFailed win32_dll_initialization_failed 0xc0000142 0 "(Win32) DLL Initialization Failed" } - {Win32FloatingPointSSEMultipleFaults win32_floating_point_sse_multiple_faults 0xc00002b4 0 "(Win32) Floating Point SSE Multiple Faults" } - {Win32FloatingPointSSEMultipleTraps win32_floating_point_sse_multiple_traps 0xc00002b5 0 "(Win32) Floating Point SSE Multiple Traps" } - {Win32AssertionFailed win32_assertion_failed 0xc0000420 1 "(Win32) Assertion Failed" } - {Win32ModuleNotFound win32_module_not_found 0xc06d007e 0 "(Win32) Module Not Found" } - {Win32ProcedureNotFound win32_procedure_not_found 0xc06d007f 0 "(Win32) Procedure Not Found" } - {Win32SanitizerErrorDetected win32_sanitizer_error_detected 0xe073616e 1 "(Win32) Sanitizer Error Detected" } - {Win32SanitizerRawAccessViolation win32_sanitizer_raw_access_violation 0xe0736171 0 "(Win32) Sanitizer Raw Access Violation" } - {Win32DirectXDebugLayer win32_directx_debug_layer 0x0000087a 1 "(Win32) DirectX Debug Layer" } -} - -//////////////////////////////// -//~ rjf: Generators - -@enum CTRL_ExceptionCodeKind: -{ - Null, - @expand(CTRL_ExceptionCodeKindTable a) `$(a.name)`, - COUNT, -} - -@data(U32) ctrl_exception_code_kind_code_table: -{ - `0`; - @expand(CTRL_ExceptionCodeKindTable a) `$(a.code)`; -} - -@data(String8) ctrl_exception_code_kind_display_string_table: -{ - `{0}`; - @expand(CTRL_ExceptionCodeKindTable a) `str8_lit_comp("$(a.display_string)")`; -} - -@data(String8) ctrl_exception_code_kind_lowercase_code_string_table: -{ - `{0}`; - @expand(CTRL_ExceptionCodeKindTable a) `str8_lit_comp("$(a.lower_name)")`; -} - -@data(B8) ctrl_exception_code_kind_default_enable_table: -{ - `0`; - @expand(CTRL_ExceptionCodeKindTable a) `$(a.default)`; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Tables + +@table(name lower_name code default display_string) +CTRL_ExceptionCodeKindTable: +{ + {Win32CtrlC win32_ctrl_c 0x40010005 1 "(Win32) Control-C" } + {Win32CtrlBreak win32_ctrl_break 0x40010008 1 "(Win32) Control-Break" } + {Win32WinRTOriginateError win32_win_rt_originate_error 0x40080201 0 "(Win32) WinRT Originate Error" } + {Win32WinRTTransformError win32_win_rt_transform_error 0x40080202 0 "(Win32) WinRT Transform Error" } + {Win32RPCCallCancelled win32_rpc_call_cancelled 0x0000071a 0 "(Win32) RPC Call Cancelled" } + {Win32DatatypeMisalignment win32_datatype_misalignment 0x80000002 0 "(Win32) Data Type Misalignment" } + {Win32AccessViolation win32_access_violation 0xc0000005 1 "(Win32) Access Violation" } + {Win32InPageError win32_in_page_error 0xc0000006 0 "(Win32) In Page Error" } + {Win32InvalidHandle win32_invalid_handle 0xc0000008 1 "(Win32) Invalid Handle Specified" } + {Win32NotEnoughQuota win32_not_enough_quota 0xc0000017 0 "(Win32) Not Enough Quota" } + {Win32IllegalInstruction win32_illegal_instruction 0xc000001d 0 "(Win32) Illegal Instruction" } + {Win32CannotContinueException win32_cannot_continue_exception 0xc0000025 0 "(Win32) Cannot Continue From Exception" } + {Win32InvalidExceptionDisposition win32_invalid_exception_disposition 0xc0000026 0 "(Win32) Invalid Exception Disposition Returned By Handler" } + {Win32ArrayBoundsExceeded win32_array_bounds_exceeded 0xc000008c 0 "(Win32) Array Bounds Exceeded" } + {Win32FloatingPointDenormalOperand win32_floating_point_denormal_operand 0xc000008d 0 "(Win32) Floating-Point Denormal Operand" } + {Win32FloatingPointDivisionByZero win32_floating_point_division_by_zero 0xc000008e 0 "(Win32) Floating-Point Division By Zero" } + {Win32FloatingPointInexactResult win32_floating_point_inexact_result 0xc000008f 0 "(Win32) Floating-Point Inexact Result" } + {Win32FloatingPointInvalidOperation win32_floating_point_invalid_operation 0xc0000090 0 "(Win32) Floating-Point Invalid Operation" } + {Win32FloatingPointOverflow win32_floating_point_overflow 0xc0000091 0 "(Win32) Floating-Point Overflow" } + {Win32FloatingPointStackCheck win32_floating_point_stack_check 0xc0000092 0 "(Win32) Floating-Point Stack Check" } + {Win32FloatingPointUnderflow win32_floating_point_underflow 0xc0000093 0 "(Win32) Floating-Point Underflow" } + {Win32IntegerDivisionByZero win32_integer_division_by_zero 0xc0000094 0 "(Win32) Integer Division By Zero" } + {Win32IntegerOverflow win32_integer_overflow 0xc0000095 0 "(Win32) Integer Overflow" } + {Win32PrivilegedInstruction win32_privileged_instruction 0xc0000096 0 "(Win32) Privileged Instruction" } + {Win32StackOverflow win32_stack_overflow 0xc00000fd 0 "(Win32) Stack Overflow" } + {Win32UnableToLocateDLL win32_unable_to_locate_dll 0xc0000135 0 "(Win32) Unable To Locate DLL" } + {Win32OrdinalNotFound win32_ordinal_not_found 0xc0000138 0 "(Win32) Ordinal Not Found" } + {Win32EntryPointNotFound win32_entry_point_not_found 0xc0000139 0 "(Win32) Entry Point Not Found" } + {Win32DLLInitializationFailed win32_dll_initialization_failed 0xc0000142 0 "(Win32) DLL Initialization Failed" } + {Win32FloatingPointSSEMultipleFaults win32_floating_point_sse_multiple_faults 0xc00002b4 0 "(Win32) Floating Point SSE Multiple Faults" } + {Win32FloatingPointSSEMultipleTraps win32_floating_point_sse_multiple_traps 0xc00002b5 0 "(Win32) Floating Point SSE Multiple Traps" } + {Win32AssertionFailed win32_assertion_failed 0xc0000420 1 "(Win32) Assertion Failed" } + {Win32ModuleNotFound win32_module_not_found 0xc06d007e 0 "(Win32) Module Not Found" } + {Win32ProcedureNotFound win32_procedure_not_found 0xc06d007f 0 "(Win32) Procedure Not Found" } + {Win32SanitizerErrorDetected win32_sanitizer_error_detected 0xe073616e 1 "(Win32) Sanitizer Error Detected" } + {Win32SanitizerRawAccessViolation win32_sanitizer_raw_access_violation 0xe0736171 0 "(Win32) Sanitizer Raw Access Violation" } + {Win32DirectXDebugLayer win32_directx_debug_layer 0x0000087a 1 "(Win32) DirectX Debug Layer" } +} + +//////////////////////////////// +//~ rjf: Generators + +@enum CTRL_ExceptionCodeKind: +{ + Null, + @expand(CTRL_ExceptionCodeKindTable a) `$(a.name)`, + COUNT, +} + +@data(U32) ctrl_exception_code_kind_code_table: +{ + `0`; + @expand(CTRL_ExceptionCodeKindTable a) `$(a.code)`; +} + +@data(String8) ctrl_exception_code_kind_display_string_table: +{ + `{0}`; + @expand(CTRL_ExceptionCodeKindTable a) `str8_lit_comp("$(a.display_string)")`; +} + +@data(String8) ctrl_exception_code_kind_lowercase_code_string_table: +{ + `{0}`; + @expand(CTRL_ExceptionCodeKindTable a) `str8_lit_comp("$(a.lower_name)")`; +} + +@data(B8) ctrl_exception_code_kind_default_enable_table: +{ + `0`; + @expand(CTRL_ExceptionCodeKindTable a) `$(a.default)`; +} diff --git a/src/ctrl/ctrl_core.h b/src/ctrl/ctrl_core.h index c198f579..09c07e42 100644 --- a/src/ctrl/ctrl_core.h +++ b/src/ctrl/ctrl_core.h @@ -1,844 +1,844 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef CTRL_CORE_H -#define CTRL_CORE_H - -//////////////////////////////// -//~ rjf: ID Types - -typedef U64 CTRL_MsgID; -typedef U64 CTRL_MachineID; - -#define CTRL_MachineID_Local (1) - -//////////////////////////////// -//~ rjf: Machine/Handle Pair Types - -typedef struct CTRL_MachineIDHandlePair CTRL_MachineIDHandlePair; -struct CTRL_MachineIDHandlePair -{ - CTRL_MachineID machine_id; - DMN_Handle handle; -}; - -typedef struct CTRL_MachineIDHandlePairNode CTRL_MachineIDHandlePairNode; -struct CTRL_MachineIDHandlePairNode -{ - CTRL_MachineIDHandlePairNode *next; - CTRL_MachineIDHandlePair v; -}; - -typedef struct CTRL_MachineIDHandlePairList CTRL_MachineIDHandlePairList; -struct CTRL_MachineIDHandlePairList -{ - CTRL_MachineIDHandlePairNode *first; - CTRL_MachineIDHandlePairNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Entity Types - -typedef enum CTRL_EntityKind -{ - CTRL_EntityKind_Null, - CTRL_EntityKind_Root, - CTRL_EntityKind_Machine, - CTRL_EntityKind_Process, - CTRL_EntityKind_Thread, - CTRL_EntityKind_Module, - CTRL_EntityKind_EntryPoint, - CTRL_EntityKind_DebugInfoPath, - CTRL_EntityKind_COUNT -} -CTRL_EntityKind; - -typedef struct CTRL_Entity CTRL_Entity; -struct CTRL_Entity -{ - CTRL_Entity *first; - CTRL_Entity *last; - CTRL_Entity *next; - CTRL_Entity *prev; - CTRL_Entity *parent; - CTRL_EntityKind kind; - Architecture arch; - CTRL_MachineID machine_id; - DMN_Handle handle; - U64 id; - Rng1U64 vaddr_range; - U64 timestamp; - String8 string; -}; - -typedef struct CTRL_EntityHashNode CTRL_EntityHashNode; -struct CTRL_EntityHashNode -{ - CTRL_EntityHashNode *next; - CTRL_EntityHashNode *prev; - CTRL_Entity *entity; -}; - -typedef struct CTRL_EntityHashSlot CTRL_EntityHashSlot; -struct CTRL_EntityHashSlot -{ - CTRL_EntityHashNode *first; - CTRL_EntityHashNode *last; -}; - -typedef struct CTRL_EntityStringChunkNode CTRL_EntityStringChunkNode; -struct CTRL_EntityStringChunkNode -{ - CTRL_EntityStringChunkNode *next; - U64 size; -}; - -typedef struct CTRL_EntityStore CTRL_EntityStore; -struct CTRL_EntityStore -{ - Arena *arena; - CTRL_Entity *root; - CTRL_Entity *free; - CTRL_EntityHashSlot *hash_slots; - CTRL_EntityHashNode *hash_node_free; - U64 hash_slots_count; - CTRL_EntityStringChunkNode *free_string_chunks[8]; -}; - -//////////////////////////////// -//~ rjf: Unwind Types - -typedef U32 CTRL_UnwindFlags; -enum -{ - CTRL_UnwindFlag_Error = (1<<0), - CTRL_UnwindFlag_Stale = (1<<1), -}; - -typedef struct CTRL_UnwindStepResult CTRL_UnwindStepResult; -struct CTRL_UnwindStepResult -{ - CTRL_UnwindFlags flags; -}; - -typedef struct CTRL_UnwindFrame CTRL_UnwindFrame; -struct CTRL_UnwindFrame -{ - void *regs; -}; - -typedef struct CTRL_UnwindFrameNode CTRL_UnwindFrameNode; -struct CTRL_UnwindFrameNode -{ - CTRL_UnwindFrameNode *next; - CTRL_UnwindFrameNode *prev; - CTRL_UnwindFrame v; -}; - -typedef struct CTRL_UnwindFrameArray CTRL_UnwindFrameArray; -struct CTRL_UnwindFrameArray -{ - CTRL_UnwindFrame *v; - U64 count; -}; - -typedef struct CTRL_Unwind CTRL_Unwind; -struct CTRL_Unwind -{ - CTRL_UnwindFrameArray frames; - CTRL_UnwindFlags flags; -}; - -//////////////////////////////// -//~ rjf: Trap Types - -typedef U32 CTRL_TrapFlags; -enum -{ - CTRL_TrapFlag_IgnoreStackPointerCheck = (1<<0), - CTRL_TrapFlag_SingleStepAfterHit = (1<<1), - CTRL_TrapFlag_SaveStackPointer = (1<<2), - CTRL_TrapFlag_BeginSpoofMode = (1<<3), - CTRL_TrapFlag_EndStepping = (1<<4), -}; - -typedef struct CTRL_Trap CTRL_Trap; -struct CTRL_Trap -{ - CTRL_TrapFlags flags; - U64 vaddr; -}; - -typedef struct CTRL_TrapNode CTRL_TrapNode; -struct CTRL_TrapNode -{ - CTRL_TrapNode *next; - CTRL_Trap v; -}; - -typedef struct CTRL_TrapList CTRL_TrapList; -struct CTRL_TrapList -{ - CTRL_TrapNode *first; - CTRL_TrapNode *last; - U64 count; -}; - -typedef struct CTRL_Spoof CTRL_Spoof; -struct CTRL_Spoof -{ - DMN_Handle process; - DMN_Handle thread; - U64 vaddr; - U64 new_ip_value; -}; - -//////////////////////////////// -//~ rjf: User Breakpoint Types - -typedef enum CTRL_UserBreakpointKind -{ - 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: Generated Code - -#include "generated/ctrl.meta.h" - -//////////////////////////////// -//~ rjf: Message Types - -typedef enum CTRL_MsgKind -{ - CTRL_MsgKind_Null, - CTRL_MsgKind_Launch, - CTRL_MsgKind_Attach, - CTRL_MsgKind_Kill, - CTRL_MsgKind_Detach, - CTRL_MsgKind_Run, - CTRL_MsgKind_SingleStep, - CTRL_MsgKind_SetUserEntryPoints, - CTRL_MsgKind_SetModuleDebugInfoPath, - CTRL_MsgKind_COUNT, -} -CTRL_MsgKind; - -typedef U32 CTRL_RunFlags; -enum -{ - CTRL_RunFlag_StopOnEntryPoint = (1<<0), -}; - -typedef struct CTRL_Msg CTRL_Msg; -struct CTRL_Msg -{ - CTRL_MsgKind kind; - CTRL_RunFlags run_flags; - CTRL_MsgID msg_id; - CTRL_MachineID machine_id; - DMN_Handle entity; - DMN_Handle parent; - U32 entity_id; - U32 exit_code; - B32 env_inherit; - U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64]; - String8 path; - String8List entry_points; - String8List cmd_line_string_list; - String8List env_string_list; - CTRL_TrapList traps; - CTRL_UserBreakpointList user_bps; - CTRL_MachineIDHandlePairList freeze_state_threads; // NOTE(rjf): can be frozen or unfrozen, depending on `freeze_state_is_frozen` - B32 freeze_state_is_frozen; -}; - -typedef struct CTRL_MsgNode CTRL_MsgNode; -struct CTRL_MsgNode -{ - CTRL_MsgNode *next; - CTRL_Msg v; -}; - -typedef struct CTRL_MsgList CTRL_MsgList; -struct CTRL_MsgList -{ - CTRL_MsgNode *first; - CTRL_MsgNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Event Types - -typedef enum CTRL_EventKind -{ - CTRL_EventKind_Null, - CTRL_EventKind_Error, - - //- rjf: starts/stops - CTRL_EventKind_Started, - CTRL_EventKind_Stopped, - - //- rjf: entity creation/deletion - CTRL_EventKind_NewProc, - CTRL_EventKind_NewThread, - CTRL_EventKind_NewModule, - CTRL_EventKind_EndProc, - CTRL_EventKind_EndThread, - CTRL_EventKind_EndModule, - - //- rjf: debug info changes - CTRL_EventKind_ModuleDebugInfoPathChange, - - //- rjf: debug strings - CTRL_EventKind_DebugString, - CTRL_EventKind_ThreadName, - - //- rjf: memory - CTRL_EventKind_MemReserve, - CTRL_EventKind_MemCommit, - CTRL_EventKind_MemDecommit, - CTRL_EventKind_MemRelease, - - CTRL_EventKind_COUNT -} -CTRL_EventKind; - -typedef enum CTRL_EventCause -{ - CTRL_EventCause_Null, - CTRL_EventCause_Error, - CTRL_EventCause_Finished, - CTRL_EventCause_EntryPoint, - CTRL_EventCause_UserBreakpoint, - CTRL_EventCause_InterruptedByTrap, - CTRL_EventCause_InterruptedByException, - CTRL_EventCause_InterruptedByHalt, - CTRL_EventCause_COUNT -} -CTRL_EventCause; - -typedef enum CTRL_ExceptionKind -{ - CTRL_ExceptionKind_Null, - CTRL_ExceptionKind_MemoryRead, - CTRL_ExceptionKind_MemoryWrite, - CTRL_ExceptionKind_MemoryExecute, - CTRL_ExceptionKind_CppThrow, - CTRL_ExceptionKind_COUNT -} -CTRL_ExceptionKind; - -typedef struct CTRL_Event CTRL_Event; -struct CTRL_Event -{ - CTRL_EventKind kind; - CTRL_EventCause cause; - CTRL_ExceptionKind exception_kind; - CTRL_MsgID msg_id; - CTRL_MachineID machine_id; - DMN_Handle entity; - DMN_Handle parent; - Architecture arch; - U64 u64_code; - U32 entity_id; - Rng1U64 vaddr_rng; - U64 rip_vaddr; - U64 stack_base; - U64 tls_root; - U64 timestamp; - U32 exception_code; - String8 string; -}; - -typedef struct CTRL_EventNode CTRL_EventNode; -struct CTRL_EventNode -{ - CTRL_EventNode *next; - CTRL_Event v; -}; - -typedef struct CTRL_EventList CTRL_EventList; -struct CTRL_EventList -{ - CTRL_EventNode *first; - CTRL_EventNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Process Memory Cache Types - -typedef struct CTRL_ProcessMemoryRangeHashNode CTRL_ProcessMemoryRangeHashNode; -struct CTRL_ProcessMemoryRangeHashNode -{ - CTRL_ProcessMemoryRangeHashNode *next; - Rng1U64 vaddr_range; - B32 zero_terminated; - Rng1U64 vaddr_range_clamped; - U128 hash; - U64 mem_gen; - U64 last_time_requested_us; - B32 is_taken; -}; - -typedef struct CTRL_ProcessMemoryRangeHashSlot CTRL_ProcessMemoryRangeHashSlot; -struct CTRL_ProcessMemoryRangeHashSlot -{ - CTRL_ProcessMemoryRangeHashNode *first; - CTRL_ProcessMemoryRangeHashNode *last; -}; - -typedef struct CTRL_ProcessMemoryCacheNode CTRL_ProcessMemoryCacheNode; -struct CTRL_ProcessMemoryCacheNode -{ - CTRL_ProcessMemoryCacheNode *next; - CTRL_ProcessMemoryCacheNode *prev; - Arena *arena; - CTRL_MachineID machine_id; - DMN_Handle process; - U64 range_hash_slots_count; - CTRL_ProcessMemoryRangeHashSlot *range_hash_slots; -}; - -typedef struct CTRL_ProcessMemoryCacheSlot CTRL_ProcessMemoryCacheSlot; -struct CTRL_ProcessMemoryCacheSlot -{ - CTRL_ProcessMemoryCacheNode *first; - CTRL_ProcessMemoryCacheNode *last; -}; - -typedef struct CTRL_ProcessMemoryCacheStripe CTRL_ProcessMemoryCacheStripe; -struct CTRL_ProcessMemoryCacheStripe -{ - OS_Handle rw_mutex; - OS_Handle cv; -}; - -typedef struct CTRL_ProcessMemoryCache CTRL_ProcessMemoryCache; -struct CTRL_ProcessMemoryCache -{ - U64 slots_count; - CTRL_ProcessMemoryCacheSlot *slots; - U64 stripes_count; - CTRL_ProcessMemoryCacheStripe *stripes; -}; - -typedef struct CTRL_ProcessMemorySlice CTRL_ProcessMemorySlice; -struct CTRL_ProcessMemorySlice -{ - String8 data; - U64 *byte_bad_flags; - U64 *byte_changed_flags; - B32 stale; - B32 any_byte_bad; - B32 any_byte_changed; -}; - -//////////////////////////////// -//~ rjf: Thread Register Cache Types - -typedef struct CTRL_ThreadRegCacheNode CTRL_ThreadRegCacheNode; -struct CTRL_ThreadRegCacheNode -{ - CTRL_ThreadRegCacheNode *next; - CTRL_ThreadRegCacheNode *prev; - CTRL_MachineID machine_id; - DMN_Handle thread; - U64 block_size; - void *block; - U64 reg_gen; -}; - -typedef struct CTRL_ThreadRegCacheSlot CTRL_ThreadRegCacheSlot; -struct CTRL_ThreadRegCacheSlot -{ - CTRL_ThreadRegCacheNode *first; - CTRL_ThreadRegCacheNode *last; -}; - -typedef struct CTRL_ThreadRegCacheStripe CTRL_ThreadRegCacheStripe; -struct CTRL_ThreadRegCacheStripe -{ - Arena *arena; - OS_Handle rw_mutex; -}; - -typedef struct CTRL_ThreadRegCache CTRL_ThreadRegCache; -struct CTRL_ThreadRegCache -{ - U64 slots_count; - CTRL_ThreadRegCacheSlot *slots; - U64 stripes_count; - CTRL_ThreadRegCacheStripe *stripes; -}; - -//////////////////////////////// -//~ rjf: Module Image Info Cache Types - -typedef struct CTRL_ModuleImageInfoCacheNode CTRL_ModuleImageInfoCacheNode; -struct CTRL_ModuleImageInfoCacheNode -{ - CTRL_ModuleImageInfoCacheNode *next; - CTRL_ModuleImageInfoCacheNode *prev; - CTRL_MachineID machine_id; - DMN_Handle module; - Arena *arena; - PE_IntelPdata *pdatas; - U64 pdatas_count; - U64 entry_point_voff; - Rng1U64 tls_vaddr_range; - String8 initial_debug_info_path; -}; - -typedef struct CTRL_ModuleImageInfoCacheSlot CTRL_ModuleImageInfoCacheSlot; -struct CTRL_ModuleImageInfoCacheSlot -{ - CTRL_ModuleImageInfoCacheNode *first; - CTRL_ModuleImageInfoCacheNode *last; -}; - -typedef struct CTRL_ModuleImageInfoCacheStripe CTRL_ModuleImageInfoCacheStripe; -struct CTRL_ModuleImageInfoCacheStripe -{ - Arena *arena; - OS_Handle rw_mutex; -}; - -typedef struct CTRL_ModuleImageInfoCache CTRL_ModuleImageInfoCache; -struct CTRL_ModuleImageInfoCache -{ - U64 slots_count; - CTRL_ModuleImageInfoCacheSlot *slots; - U64 stripes_count; - CTRL_ModuleImageInfoCacheStripe *stripes; -}; - -//////////////////////////////// -//~ rjf: Wakeup Hook Function Types - -#define CTRL_WAKEUP_FUNCTION_DEF(name) void name(void) -typedef CTRL_WAKEUP_FUNCTION_DEF(CTRL_WakeupFunctionType); - -//////////////////////////////// -//~ rjf: Main State Types - -typedef struct CTRL_State CTRL_State; -struct CTRL_State -{ - Arena *arena; - CTRL_WakeupFunctionType *wakeup_hook; - - // rjf: name -> register/alias hash tables for eval - EVAL_String2NumMap arch_string2reg_tables[Architecture_COUNT]; - EVAL_String2NumMap arch_string2alias_tables[Architecture_COUNT]; - - // rjf: caches - CTRL_ProcessMemoryCache process_memory_cache; - CTRL_ThreadRegCache thread_reg_cache; - CTRL_ModuleImageInfoCache module_image_info_cache; - - // rjf: user -> ctrl msg ring buffer - U64 u2c_ring_size; - U8 *u2c_ring_base; - U64 u2c_ring_write_pos; - U64 u2c_ring_read_pos; - OS_Handle u2c_ring_mutex; - OS_Handle u2c_ring_cv; - - // rjf: ctrl -> user event ring buffer - U64 c2u_ring_size; - U64 c2u_ring_max_string_size; - U8 *c2u_ring_base; - U64 c2u_ring_write_pos; - U64 c2u_ring_read_pos; - OS_Handle c2u_ring_mutex; - OS_Handle c2u_ring_cv; - - // rjf: ctrl thread state - String8 ctrl_thread_log_path; - OS_Handle ctrl_thread; - Log *ctrl_thread_log; - 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; - U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64]; - U64 process_counter; - - // 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; - - // rjf: memory stream threads - U64 ms_thread_count; - OS_Handle *ms_threads; -}; - -//////////////////////////////// -//~ rjf: Globals - -global CTRL_State *ctrl_state = 0; -read_only global CTRL_Entity ctrl_entity_nil = -{ - &ctrl_entity_nil, - &ctrl_entity_nil, - &ctrl_entity_nil, - &ctrl_entity_nil, - &ctrl_entity_nil, -}; - -//////////////////////////////// -//~ rjf: Logging Markup - -#define CTRL_CtrlThreadLogScope DeferLoop(log_scope_begin(), ctrl_thread__end_and_flush_info_log()) - -//////////////////////////////// -//~ rjf: Basic Type Functions - -internal U64 ctrl_hash_from_string(String8 string); -internal U64 ctrl_hash_from_machine_id_handle(CTRL_MachineID machine_id, DMN_Handle handle); -internal CTRL_EventCause ctrl_event_cause_from_dmn_event_kind(DMN_EventKind event_kind); -internal String8 ctrl_string_from_event_kind(CTRL_EventKind kind); -internal String8 ctrl_string_from_msg_kind(CTRL_MsgKind kind); - -//////////////////////////////// -//~ rjf: Machine/Handle Pair Type Functions - -internal void ctrl_machine_id_handle_pair_list_push(Arena *arena, CTRL_MachineIDHandlePairList *list, CTRL_MachineIDHandlePair *pair); -internal CTRL_MachineIDHandlePairList ctrl_machine_id_handle_pair_list_copy(Arena *arena, CTRL_MachineIDHandlePairList *src); - -//////////////////////////////// -//~ rjf: Trap Type Functions - -internal void ctrl_trap_list_push(Arena *arena, CTRL_TrapList *list, CTRL_Trap *trap); -internal CTRL_TrapList ctrl_trap_list_copy(Arena *arena, CTRL_TrapList *src); - -//////////////////////////////// -//~ rjf: User Breakpoint Type Functions - -internal void ctrl_user_breakpoint_list_push(Arena *arena, CTRL_UserBreakpointList *list, CTRL_UserBreakpoint *bp); -internal CTRL_UserBreakpointList ctrl_user_breakpoint_list_copy(Arena *arena, CTRL_UserBreakpointList *src); - -//////////////////////////////// -//~ rjf: Message Type Functions - -//- rjf: deep copying -internal void ctrl_msg_deep_copy(Arena *arena, CTRL_Msg *dst, CTRL_Msg *src); - -//- rjf: list building -internal CTRL_Msg *ctrl_msg_list_push(Arena *arena, CTRL_MsgList *list); - -//- rjf: serialization -internal String8 ctrl_serialized_string_from_msg_list(Arena *arena, CTRL_MsgList *msgs); -internal CTRL_MsgList ctrl_msg_list_from_serialized_string(Arena *arena, String8 string); - -//////////////////////////////// -//~ rjf: Event Type Functions - -//- rjf: list building -internal CTRL_Event *ctrl_event_list_push(Arena *arena, CTRL_EventList *list); -internal void ctrl_event_list_concat_in_place(CTRL_EventList *dst, CTRL_EventList *to_push); - -//- rjf: serialization -internal String8 ctrl_serialized_string_from_event(Arena *arena, CTRL_Event *event, U64 max); -internal CTRL_Event ctrl_event_from_serialized_string(Arena *arena, String8 string); - -//////////////////////////////// -//~ rjf: Entity Type Functions - -//- 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, Architecture arch, CTRL_MachineID machine_id, DMN_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_machine_id_handle(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle handle); -internal CTRL_Entity *ctrl_entity_child_from_kind(CTRL_Entity *parent, CTRL_EntityKind kind); - -//- rjf: applying events to entity caches -internal void ctrl_entity_store_apply_events(CTRL_EntityStore *store, CTRL_EventList *list); - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void ctrl_init(void); - -//////////////////////////////// -//~ rjf: Wakeup Callback Registration - -internal void ctrl_set_wakeup_hook(CTRL_WakeupFunctionType *wakeup_hook); - -//////////////////////////////// -//~ rjf: Process Memory Functions - -//- rjf: process memory cache interaction -internal U128 ctrl_calc_hash_store_key_from_process_vaddr_range(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 zero_terminated); -internal U128 ctrl_stored_hash_from_process_vaddr_range(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 zero_terminated, B32 *out_is_stale, U64 endt_us); - -//- rjf: bundled key/stream helper -internal U128 ctrl_hash_store_key_from_process_vaddr_range(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 zero_terminated); - -//- rjf: process memory cache reading helpers -internal CTRL_ProcessMemorySlice ctrl_query_cached_data_from_process_vaddr_range(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, U64 endt_us); -internal CTRL_ProcessMemorySlice ctrl_query_cached_zero_terminated_data_from_process_vaddr_limit(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, U64 vaddr, U64 limit, U64 element_size, U64 endt_us); -internal B32 ctrl_read_cached_process_memory(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 *is_stale_out, void *out, U64 endt_us); -#define ctrl_read_cached_process_memory_struct(machine_id, process, vaddr, is_stale_out, ptr, endt_us) ctrl_read_cached_process_memory((machine_id), (process), r1u64((vaddr), (vaddr)+(sizeof(*(ptr)))), (is_stale_out), (ptr), (endt_us)) - -//- rjf: process memory writing -internal B32 ctrl_process_write(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, void *src); - -//////////////////////////////// -//~ rjf: Thread Register Functions - -//- rjf: thread register cache reading -internal void *ctrl_query_cached_reg_block_from_thread(Arena *arena, CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread); -internal U64 ctrl_query_cached_tls_root_vaddr_from_thread(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread); -internal U64 ctrl_query_cached_rip_from_thread(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread); -internal U64 ctrl_query_cached_rsp_from_thread(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread); - -//- rjf: thread register writing -internal B32 ctrl_thread_write_reg_block(CTRL_MachineID machine_id, DMN_Handle thread, void *block); - -//////////////////////////////// -//~ rjf: Module Image Info Functions - -//- rjf: cache lookups -internal PE_IntelPdata *ctrl_intel_pdata_from_module_voff(Arena *arena, CTRL_MachineID machine_id, DMN_Handle module_handle, U64 voff); -internal U64 ctrl_entry_point_voff_from_module(CTRL_MachineID machine_id, DMN_Handle module_handle); -internal Rng1U64 ctrl_tls_vaddr_range_from_module(CTRL_MachineID machine_id, DMN_Handle module_handle); -internal String8 ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_MachineID machine_id, DMN_Handle module_handle); - -//////////////////////////////// -//~ rjf: Unwinding Functions - -//- rjf: unwind deep copier -internal CTRL_Unwind ctrl_unwind_deep_copy(Arena *arena, Architecture arch, CTRL_Unwind *src); - -//- 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_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle process_handle, DMN_Handle module, REGS_RegBlockX64 *regs, U64 endt_us); - -//- rjf: abstracted unwind step -internal CTRL_UnwindStepResult ctrl_unwind_step(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle process_handle, DMN_Handle module, Architecture arch, void *reg_block, U64 endt_us); - -//- rjf: abstracted full unwind -internal CTRL_Unwind ctrl_unwind_from_thread(Arena *arena, CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread, U64 endt_us); - -//////////////////////////////// -//~ rjf: Halting All Attached Processes - -internal void ctrl_halt(void); - -//////////////////////////////// -//~ rjf: Shared Accessor Functions - -//- rjf: generation counters -internal U64 ctrl_run_gen(void); -internal U64 ctrl_mem_gen(void); -internal U64 ctrl_reg_gen(void); - -//- rjf: name -> register/alias hash tables, for eval -internal EVAL_String2NumMap *ctrl_string2reg_from_arch(Architecture arch); -internal EVAL_String2NumMap *ctrl_string2alias_from_arch(Architecture arch); - -//////////////////////////////// -//~ rjf: Control-Thread Functions - -//- rjf: user -> control thread communication -internal B32 ctrl_u2c_push_msgs(CTRL_MsgList *msgs, U64 endt_us); -internal CTRL_MsgList ctrl_u2c_pop_msgs(Arena *arena); - -//- rjf: control -> user thread communication -internal void ctrl_c2u_push_events(CTRL_EventList *events); -internal CTRL_EventList ctrl_c2u_pop_events(Arena *arena); - -//- rjf: entry point -internal void ctrl_thread__entry_point(void *p); - -//- rjf: breakpoint resolution -internal void ctrl_thread__append_resolved_module_user_bp_traps(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, DMN_Handle module, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out); -internal void ctrl_thread__append_resolved_process_user_bp_traps(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out); - -//- rjf: module lifetime open/close work -internal void ctrl_thread__module_open(CTRL_MachineID machine_id, DMN_Handle process, DMN_Handle module, Rng1U64 vaddr_range, String8 path); -internal void ctrl_thread__module_close(CTRL_MachineID machine_id, DMN_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 B32 ctrl_eval_memory_read(void *u, void *out, U64 addr, U64 size); - -//- rjf: log flusher -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); -internal void ctrl_thread__attach(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); -internal void ctrl_thread__kill(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); -internal void ctrl_thread__detach(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); -internal void ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); -internal void ctrl_thread__single_step(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); - -//////////////////////////////// -//~ rjf: Memory-Stream Thread Functions - -//- rjf: user -> memory stream communication -internal B32 ctrl_u2ms_enqueue_req(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 vaddr_range, B32 zero_terminated, U64 endt_us); -internal void ctrl_u2ms_dequeue_req(CTRL_MachineID *out_machine_id, DMN_Handle *out_process, Rng1U64 *out_vaddr_range, B32 *out_zero_terminated); - -//- rjf: entry point -internal void ctrl_mem_stream_thread__entry_point(void *p); - -#endif // CTRL_CORE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef CTRL_CORE_H +#define CTRL_CORE_H + +//////////////////////////////// +//~ rjf: ID Types + +typedef U64 CTRL_MsgID; +typedef U64 CTRL_MachineID; + +#define CTRL_MachineID_Local (1) + +//////////////////////////////// +//~ rjf: Machine/Handle Pair Types + +typedef struct CTRL_MachineIDHandlePair CTRL_MachineIDHandlePair; +struct CTRL_MachineIDHandlePair +{ + CTRL_MachineID machine_id; + DMN_Handle handle; +}; + +typedef struct CTRL_MachineIDHandlePairNode CTRL_MachineIDHandlePairNode; +struct CTRL_MachineIDHandlePairNode +{ + CTRL_MachineIDHandlePairNode *next; + CTRL_MachineIDHandlePair v; +}; + +typedef struct CTRL_MachineIDHandlePairList CTRL_MachineIDHandlePairList; +struct CTRL_MachineIDHandlePairList +{ + CTRL_MachineIDHandlePairNode *first; + CTRL_MachineIDHandlePairNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Entity Types + +typedef enum CTRL_EntityKind +{ + CTRL_EntityKind_Null, + CTRL_EntityKind_Root, + CTRL_EntityKind_Machine, + CTRL_EntityKind_Process, + CTRL_EntityKind_Thread, + CTRL_EntityKind_Module, + CTRL_EntityKind_EntryPoint, + CTRL_EntityKind_DebugInfoPath, + CTRL_EntityKind_COUNT +} +CTRL_EntityKind; + +typedef struct CTRL_Entity CTRL_Entity; +struct CTRL_Entity +{ + CTRL_Entity *first; + CTRL_Entity *last; + CTRL_Entity *next; + CTRL_Entity *prev; + CTRL_Entity *parent; + CTRL_EntityKind kind; + Architecture arch; + CTRL_MachineID machine_id; + DMN_Handle handle; + U64 id; + Rng1U64 vaddr_range; + U64 timestamp; + String8 string; +}; + +typedef struct CTRL_EntityHashNode CTRL_EntityHashNode; +struct CTRL_EntityHashNode +{ + CTRL_EntityHashNode *next; + CTRL_EntityHashNode *prev; + CTRL_Entity *entity; +}; + +typedef struct CTRL_EntityHashSlot CTRL_EntityHashSlot; +struct CTRL_EntityHashSlot +{ + CTRL_EntityHashNode *first; + CTRL_EntityHashNode *last; +}; + +typedef struct CTRL_EntityStringChunkNode CTRL_EntityStringChunkNode; +struct CTRL_EntityStringChunkNode +{ + CTRL_EntityStringChunkNode *next; + U64 size; +}; + +typedef struct CTRL_EntityStore CTRL_EntityStore; +struct CTRL_EntityStore +{ + Arena *arena; + CTRL_Entity *root; + CTRL_Entity *free; + CTRL_EntityHashSlot *hash_slots; + CTRL_EntityHashNode *hash_node_free; + U64 hash_slots_count; + CTRL_EntityStringChunkNode *free_string_chunks[8]; +}; + +//////////////////////////////// +//~ rjf: Unwind Types + +typedef U32 CTRL_UnwindFlags; +enum +{ + CTRL_UnwindFlag_Error = (1<<0), + CTRL_UnwindFlag_Stale = (1<<1), +}; + +typedef struct CTRL_UnwindStepResult CTRL_UnwindStepResult; +struct CTRL_UnwindStepResult +{ + CTRL_UnwindFlags flags; +}; + +typedef struct CTRL_UnwindFrame CTRL_UnwindFrame; +struct CTRL_UnwindFrame +{ + void *regs; +}; + +typedef struct CTRL_UnwindFrameNode CTRL_UnwindFrameNode; +struct CTRL_UnwindFrameNode +{ + CTRL_UnwindFrameNode *next; + CTRL_UnwindFrameNode *prev; + CTRL_UnwindFrame v; +}; + +typedef struct CTRL_UnwindFrameArray CTRL_UnwindFrameArray; +struct CTRL_UnwindFrameArray +{ + CTRL_UnwindFrame *v; + U64 count; +}; + +typedef struct CTRL_Unwind CTRL_Unwind; +struct CTRL_Unwind +{ + CTRL_UnwindFrameArray frames; + CTRL_UnwindFlags flags; +}; + +//////////////////////////////// +//~ rjf: Trap Types + +typedef U32 CTRL_TrapFlags; +enum +{ + CTRL_TrapFlag_IgnoreStackPointerCheck = (1<<0), + CTRL_TrapFlag_SingleStepAfterHit = (1<<1), + CTRL_TrapFlag_SaveStackPointer = (1<<2), + CTRL_TrapFlag_BeginSpoofMode = (1<<3), + CTRL_TrapFlag_EndStepping = (1<<4), +}; + +typedef struct CTRL_Trap CTRL_Trap; +struct CTRL_Trap +{ + CTRL_TrapFlags flags; + U64 vaddr; +}; + +typedef struct CTRL_TrapNode CTRL_TrapNode; +struct CTRL_TrapNode +{ + CTRL_TrapNode *next; + CTRL_Trap v; +}; + +typedef struct CTRL_TrapList CTRL_TrapList; +struct CTRL_TrapList +{ + CTRL_TrapNode *first; + CTRL_TrapNode *last; + U64 count; +}; + +typedef struct CTRL_Spoof CTRL_Spoof; +struct CTRL_Spoof +{ + DMN_Handle process; + DMN_Handle thread; + U64 vaddr; + U64 new_ip_value; +}; + +//////////////////////////////// +//~ rjf: User Breakpoint Types + +typedef enum CTRL_UserBreakpointKind +{ + 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: Generated Code + +#include "generated/ctrl.meta.h" + +//////////////////////////////// +//~ rjf: Message Types + +typedef enum CTRL_MsgKind +{ + CTRL_MsgKind_Null, + CTRL_MsgKind_Launch, + CTRL_MsgKind_Attach, + CTRL_MsgKind_Kill, + CTRL_MsgKind_Detach, + CTRL_MsgKind_Run, + CTRL_MsgKind_SingleStep, + CTRL_MsgKind_SetUserEntryPoints, + CTRL_MsgKind_SetModuleDebugInfoPath, + CTRL_MsgKind_COUNT, +} +CTRL_MsgKind; + +typedef U32 CTRL_RunFlags; +enum +{ + CTRL_RunFlag_StopOnEntryPoint = (1<<0), +}; + +typedef struct CTRL_Msg CTRL_Msg; +struct CTRL_Msg +{ + CTRL_MsgKind kind; + CTRL_RunFlags run_flags; + CTRL_MsgID msg_id; + CTRL_MachineID machine_id; + DMN_Handle entity; + DMN_Handle parent; + U32 entity_id; + U32 exit_code; + B32 env_inherit; + U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64]; + String8 path; + String8List entry_points; + String8List cmd_line_string_list; + String8List env_string_list; + CTRL_TrapList traps; + CTRL_UserBreakpointList user_bps; + CTRL_MachineIDHandlePairList freeze_state_threads; // NOTE(rjf): can be frozen or unfrozen, depending on `freeze_state_is_frozen` + B32 freeze_state_is_frozen; +}; + +typedef struct CTRL_MsgNode CTRL_MsgNode; +struct CTRL_MsgNode +{ + CTRL_MsgNode *next; + CTRL_Msg v; +}; + +typedef struct CTRL_MsgList CTRL_MsgList; +struct CTRL_MsgList +{ + CTRL_MsgNode *first; + CTRL_MsgNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Event Types + +typedef enum CTRL_EventKind +{ + CTRL_EventKind_Null, + CTRL_EventKind_Error, + + //- rjf: starts/stops + CTRL_EventKind_Started, + CTRL_EventKind_Stopped, + + //- rjf: entity creation/deletion + CTRL_EventKind_NewProc, + CTRL_EventKind_NewThread, + CTRL_EventKind_NewModule, + CTRL_EventKind_EndProc, + CTRL_EventKind_EndThread, + CTRL_EventKind_EndModule, + + //- rjf: debug info changes + CTRL_EventKind_ModuleDebugInfoPathChange, + + //- rjf: debug strings + CTRL_EventKind_DebugString, + CTRL_EventKind_ThreadName, + + //- rjf: memory + CTRL_EventKind_MemReserve, + CTRL_EventKind_MemCommit, + CTRL_EventKind_MemDecommit, + CTRL_EventKind_MemRelease, + + CTRL_EventKind_COUNT +} +CTRL_EventKind; + +typedef enum CTRL_EventCause +{ + CTRL_EventCause_Null, + CTRL_EventCause_Error, + CTRL_EventCause_Finished, + CTRL_EventCause_EntryPoint, + CTRL_EventCause_UserBreakpoint, + CTRL_EventCause_InterruptedByTrap, + CTRL_EventCause_InterruptedByException, + CTRL_EventCause_InterruptedByHalt, + CTRL_EventCause_COUNT +} +CTRL_EventCause; + +typedef enum CTRL_ExceptionKind +{ + CTRL_ExceptionKind_Null, + CTRL_ExceptionKind_MemoryRead, + CTRL_ExceptionKind_MemoryWrite, + CTRL_ExceptionKind_MemoryExecute, + CTRL_ExceptionKind_CppThrow, + CTRL_ExceptionKind_COUNT +} +CTRL_ExceptionKind; + +typedef struct CTRL_Event CTRL_Event; +struct CTRL_Event +{ + CTRL_EventKind kind; + CTRL_EventCause cause; + CTRL_ExceptionKind exception_kind; + CTRL_MsgID msg_id; + CTRL_MachineID machine_id; + DMN_Handle entity; + DMN_Handle parent; + Architecture arch; + U64 u64_code; + U32 entity_id; + Rng1U64 vaddr_rng; + U64 rip_vaddr; + U64 stack_base; + U64 tls_root; + U64 timestamp; + U32 exception_code; + String8 string; +}; + +typedef struct CTRL_EventNode CTRL_EventNode; +struct CTRL_EventNode +{ + CTRL_EventNode *next; + CTRL_Event v; +}; + +typedef struct CTRL_EventList CTRL_EventList; +struct CTRL_EventList +{ + CTRL_EventNode *first; + CTRL_EventNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Process Memory Cache Types + +typedef struct CTRL_ProcessMemoryRangeHashNode CTRL_ProcessMemoryRangeHashNode; +struct CTRL_ProcessMemoryRangeHashNode +{ + CTRL_ProcessMemoryRangeHashNode *next; + Rng1U64 vaddr_range; + B32 zero_terminated; + Rng1U64 vaddr_range_clamped; + U128 hash; + U64 mem_gen; + U64 last_time_requested_us; + B32 is_taken; +}; + +typedef struct CTRL_ProcessMemoryRangeHashSlot CTRL_ProcessMemoryRangeHashSlot; +struct CTRL_ProcessMemoryRangeHashSlot +{ + CTRL_ProcessMemoryRangeHashNode *first; + CTRL_ProcessMemoryRangeHashNode *last; +}; + +typedef struct CTRL_ProcessMemoryCacheNode CTRL_ProcessMemoryCacheNode; +struct CTRL_ProcessMemoryCacheNode +{ + CTRL_ProcessMemoryCacheNode *next; + CTRL_ProcessMemoryCacheNode *prev; + Arena *arena; + CTRL_MachineID machine_id; + DMN_Handle process; + U64 range_hash_slots_count; + CTRL_ProcessMemoryRangeHashSlot *range_hash_slots; +}; + +typedef struct CTRL_ProcessMemoryCacheSlot CTRL_ProcessMemoryCacheSlot; +struct CTRL_ProcessMemoryCacheSlot +{ + CTRL_ProcessMemoryCacheNode *first; + CTRL_ProcessMemoryCacheNode *last; +}; + +typedef struct CTRL_ProcessMemoryCacheStripe CTRL_ProcessMemoryCacheStripe; +struct CTRL_ProcessMemoryCacheStripe +{ + OS_Handle rw_mutex; + OS_Handle cv; +}; + +typedef struct CTRL_ProcessMemoryCache CTRL_ProcessMemoryCache; +struct CTRL_ProcessMemoryCache +{ + U64 slots_count; + CTRL_ProcessMemoryCacheSlot *slots; + U64 stripes_count; + CTRL_ProcessMemoryCacheStripe *stripes; +}; + +typedef struct CTRL_ProcessMemorySlice CTRL_ProcessMemorySlice; +struct CTRL_ProcessMemorySlice +{ + String8 data; + U64 *byte_bad_flags; + U64 *byte_changed_flags; + B32 stale; + B32 any_byte_bad; + B32 any_byte_changed; +}; + +//////////////////////////////// +//~ rjf: Thread Register Cache Types + +typedef struct CTRL_ThreadRegCacheNode CTRL_ThreadRegCacheNode; +struct CTRL_ThreadRegCacheNode +{ + CTRL_ThreadRegCacheNode *next; + CTRL_ThreadRegCacheNode *prev; + CTRL_MachineID machine_id; + DMN_Handle thread; + U64 block_size; + void *block; + U64 reg_gen; +}; + +typedef struct CTRL_ThreadRegCacheSlot CTRL_ThreadRegCacheSlot; +struct CTRL_ThreadRegCacheSlot +{ + CTRL_ThreadRegCacheNode *first; + CTRL_ThreadRegCacheNode *last; +}; + +typedef struct CTRL_ThreadRegCacheStripe CTRL_ThreadRegCacheStripe; +struct CTRL_ThreadRegCacheStripe +{ + Arena *arena; + OS_Handle rw_mutex; +}; + +typedef struct CTRL_ThreadRegCache CTRL_ThreadRegCache; +struct CTRL_ThreadRegCache +{ + U64 slots_count; + CTRL_ThreadRegCacheSlot *slots; + U64 stripes_count; + CTRL_ThreadRegCacheStripe *stripes; +}; + +//////////////////////////////// +//~ rjf: Module Image Info Cache Types + +typedef struct CTRL_ModuleImageInfoCacheNode CTRL_ModuleImageInfoCacheNode; +struct CTRL_ModuleImageInfoCacheNode +{ + CTRL_ModuleImageInfoCacheNode *next; + CTRL_ModuleImageInfoCacheNode *prev; + CTRL_MachineID machine_id; + DMN_Handle module; + Arena *arena; + PE_IntelPdata *pdatas; + U64 pdatas_count; + U64 entry_point_voff; + Rng1U64 tls_vaddr_range; + String8 initial_debug_info_path; +}; + +typedef struct CTRL_ModuleImageInfoCacheSlot CTRL_ModuleImageInfoCacheSlot; +struct CTRL_ModuleImageInfoCacheSlot +{ + CTRL_ModuleImageInfoCacheNode *first; + CTRL_ModuleImageInfoCacheNode *last; +}; + +typedef struct CTRL_ModuleImageInfoCacheStripe CTRL_ModuleImageInfoCacheStripe; +struct CTRL_ModuleImageInfoCacheStripe +{ + Arena *arena; + OS_Handle rw_mutex; +}; + +typedef struct CTRL_ModuleImageInfoCache CTRL_ModuleImageInfoCache; +struct CTRL_ModuleImageInfoCache +{ + U64 slots_count; + CTRL_ModuleImageInfoCacheSlot *slots; + U64 stripes_count; + CTRL_ModuleImageInfoCacheStripe *stripes; +}; + +//////////////////////////////// +//~ rjf: Wakeup Hook Function Types + +#define CTRL_WAKEUP_FUNCTION_DEF(name) void name(void) +typedef CTRL_WAKEUP_FUNCTION_DEF(CTRL_WakeupFunctionType); + +//////////////////////////////// +//~ rjf: Main State Types + +typedef struct CTRL_State CTRL_State; +struct CTRL_State +{ + Arena *arena; + CTRL_WakeupFunctionType *wakeup_hook; + + // rjf: name -> register/alias hash tables for eval + EVAL_String2NumMap arch_string2reg_tables[Architecture_COUNT]; + EVAL_String2NumMap arch_string2alias_tables[Architecture_COUNT]; + + // rjf: caches + CTRL_ProcessMemoryCache process_memory_cache; + CTRL_ThreadRegCache thread_reg_cache; + CTRL_ModuleImageInfoCache module_image_info_cache; + + // rjf: user -> ctrl msg ring buffer + U64 u2c_ring_size; + U8 *u2c_ring_base; + U64 u2c_ring_write_pos; + U64 u2c_ring_read_pos; + OS_Handle u2c_ring_mutex; + OS_Handle u2c_ring_cv; + + // rjf: ctrl -> user event ring buffer + U64 c2u_ring_size; + U64 c2u_ring_max_string_size; + U8 *c2u_ring_base; + U64 c2u_ring_write_pos; + U64 c2u_ring_read_pos; + OS_Handle c2u_ring_mutex; + OS_Handle c2u_ring_cv; + + // rjf: ctrl thread state + String8 ctrl_thread_log_path; + OS_Handle ctrl_thread; + Log *ctrl_thread_log; + 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; + U64 exception_code_filters[(CTRL_ExceptionCodeKind_COUNT+63)/64]; + U64 process_counter; + + // 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; + + // rjf: memory stream threads + U64 ms_thread_count; + OS_Handle *ms_threads; +}; + +//////////////////////////////// +//~ rjf: Globals + +global CTRL_State *ctrl_state = 0; +read_only global CTRL_Entity ctrl_entity_nil = +{ + &ctrl_entity_nil, + &ctrl_entity_nil, + &ctrl_entity_nil, + &ctrl_entity_nil, + &ctrl_entity_nil, +}; + +//////////////////////////////// +//~ rjf: Logging Markup + +#define CTRL_CtrlThreadLogScope DeferLoop(log_scope_begin(), ctrl_thread__end_and_flush_info_log()) + +//////////////////////////////// +//~ rjf: Basic Type Functions + +internal U64 ctrl_hash_from_string(String8 string); +internal U64 ctrl_hash_from_machine_id_handle(CTRL_MachineID machine_id, DMN_Handle handle); +internal CTRL_EventCause ctrl_event_cause_from_dmn_event_kind(DMN_EventKind event_kind); +internal String8 ctrl_string_from_event_kind(CTRL_EventKind kind); +internal String8 ctrl_string_from_msg_kind(CTRL_MsgKind kind); + +//////////////////////////////// +//~ rjf: Machine/Handle Pair Type Functions + +internal void ctrl_machine_id_handle_pair_list_push(Arena *arena, CTRL_MachineIDHandlePairList *list, CTRL_MachineIDHandlePair *pair); +internal CTRL_MachineIDHandlePairList ctrl_machine_id_handle_pair_list_copy(Arena *arena, CTRL_MachineIDHandlePairList *src); + +//////////////////////////////// +//~ rjf: Trap Type Functions + +internal void ctrl_trap_list_push(Arena *arena, CTRL_TrapList *list, CTRL_Trap *trap); +internal CTRL_TrapList ctrl_trap_list_copy(Arena *arena, CTRL_TrapList *src); + +//////////////////////////////// +//~ rjf: User Breakpoint Type Functions + +internal void ctrl_user_breakpoint_list_push(Arena *arena, CTRL_UserBreakpointList *list, CTRL_UserBreakpoint *bp); +internal CTRL_UserBreakpointList ctrl_user_breakpoint_list_copy(Arena *arena, CTRL_UserBreakpointList *src); + +//////////////////////////////// +//~ rjf: Message Type Functions + +//- rjf: deep copying +internal void ctrl_msg_deep_copy(Arena *arena, CTRL_Msg *dst, CTRL_Msg *src); + +//- rjf: list building +internal CTRL_Msg *ctrl_msg_list_push(Arena *arena, CTRL_MsgList *list); + +//- rjf: serialization +internal String8 ctrl_serialized_string_from_msg_list(Arena *arena, CTRL_MsgList *msgs); +internal CTRL_MsgList ctrl_msg_list_from_serialized_string(Arena *arena, String8 string); + +//////////////////////////////// +//~ rjf: Event Type Functions + +//- rjf: list building +internal CTRL_Event *ctrl_event_list_push(Arena *arena, CTRL_EventList *list); +internal void ctrl_event_list_concat_in_place(CTRL_EventList *dst, CTRL_EventList *to_push); + +//- rjf: serialization +internal String8 ctrl_serialized_string_from_event(Arena *arena, CTRL_Event *event, U64 max); +internal CTRL_Event ctrl_event_from_serialized_string(Arena *arena, String8 string); + +//////////////////////////////// +//~ rjf: Entity Type Functions + +//- 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, Architecture arch, CTRL_MachineID machine_id, DMN_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_machine_id_handle(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle handle); +internal CTRL_Entity *ctrl_entity_child_from_kind(CTRL_Entity *parent, CTRL_EntityKind kind); + +//- rjf: applying events to entity caches +internal void ctrl_entity_store_apply_events(CTRL_EntityStore *store, CTRL_EventList *list); + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void ctrl_init(void); + +//////////////////////////////// +//~ rjf: Wakeup Callback Registration + +internal void ctrl_set_wakeup_hook(CTRL_WakeupFunctionType *wakeup_hook); + +//////////////////////////////// +//~ rjf: Process Memory Functions + +//- rjf: process memory cache interaction +internal U128 ctrl_calc_hash_store_key_from_process_vaddr_range(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 zero_terminated); +internal U128 ctrl_stored_hash_from_process_vaddr_range(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 zero_terminated, B32 *out_is_stale, U64 endt_us); + +//- rjf: bundled key/stream helper +internal U128 ctrl_hash_store_key_from_process_vaddr_range(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 zero_terminated); + +//- rjf: process memory cache reading helpers +internal CTRL_ProcessMemorySlice ctrl_query_cached_data_from_process_vaddr_range(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, U64 endt_us); +internal CTRL_ProcessMemorySlice ctrl_query_cached_zero_terminated_data_from_process_vaddr_limit(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, U64 vaddr, U64 limit, U64 element_size, U64 endt_us); +internal B32 ctrl_read_cached_process_memory(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, B32 *is_stale_out, void *out, U64 endt_us); +#define ctrl_read_cached_process_memory_struct(machine_id, process, vaddr, is_stale_out, ptr, endt_us) ctrl_read_cached_process_memory((machine_id), (process), r1u64((vaddr), (vaddr)+(sizeof(*(ptr)))), (is_stale_out), (ptr), (endt_us)) + +//- rjf: process memory writing +internal B32 ctrl_process_write(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 range, void *src); + +//////////////////////////////// +//~ rjf: Thread Register Functions + +//- rjf: thread register cache reading +internal void *ctrl_query_cached_reg_block_from_thread(Arena *arena, CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread); +internal U64 ctrl_query_cached_tls_root_vaddr_from_thread(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread); +internal U64 ctrl_query_cached_rip_from_thread(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread); +internal U64 ctrl_query_cached_rsp_from_thread(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread); + +//- rjf: thread register writing +internal B32 ctrl_thread_write_reg_block(CTRL_MachineID machine_id, DMN_Handle thread, void *block); + +//////////////////////////////// +//~ rjf: Module Image Info Functions + +//- rjf: cache lookups +internal PE_IntelPdata *ctrl_intel_pdata_from_module_voff(Arena *arena, CTRL_MachineID machine_id, DMN_Handle module_handle, U64 voff); +internal U64 ctrl_entry_point_voff_from_module(CTRL_MachineID machine_id, DMN_Handle module_handle); +internal Rng1U64 ctrl_tls_vaddr_range_from_module(CTRL_MachineID machine_id, DMN_Handle module_handle); +internal String8 ctrl_initial_debug_info_path_from_module(Arena *arena, CTRL_MachineID machine_id, DMN_Handle module_handle); + +//////////////////////////////// +//~ rjf: Unwinding Functions + +//- rjf: unwind deep copier +internal CTRL_Unwind ctrl_unwind_deep_copy(Arena *arena, Architecture arch, CTRL_Unwind *src); + +//- 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_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle process_handle, DMN_Handle module, REGS_RegBlockX64 *regs, U64 endt_us); + +//- rjf: abstracted unwind step +internal CTRL_UnwindStepResult ctrl_unwind_step(CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle process_handle, DMN_Handle module, Architecture arch, void *reg_block, U64 endt_us); + +//- rjf: abstracted full unwind +internal CTRL_Unwind ctrl_unwind_from_thread(Arena *arena, CTRL_EntityStore *store, CTRL_MachineID machine_id, DMN_Handle thread, U64 endt_us); + +//////////////////////////////// +//~ rjf: Halting All Attached Processes + +internal void ctrl_halt(void); + +//////////////////////////////// +//~ rjf: Shared Accessor Functions + +//- rjf: generation counters +internal U64 ctrl_run_gen(void); +internal U64 ctrl_mem_gen(void); +internal U64 ctrl_reg_gen(void); + +//- rjf: name -> register/alias hash tables, for eval +internal EVAL_String2NumMap *ctrl_string2reg_from_arch(Architecture arch); +internal EVAL_String2NumMap *ctrl_string2alias_from_arch(Architecture arch); + +//////////////////////////////// +//~ rjf: Control-Thread Functions + +//- rjf: user -> control thread communication +internal B32 ctrl_u2c_push_msgs(CTRL_MsgList *msgs, U64 endt_us); +internal CTRL_MsgList ctrl_u2c_pop_msgs(Arena *arena); + +//- rjf: control -> user thread communication +internal void ctrl_c2u_push_events(CTRL_EventList *events); +internal CTRL_EventList ctrl_c2u_pop_events(Arena *arena); + +//- rjf: entry point +internal void ctrl_thread__entry_point(void *p); + +//- rjf: breakpoint resolution +internal void ctrl_thread__append_resolved_module_user_bp_traps(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, DMN_Handle module, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out); +internal void ctrl_thread__append_resolved_process_user_bp_traps(Arena *arena, CTRL_MachineID machine_id, DMN_Handle process, CTRL_UserBreakpointList *user_bps, DMN_TrapChunkList *traps_out); + +//- rjf: module lifetime open/close work +internal void ctrl_thread__module_open(CTRL_MachineID machine_id, DMN_Handle process, DMN_Handle module, Rng1U64 vaddr_range, String8 path); +internal void ctrl_thread__module_close(CTRL_MachineID machine_id, DMN_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 B32 ctrl_eval_memory_read(void *u, void *out, U64 addr, U64 size); + +//- rjf: log flusher +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); +internal void ctrl_thread__attach(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); +internal void ctrl_thread__kill(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); +internal void ctrl_thread__detach(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); +internal void ctrl_thread__run(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); +internal void ctrl_thread__single_step(DMN_CtrlCtx *ctrl_ctx, CTRL_Msg *msg); + +//////////////////////////////// +//~ rjf: Memory-Stream Thread Functions + +//- rjf: user -> memory stream communication +internal B32 ctrl_u2ms_enqueue_req(CTRL_MachineID machine_id, DMN_Handle process, Rng1U64 vaddr_range, B32 zero_terminated, U64 endt_us); +internal void ctrl_u2ms_dequeue_req(CTRL_MachineID *out_machine_id, DMN_Handle *out_process, Rng1U64 *out_vaddr_range, B32 *out_zero_terminated); + +//- rjf: entry point +internal void ctrl_mem_stream_thread__entry_point(void *p); + +#endif // CTRL_CORE_H diff --git a/src/ctrl/ctrl_inc.c b/src/ctrl/ctrl_inc.c index cdbe3eda..ce59e6dc 100644 --- a/src/ctrl/ctrl_inc.c +++ b/src/ctrl/ctrl_inc.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#include "ctrl_core.c" +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "ctrl_core.c" diff --git a/src/ctrl/ctrl_inc.h b/src/ctrl/ctrl_inc.h index 24b17db2..5a0fcb0d 100644 --- a/src/ctrl/ctrl_inc.h +++ b/src/ctrl/ctrl_inc.h @@ -1,77 +1,77 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef CTRL_INC_H -#define CTRL_INC_H - -//////////////////////////////// -//~ NOTE(rjf): Control Layer Overview (2023/8/29) -// -// This layer's purpose is to provide access to the asynchronously-running, low -// level parts of a debugger, running on the debugger client. This primarily -// consists of process control, using the Demon layer (the lower level -// abstraction layer for process control, across multiple OSes), but including -// higher-level concepts, like stepping, breakpoint resolution, conditional -// breakpoint evaluation, and so on. Right now, this just includes process -// control *local to the debugger client machine*. But in the future, this can -// also include communication to multiple target machines, all running their -// own process controller, using the Demon layer. -// -// This part of a debugger must run asynchronously to prevent blocking the UI - -// ideally our debugger is designed such that, if targets are running, the -// debugger frontend is still usable for a variety of purposes. So, in short, -// the asynchronously-running "control thread", implemented by this layer, is -// tasked with communicating with a separately executing "user thread". This -// communication happens in two directions - `user -> ctrl`, and the reverse, -// `ctrl -> user`. -// -// In the case of `user -> ctrl` communication, this is done with a ring buffer -// of "messages" (`CTRL_Msg`), pushed via `ctrl_u2c_push_msgs`. These messages -// include commands like: launching targets, attaching to targets, killing -// targets, detaching from targets, stepping/running, or single stepping. -// -// In the case of `ctrl -> user` communication, this is done with a ring buffer -// of "events" (`CTRL_Event`), popped via `ctrl_c2u_pop_events`. These events -// include information about what happened during the execution of targets - -// including: process/module/thread creation, process/module/thread deletion, -// debug strings, thread name events, memory allocation events, and stop events -// (where stops can be caused by: user breakpoints, traps set for stepping, -// exceptions, halts, or errors). -// -// The various stepping algorithms are implemented with two concepts: (a) the -// "trap net", and (b) "spoofs". -// -// A "trap net" is a term which refers to a set of addresses paired with a set -// of behavioral flags. Before targets run, trap instructions are written to -// these addresses. After targets stop, these addresses are reset to their -// original bytes. These trap instructions cause the debugger's targets to -// stop executing, and based on which behavioral flags are associated with -// the instruction causing the stop, the control thread may adjust parameters -// used for running, then continue execution, or it will not resume target -// execution, and will report stopped events. These behavioral flags can -// include: single-stepping the stopped thread to execute the instruction at -// the trap location, saving a stack pointer "check value" (where this check -// value is compared against when making decisions about whether to continue -// running or not), and so on. It's complicated to unpack why exactly these -// behaviors are useful, but the TL;DR of it is that they are used for a -// variety of stepping behaviors. For example, when doing a "step into" step, -// a `call` instruction can have a trap set at it, and will be marked with -// a "single-step-after" trap flag, as well as the "end stepping" trap flag, -// such that the step operation will complete after the `call` has executed. -// -// A "spoof" is a feature the control layer uses to detect when some thread -// returns from a particular sub-callstack. This is useful when implementing -// "step over" in functions that may be recursive. In short, unlike a trap, -// which writes a trap instruction (like `int3`) into an instruction stream, -// a spoof overwrites a *return address* on some thread's *stack*. This return -// address is not a valid address for executing code -- it is simply a value -// that the debugger can recognize, such that it is notified when the thread -// returns from some level in a callstack. When the thread exits some function, -// it will return to the "spoofed" address, and it will immediately hit an -// exception, because the spoofed address will not be a valid address for -// code execution. At that point, the debugger can move the thread back to -// the pre-spoof return address, and resume execution. - -#include "ctrl_core.h" - -#endif // CTRL_INC_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef CTRL_INC_H +#define CTRL_INC_H + +//////////////////////////////// +//~ NOTE(rjf): Control Layer Overview (2023/8/29) +// +// This layer's purpose is to provide access to the asynchronously-running, low +// level parts of a debugger, running on the debugger client. This primarily +// consists of process control, using the Demon layer (the lower level +// abstraction layer for process control, across multiple OSes), but including +// higher-level concepts, like stepping, breakpoint resolution, conditional +// breakpoint evaluation, and so on. Right now, this just includes process +// control *local to the debugger client machine*. But in the future, this can +// also include communication to multiple target machines, all running their +// own process controller, using the Demon layer. +// +// This part of a debugger must run asynchronously to prevent blocking the UI - +// ideally our debugger is designed such that, if targets are running, the +// debugger frontend is still usable for a variety of purposes. So, in short, +// the asynchronously-running "control thread", implemented by this layer, is +// tasked with communicating with a separately executing "user thread". This +// communication happens in two directions - `user -> ctrl`, and the reverse, +// `ctrl -> user`. +// +// In the case of `user -> ctrl` communication, this is done with a ring buffer +// of "messages" (`CTRL_Msg`), pushed via `ctrl_u2c_push_msgs`. These messages +// include commands like: launching targets, attaching to targets, killing +// targets, detaching from targets, stepping/running, or single stepping. +// +// In the case of `ctrl -> user` communication, this is done with a ring buffer +// of "events" (`CTRL_Event`), popped via `ctrl_c2u_pop_events`. These events +// include information about what happened during the execution of targets - +// including: process/module/thread creation, process/module/thread deletion, +// debug strings, thread name events, memory allocation events, and stop events +// (where stops can be caused by: user breakpoints, traps set for stepping, +// exceptions, halts, or errors). +// +// The various stepping algorithms are implemented with two concepts: (a) the +// "trap net", and (b) "spoofs". +// +// A "trap net" is a term which refers to a set of addresses paired with a set +// of behavioral flags. Before targets run, trap instructions are written to +// these addresses. After targets stop, these addresses are reset to their +// original bytes. These trap instructions cause the debugger's targets to +// stop executing, and based on which behavioral flags are associated with +// the instruction causing the stop, the control thread may adjust parameters +// used for running, then continue execution, or it will not resume target +// execution, and will report stopped events. These behavioral flags can +// include: single-stepping the stopped thread to execute the instruction at +// the trap location, saving a stack pointer "check value" (where this check +// value is compared against when making decisions about whether to continue +// running or not), and so on. It's complicated to unpack why exactly these +// behaviors are useful, but the TL;DR of it is that they are used for a +// variety of stepping behaviors. For example, when doing a "step into" step, +// a `call` instruction can have a trap set at it, and will be marked with +// a "single-step-after" trap flag, as well as the "end stepping" trap flag, +// such that the step operation will complete after the `call` has executed. +// +// A "spoof" is a feature the control layer uses to detect when some thread +// returns from a particular sub-callstack. This is useful when implementing +// "step over" in functions that may be recursive. In short, unlike a trap, +// which writes a trap instruction (like `int3`) into an instruction stream, +// a spoof overwrites a *return address* on some thread's *stack*. This return +// address is not a valid address for executing code -- it is simply a value +// that the debugger can recognize, such that it is notified when the thread +// returns from some level in a callstack. When the thread exits some function, +// it will return to the "spoofed" address, and it will immediately hit an +// exception, because the spoofed address will not be a valid address for +// code execution. At that point, the debugger can move the thread back to +// the pre-spoof return address, and resume execution. + +#include "ctrl_core.h" + +#endif // CTRL_INC_H diff --git a/src/dbgi/dbgi.h b/src/dbgi/dbgi.h index 72e166b2..be9a7ec0 100644 --- a/src/dbgi/dbgi.h +++ b/src/dbgi/dbgi.h @@ -1,255 +1,255 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DI_H -#define DI_H - -//////////////////////////////// -//~ rjf: Cache Key Type - -typedef struct DI_Key DI_Key; -struct DI_Key -{ - String8 path; - U64 min_timestamp; -}; - -typedef struct DI_KeyNode DI_KeyNode; -struct DI_KeyNode -{ - DI_KeyNode *next; - DI_Key v; -}; - -typedef struct DI_KeyList DI_KeyList; -struct DI_KeyList -{ - DI_KeyNode *first; - DI_KeyNode *last; - U64 count; -}; - -typedef struct DI_KeyArray DI_KeyArray; -struct DI_KeyArray -{ - DI_Key *v; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Event Types - -typedef enum DI_EventKind -{ - DI_EventKind_Null, - DI_EventKind_ConversionStarted, - DI_EventKind_ConversionEnded, - DI_EventKind_ConversionFailureUnsupportedFormat, - DI_EventKind_COUNT -} -DI_EventKind; - -typedef struct DI_Event DI_Event; -struct DI_Event -{ - DI_EventKind kind; - String8 string; -}; - -typedef struct DI_EventNode DI_EventNode; -struct DI_EventNode -{ - DI_EventNode *next; - DI_Event v; -}; - -typedef struct DI_EventList DI_EventList; -struct DI_EventList -{ - DI_EventNode *first; - DI_EventNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Cache Types - -typedef struct DI_StringChunkNode DI_StringChunkNode; -struct DI_StringChunkNode -{ - DI_StringChunkNode *next; - U64 size; -}; - -typedef struct DI_Node DI_Node; -struct DI_Node -{ - // rjf: links - DI_Node *next; - DI_Node *prev; - - // rjf: metadata - U64 ref_count; - U64 touch_count; - U64 is_working; - U64 last_time_requested_us; - - // rjf: key - DI_Key key; - - // rjf: file handles - OS_Handle file; - OS_Handle file_map; - void *file_base; - FileProperties file_props; - - // rjf: parse artifacts - Arena *arena; - RDI_Parsed rdi; - B32 parse_done; -}; - -typedef struct DI_Slot DI_Slot; -struct DI_Slot -{ - DI_Node *first; - DI_Node *last; -}; - -typedef struct DI_Stripe DI_Stripe; -struct DI_Stripe -{ - Arena *arena; - DI_Node *free_node; - DI_StringChunkNode *free_string_chunks[8]; - OS_Handle rw_mutex; - OS_Handle cv; -}; - -//////////////////////////////// -//~ rjf: Scoped Access Types - -typedef struct DI_Touch DI_Touch; -struct DI_Touch -{ - DI_Touch *next; - DI_Node *node; -}; - -typedef struct DI_Scope DI_Scope; -struct DI_Scope -{ - DI_Scope *next; - DI_Touch *first_touch; - DI_Touch *last_touch; -}; - -typedef struct DI_TCTX DI_TCTX; -struct DI_TCTX -{ - Arena *arena; - DI_Scope *free_scope; - DI_Touch *free_touch; -}; - -//////////////////////////////// -//~ rjf: Shared State Types - -typedef struct DI_Shared DI_Shared; -struct DI_Shared -{ - Arena *arena; - - // rjf: node cache - U64 slots_count; - DI_Slot *slots; - U64 stripes_count; - DI_Stripe *stripes; - - // rjf: user -> parse ring - OS_Handle u2p_ring_mutex; - OS_Handle u2p_ring_cv; - U64 u2p_ring_size; - U8 *u2p_ring_base; - U64 u2p_ring_write_pos; - U64 u2p_ring_read_pos; - - // rjf: parse -> user event ring - OS_Handle p2u_ring_mutex; - OS_Handle p2u_ring_cv; - U64 p2u_ring_size; - U8 *p2u_ring_base; - U64 p2u_ring_write_pos; - U64 p2u_ring_read_pos; - - // rjf: threads - U64 parse_thread_count; - OS_Handle *parse_threads; -}; - -//////////////////////////////// -//~ rjf: Globals - -global DI_Shared *di_shared = 0; -thread_static DI_TCTX *di_tctx = 0; -global RDI_Parsed di_rdi_parsed_nil = {0}; - -//////////////////////////////// -//~ rjf: Basic Helpers - -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); - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void di_init(void); - -//////////////////////////////// -//~ rjf: Scope Functions - -internal DI_Scope *di_scope_open(void); -internal void di_scope_close(DI_Scope *scope); -internal void di_scope_touch_node__stripe_mutex_r_guarded(DI_Scope *scope, DI_Node *node); - -//////////////////////////////// -//~ rjf: Per-Slot Functions - -internal DI_Node *di_node_from_key_slot__stripe_mutex_r_guarded(DI_Slot *slot, DI_Key *key); - -//////////////////////////////// -//~ rjf: Per-Stripe Functions - -internal U64 di_string_bucket_idx_from_string_size(U64 size); -internal String8 di_string_alloc__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string); -internal void di_string_release__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string); - -//////////////////////////////// -//~ rjf: Key Opening/Closing - -internal void di_open(DI_Key *key); -internal void di_close(DI_Key *key); - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal RDI_Parsed *di_rdi_from_key(DI_Scope *scope, DI_Key *key, U64 endt_us); - -//////////////////////////////// -//~ rjf: Parse Threads - -internal B32 di_u2p_enqueue_key(DI_Key *key, U64 endt_us); -internal void di_u2p_dequeue_key(Arena *arena, DI_Key *out_key); - -internal void di_p2u_push_event(DI_Event *event); -internal DI_EventList di_p2u_pop_events(Arena *arena, U64 endt_us); - -internal void di_parse_thread__entry_point(void *p); - -#endif // DI_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DI_H +#define DI_H + +//////////////////////////////// +//~ rjf: Cache Key Type + +typedef struct DI_Key DI_Key; +struct DI_Key +{ + String8 path; + U64 min_timestamp; +}; + +typedef struct DI_KeyNode DI_KeyNode; +struct DI_KeyNode +{ + DI_KeyNode *next; + DI_Key v; +}; + +typedef struct DI_KeyList DI_KeyList; +struct DI_KeyList +{ + DI_KeyNode *first; + DI_KeyNode *last; + U64 count; +}; + +typedef struct DI_KeyArray DI_KeyArray; +struct DI_KeyArray +{ + DI_Key *v; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Event Types + +typedef enum DI_EventKind +{ + DI_EventKind_Null, + DI_EventKind_ConversionStarted, + DI_EventKind_ConversionEnded, + DI_EventKind_ConversionFailureUnsupportedFormat, + DI_EventKind_COUNT +} +DI_EventKind; + +typedef struct DI_Event DI_Event; +struct DI_Event +{ + DI_EventKind kind; + String8 string; +}; + +typedef struct DI_EventNode DI_EventNode; +struct DI_EventNode +{ + DI_EventNode *next; + DI_Event v; +}; + +typedef struct DI_EventList DI_EventList; +struct DI_EventList +{ + DI_EventNode *first; + DI_EventNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Cache Types + +typedef struct DI_StringChunkNode DI_StringChunkNode; +struct DI_StringChunkNode +{ + DI_StringChunkNode *next; + U64 size; +}; + +typedef struct DI_Node DI_Node; +struct DI_Node +{ + // rjf: links + DI_Node *next; + DI_Node *prev; + + // rjf: metadata + U64 ref_count; + U64 touch_count; + U64 is_working; + U64 last_time_requested_us; + + // rjf: key + DI_Key key; + + // rjf: file handles + OS_Handle file; + OS_Handle file_map; + void *file_base; + FileProperties file_props; + + // rjf: parse artifacts + Arena *arena; + RDI_Parsed rdi; + B32 parse_done; +}; + +typedef struct DI_Slot DI_Slot; +struct DI_Slot +{ + DI_Node *first; + DI_Node *last; +}; + +typedef struct DI_Stripe DI_Stripe; +struct DI_Stripe +{ + Arena *arena; + DI_Node *free_node; + DI_StringChunkNode *free_string_chunks[8]; + OS_Handle rw_mutex; + OS_Handle cv; +}; + +//////////////////////////////// +//~ rjf: Scoped Access Types + +typedef struct DI_Touch DI_Touch; +struct DI_Touch +{ + DI_Touch *next; + DI_Node *node; +}; + +typedef struct DI_Scope DI_Scope; +struct DI_Scope +{ + DI_Scope *next; + DI_Touch *first_touch; + DI_Touch *last_touch; +}; + +typedef struct DI_TCTX DI_TCTX; +struct DI_TCTX +{ + Arena *arena; + DI_Scope *free_scope; + DI_Touch *free_touch; +}; + +//////////////////////////////// +//~ rjf: Shared State Types + +typedef struct DI_Shared DI_Shared; +struct DI_Shared +{ + Arena *arena; + + // rjf: node cache + U64 slots_count; + DI_Slot *slots; + U64 stripes_count; + DI_Stripe *stripes; + + // rjf: user -> parse ring + OS_Handle u2p_ring_mutex; + OS_Handle u2p_ring_cv; + U64 u2p_ring_size; + U8 *u2p_ring_base; + U64 u2p_ring_write_pos; + U64 u2p_ring_read_pos; + + // rjf: parse -> user event ring + OS_Handle p2u_ring_mutex; + OS_Handle p2u_ring_cv; + U64 p2u_ring_size; + U8 *p2u_ring_base; + U64 p2u_ring_write_pos; + U64 p2u_ring_read_pos; + + // rjf: threads + U64 parse_thread_count; + OS_Handle *parse_threads; +}; + +//////////////////////////////// +//~ rjf: Globals + +global DI_Shared *di_shared = 0; +thread_static DI_TCTX *di_tctx = 0; +global RDI_Parsed di_rdi_parsed_nil = {0}; + +//////////////////////////////// +//~ rjf: Basic Helpers + +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); + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void di_init(void); + +//////////////////////////////// +//~ rjf: Scope Functions + +internal DI_Scope *di_scope_open(void); +internal void di_scope_close(DI_Scope *scope); +internal void di_scope_touch_node__stripe_mutex_r_guarded(DI_Scope *scope, DI_Node *node); + +//////////////////////////////// +//~ rjf: Per-Slot Functions + +internal DI_Node *di_node_from_key_slot__stripe_mutex_r_guarded(DI_Slot *slot, DI_Key *key); + +//////////////////////////////// +//~ rjf: Per-Stripe Functions + +internal U64 di_string_bucket_idx_from_string_size(U64 size); +internal String8 di_string_alloc__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string); +internal void di_string_release__stripe_mutex_w_guarded(DI_Stripe *stripe, String8 string); + +//////////////////////////////// +//~ rjf: Key Opening/Closing + +internal void di_open(DI_Key *key); +internal void di_close(DI_Key *key); + +//////////////////////////////// +//~ rjf: Cache Lookups + +internal RDI_Parsed *di_rdi_from_key(DI_Scope *scope, DI_Key *key, U64 endt_us); + +//////////////////////////////// +//~ rjf: Parse Threads + +internal B32 di_u2p_enqueue_key(DI_Key *key, U64 endt_us); +internal void di_u2p_dequeue_key(Arena *arena, DI_Key *out_key); + +internal void di_p2u_push_event(DI_Event *event); +internal DI_EventList di_p2u_pop_events(Arena *arena, U64 endt_us); + +internal void di_parse_thread__entry_point(void *p); + +#endif // DI_H diff --git a/src/demon/demon_core.c b/src/demon/demon_core.c index d2f3de2d..7d2a18f5 100644 --- a/src/demon/demon_core.c +++ b/src/demon/demon_core.c @@ -1,158 +1,158 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/demon.meta.c" - -//////////////////////////////// -//~ rjf: Basic Type Functions (Helpers, Implemented Once) - -//- rjf: handles - -internal DMN_Handle -dmn_handle_zero(void) -{ - DMN_Handle h = {0}; - return h; -} - -internal B32 -dmn_handle_match(DMN_Handle a, DMN_Handle b) -{ - return a.u32[0] == b.u32[0] && a.u32[1] == b.u32[1]; -} - -//- rjf: trap chunk lists - -internal void -dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap) -{ - DMN_TrapChunkNode *node = list->last; - if(node == 0 || node->count >= node->cap) - { - node = push_array(arena, DMN_TrapChunkNode, 1); - node->cap = cap; - node->v = push_array_no_zero(arena, DMN_Trap, node->cap); - SLLQueuePush(list->first, list->last, node); - list->node_count += 1; - } - MemoryCopyStruct(&node->v[node->count], trap); - node->count += 1; - list->trap_count += 1; -} - -internal void -dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push) -{ - if(dst->last == 0) - { - MemoryCopyStruct(dst, to_push); - } - else if(to_push->first != 0) - { - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->node_count += to_push->node_count; - dst->trap_count += to_push->trap_count; - } - MemoryZeroStruct(to_push); -} - -internal void -dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push) -{ - for(DMN_TrapChunkNode *src_n = to_push->first; src_n != 0; src_n = src_n->next) - { - DMN_TrapChunkNode *dst_n = push_array(arena, DMN_TrapChunkNode, 1); - dst_n->v = src_n->v; - dst_n->cap = src_n->cap; - dst_n->count = src_n->count; - SLLQueuePush(dst->first, dst->last, dst_n); - dst->node_count += 1; - dst->trap_count += dst_n->count; - } -} - -//- rjf: handle lists - -internal void -dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle) -{ - DMN_HandleNode *node = push_array(arena, DMN_HandleNode, 1); - SLLQueuePush(list->first, list->last, node); - node->v = handle; - list->count += 1; -} - -internal DMN_HandleArray -dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list) -{ - DMN_HandleArray array = {0}; - array.count = list->count; - array.handles = push_array_no_zero(arena, DMN_Handle, array.count); - U64 idx = 0; - for(DMN_HandleNode *n = list->first; n != 0; n = n->next, idx += 1) - { - array.handles[idx] = n->v; - } - return array; -} - -internal DMN_HandleArray -dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src) -{ - DMN_HandleArray dst = {0}; - dst.count = src->count; - dst.handles = push_array_no_zero(arena, DMN_Handle, dst.count); - MemoryCopy(dst.handles, src->handles, sizeof(DMN_Handle)*dst.count); - return dst; -} - -//- rjf: event list building - -internal DMN_Event * -dmn_event_list_push(Arena *arena, DMN_EventList *list) -{ - DMN_EventNode *n = push_array(arena, DMN_EventNode, 1); - SLLQueuePush(list->first, list->last, n); - list->count += 1; - DMN_Event *result = &n->v; - return result; -} - -//////////////////////////////// -//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once) - -internal U64 -dmn_rip_from_thread(DMN_Handle thread) -{ - U64 result = 0; - Temp scratch = scratch_begin(0, 0); - { - Architecture arch = dmn_arch_from_thread(thread); - U64 reg_block_size = regs_block_size_from_architecture(arch); - void *reg_block = push_array(scratch.arena, U8, reg_block_size); - dmn_thread_read_reg_block(thread, reg_block); - result = regs_rip_from_arch_block(arch, reg_block); - } - scratch_end(scratch); - return result; -} - -internal U64 -dmn_rsp_from_thread(DMN_Handle thread) -{ - U64 result = 0; - Temp scratch = scratch_begin(0, 0); - { - Architecture arch = dmn_arch_from_thread(thread); - U64 reg_block_size = regs_block_size_from_architecture(arch); - void *reg_block = push_array(scratch.arena, U8, reg_block_size); - dmn_thread_read_reg_block(thread, reg_block); - result = regs_rsp_from_arch_block(arch, reg_block); - } - scratch_end(scratch); - return result; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Generated Code + +#include "generated/demon.meta.c" + +//////////////////////////////// +//~ rjf: Basic Type Functions (Helpers, Implemented Once) + +//- rjf: handles + +internal DMN_Handle +dmn_handle_zero(void) +{ + DMN_Handle h = {0}; + return h; +} + +internal B32 +dmn_handle_match(DMN_Handle a, DMN_Handle b) +{ + return a.u32[0] == b.u32[0] && a.u32[1] == b.u32[1]; +} + +//- rjf: trap chunk lists + +internal void +dmn_trap_chunk_list_push(Arena *arena, DMN_TrapChunkList *list, U64 cap, DMN_Trap *trap) +{ + DMN_TrapChunkNode *node = list->last; + if(node == 0 || node->count >= node->cap) + { + node = push_array(arena, DMN_TrapChunkNode, 1); + node->cap = cap; + node->v = push_array_no_zero(arena, DMN_Trap, node->cap); + SLLQueuePush(list->first, list->last, node); + list->node_count += 1; + } + MemoryCopyStruct(&node->v[node->count], trap); + node->count += 1; + list->trap_count += 1; +} + +internal void +dmn_trap_chunk_list_concat_in_place(DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push) +{ + if(dst->last == 0) + { + MemoryCopyStruct(dst, to_push); + } + else if(to_push->first != 0) + { + dst->last->next = to_push->first; + dst->last = to_push->last; + dst->node_count += to_push->node_count; + dst->trap_count += to_push->trap_count; + } + MemoryZeroStruct(to_push); +} + +internal void +dmn_trap_chunk_list_concat_shallow_copy(Arena *arena, DMN_TrapChunkList *dst, DMN_TrapChunkList *to_push) +{ + for(DMN_TrapChunkNode *src_n = to_push->first; src_n != 0; src_n = src_n->next) + { + DMN_TrapChunkNode *dst_n = push_array(arena, DMN_TrapChunkNode, 1); + dst_n->v = src_n->v; + dst_n->cap = src_n->cap; + dst_n->count = src_n->count; + SLLQueuePush(dst->first, dst->last, dst_n); + dst->node_count += 1; + dst->trap_count += dst_n->count; + } +} + +//- rjf: handle lists + +internal void +dmn_handle_list_push(Arena *arena, DMN_HandleList *list, DMN_Handle handle) +{ + DMN_HandleNode *node = push_array(arena, DMN_HandleNode, 1); + SLLQueuePush(list->first, list->last, node); + node->v = handle; + list->count += 1; +} + +internal DMN_HandleArray +dmn_handle_array_from_list(Arena *arena, DMN_HandleList *list) +{ + DMN_HandleArray array = {0}; + array.count = list->count; + array.handles = push_array_no_zero(arena, DMN_Handle, array.count); + U64 idx = 0; + for(DMN_HandleNode *n = list->first; n != 0; n = n->next, idx += 1) + { + array.handles[idx] = n->v; + } + return array; +} + +internal DMN_HandleArray +dmn_handle_array_copy(Arena *arena, DMN_HandleArray *src) +{ + DMN_HandleArray dst = {0}; + dst.count = src->count; + dst.handles = push_array_no_zero(arena, DMN_Handle, dst.count); + MemoryCopy(dst.handles, src->handles, sizeof(DMN_Handle)*dst.count); + return dst; +} + +//- rjf: event list building + +internal DMN_Event * +dmn_event_list_push(Arena *arena, DMN_EventList *list) +{ + DMN_EventNode *n = push_array(arena, DMN_EventNode, 1); + SLLQueuePush(list->first, list->last, n); + list->count += 1; + DMN_Event *result = &n->v; + return result; +} + +//////////////////////////////// +//~ rjf: Thread Reading Helper Functions (Helpers, Implemented Once) + +internal U64 +dmn_rip_from_thread(DMN_Handle thread) +{ + U64 result = 0; + Temp scratch = scratch_begin(0, 0); + { + Architecture arch = dmn_arch_from_thread(thread); + U64 reg_block_size = regs_block_size_from_architecture(arch); + void *reg_block = push_array(scratch.arena, U8, reg_block_size); + dmn_thread_read_reg_block(thread, reg_block); + result = regs_rip_from_arch_block(arch, reg_block); + } + scratch_end(scratch); + return result; +} + +internal U64 +dmn_rsp_from_thread(DMN_Handle thread) +{ + U64 result = 0; + Temp scratch = scratch_begin(0, 0); + { + Architecture arch = dmn_arch_from_thread(thread); + U64 reg_block_size = regs_block_size_from_architecture(arch); + void *reg_block = push_array(scratch.arena, U8, reg_block_size); + dmn_thread_read_reg_block(thread, reg_block); + result = regs_rsp_from_arch_block(arch, reg_block); + } + scratch_end(scratch); + return result; +} diff --git a/src/demon/demon_core.mdesk b/src/demon/demon_core.mdesk index c0d05ed7..1ccc3c14 100644 --- a/src/demon/demon_core.mdesk +++ b/src/demon/demon_core.mdesk @@ -1,90 +1,90 @@ -//////////////////////////////// -//~ rjf: Event Kind Tables - -@table(name) -DMN_EventKindTable: -{ - {Null} - {Error} - {HandshakeComplete} - {CreateProcess} - {ExitProcess} - {CreateThread} - {ExitThread} - {LoadModule} - {UnloadModule} - {Breakpoint} - {Trap} - {SingleStep} - {Exception} - {Halt} - {Memory} - {DebugString} - {SetThreadName} -} - -@table(name) -DMN_ErrorKindTable: -{ - {Null} - {NotAttached} - {UnexpectedFailure} - {InvalidHandle} -} - -@table(name) -DMN_MemoryEventKindTable: -{ - {Null} - {Commit} - {Reserve} - {Decommit} - {Release} -} - -@table(name) -DMN_ExceptionKindTable: -{ - {Null} - {MemoryRead} - {MemoryWrite} - {MemoryExecute} - {CppThrow} -} - -//////////////////////////////// -//~ rjf: Generators - -@enum DMN_EventKind: -{ - @expand(DMN_EventKindTable a) `$(a.name)`, - COUNT -} - -@data(String8) dmn_event_kind_string_table: -{ - @expand(DMN_EventKindTable a) `str8_lit_comp("$(a.name)")` -} - -@enum DMN_ErrorKind: -{ - @expand(DMN_ErrorKindTable a) `$(a.name)`, - COUNT -} - -@enum DMN_MemoryEventKind: -{ - @expand(DMN_MemoryEventKindTable a) `$(a.name)`, - COUNT -} - -@enum DMN_ExceptionKind: -{ - @expand(DMN_ExceptionKindTable a) `$(a.name)`, - COUNT -} - -@data(String8) dmn_exception_kind_string_table: -{ - @expand(DMN_ExceptionKindTable a) `str8_lit_comp("$(a.name)")` -} +//////////////////////////////// +//~ rjf: Event Kind Tables + +@table(name) +DMN_EventKindTable: +{ + {Null} + {Error} + {HandshakeComplete} + {CreateProcess} + {ExitProcess} + {CreateThread} + {ExitThread} + {LoadModule} + {UnloadModule} + {Breakpoint} + {Trap} + {SingleStep} + {Exception} + {Halt} + {Memory} + {DebugString} + {SetThreadName} +} + +@table(name) +DMN_ErrorKindTable: +{ + {Null} + {NotAttached} + {UnexpectedFailure} + {InvalidHandle} +} + +@table(name) +DMN_MemoryEventKindTable: +{ + {Null} + {Commit} + {Reserve} + {Decommit} + {Release} +} + +@table(name) +DMN_ExceptionKindTable: +{ + {Null} + {MemoryRead} + {MemoryWrite} + {MemoryExecute} + {CppThrow} +} + +//////////////////////////////// +//~ rjf: Generators + +@enum DMN_EventKind: +{ + @expand(DMN_EventKindTable a) `$(a.name)`, + COUNT +} + +@data(String8) dmn_event_kind_string_table: +{ + @expand(DMN_EventKindTable a) `str8_lit_comp("$(a.name)")` +} + +@enum DMN_ErrorKind: +{ + @expand(DMN_ErrorKindTable a) `$(a.name)`, + COUNT +} + +@enum DMN_MemoryEventKind: +{ + @expand(DMN_MemoryEventKindTable a) `$(a.name)`, + COUNT +} + +@enum DMN_ExceptionKind: +{ + @expand(DMN_ExceptionKindTable a) `$(a.name)`, + COUNT +} + +@data(String8) dmn_exception_kind_string_table: +{ + @expand(DMN_ExceptionKindTable a) `str8_lit_comp("$(a.name)")` +} diff --git a/src/demon/linux/demon_os_linux.h b/src/demon/linux/demon_os_linux.h index 4e039e35..adba6853 100644 --- a/src/demon/linux/demon_os_linux.h +++ b/src/demon/linux/demon_os_linux.h @@ -1,222 +1,222 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DEMON_OS_LINUX_H -#define DEMON_OS_LINUX_H - -// TODO(allen): Potential Upgrades: -// -// memory fd upgrade - Right now for each process we hold open a file -// descriptor for the process's memory (/proc/%d/mem) for the entire lifetime -// of the process; it could be opened and closed with some kind of LRU cache -// to put a finite cap on the number of handles the demon holds -// - -//////////////////////////////// -//~ NOTE(allen): Get The Linux Includes - -#include -#include -#include -#include -#include -#include -#include - -//////////////////////////////// -//~ NOTE(allen): Linux Demon Types - -//- entities - -// Demon Linux Entity Extensions -// Process: ext_u64 set to memory file descriptor -// Thread : ext_u64 cast to DEMON_LNX_ThreadExt -// Module : ext_u64 set to U64 (address of name) - -struct DEMON_LNX_ThreadExt{ - B32 expecting_dummy_sigstop; -}; -StaticAssert(sizeof(DEMON_LNX_ThreadExt) <= sizeof(Member(DEMON_Entity, ext_u64)), check_demon_lnx_thread_ext); - -//- helpers - -struct DEMON_LNX_AttachNode{ - DEMON_LNX_AttachNode *next; - pid_t pid; -}; - -struct DEMON_LNX_ProcessAux{ - B32 filled; - U64 phnum; - U64 phent; - U64 phdr; - U64 execfn; -}; - -struct DEMON_LNX_PhdrInfo{ - Rng1U64 range; - U64 dynamic; -}; - -struct DEMON_LNX_ModuleNode{ - DEMON_LNX_ModuleNode *next; - U64 vaddr; - U64 size; - U64 name; - U64 already_known; -}; - -struct DEMON_LNX_EntityNode{ - DEMON_LNX_EntityNode *next; - DEMON_Entity *entity; -}; - -//////////////////////////////// -//~ NOTE(allen): Linux Demon Register Layouts - -// these are defined in but only for one architecture at a time -// (and we can't really trick it into giving us both in any obvious way) -// we define them here so that we have them all "at once" - -struct DEMON_LNX_UserRegsX64{ - U64 r15; - U64 r14; - U64 r13; - U64 r12; - U64 rbp; - U64 rbx; - U64 r11; - U64 r10; - U64 r9; - U64 r8; - U64 rax; - U64 rcx; - U64 rdx; - U64 rsi; - U64 rdi; - U64 orig_rax; - U64 rip; - U64 cs; - U64 rflags; - U64 rsp; - U64 ss; - U64 fsbase; - U64 gsbase; - U64 ds; - U64 es; - U64 fs; - U64 gs; -}; - -struct DEMON_LNX_UserX64{ - DEMON_LNX_UserRegsX64 regs; - S32 u_fpvalid, _pad0; - SYMS_XSaveLegacy i387; - U64 u_tsize, u_dsize, u_ssize, start_code, start_stack; - U64 signal; - S32 reserved, _pad1; - U64 u_ar0, u_fpstate; - U64 magic; - U8 u_comm[32]; - U64 u_debugreg[8]; -}; - -struct DEMON_LNX_UserRegsX86{ - U32 ebx; - U32 ecx; - U32 edx; - U32 esi; - U32 edi; - U32 ebp; - U32 eax; - U32 ds; - U32 es; - U32 fs; - U32 gs; - U32 orig_eax; - U32 eip; - U32 cs; - U32 eflags; - U32 sp; - U32 ss; -}; - -struct DEMON_LNX_UserX86{ - DEMON_LNX_UserRegsX86 regs; - S32 u_fpvalid; - SYMS_FSave i387; - U32 u_tsize, u_dsize, u_ssize, start_code, start_stack; - S32 signal, reserved; - U32 u_ar0, u_fpstate; - U32 magic; - U8 u_comm[32]; - U32 u_debugreg[8]; -}; - -//////////////////////////////// - -enum -{ - DEMON_LNX_PermFlags_Read = (1 << 0), - DEMON_LNX_PermFlags_Write = (1 << 1), - DEMON_LNX_PermFlags_Exec = (1 << 2), - DEMON_LNX_PermFlags_Private = (1 << 3) -}; -typedef int DEMON_LNX_PermFlags; - -enum -{ - DEMON_LNX_MapsEntryType_Null, - DEMON_LNX_MapsEntryType_Path, - DEMON_LNX_MapsEntryType_Heap, - DEMON_LNX_MapsEntryType_Stack, - DEMON_LNX_MapsEntryType_VDSO, -}; -typedef int DEMON_LNX_MapsEntryType; - -struct DEMON_LNX_MapsEntry -{ - U64 address_lo; - U64 address_hi; - DEMON_LNX_PermFlags perms; - U64 offset; - U32 dev_major; - U32 dev_minor; - U64 inode; - String8 pathname; - DEMON_LNX_MapsEntryType type; - pid_t stack_tid; -}; - -//////////////////////////////// -//~ rjf: Helpers - -internal DEMON_LNX_ThreadExt* demon_lnx_thread_ext(DEMON_Entity *entity); - -internal B32 demon_lnx_attach_pid(Arena *arena, pid_t pid, DEMON_LNX_AttachNode **new_node); - -internal String8 demon_lnx_executable_path_from_pid(Arena *arena, pid_t pid); -internal int demon_lnx_open_memory_fd_for_pid(pid_t pid); - -internal Architecture demon_lnx_arch_from_pid(pid_t pid); -internal DEMON_LNX_ProcessAux demon_lnx_aux_from_pid(pid_t pid, Architecture arch); -internal DEMON_LNX_PhdrInfo demon_lnx_phdr_info_from_memory(int memory_fd, B32 is_32bit, - U64 phvaddr, U64 phstride, U64 phcount); -internal DEMON_LNX_ModuleNode* demon_lnx_module_list_from_process(Arena *arena, DEMON_Entity *process); - -internal U64 demon_lnx_read_memory(int memory_fd, void *dst, U64 src, U64 size); -internal B32 demon_lnx_write_memory(int memory_fd, U64 dst, void *src, U64 size); -internal String8 demon_lnx_read_memory_str(Arena *arena, int memory_fd, U64 address); - -internal void demon_lnx_regs_x64_from_usr_regs_x64(SYMS_RegX64 *dst, DEMON_LNX_UserRegsX64 *src); -internal void demon_lnx_usr_regs_x64_from_regs_x64(DEMON_LNX_UserRegsX64 *dst, SYMS_RegX64 *src); - -internal String8 demon_lnx_read_int_string(int fd); -internal B32 demon_lnx_read_expect(int fd, char expect); -internal int demon_lnx_read_whitespace(int fd); -internal String8 demon_lnx_read_string(Arena *arena, int fd); - -internal int demon_lnx_open_maps(pid_t pid); -internal B32 demon_lnx_next_map(Arena *arena, int maps, DEMON_LNX_MapsEntry *entry_out); - -#endif //DEMON_OS_LINUX_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DEMON_OS_LINUX_H +#define DEMON_OS_LINUX_H + +// TODO(allen): Potential Upgrades: +// +// memory fd upgrade - Right now for each process we hold open a file +// descriptor for the process's memory (/proc/%d/mem) for the entire lifetime +// of the process; it could be opened and closed with some kind of LRU cache +// to put a finite cap on the number of handles the demon holds +// + +//////////////////////////////// +//~ NOTE(allen): Get The Linux Includes + +#include +#include +#include +#include +#include +#include +#include + +//////////////////////////////// +//~ NOTE(allen): Linux Demon Types + +//- entities + +// Demon Linux Entity Extensions +// Process: ext_u64 set to memory file descriptor +// Thread : ext_u64 cast to DEMON_LNX_ThreadExt +// Module : ext_u64 set to U64 (address of name) + +struct DEMON_LNX_ThreadExt{ + B32 expecting_dummy_sigstop; +}; +StaticAssert(sizeof(DEMON_LNX_ThreadExt) <= sizeof(Member(DEMON_Entity, ext_u64)), check_demon_lnx_thread_ext); + +//- helpers + +struct DEMON_LNX_AttachNode{ + DEMON_LNX_AttachNode *next; + pid_t pid; +}; + +struct DEMON_LNX_ProcessAux{ + B32 filled; + U64 phnum; + U64 phent; + U64 phdr; + U64 execfn; +}; + +struct DEMON_LNX_PhdrInfo{ + Rng1U64 range; + U64 dynamic; +}; + +struct DEMON_LNX_ModuleNode{ + DEMON_LNX_ModuleNode *next; + U64 vaddr; + U64 size; + U64 name; + U64 already_known; +}; + +struct DEMON_LNX_EntityNode{ + DEMON_LNX_EntityNode *next; + DEMON_Entity *entity; +}; + +//////////////////////////////// +//~ NOTE(allen): Linux Demon Register Layouts + +// these are defined in but only for one architecture at a time +// (and we can't really trick it into giving us both in any obvious way) +// we define them here so that we have them all "at once" + +struct DEMON_LNX_UserRegsX64{ + U64 r15; + U64 r14; + U64 r13; + U64 r12; + U64 rbp; + U64 rbx; + U64 r11; + U64 r10; + U64 r9; + U64 r8; + U64 rax; + U64 rcx; + U64 rdx; + U64 rsi; + U64 rdi; + U64 orig_rax; + U64 rip; + U64 cs; + U64 rflags; + U64 rsp; + U64 ss; + U64 fsbase; + U64 gsbase; + U64 ds; + U64 es; + U64 fs; + U64 gs; +}; + +struct DEMON_LNX_UserX64{ + DEMON_LNX_UserRegsX64 regs; + S32 u_fpvalid, _pad0; + SYMS_XSaveLegacy i387; + U64 u_tsize, u_dsize, u_ssize, start_code, start_stack; + U64 signal; + S32 reserved, _pad1; + U64 u_ar0, u_fpstate; + U64 magic; + U8 u_comm[32]; + U64 u_debugreg[8]; +}; + +struct DEMON_LNX_UserRegsX86{ + U32 ebx; + U32 ecx; + U32 edx; + U32 esi; + U32 edi; + U32 ebp; + U32 eax; + U32 ds; + U32 es; + U32 fs; + U32 gs; + U32 orig_eax; + U32 eip; + U32 cs; + U32 eflags; + U32 sp; + U32 ss; +}; + +struct DEMON_LNX_UserX86{ + DEMON_LNX_UserRegsX86 regs; + S32 u_fpvalid; + SYMS_FSave i387; + U32 u_tsize, u_dsize, u_ssize, start_code, start_stack; + S32 signal, reserved; + U32 u_ar0, u_fpstate; + U32 magic; + U8 u_comm[32]; + U32 u_debugreg[8]; +}; + +//////////////////////////////// + +enum +{ + DEMON_LNX_PermFlags_Read = (1 << 0), + DEMON_LNX_PermFlags_Write = (1 << 1), + DEMON_LNX_PermFlags_Exec = (1 << 2), + DEMON_LNX_PermFlags_Private = (1 << 3) +}; +typedef int DEMON_LNX_PermFlags; + +enum +{ + DEMON_LNX_MapsEntryType_Null, + DEMON_LNX_MapsEntryType_Path, + DEMON_LNX_MapsEntryType_Heap, + DEMON_LNX_MapsEntryType_Stack, + DEMON_LNX_MapsEntryType_VDSO, +}; +typedef int DEMON_LNX_MapsEntryType; + +struct DEMON_LNX_MapsEntry +{ + U64 address_lo; + U64 address_hi; + DEMON_LNX_PermFlags perms; + U64 offset; + U32 dev_major; + U32 dev_minor; + U64 inode; + String8 pathname; + DEMON_LNX_MapsEntryType type; + pid_t stack_tid; +}; + +//////////////////////////////// +//~ rjf: Helpers + +internal DEMON_LNX_ThreadExt* demon_lnx_thread_ext(DEMON_Entity *entity); + +internal B32 demon_lnx_attach_pid(Arena *arena, pid_t pid, DEMON_LNX_AttachNode **new_node); + +internal String8 demon_lnx_executable_path_from_pid(Arena *arena, pid_t pid); +internal int demon_lnx_open_memory_fd_for_pid(pid_t pid); + +internal Architecture demon_lnx_arch_from_pid(pid_t pid); +internal DEMON_LNX_ProcessAux demon_lnx_aux_from_pid(pid_t pid, Architecture arch); +internal DEMON_LNX_PhdrInfo demon_lnx_phdr_info_from_memory(int memory_fd, B32 is_32bit, + U64 phvaddr, U64 phstride, U64 phcount); +internal DEMON_LNX_ModuleNode* demon_lnx_module_list_from_process(Arena *arena, DEMON_Entity *process); + +internal U64 demon_lnx_read_memory(int memory_fd, void *dst, U64 src, U64 size); +internal B32 demon_lnx_write_memory(int memory_fd, U64 dst, void *src, U64 size); +internal String8 demon_lnx_read_memory_str(Arena *arena, int memory_fd, U64 address); + +internal void demon_lnx_regs_x64_from_usr_regs_x64(SYMS_RegX64 *dst, DEMON_LNX_UserRegsX64 *src); +internal void demon_lnx_usr_regs_x64_from_regs_x64(DEMON_LNX_UserRegsX64 *dst, SYMS_RegX64 *src); + +internal String8 demon_lnx_read_int_string(int fd); +internal B32 demon_lnx_read_expect(int fd, char expect); +internal int demon_lnx_read_whitespace(int fd); +internal String8 demon_lnx_read_string(Arena *arena, int fd); + +internal int demon_lnx_open_maps(pid_t pid); +internal B32 demon_lnx_next_map(Arena *arena, int maps, DEMON_LNX_MapsEntry *entry_out); + +#endif //DEMON_OS_LINUX_H diff --git a/src/demon/win32/demon_core_win32.h b/src/demon/win32/demon_core_win32.h index f3cbb398..ce4d9534 100644 --- a/src/demon/win32/demon_core_win32.h +++ b/src/demon/win32/demon_core_win32.h @@ -1,286 +1,286 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DEMON_CORE_WIN32_H -#define DEMON_CORE_WIN32_H - -//////////////////////////////// -//~ rjf: Windows Includes - -#define WIN32_LEAN_AND_MEAN -#include -#include -#include - -//////////////////////////////// -//~ rjf: Win32 Exception Codes - -#define DMN_W32_EXCEPTION_BREAKPOINT 0x80000003u -#define DMN_W32_EXCEPTION_SINGLE_STEP 0x80000004u -#define DMN_W32_EXCEPTION_LONG_JUMP 0x80000026u -#define DMN_W32_EXCEPTION_ACCESS_VIOLATION 0xC0000005u -#define DMN_W32_EXCEPTION_ARRAY_BOUNDS_EXCEEDED 0xC000008Cu -#define DMN_W32_EXCEPTION_DATA_TYPE_MISALIGNMENT 0x80000002u -#define DMN_W32_EXCEPTION_GUARD_PAGE_VIOLATION 0x80000001u -#define DMN_W32_EXCEPTION_FLT_DENORMAL_OPERAND 0xC000008Du -#define DMN_W32_EXCEPTION_FLT_DEVIDE_BY_ZERO 0xC000008Eu -#define DMN_W32_EXCEPTION_FLT_INEXACT_RESULT 0xC000008Fu -#define DMN_W32_EXCEPTION_FLT_INVALID_OPERATION 0xC0000090u -#define DMN_W32_EXCEPTION_FLT_OVERFLOW 0xC0000091u -#define DMN_W32_EXCEPTION_FLT_STACK_CHECK 0xC0000092u -#define DMN_W32_EXCEPTION_FLT_UNDERFLOW 0xC0000093u -#define DMN_W32_EXCEPTION_INT_DIVIDE_BY_ZERO 0xC0000094u -#define DMN_W32_EXCEPTION_INT_OVERFLOW 0xC0000095u -#define DMN_W32_EXCEPTION_PRIVILEGED_INSTRUCTION 0xC0000096u -#define DMN_W32_EXCEPTION_ILLEGAL_INSTRUCTION 0xC000001Du -#define DMN_W32_EXCEPTION_IN_PAGE_ERROR 0xC0000006u -#define DMN_W32_EXCEPTION_INVALID_DISPOSITION 0xC0000026u -#define DMN_W32_EXCEPTION_NONCONTINUABLE 0xC0000025u -#define DMN_W32_EXCEPTION_STACK_OVERFLOW 0xC00000FDu -#define DMN_W32_EXCEPTION_INVALID_HANDLE 0xC0000008u -#define DMN_W32_EXCEPTION_UNWIND_CONSOLIDATE 0x80000029u -#define DMN_W32_EXCEPTION_DLL_NOT_FOUND 0xC0000135u -#define DMN_W32_EXCEPTION_ORDINAL_NOT_FOUND 0xC0000138u -#define DMN_W32_EXCEPTION_ENTRY_POINT_NOT_FOUND 0xC0000139u -#define DMN_W32_EXCEPTION_DLL_INIT_FAILED 0xC0000142u -#define DMN_W32_EXCEPTION_CONTROL_C_EXIT 0xC000013Au -#define DMN_W32_EXCEPTION_FLT_MULTIPLE_FAULTS 0xC00002B4u -#define DMN_W32_EXCEPTION_FLT_MULTIPLE_TRAPS 0xC00002B5u -#define DMN_W32_EXCEPTION_NAT_CONSUMPTION 0xC00002C9u -#define DMN_W32_EXCEPTION_HEAP_CORRUPTION 0xC0000374u -#define DMN_W32_EXCEPTION_STACK_BUFFER_OVERRUN 0xC0000409u -#define DMN_W32_EXCEPTION_INVALID_CRUNTIME_PARAM 0xC0000417u -#define DMN_W32_EXCEPTION_ASSERT_FAILURE 0xC0000420u -#define DMN_W32_EXCEPTION_NO_MEMORY 0xC0000017u -#define DMN_W32_EXCEPTION_THROW 0xE06D7363u -#define DMN_W32_EXCEPTION_SET_THREAD_NAME 0x406d1388u -#define DMN_w32_EXCEPTION_CLRDBG_NOTIFICATION 0x04242420u -#define DMN_w32_EXCEPTION_CLR 0xE0434352u - -//////////////////////////////// -//~ rjf: Win32 Register Codes - -#define DMN_W32_CTX_X86 0x00010000 -#define DMN_W32_CTX_X64 0x00100000 - -#define DMN_W32_CTX_INTEL_CONTROL 0x0001 // segss, rsp, segcs, rip, and rflags -#define DMN_W32_CTX_INTEL_INTEGER 0x0002 // rax, rcx, rdx, rbx, rbp, rsi, rdi, and r8-r15 -#define DMN_W32_CTX_INTEL_SEGMENTS 0x0004 // segds, seges, segfs, and seggs -#define DMN_W32_CTX_INTEL_FLOATS 0x0008 // xmm0-xmm15 -#define DMN_W32_CTX_INTEL_DEBUG 0x0010 // dr0-dr3 and dr6-dr7 -#define DMN_W32_CTX_INTEL_EXTENDED 0x0020 -#define DMN_W32_CTX_INTEL_XSTATE 0x0040 - -#define DMN_W32_CTX_X86_ALL (DMN_W32_CTX_X86 | \ -DMN_W32_CTX_INTEL_CONTROL | DMN_W32_CTX_INTEL_INTEGER | \ -DMN_W32_CTX_INTEL_SEGMENTS | DMN_W32_CTX_INTEL_DEBUG | \ -DMN_W32_CTX_INTEL_EXTENDED) -#define DMN_W32_CTX_X64_ALL (DMN_W32_CTX_X64 | \ -DMN_W32_CTX_INTEL_CONTROL | DMN_W32_CTX_INTEL_INTEGER | \ -DMN_W32_CTX_INTEL_SEGMENTS | DMN_W32_CTX_INTEL_FLOATS | \ -DMN_W32_CTX_INTEL_DEBUG) - -//////////////////////////////// -//~ rjf: Per-Entity State - -typedef enum DMN_W32_EntityKind -{ - DMN_W32_EntityKind_Null, - DMN_W32_EntityKind_Root, - DMN_W32_EntityKind_Process, - DMN_W32_EntityKind_Thread, - DMN_W32_EntityKind_Module, - DMN_W32_EntityKind_COUNT -} -DMN_W32_EntityKind; - -typedef struct DMN_W32_Entity DMN_W32_Entity; -struct DMN_W32_Entity -{ - DMN_W32_Entity *first; - DMN_W32_Entity *last; - DMN_W32_Entity *next; - DMN_W32_Entity *prev; - DMN_W32_Entity *parent; - DMN_W32_EntityKind kind; - U32 gen; - U64 id; - HANDLE handle; - Architecture arch; - union - { - struct - { - U64 injection_address; - B32 did_first_bp; - } - proc; - struct - { - U64 thread_local_base; - U64 last_name_hash; - U64 name_gather_time_us; - } - thread; - struct - { - Rng1U64 vaddr_range; - U64 address_of_name_pointer; - B32 is_main; - B32 name_is_unicode; - } - module; - }; -}; - -typedef struct DMN_W32_EntityNode DMN_W32_EntityNode; -struct DMN_W32_EntityNode -{ - DMN_W32_EntityNode *next; - DMN_W32_Entity *v; -}; - -typedef struct DMN_W32_EntityIDHashNode DMN_W32_EntityIDHashNode; -struct DMN_W32_EntityIDHashNode -{ - DMN_W32_EntityIDHashNode *next; - DMN_W32_EntityIDHashNode *prev; - U64 id; - DMN_W32_Entity *entity; -}; - -typedef struct DMN_W32_EntityIDHashSlot DMN_W32_EntityIDHashSlot; -struct DMN_W32_EntityIDHashSlot -{ - DMN_W32_EntityIDHashNode *first; - DMN_W32_EntityIDHashNode *last; -}; - -//////////////////////////////// -//~ rjf: Injection Types - -typedef struct DMN_W32_InjectedBreak DMN_W32_InjectedBreak; -struct DMN_W32_InjectedBreak -{ - U64 code; - U64 user_data; -}; - -#define DMN_W32_INJECTED_CODE_SIZE 32 - -//////////////////////////////// -//~ rjf: Image Info Types - -typedef struct DMN_W32_ImageInfo DMN_W32_ImageInfo; -struct DMN_W32_ImageInfo -{ - Architecture arch; - U32 size; -}; - -//////////////////////////////// -//~ rjf: Dynamically-Loaded Win32 Function Types - -typedef HRESULT DMN_W32_GetThreadDescriptionFunctionType(HANDLE hThread, WCHAR **ppszThreadDescription); - -//////////////////////////////// -//~ rjf: Shared State Bundle - -typedef struct DMN_W32_Shared DMN_W32_Shared; -struct DMN_W32_Shared -{ - // rjf: top-level info - Arena *arena; - String8List env_strings; - - // rjf: access locking mechanism - OS_Handle access_mutex; - B32 access_run_state; - - // rjf: run/mem/reg gens - U64 run_gen; - U64 mem_gen; - U64 reg_gen; - - // rjf: detaching info - Arena *detach_arena; - DMN_HandleList detach_processes; - - // rjf: entity state - Arena *entities_arena; - DMN_W32_Entity *entities_base; - DMN_W32_Entity *entities_first_free; - U64 entities_count; - DMN_W32_EntityIDHashSlot *entities_id_hash_slots; - U64 entities_id_hash_slots_count; - DMN_W32_EntityIDHashNode *entities_id_hash_node_free; - - // rjf: launch state - B32 new_process_pending; - - // rjf: run results - B32 resume_needed; - U32 resume_pid; - U32 resume_tid; - B32 exception_not_handled; - - // rjf: halting info - DMN_Handle halter_process; - U32 halter_tid; -}; - -//////////////////////////////// -//~ rjf: Globals - -global DMN_W32_Shared *dmn_w32_shared = 0; -global DMN_W32_Entity dmn_w32_entity_nil = {&dmn_w32_entity_nil, &dmn_w32_entity_nil, &dmn_w32_entity_nil, &dmn_w32_entity_nil, &dmn_w32_entity_nil}; -global DMN_W32_GetThreadDescriptionFunctionType *dmn_w32_GetThreadDescription = 0; -thread_static B32 dmn_w32_ctrl_thread = 0; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal U64 dmn_w32_hash_from_string(String8 string); -internal U64 dmn_w32_hash_from_id(U64 id); - -//////////////////////////////// -//~ rjf: Entity Helpers - -//- rjf: entity <-> handle -internal DMN_Handle dmn_w32_handle_from_entity(DMN_W32_Entity *entity); -internal DMN_W32_Entity *dmn_w32_entity_from_handle(DMN_Handle handle); - -//- rjf: entity allocation/deallocation -internal DMN_W32_Entity *dmn_w32_entity_alloc(DMN_W32_Entity *parent, DMN_W32_EntityKind kind, U64 id); -internal void dmn_w32_entity_release(DMN_W32_Entity *entity); - -//- rjf: kind*id -> entity -internal DMN_W32_Entity *dmn_w32_entity_from_kind_id(DMN_W32_EntityKind kind, U64 id); - -//////////////////////////////// -//~ rjf: Module Info Extraction - -internal String8 dmn_w32_full_path_from_module(Arena *arena, DMN_W32_Entity *module); - -//////////////////////////////// -//~ rjf: Win32-Level Process/Thread Reads/Writes - -//- rjf: processes -internal U64 dmn_w32_process_read(HANDLE process, Rng1U64 range, void *dst); -internal B32 dmn_w32_process_write(HANDLE process, Rng1U64 range, void *src); -internal String8 dmn_w32_read_memory_str(Arena *arena, HANDLE process_handle, U64 address); -internal String16 dmn_w32_read_memory_str16(Arena *arena, HANDLE process_handle, U64 address); -#define dmn_w32_process_read_struct(process, vaddr, ptr) dmn_w32_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) -#define dmn_w32_process_write_struct(process, vaddr, ptr) dmn_w32_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) -internal DMN_W32_ImageInfo dmn_w32_image_info_from_process_base_vaddr(HANDLE process, U64 base_vaddr); - -//- rjf: threads -internal U16 dmn_w32_real_tag_word_from_xsave(XSAVE_FORMAT *fxsave); -internal U16 dmn_w32_xsave_tag_word_from_real_tag_word(U16 ftw); -internal B32 dmn_w32_thread_read_reg_block(Architecture arch, HANDLE thread, void *reg_block); -internal B32 dmn_w32_thread_write_reg_block(Architecture arch, HANDLE thread, void *reg_block); - -//- rjf: remote thread injection -internal DWORD dmn_w32_inject_thread(HANDLE process, U64 start_address); - -#endif // DEMON_CORE_WIN32_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DEMON_CORE_WIN32_H +#define DEMON_CORE_WIN32_H + +//////////////////////////////// +//~ rjf: Windows Includes + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +//////////////////////////////// +//~ rjf: Win32 Exception Codes + +#define DMN_W32_EXCEPTION_BREAKPOINT 0x80000003u +#define DMN_W32_EXCEPTION_SINGLE_STEP 0x80000004u +#define DMN_W32_EXCEPTION_LONG_JUMP 0x80000026u +#define DMN_W32_EXCEPTION_ACCESS_VIOLATION 0xC0000005u +#define DMN_W32_EXCEPTION_ARRAY_BOUNDS_EXCEEDED 0xC000008Cu +#define DMN_W32_EXCEPTION_DATA_TYPE_MISALIGNMENT 0x80000002u +#define DMN_W32_EXCEPTION_GUARD_PAGE_VIOLATION 0x80000001u +#define DMN_W32_EXCEPTION_FLT_DENORMAL_OPERAND 0xC000008Du +#define DMN_W32_EXCEPTION_FLT_DEVIDE_BY_ZERO 0xC000008Eu +#define DMN_W32_EXCEPTION_FLT_INEXACT_RESULT 0xC000008Fu +#define DMN_W32_EXCEPTION_FLT_INVALID_OPERATION 0xC0000090u +#define DMN_W32_EXCEPTION_FLT_OVERFLOW 0xC0000091u +#define DMN_W32_EXCEPTION_FLT_STACK_CHECK 0xC0000092u +#define DMN_W32_EXCEPTION_FLT_UNDERFLOW 0xC0000093u +#define DMN_W32_EXCEPTION_INT_DIVIDE_BY_ZERO 0xC0000094u +#define DMN_W32_EXCEPTION_INT_OVERFLOW 0xC0000095u +#define DMN_W32_EXCEPTION_PRIVILEGED_INSTRUCTION 0xC0000096u +#define DMN_W32_EXCEPTION_ILLEGAL_INSTRUCTION 0xC000001Du +#define DMN_W32_EXCEPTION_IN_PAGE_ERROR 0xC0000006u +#define DMN_W32_EXCEPTION_INVALID_DISPOSITION 0xC0000026u +#define DMN_W32_EXCEPTION_NONCONTINUABLE 0xC0000025u +#define DMN_W32_EXCEPTION_STACK_OVERFLOW 0xC00000FDu +#define DMN_W32_EXCEPTION_INVALID_HANDLE 0xC0000008u +#define DMN_W32_EXCEPTION_UNWIND_CONSOLIDATE 0x80000029u +#define DMN_W32_EXCEPTION_DLL_NOT_FOUND 0xC0000135u +#define DMN_W32_EXCEPTION_ORDINAL_NOT_FOUND 0xC0000138u +#define DMN_W32_EXCEPTION_ENTRY_POINT_NOT_FOUND 0xC0000139u +#define DMN_W32_EXCEPTION_DLL_INIT_FAILED 0xC0000142u +#define DMN_W32_EXCEPTION_CONTROL_C_EXIT 0xC000013Au +#define DMN_W32_EXCEPTION_FLT_MULTIPLE_FAULTS 0xC00002B4u +#define DMN_W32_EXCEPTION_FLT_MULTIPLE_TRAPS 0xC00002B5u +#define DMN_W32_EXCEPTION_NAT_CONSUMPTION 0xC00002C9u +#define DMN_W32_EXCEPTION_HEAP_CORRUPTION 0xC0000374u +#define DMN_W32_EXCEPTION_STACK_BUFFER_OVERRUN 0xC0000409u +#define DMN_W32_EXCEPTION_INVALID_CRUNTIME_PARAM 0xC0000417u +#define DMN_W32_EXCEPTION_ASSERT_FAILURE 0xC0000420u +#define DMN_W32_EXCEPTION_NO_MEMORY 0xC0000017u +#define DMN_W32_EXCEPTION_THROW 0xE06D7363u +#define DMN_W32_EXCEPTION_SET_THREAD_NAME 0x406d1388u +#define DMN_w32_EXCEPTION_CLRDBG_NOTIFICATION 0x04242420u +#define DMN_w32_EXCEPTION_CLR 0xE0434352u + +//////////////////////////////// +//~ rjf: Win32 Register Codes + +#define DMN_W32_CTX_X86 0x00010000 +#define DMN_W32_CTX_X64 0x00100000 + +#define DMN_W32_CTX_INTEL_CONTROL 0x0001 // segss, rsp, segcs, rip, and rflags +#define DMN_W32_CTX_INTEL_INTEGER 0x0002 // rax, rcx, rdx, rbx, rbp, rsi, rdi, and r8-r15 +#define DMN_W32_CTX_INTEL_SEGMENTS 0x0004 // segds, seges, segfs, and seggs +#define DMN_W32_CTX_INTEL_FLOATS 0x0008 // xmm0-xmm15 +#define DMN_W32_CTX_INTEL_DEBUG 0x0010 // dr0-dr3 and dr6-dr7 +#define DMN_W32_CTX_INTEL_EXTENDED 0x0020 +#define DMN_W32_CTX_INTEL_XSTATE 0x0040 + +#define DMN_W32_CTX_X86_ALL (DMN_W32_CTX_X86 | \ +DMN_W32_CTX_INTEL_CONTROL | DMN_W32_CTX_INTEL_INTEGER | \ +DMN_W32_CTX_INTEL_SEGMENTS | DMN_W32_CTX_INTEL_DEBUG | \ +DMN_W32_CTX_INTEL_EXTENDED) +#define DMN_W32_CTX_X64_ALL (DMN_W32_CTX_X64 | \ +DMN_W32_CTX_INTEL_CONTROL | DMN_W32_CTX_INTEL_INTEGER | \ +DMN_W32_CTX_INTEL_SEGMENTS | DMN_W32_CTX_INTEL_FLOATS | \ +DMN_W32_CTX_INTEL_DEBUG) + +//////////////////////////////// +//~ rjf: Per-Entity State + +typedef enum DMN_W32_EntityKind +{ + DMN_W32_EntityKind_Null, + DMN_W32_EntityKind_Root, + DMN_W32_EntityKind_Process, + DMN_W32_EntityKind_Thread, + DMN_W32_EntityKind_Module, + DMN_W32_EntityKind_COUNT +} +DMN_W32_EntityKind; + +typedef struct DMN_W32_Entity DMN_W32_Entity; +struct DMN_W32_Entity +{ + DMN_W32_Entity *first; + DMN_W32_Entity *last; + DMN_W32_Entity *next; + DMN_W32_Entity *prev; + DMN_W32_Entity *parent; + DMN_W32_EntityKind kind; + U32 gen; + U64 id; + HANDLE handle; + Architecture arch; + union + { + struct + { + U64 injection_address; + B32 did_first_bp; + } + proc; + struct + { + U64 thread_local_base; + U64 last_name_hash; + U64 name_gather_time_us; + } + thread; + struct + { + Rng1U64 vaddr_range; + U64 address_of_name_pointer; + B32 is_main; + B32 name_is_unicode; + } + module; + }; +}; + +typedef struct DMN_W32_EntityNode DMN_W32_EntityNode; +struct DMN_W32_EntityNode +{ + DMN_W32_EntityNode *next; + DMN_W32_Entity *v; +}; + +typedef struct DMN_W32_EntityIDHashNode DMN_W32_EntityIDHashNode; +struct DMN_W32_EntityIDHashNode +{ + DMN_W32_EntityIDHashNode *next; + DMN_W32_EntityIDHashNode *prev; + U64 id; + DMN_W32_Entity *entity; +}; + +typedef struct DMN_W32_EntityIDHashSlot DMN_W32_EntityIDHashSlot; +struct DMN_W32_EntityIDHashSlot +{ + DMN_W32_EntityIDHashNode *first; + DMN_W32_EntityIDHashNode *last; +}; + +//////////////////////////////// +//~ rjf: Injection Types + +typedef struct DMN_W32_InjectedBreak DMN_W32_InjectedBreak; +struct DMN_W32_InjectedBreak +{ + U64 code; + U64 user_data; +}; + +#define DMN_W32_INJECTED_CODE_SIZE 32 + +//////////////////////////////// +//~ rjf: Image Info Types + +typedef struct DMN_W32_ImageInfo DMN_W32_ImageInfo; +struct DMN_W32_ImageInfo +{ + Architecture arch; + U32 size; +}; + +//////////////////////////////// +//~ rjf: Dynamically-Loaded Win32 Function Types + +typedef HRESULT DMN_W32_GetThreadDescriptionFunctionType(HANDLE hThread, WCHAR **ppszThreadDescription); + +//////////////////////////////// +//~ rjf: Shared State Bundle + +typedef struct DMN_W32_Shared DMN_W32_Shared; +struct DMN_W32_Shared +{ + // rjf: top-level info + Arena *arena; + String8List env_strings; + + // rjf: access locking mechanism + OS_Handle access_mutex; + B32 access_run_state; + + // rjf: run/mem/reg gens + U64 run_gen; + U64 mem_gen; + U64 reg_gen; + + // rjf: detaching info + Arena *detach_arena; + DMN_HandleList detach_processes; + + // rjf: entity state + Arena *entities_arena; + DMN_W32_Entity *entities_base; + DMN_W32_Entity *entities_first_free; + U64 entities_count; + DMN_W32_EntityIDHashSlot *entities_id_hash_slots; + U64 entities_id_hash_slots_count; + DMN_W32_EntityIDHashNode *entities_id_hash_node_free; + + // rjf: launch state + B32 new_process_pending; + + // rjf: run results + B32 resume_needed; + U32 resume_pid; + U32 resume_tid; + B32 exception_not_handled; + + // rjf: halting info + DMN_Handle halter_process; + U32 halter_tid; +}; + +//////////////////////////////// +//~ rjf: Globals + +global DMN_W32_Shared *dmn_w32_shared = 0; +global DMN_W32_Entity dmn_w32_entity_nil = {&dmn_w32_entity_nil, &dmn_w32_entity_nil, &dmn_w32_entity_nil, &dmn_w32_entity_nil, &dmn_w32_entity_nil}; +global DMN_W32_GetThreadDescriptionFunctionType *dmn_w32_GetThreadDescription = 0; +thread_static B32 dmn_w32_ctrl_thread = 0; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal U64 dmn_w32_hash_from_string(String8 string); +internal U64 dmn_w32_hash_from_id(U64 id); + +//////////////////////////////// +//~ rjf: Entity Helpers + +//- rjf: entity <-> handle +internal DMN_Handle dmn_w32_handle_from_entity(DMN_W32_Entity *entity); +internal DMN_W32_Entity *dmn_w32_entity_from_handle(DMN_Handle handle); + +//- rjf: entity allocation/deallocation +internal DMN_W32_Entity *dmn_w32_entity_alloc(DMN_W32_Entity *parent, DMN_W32_EntityKind kind, U64 id); +internal void dmn_w32_entity_release(DMN_W32_Entity *entity); + +//- rjf: kind*id -> entity +internal DMN_W32_Entity *dmn_w32_entity_from_kind_id(DMN_W32_EntityKind kind, U64 id); + +//////////////////////////////// +//~ rjf: Module Info Extraction + +internal String8 dmn_w32_full_path_from_module(Arena *arena, DMN_W32_Entity *module); + +//////////////////////////////// +//~ rjf: Win32-Level Process/Thread Reads/Writes + +//- rjf: processes +internal U64 dmn_w32_process_read(HANDLE process, Rng1U64 range, void *dst); +internal B32 dmn_w32_process_write(HANDLE process, Rng1U64 range, void *src); +internal String8 dmn_w32_read_memory_str(Arena *arena, HANDLE process_handle, U64 address); +internal String16 dmn_w32_read_memory_str16(Arena *arena, HANDLE process_handle, U64 address); +#define dmn_w32_process_read_struct(process, vaddr, ptr) dmn_w32_process_read((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) +#define dmn_w32_process_write_struct(process, vaddr, ptr) dmn_w32_process_write((process), r1u64((vaddr), (vaddr)+(sizeof(*ptr))), ptr) +internal DMN_W32_ImageInfo dmn_w32_image_info_from_process_base_vaddr(HANDLE process, U64 base_vaddr); + +//- rjf: threads +internal U16 dmn_w32_real_tag_word_from_xsave(XSAVE_FORMAT *fxsave); +internal U16 dmn_w32_xsave_tag_word_from_real_tag_word(U16 ftw); +internal B32 dmn_w32_thread_read_reg_block(Architecture arch, HANDLE thread, void *reg_block); +internal B32 dmn_w32_thread_write_reg_block(Architecture arch, HANDLE thread, void *reg_block); + +//- rjf: remote thread injection +internal DWORD dmn_w32_inject_thread(HANDLE process, U64 start_address); + +#endif // DEMON_CORE_WIN32_H diff --git a/src/df/df_inc.c b/src/df/df_inc.c index 206b7585..f81c032d 100644 --- a/src/df/df_inc.c +++ b/src/df/df_inc.c @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#include "df/core/df_core.c" -#include "df/gfx/df_gfx.c" -#include "df/gfx/df_views.c" -#include "df/gfx/df_view_rules.c" +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "df/core/df_core.c" +#include "df/gfx/df_gfx.c" +#include "df/gfx/df_views.c" +#include "df/gfx/df_view_rules.c" diff --git a/src/df/df_inc.h b/src/df/df_inc.h index b1ed72ea..edbb7d3e 100644 --- a/src/df/df_inc.h +++ b/src/df/df_inc.h @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DEBUG_FRONTEND_INC_H -#define DEBUG_FRONTEND_INC_H - -#include "df/core/df_core.h" -#include "df/gfx/df_gfx.h" -#include "df/gfx/df_views.h" -#include "df/gfx/df_view_rules.h" - -#endif // DEBUG_FRONTEND_INC_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DEBUG_FRONTEND_INC_H +#define DEBUG_FRONTEND_INC_H + +#include "df/core/df_core.h" +#include "df/gfx/df_gfx.h" +#include "df/gfx/df_views.h" +#include "df/gfx/df_view_rules.h" + +#endif // DEBUG_FRONTEND_INC_H diff --git a/src/df/gfx/df_gfx.h b/src/df/gfx/df_gfx.h index ba249932..dc46b762 100644 --- a/src/df/gfx/df_gfx.h +++ b/src/df/gfx/df_gfx.h @@ -1,1119 +1,1119 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DF_GFX_H -#define DF_GFX_H - -//////////////////////////////// -//~ rjf: Basic Types - -typedef struct DF_PathQuery DF_PathQuery; -struct DF_PathQuery -{ - String8 prefix; - String8 path; - String8 search; -}; - -//////////////////////////////// -//~ rjf: Binding Types - -typedef struct DF_Binding DF_Binding; -struct DF_Binding -{ - OS_Key key; - OS_EventFlags flags; -}; - -typedef struct DF_BindingNode DF_BindingNode; -struct DF_BindingNode -{ - DF_BindingNode *next; - DF_Binding binding; -}; - -typedef struct DF_BindingList DF_BindingList; -struct DF_BindingList -{ - DF_BindingNode *first; - DF_BindingNode *last; - U64 count; -}; - -typedef struct DF_StringBindingPair DF_StringBindingPair; -struct DF_StringBindingPair -{ - String8 string; - DF_Binding binding; -}; - -//////////////////////////////// -//~ rjf: Key Map Types - -typedef struct DF_KeyMapNode DF_KeyMapNode; -struct DF_KeyMapNode -{ - DF_KeyMapNode *hash_next; - DF_KeyMapNode *hash_prev; - DF_CmdSpec *spec; - DF_Binding binding; -}; - -typedef struct DF_KeyMapSlot DF_KeyMapSlot; -struct DF_KeyMapSlot -{ - DF_KeyMapNode *first; - DF_KeyMapNode *last; -}; - -//////////////////////////////// -//~ rjf: Setting Types - -typedef struct DF_SettingVal DF_SettingVal; -struct DF_SettingVal -{ - B32 set; - S32 s32; -}; - -//////////////////////////////// -//~ rjf: View Functions - -typedef struct DF_View DF_View; -typedef struct DF_Panel DF_Panel; -typedef struct DF_Window DF_Window; - -#define DF_VIEW_SETUP_FUNCTION_SIG(name) void name(DF_Window *ws, struct DF_View *view, DF_CfgNode *cfg_root) -#define DF_VIEW_SETUP_FUNCTION_NAME(name) df_view_setup_##name -#define DF_VIEW_SETUP_FUNCTION_DEF(name) internal DF_VIEW_SETUP_FUNCTION_SIG(DF_VIEW_SETUP_FUNCTION_NAME(name)) -typedef DF_VIEW_SETUP_FUNCTION_SIG(DF_ViewSetupFunctionType); - -#define DF_VIEW_STRING_FROM_STATE_FUNCTION_SIG(name) String8 name(Arena *arena, struct DF_View *view) -#define DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(name) df_view_string_from_state_##name -#define DF_VIEW_STRING_FROM_STATE_FUNCTION_DEF(name) internal DF_VIEW_STRING_FROM_STATE_FUNCTION_SIG(DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(name)) -typedef DF_VIEW_STRING_FROM_STATE_FUNCTION_SIG(DF_ViewStringFromStateFunctionType); - -#define DF_VIEW_CMD_FUNCTION_SIG(name) void name(struct DF_Window *ws, struct DF_Panel *panel, struct DF_View *view, struct DF_CmdList *cmds) -#define DF_VIEW_CMD_FUNCTION_NAME(name) df_view_cmds_##name -#define DF_VIEW_CMD_FUNCTION_DEF(name) internal DF_VIEW_CMD_FUNCTION_SIG(DF_VIEW_CMD_FUNCTION_NAME(name)) -typedef DF_VIEW_CMD_FUNCTION_SIG(DF_ViewCmdFunctionType); - -#define DF_VIEW_UI_FUNCTION_SIG(name) void name(struct DF_Window *ws, struct DF_Panel *panel, struct DF_View *view, Rng2F32 rect) -#define DF_VIEW_UI_FUNCTION_NAME(name) df_view_ui_##name -#define DF_VIEW_UI_FUNCTION_DEF(name) internal DF_VIEW_UI_FUNCTION_SIG(DF_VIEW_UI_FUNCTION_NAME(name)) -typedef DF_VIEW_UI_FUNCTION_SIG(DF_ViewUIFunctionType); - -//////////////////////////////// -//~ rjf: View Specification Types - -typedef U32 DF_ViewSpecFlags; -enum -{ - DF_ViewSpecFlag_ParameterizedByEntity = (1<<0), - DF_ViewSpecFlag_ProjectSpecific = (1<<1), - DF_ViewSpecFlag_CanSerialize = (1<<2), - DF_ViewSpecFlag_CanSerializeEntityPath = (1<<3), - DF_ViewSpecFlag_CanSerializeQuery = (1<<4), - DF_ViewSpecFlag_CanFilter = (1<<5), - DF_ViewSpecFlag_FilterIsCode = (1<<6), - DF_ViewSpecFlag_TypingAutomaticallyFilters = (1<<7), -}; - -typedef enum DF_NameKind -{ - DF_NameKind_Null, - DF_NameKind_EntityName, - DF_NameKind_COUNT -} -DF_NameKind; - -typedef struct DF_ViewSpecInfo DF_ViewSpecInfo; -struct DF_ViewSpecInfo -{ - DF_ViewSpecFlags flags; - String8 name; - String8 display_string; - DF_NameKind name_kind; - DF_IconKind icon_kind; - DF_ViewSetupFunctionType *setup_hook; - DF_ViewStringFromStateFunctionType *string_from_state_hook; - DF_ViewCmdFunctionType *cmd_hook; - DF_ViewUIFunctionType *ui_hook; -}; - -typedef struct DF_ViewSpec DF_ViewSpec; -struct DF_ViewSpec -{ - DF_ViewSpec *hash_next; - DF_ViewSpecInfo info; -}; - -typedef struct DF_ViewSpecInfoArray DF_ViewSpecInfoArray; -struct DF_ViewSpecInfoArray -{ - DF_ViewSpecInfo *v; - U64 count; -}; - -typedef struct DF_CmdParamSlotViewSpecRuleNode DF_CmdParamSlotViewSpecRuleNode; -struct DF_CmdParamSlotViewSpecRuleNode -{ - DF_CmdParamSlotViewSpecRuleNode *next; - DF_ViewSpec *view_spec; - DF_CmdSpec *cmd_spec; -}; - -typedef struct DF_CmdParamSlotViewSpecRuleList DF_CmdParamSlotViewSpecRuleList; -struct DF_CmdParamSlotViewSpecRuleList -{ - DF_CmdParamSlotViewSpecRuleNode *first; - DF_CmdParamSlotViewSpecRuleNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: View Types - -typedef struct DF_ArenaExt DF_ArenaExt; -struct DF_ArenaExt -{ - DF_ArenaExt *next; - Arena *arena; -}; - -typedef struct DF_View DF_View; -struct DF_View -{ - // rjf: ownership links ('owners' can have lists of views) - DF_View *next; - DF_View *prev; - - // rjf: allocation info - U64 generation; - - // rjf: loading animation state - F32 loading_t; - F32 loading_t_target; - U64 loading_progress_v; - U64 loading_progress_v_target; - - // rjf: view state - UI_ScrollPt2 scroll_pos; - TxtPt cursor; - TxtPt mark; - - // rjf: ctrl context overrides - DF_CtrlCtx ctrl_ctx_overrides; - - // rjf: allocation & user data extensions - Arena *arena; - DF_ArenaExt *first_arena_ext; - DF_ArenaExt *last_arena_ext; - void *user_data; - - // rjf: view kind info - DF_ViewSpec *spec; - DF_Handle entity; - DF_Handle project; - - // rjf: filter mode - B32 is_filtering; - F32 is_filtering_t; - - // rjf: query -> params data - TxtPt query_cursor; - TxtPt query_mark; - U8 query_buffer[1024]; - U64 query_string_size; -}; - -//////////////////////////////// -//~ rjf: Panel Types - -typedef struct DF_Panel DF_Panel; -struct DF_Panel -{ - // rjf: tree links/data - DF_Panel *first; - DF_Panel *last; - DF_Panel *next; - DF_Panel *prev; - DF_Panel *parent; - U64 child_count; - - // rjf: allocation data - U64 generation; - - // rjf: split data - Axis2 split_axis; - F32 pct_of_parent; - - // rjf: animated rectangle data - Rng2F32 animated_rect_pct; - - // rjf: tab params - Side tab_side; - - // rjf: stable views (tabs) - DF_View *first_tab_view; - DF_View *last_tab_view; - U64 tab_view_count; - DF_Handle selected_tab_view; -}; - -typedef struct DF_PanelRec DF_PanelRec; -struct DF_PanelRec -{ - DF_Panel *next; - int push_count; - int pop_count; -}; - -//////////////////////////////// -//~ rjf: Drag/Drop Types - -typedef enum DF_DragDropState -{ - DF_DragDropState_Null, - DF_DragDropState_Dragging, - DF_DragDropState_Dropping, - DF_DragDropState_COUNT -} -DF_DragDropState; - -typedef struct DF_DragDropPayload DF_DragDropPayload; -struct DF_DragDropPayload -{ - UI_Key key; - DF_Handle panel; - DF_Handle view; - DF_Handle entity; - TxtPt text_point; -}; - -//////////////////////////////// -//~ rjf: Rich Hover Types - -typedef struct DF_RichHoverInfo DF_RichHoverInfo; -struct DF_RichHoverInfo -{ - DF_Handle process; - Rng1U64 vaddr_range; - DF_Handle module; - Rng1U64 voff_range; - DI_Key dbgi_key; -}; - -//////////////////////////////// -//~ rjf: View Rule Spec Types - -typedef U32 DF_GfxViewRuleSpecInfoFlags; // NOTE(rjf): see @view_rule_info -enum -{ - DF_GfxViewRuleSpecInfoFlag_VizRowProd = (1<<0), - DF_GfxViewRuleSpecInfoFlag_LineStringize = (1<<1), - DF_GfxViewRuleSpecInfoFlag_RowUI = (1<<2), - DF_GfxViewRuleSpecInfoFlag_BlockUI = (1<<3), -}; - -#define DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_SIG(name) void name(void) -#define DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_NAME(name) df_gfx_view_rule_viz_row_prod__##name -#define DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_DEF(name) internal DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_SIG(DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_NAME(name)) - -#define DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_SIG(name) void name(void) -#define DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_NAME(name) df_gfx_view_rule_line_stringize__##name -#define DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_DEF(name) internal DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_SIG(DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_NAME(name)) - -#define DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_SIG(name) void name(struct DF_Window *ws, DF_ExpandKey key, DF_Eval eval, DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, struct DF_CfgNode *cfg) -#define DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_NAME(name) df_gfx_view_rule_row_ui__##name -#define DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_DEF(name) DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_SIG(DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_NAME(name)) - -#define DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_SIG(name) void name(struct DF_Window *ws, DF_ExpandKey key, DF_Eval eval, String8 string, DI_Scope *di_scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, struct DF_CfgNode *cfg, Vec2F32 dim) -#define DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_NAME(name) df_gfx_view_rule_block_ui__##name -#define DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_DEF(name) DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_SIG(DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_NAME(name)) - -typedef DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_SIG(DF_GfxViewRuleVizRowProdHookFunctionType); -typedef DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_SIG(DF_GfxViewRuleLineStringizeHookFunctionType); -typedef DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_SIG(DF_GfxViewRuleRowUIFunctionType); -typedef DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_SIG(DF_GfxViewRuleBlockUIFunctionType); - -typedef struct DF_GfxViewRuleSpecInfo DF_GfxViewRuleSpecInfo; -struct DF_GfxViewRuleSpecInfo -{ - String8 string; - DF_GfxViewRuleSpecInfoFlags flags; - DF_GfxViewRuleVizRowProdHookFunctionType *viz_row_prod; - DF_GfxViewRuleLineStringizeHookFunctionType *line_stringize; - DF_GfxViewRuleRowUIFunctionType *row_ui; - DF_GfxViewRuleBlockUIFunctionType *block_ui; - String8 tab_view_spec_name; -}; - -typedef struct DF_GfxViewRuleSpecInfoArray DF_GfxViewRuleSpecInfoArray; -struct DF_GfxViewRuleSpecInfoArray -{ - DF_GfxViewRuleSpecInfo *v; - U64 count; -}; - -typedef struct DF_GfxViewRuleSpec DF_GfxViewRuleSpec; -struct DF_GfxViewRuleSpec -{ - DF_GfxViewRuleSpec *hash_next; - DF_GfxViewRuleSpecInfo info; -}; - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/df_gfx.meta.h" - -//////////////////////////////// -//~ rjf: Theme Types - -typedef struct DF_Theme DF_Theme; -struct DF_Theme -{ - Vec4F32 colors[DF_ThemeColor_COUNT]; -}; - -typedef enum DF_FontSlot -{ - DF_FontSlot_Main, - DF_FontSlot_Code, - DF_FontSlot_Icons, - DF_FontSlot_COUNT -} -DF_FontSlot; - -typedef enum DF_PaletteCode -{ - DF_PaletteCode_Base, - DF_PaletteCode_MenuBar, - DF_PaletteCode_Floating, - DF_PaletteCode_ImplicitButton, - DF_PaletteCode_PlainButton, - DF_PaletteCode_PositivePopButton, - DF_PaletteCode_NegativePopButton, - DF_PaletteCode_NeutralPopButton, - DF_PaletteCode_ScrollBarButton, - DF_PaletteCode_Tab, - DF_PaletteCode_TabInactive, - DF_PaletteCode_DropSiteOverlay, - DF_PaletteCode_COUNT -} -DF_PaletteCode; - -//////////////////////////////// -//~ rjf: UI Helper & Widget Types - -//- rjf: line edits - -typedef U32 DF_LineEditFlags; -enum -{ - DF_LineEditFlag_Expander = (1<<0), - DF_LineEditFlag_ExpanderSpace = (1<<1), - DF_LineEditFlag_ExpanderPlaceholder = (1<<2), - DF_LineEditFlag_DisableEdit = (1<<3), - DF_LineEditFlag_CodeContents = (1<<4), - DF_LineEditFlag_Border = (1<<5), - DF_LineEditFlag_NoBackground = (1<<6), - DF_LineEditFlag_PreferDisplayString = (1<<7), - DF_LineEditFlag_DisplayStringIsCode = (1<<8), -}; - -//- rjf: code viewing/editing widgets - -typedef U32 DF_CodeSliceFlags; -enum -{ - DF_CodeSliceFlag_Clickable = (1<<0), - DF_CodeSliceFlag_PriorityMargin = (1<<1), - DF_CodeSliceFlag_CatchallMargin = (1<<2), - DF_CodeSliceFlag_LineNums = (1<<3), -}; - -typedef struct DF_CodeSliceParams DF_CodeSliceParams; -struct DF_CodeSliceParams -{ - // rjf: content - DF_CodeSliceFlags flags; - Rng1S64 line_num_range; - String8 *line_text; - Rng1U64 *line_ranges; - TXT_TokenArray *line_tokens; - DF_EntityList *line_bps; - DF_EntityList *line_ips; - DF_EntityList *line_pins; - U64 *line_vaddrs; - DF_LineList *line_infos; - DI_KeyList relevant_dbgi_keys; - - // rjf: visual parameters - F_Tag font; - F32 font_size; - F32 tab_size; - String8 search_query; - F32 line_height_px; - F32 priority_margin_width_px; - F32 catchall_margin_width_px; - F32 line_num_width_px; - F32 line_text_max_width_px; - F32 margin_float_off_px; -}; - -typedef struct DF_CodeSliceSignal DF_CodeSliceSignal; -struct DF_CodeSliceSignal -{ - UI_Signal base; - TxtPt mouse_pt; - TxtRng mouse_expr_rng; - Vec2F32 mouse_expr_baseline_pos; - S64 clicked_margin_line_num; - DF_Entity *dropped_entity; - S64 dropped_entity_line_num; - TxtRng copy_range; - B32 toggle_cursor_watch; - S64 set_next_statement_line_num; - S64 run_to_line_num; - S64 goto_disasm_line_num; - S64 goto_src_line_num; -}; - -//////////////////////////////// -//~ rjf: Auto-Complete Lister Types - -typedef U32 DF_AutoCompListerFlags; -enum -{ - DF_AutoCompListerFlag_Locals = (1<<0), - DF_AutoCompListerFlag_Registers = (1<<1), - DF_AutoCompListerFlag_ViewRules = (1<<2), - DF_AutoCompListerFlag_ViewRuleParams= (1<<3), - DF_AutoCompListerFlag_Members = (1<<4), - DF_AutoCompListerFlag_Languages = (1<<5), - DF_AutoCompListerFlag_Architectures = (1<<6), - DF_AutoCompListerFlag_Tex2DFormats = (1<<7), -}; - -typedef struct DF_AutoCompListerItem DF_AutoCompListerItem; -struct DF_AutoCompListerItem -{ - String8 string; - String8 kind_string; - FuzzyMatchRangeList matches; -}; - -typedef struct DF_AutoCompListerItemChunkNode DF_AutoCompListerItemChunkNode; -struct DF_AutoCompListerItemChunkNode -{ - DF_AutoCompListerItemChunkNode *next; - DF_AutoCompListerItem *v; - U64 count; - U64 cap; -}; - -typedef struct DF_AutoCompListerItemChunkList DF_AutoCompListerItemChunkList; -struct DF_AutoCompListerItemChunkList -{ - DF_AutoCompListerItemChunkNode *first; - DF_AutoCompListerItemChunkNode *last; - U64 chunk_count; - U64 total_count; -}; - -typedef struct DF_AutoCompListerItemArray DF_AutoCompListerItemArray; -struct DF_AutoCompListerItemArray -{ - DF_AutoCompListerItem *v; - U64 count; -}; - -typedef struct DF_AutoCompListerParams DF_AutoCompListerParams; -struct DF_AutoCompListerParams -{ - DF_AutoCompListerFlags flags; - String8List strings; -}; - -//////////////////////////////// -//~ rjf: Per-Window State - -typedef struct DF_Window DF_Window; -struct DF_Window -{ - // rjf: links & metadata - DF_Window *next; - DF_Window *prev; - U64 gen; - U64 frames_alive; - DF_CfgSrc cfg_src; - - // rjf: top-level info & handles - Arena *arena; - OS_Handle os; - R_Handle r; - UI_State *ui; - F32 last_dpi; - B32 window_temporarily_focused_ipc; - - // rjf: config/settings - DF_SettingVal setting_vals[DF_SettingCode_COUNT]; - UI_Palette cfg_palettes[DF_PaletteCode_COUNT]; // derivative from theme - - // rjf: view state delta history - DF_StateDeltaHistory *view_state_hist; - - // rjf: dev interface state - B32 dev_menu_is_open; - - // rjf: menu bar state - B32 menu_bar_focused; - B32 menu_bar_focused_on_press; - B32 menu_bar_key_held; - B32 menu_bar_focus_press_started; - - // rjf: code context menu state - Arena *code_ctx_menu_arena; - UI_Key code_ctx_menu_key; - DF_Handle code_ctx_menu_file; - U128 code_ctx_menu_text_key; - TXT_LangKind code_ctx_menu_lang_kind; - TxtRng code_ctx_menu_range; - U64 code_ctx_menu_vaddr; - DF_LineList code_ctx_menu_lines; - - // rjf: entity context menu state - UI_Key entity_ctx_menu_key; - DF_Handle entity_ctx_menu_entity; - U8 entity_ctx_menu_input_buffer[1024]; - U64 entity_ctx_menu_input_size; - TxtPt entity_ctx_menu_input_cursor; - TxtPt entity_ctx_menu_input_mark; - - // rjf: tab context menu state - UI_Key tab_ctx_menu_key; - DF_Handle tab_ctx_menu_panel; - DF_Handle tab_ctx_menu_view; - - // rjf: autocomplete lister state - U64 autocomp_last_frame_idx; - B32 autocomp_force_closed; - B32 autocomp_query_dirty; - UI_Key autocomp_root_key; - DF_CtrlCtx autocomp_ctrl_ctx; - Arena *autocomp_lister_params_arena; - DF_AutoCompListerParams autocomp_lister_params; - U64 autocomp_cursor_off; - U8 autocomp_lister_query_buffer[1024]; - U64 autocomp_lister_query_size; - F32 autocomp_open_t; - F32 autocomp_num_visible_rows_t; - S64 autocomp_cursor_num; - - // rjf: query view stack - Arena *query_cmd_arena; - DF_CmdSpec *query_cmd_spec; - DF_CmdParams query_cmd_params; - DF_View *query_view_stack_top; - B32 query_view_selected; - F32 query_view_selected_t; - F32 query_view_t; - - // rjf: hover eval stable state - B32 hover_eval_focused; - TxtPt hover_eval_txt_cursor; - TxtPt hover_eval_txt_mark; - U8 hover_eval_txt_buffer[1024]; - U64 hover_eval_txt_size; - Arena *hover_eval_arena; - Vec2F32 hover_eval_spawn_pos; - String8 hover_eval_string; - - // rjf: hover eval timer - U64 hover_eval_first_frame_idx; - U64 hover_eval_last_frame_idx; - - // rjf: hover eval params - DF_CtrlCtx hover_eval_ctrl_ctx; - DF_Handle hover_eval_file; - TxtPt hover_eval_file_pt; - U64 hover_eval_vaddr; - F32 hover_eval_open_t; - F32 hover_eval_num_visible_rows_t; - - // rjf: error state - U8 error_buffer[512]; - U64 error_string_size; - F32 error_t; - - // rjf: context overrides - DF_CtrlCtx ctrl_ctx_overrides; - - // rjf: panel state - DF_Panel *root_panel; - DF_Panel *free_panel; - DF_Panel *focused_panel; - - // rjf: per-frame drawing state - D_Bucket *draw_bucket; -}; - -//////////////////////////////// -//~ rjf: View Rule Block State Types - -typedef struct DF_ViewRuleBlockArenaExt DF_ViewRuleBlockArenaExt; -struct DF_ViewRuleBlockArenaExt -{ - DF_ViewRuleBlockArenaExt *next; - Arena *arena; -}; - -typedef struct DF_ViewRuleBlockNode DF_ViewRuleBlockNode; -struct DF_ViewRuleBlockNode -{ - DF_ViewRuleBlockNode *next; - DF_ExpandKey key; - DF_ViewRuleBlockArenaExt *first_arena_ext; - DF_ViewRuleBlockArenaExt *last_arena_ext; - Arena *user_state_arena; - void *user_state; - U64 user_state_size; -}; - -typedef struct DF_ViewRuleBlockSlot DF_ViewRuleBlockSlot; -struct DF_ViewRuleBlockSlot -{ - DF_ViewRuleBlockNode *first; - DF_ViewRuleBlockNode *last; -}; - -//////////////////////////////// -//~ rjf: Main Per-Process Graphical State - -typedef struct DF_String2ViewNode DF_String2ViewNode; -struct DF_String2ViewNode -{ - DF_String2ViewNode *hash_next; - String8 string; - String8 view_name; -}; - -typedef struct DF_String2ViewSlot DF_String2ViewSlot; -struct DF_String2ViewSlot -{ - DF_String2ViewNode *first; - DF_String2ViewNode *last; -}; - -typedef struct DF_GfxState DF_GfxState; -struct DF_GfxState -{ - // rjf: arenas - Arena *arena; - - // rjf: frame request state - U64 num_frames_requested; - - // rjf: history cache - DF_StateDeltaHistory *hist; - - // rjf: key map table - Arena *key_map_arena; - U64 key_map_table_size; - DF_KeyMapSlot *key_map_table; - DF_KeyMapNode *free_key_map_node; - U64 key_map_total_count; - - // rjf: bind change - B32 bind_change_active; - DF_CmdSpec *bind_change_cmd_spec; - DF_Binding bind_change_binding; - - // rjf: confirmation popup state - UI_Key confirm_key; - B32 confirm_active; - F32 confirm_t; - Arena *confirm_arena; - DF_CmdList confirm_cmds; - String8 confirm_title; - String8 confirm_msg; - - // rjf: string search state - Arena *string_search_arena; - String8 string_search_string; - - // rjf: view specs - U64 view_spec_table_size; - DF_ViewSpec **view_spec_table; - - // rjf: view rule specs - U64 view_rule_spec_table_size; - DF_GfxViewRuleSpec **view_rule_spec_table; - - // rjf: view rule block state - U64 view_rule_block_slots_count; - DF_ViewRuleBlockSlot *view_rule_block_slots; - DF_ViewRuleBlockNode *free_view_rule_block_node; - - // rjf: cmd param slot -> view spec rule table - DF_CmdParamSlotViewSpecRuleList cmd_param_slot_view_spec_table[DF_CmdParamSlot_COUNT]; - - // rjf: windows - OS_WindowRepaintFunctionType *repaint_hook; - DF_Window *first_window; - DF_Window *last_window; - DF_Window *free_window; - U64 window_count; - B32 last_window_queued_save; - - // rjf: view state - DF_View *free_view; - U64 free_view_count; - U64 allocated_view_count; - - // rjf: drag/drop state machine - DF_DragDropState drag_drop_state; - - // rjf: rich hover info - Arena *rich_hover_info_next_arena; - Arena *rich_hover_info_current_arena; - DF_RichHoverInfo rich_hover_info_next; - DF_RichHoverInfo rich_hover_info_current; - - // rjf: running theme state - DF_Theme cfg_theme_target; - DF_Theme cfg_theme; - Arena *cfg_main_font_path_arena; - Arena *cfg_code_font_path_arena; - String8 cfg_main_font_path; - String8 cfg_code_font_path; - F_Tag cfg_font_tags[DF_FontSlot_COUNT]; // derivative from font paths - - // rjf: global settings - DF_SettingVal cfg_setting_vals[DF_CfgSrc_COUNT][DF_SettingCode_COUNT]; - - // rjf: icon texture - R_Handle icon_texture; -}; - -//////////////////////////////// -//~ rjf: Globals - -read_only global DF_ViewSpec df_g_nil_view_spec = -{ - &df_g_nil_view_spec, - { - 0, - {0}, - {0}, - DF_NameKind_Null, - DF_IconKind_Null, - DF_VIEW_SETUP_FUNCTION_NAME(Null), - DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(Null), - DF_VIEW_CMD_FUNCTION_NAME(Null), - DF_VIEW_UI_FUNCTION_NAME(Null), - }, -}; - -read_only global DF_GfxViewRuleSpec df_g_nil_gfx_view_rule_spec = -{ - &df_g_nil_gfx_view_rule_spec, -}; - -read_only global DF_View df_g_nil_view = -{ - &df_g_nil_view, - &df_g_nil_view, - 0, - 0, - 0, - 0, - 0, - {0}, - {0}, - {0}, - {0}, - 0, - 0, - 0, - 0, - &df_g_nil_view_spec, - {0}, -}; - -read_only global DF_Panel df_g_nil_panel = -{ - &df_g_nil_panel, - &df_g_nil_panel, - &df_g_nil_panel, - &df_g_nil_panel, - &df_g_nil_panel, - 0, -}; - -global DF_GfxState *df_gfx_state = 0; -global DF_DragDropPayload df_g_drag_drop_payload = {0}; -global DF_Handle df_g_last_drag_drop_panel = {0}; -global DF_Handle df_g_last_drag_drop_prev_tab = {0}; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal DF_PathQuery df_path_query_from_string(String8 string); - -//////////////////////////////// -//~ rjf: View Type Functions - -internal B32 df_view_is_nil(DF_View *view); -internal B32 df_view_is_project_filtered(DF_View *view); -internal DF_Handle df_handle_from_view(DF_View *view); -internal DF_View *df_view_from_handle(DF_Handle handle); - -//////////////////////////////// -//~ rjf: View Spec Type Functions - -internal DF_GfxViewKind df_gfx_view_kind_from_string(String8 string); - -//////////////////////////////// -//~ rjf: Panel Type Functions - -//- rjf: basic type functions -internal B32 df_panel_is_nil(DF_Panel *panel); -internal DF_Handle df_handle_from_panel(DF_Panel *panel); -internal DF_Panel *df_panel_from_handle(DF_Handle handle); -internal UI_Key df_ui_key_from_panel(DF_Panel *panel); - -//- rjf: panel tree mutation notification -internal void df_panel_notify_mutation(DF_Window *window, DF_Panel *panel); - -//- rjf: tree construction -internal void df_panel_insert(DF_Panel *parent, DF_Panel *prev_child, DF_Panel *new_child); -internal void df_panel_remove(DF_Panel *parent, DF_Panel *child); - -//- rjf: tree walk -internal DF_PanelRec df_panel_rec_df(DF_Panel *panel, U64 sib_off, U64 child_off); -#define df_panel_rec_df_pre(panel) df_panel_rec_df(panel, OffsetOf(DF_Panel, next), OffsetOf(DF_Panel, first)) -#define df_panel_rec_df_post(panel) df_panel_rec_df(panel, OffsetOf(DF_Panel, prev), OffsetOf(DF_Panel, last)) - -//- rjf: panel -> rect calculations -internal Rng2F32 df_target_rect_from_panel_child(Rng2F32 parent_rect, DF_Panel *parent, DF_Panel *panel); -internal Rng2F32 df_target_rect_from_panel(Rng2F32 root_rect, DF_Panel *root, DF_Panel *panel); - -//- rjf: view ownership insertion/removal -internal void df_panel_insert_tab_view(DF_Panel *panel, DF_View *prev_view, DF_View *view); -internal void df_panel_remove_tab_view(DF_Panel *panel, DF_View *view); -internal DF_View *df_selected_tab_from_panel(DF_Panel *panel); - -//- rjf: icons & display strings -internal String8 df_display_string_from_view(Arena *arena, DF_CtrlCtx ctrl_ctx, DF_View *view); -internal DF_IconKind df_icon_kind_from_view(DF_View *view); - -//////////////////////////////// -//~ rjf: Window Type Functions - -internal DF_Handle df_handle_from_window(DF_Window *window); -internal DF_Window *df_window_from_handle(DF_Handle handle); - -//////////////////////////////// -//~ rjf: Control Context - -internal DF_CtrlCtx df_ctrl_ctx_from_window(DF_Window *ws); -internal DF_CtrlCtx df_ctrl_ctx_from_view(DF_Window *ws, DF_View *view); - -//////////////////////////////// -//~ rjf: Command Parameters From Context - -internal DF_CmdParams df_cmd_params_from_gfx(void); -internal B32 df_prefer_dasm_from_window(DF_Window *window); -internal DF_CmdParams df_cmd_params_from_window(DF_Window *window); -internal DF_CmdParams df_cmd_params_from_panel(DF_Window *window, DF_Panel *panel); -internal DF_CmdParams df_cmd_params_from_view(DF_Window *window, DF_Panel *panel, DF_View *view); -internal DF_CmdParams df_cmd_params_copy(Arena *arena, DF_CmdParams *src); - -//////////////////////////////// -//~ rjf: Global Cross-Window UI Interaction State Functions - -internal B32 df_drag_is_active(void); -internal void df_drag_begin(DF_DragDropPayload *payload); -internal B32 df_drag_drop(DF_DragDropPayload *out_payload); -internal void df_drag_kill(void); -internal void df_queue_drag_drop(void); - -internal void df_set_rich_hover_info(DF_RichHoverInfo *info); -internal DF_RichHoverInfo df_get_rich_hover_info(void); - -//////////////////////////////// -//~ rjf: View Spec State Functions - -internal void df_register_view_specs(DF_ViewSpecInfoArray specs); -internal DF_ViewSpec *df_view_spec_from_string(String8 string); -internal DF_ViewSpec *df_view_spec_from_gfx_view_kind(DF_GfxViewKind gfx_view_kind); -internal DF_ViewSpec *df_view_spec_from_cmd_param_slot_spec(DF_CmdParamSlot slot, DF_CmdSpec *cmd_spec); - -//////////////////////////////// -//~ rjf: View Rule Spec State Functions - -internal void df_register_gfx_view_rule_specs(DF_GfxViewRuleSpecInfoArray specs); -internal DF_GfxViewRuleSpec *df_gfx_view_rule_spec_from_string(String8 string); -internal DF_ViewSpec *df_tab_view_spec_from_gfx_view_rule_spec(DF_GfxViewRuleSpec *spec); - -//////////////////////////////// -//~ rjf: View State Functions - -internal DF_View *df_view_alloc(void); -internal void df_view_release(DF_View *view); -internal void df_view_equip_spec(DF_Window *window, DF_View *view, DF_ViewSpec *spec, DF_Entity *entity, String8 default_query, DF_CfgNode *cfg_root); -internal void df_view_equip_loading_info(DF_View *view, B32 is_loading, U64 progress_v, U64 progress_target); -internal void df_view_clear_user_state(DF_View *view); -internal void *df_view_get_or_push_user_state(DF_View *view, U64 size); -internal Arena *df_view_push_arena_ext(DF_View *view); -#define df_view_user_state(view, type) (type *)df_view_get_or_push_user_state((view), sizeof(type)) - -//////////////////////////////// -//~ rjf: View Rule Instance State Functions - -internal void *df_view_rule_block_get_or_push_user_state(DF_ExpandKey key, U64 size); -#define df_view_rule_block_user_state(key, type) (type *)df_view_rule_block_get_or_push_user_state(key, sizeof(type)) -internal Arena *df_view_rule_block_push_arena_ext(DF_ExpandKey key); - -//////////////////////////////// -//~ rjf: Panel State Functions - -internal DF_Panel *df_panel_alloc(DF_Window *ws); -internal void df_panel_release(DF_Window *ws, DF_Panel *panel); -internal void df_panel_release_all_views(DF_Panel *panel); - -//////////////////////////////// -//~ rjf: Window State Functions - -internal DF_Window *df_window_open(Vec2F32 size, OS_Handle preferred_monitor, DF_CfgSrc cfg_src); - -internal DF_Window *df_window_from_os_handle(OS_Handle os); - -internal void df_window_update_and_render(Arena *arena, DF_Window *ws, DF_CmdList *cmds); - -//////////////////////////////// -//~ rjf: Eval Viz - -internal String8 df_eval_escaped_from_raw_string(Arena *arena, String8 raw); -internal String8List df_single_line_eval_value_strings_from_eval(Arena *arena, DF_EvalVizStringFlags flags, TG_Graph *graph, RDI_Parsed *rdi, DF_CtrlCtx *ctrl_ctx, U32 default_radix, F_Tag font, F32 font_size, F32 max_size, S32 depth, DF_Eval eval, TG_Member *opt_member, DF_CfgTable *cfg_table); -internal DF_EvalVizWindowedRowList df_eval_viz_windowed_row_list_from_viz_block_list(Arena *arena, DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, DF_EvalView *eval_view, U32 default_radix, F_Tag font, F32 font_size, Rng1S64 visible_range, DF_EvalVizBlockList *blocks); - -//////////////////////////////// -//~ rjf: Hover Eval - -internal void df_set_hover_eval(DF_Window *ws, Vec2F32 pos, DF_CtrlCtx ctrl_ctx, DF_Entity *file, TxtPt pt, U64 vaddr, String8 string); - -//////////////////////////////// -//~ rjf: Auto-Complete Lister - -internal void df_autocomp_lister_item_chunk_list_push(Arena *arena, DF_AutoCompListerItemChunkList *list, U64 cap, DF_AutoCompListerItem *item); -internal DF_AutoCompListerItemArray df_autocomp_lister_item_array_from_chunk_list(Arena *arena, DF_AutoCompListerItemChunkList *list); -internal int df_autocomp_lister_item_qsort_compare(DF_AutoCompListerItem *a, DF_AutoCompListerItem *b); -internal void df_autocomp_lister_item_array_sort__in_place(DF_AutoCompListerItemArray *array); - -internal String8 df_autocomp_query_word_from_input_string_off(String8 input, U64 cursor_off); -internal DF_AutoCompListerParams df_view_rule_autocomp_lister_params_from_input_cursor(Arena *arena, String8 string, U64 cursor_off); -internal void df_set_autocomp_lister_query(DF_Window *ws, UI_Key root_key, DF_CtrlCtx ctrl_ctx, DF_AutoCompListerParams *params, String8 input, U64 cursor_off); - -//////////////////////////////// -//~ rjf: Search Strings - -internal void df_set_search_string(String8 string); -internal String8 df_push_search_string(Arena *arena); - -//////////////////////////////// -//~ rjf: Colors, Fonts, Config - -//- rjf: keybindings -internal OS_Key df_os_key_from_cfg_string(String8 string); -internal void df_clear_bindings(void); -internal DF_BindingList df_bindings_from_spec(Arena *arena, DF_CmdSpec *spec); -internal void df_bind_spec(DF_CmdSpec *spec, DF_Binding binding); -internal void df_unbind_spec(DF_CmdSpec *spec, DF_Binding binding); -internal DF_CmdSpecList df_cmd_spec_list_from_binding(Arena *arena, DF_Binding binding); -internal DF_CmdSpecList df_cmd_spec_list_from_event_flags(Arena *arena, OS_EventFlags flags); - -//- rjf: colors -internal Vec4F32 df_rgba_from_theme_color(DF_ThemeColor color); -internal DF_ThemeColor df_theme_color_from_txt_token_kind(TXT_TokenKind kind); - -//- rjf: code -> palette -internal UI_Palette *df_palette_from_code(DF_Window *ws, DF_PaletteCode code); - -//- rjf: fonts/sizes -internal F_Tag df_font_from_slot(DF_FontSlot slot); -internal F32 df_font_size_from_slot(DF_Window *ws, DF_FontSlot slot); -internal F_RasterFlags df_raster_flags_from_slot(DF_Window *ws, DF_FontSlot slot); - -//- rjf: settings -internal DF_SettingVal df_setting_val_from_code(DF_Window *optional_window, DF_SettingCode code); - -//- rjf: config serialization -internal int df_qsort_compare__cfg_string_bindings(DF_StringBindingPair *a, DF_StringBindingPair *b); -internal String8List df_cfg_strings_from_gfx(Arena *arena, String8 root_path, DF_CfgSrc source); - -//////////////////////////////// -//~ rjf: Process Control Info Stringification - -internal String8 df_string_from_exception_code(U32 code); -internal String8 df_stop_explanation_string_icon_from_ctrl_event(Arena *arena, CTRL_Event *event, DF_IconKind *icon_out); - -//////////////////////////////// -//~ rjf: UI Building Helpers - -#define DF_Palette(ws, code) UI_Palette(df_palette_from_code((ws), (code))) -#define DF_Font(ws, slot) UI_Font(df_font_from_slot(slot)) UI_TextRasterFlags(df_raster_flags_from_slot((ws), (slot))) - -//////////////////////////////// -//~ rjf: UI Widgets: Fancy Buttons - -internal void df_cmd_binding_buttons(DF_Window *ws, DF_CmdSpec *spec); -internal UI_Signal df_menu_bar_button(String8 string); -internal UI_Signal df_cmd_spec_button(DF_Window *ws, DF_CmdSpec *spec); -internal void df_cmd_list_menu_buttons(DF_Window *ws, U64 count, DF_CoreCmdKind *cmds, U32 *fastpath_codepoints); -internal UI_Signal df_icon_button(DF_Window *ws, DF_IconKind kind, FuzzyMatchRangeList *matches, String8 string); -internal UI_Signal df_icon_buttonf(DF_Window *ws, DF_IconKind kind, FuzzyMatchRangeList *matches, char *fmt, ...); -internal void df_entity_tooltips(DF_Window *ws, DF_Entity *entity); -internal UI_Signal df_entity_desc_button(DF_Window *ws, DF_Entity *entity, FuzzyMatchRangeList *name_matches, String8 fuzzy_query, B32 is_implicit); -internal void df_entity_src_loc_button(DF_Window *ws, DF_Entity *entity, TxtPt point); - -//////////////////////////////// -//~ rjf: UI Widgets: Text View - -internal UI_BOX_CUSTOM_DRAW(df_thread_box_draw_extensions); -internal UI_BOX_CUSTOM_DRAW(df_bp_box_draw_extensions); -internal DF_CodeSliceSignal df_code_slice(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *preferred_column, String8 string); -internal DF_CodeSliceSignal df_code_slicef(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *preferred_column, char *fmt, ...); - -internal B32 df_do_txt_controls(TXT_TextInfo *info, String8 data, U64 line_count_per_page, TxtPt *cursor, TxtPt *mark, S64 *preferred_column); - -//////////////////////////////// -//~ rjf: UI Widgets: Fancy Labels - -internal UI_Signal df_error_label(String8 string); -internal B32 df_help_label(String8 string); -internal D_FancyStringList df_fancy_string_list_from_code_string(Arena *arena, F32 alpha, B32 indirection_size_change, Vec4F32 base_color, String8 string); -internal UI_Box *df_code_label(F32 alpha, B32 indirection_size_change, Vec4F32 base_color, String8 string); - -//////////////////////////////// -//~ rjf: UI Widgets: Line Edit - -internal UI_Signal df_line_edit(DF_Window *ws, DF_LineEditFlags flags, S32 depth, FuzzyMatchRangeList *matches, TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, B32 *expanded_out, String8 pre_edit_value, String8 string); -internal UI_Signal df_line_editf(DF_Window *ws, DF_LineEditFlags flags, S32 depth, FuzzyMatchRangeList *matches, TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, B32 *expanded_out, String8 pre_edit_value, char *fmt, ...); - -//////////////////////////////// -//~ rjf: Continuous Frame Requests - -internal void df_gfx_request_frame(void); - -//////////////////////////////// -//~ rjf: Main Layer Top-Level Calls - -internal void df_gfx_init(OS_WindowRepaintFunctionType *window_repaint_entry_point, DF_StateDeltaHistory *hist); -internal void df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds); -internal void df_gfx_end_frame(void); - -#endif // DF_GFX_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DF_GFX_H +#define DF_GFX_H + +//////////////////////////////// +//~ rjf: Basic Types + +typedef struct DF_PathQuery DF_PathQuery; +struct DF_PathQuery +{ + String8 prefix; + String8 path; + String8 search; +}; + +//////////////////////////////// +//~ rjf: Binding Types + +typedef struct DF_Binding DF_Binding; +struct DF_Binding +{ + OS_Key key; + OS_EventFlags flags; +}; + +typedef struct DF_BindingNode DF_BindingNode; +struct DF_BindingNode +{ + DF_BindingNode *next; + DF_Binding binding; +}; + +typedef struct DF_BindingList DF_BindingList; +struct DF_BindingList +{ + DF_BindingNode *first; + DF_BindingNode *last; + U64 count; +}; + +typedef struct DF_StringBindingPair DF_StringBindingPair; +struct DF_StringBindingPair +{ + String8 string; + DF_Binding binding; +}; + +//////////////////////////////// +//~ rjf: Key Map Types + +typedef struct DF_KeyMapNode DF_KeyMapNode; +struct DF_KeyMapNode +{ + DF_KeyMapNode *hash_next; + DF_KeyMapNode *hash_prev; + DF_CmdSpec *spec; + DF_Binding binding; +}; + +typedef struct DF_KeyMapSlot DF_KeyMapSlot; +struct DF_KeyMapSlot +{ + DF_KeyMapNode *first; + DF_KeyMapNode *last; +}; + +//////////////////////////////// +//~ rjf: Setting Types + +typedef struct DF_SettingVal DF_SettingVal; +struct DF_SettingVal +{ + B32 set; + S32 s32; +}; + +//////////////////////////////// +//~ rjf: View Functions + +typedef struct DF_View DF_View; +typedef struct DF_Panel DF_Panel; +typedef struct DF_Window DF_Window; + +#define DF_VIEW_SETUP_FUNCTION_SIG(name) void name(DF_Window *ws, struct DF_View *view, DF_CfgNode *cfg_root) +#define DF_VIEW_SETUP_FUNCTION_NAME(name) df_view_setup_##name +#define DF_VIEW_SETUP_FUNCTION_DEF(name) internal DF_VIEW_SETUP_FUNCTION_SIG(DF_VIEW_SETUP_FUNCTION_NAME(name)) +typedef DF_VIEW_SETUP_FUNCTION_SIG(DF_ViewSetupFunctionType); + +#define DF_VIEW_STRING_FROM_STATE_FUNCTION_SIG(name) String8 name(Arena *arena, struct DF_View *view) +#define DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(name) df_view_string_from_state_##name +#define DF_VIEW_STRING_FROM_STATE_FUNCTION_DEF(name) internal DF_VIEW_STRING_FROM_STATE_FUNCTION_SIG(DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(name)) +typedef DF_VIEW_STRING_FROM_STATE_FUNCTION_SIG(DF_ViewStringFromStateFunctionType); + +#define DF_VIEW_CMD_FUNCTION_SIG(name) void name(struct DF_Window *ws, struct DF_Panel *panel, struct DF_View *view, struct DF_CmdList *cmds) +#define DF_VIEW_CMD_FUNCTION_NAME(name) df_view_cmds_##name +#define DF_VIEW_CMD_FUNCTION_DEF(name) internal DF_VIEW_CMD_FUNCTION_SIG(DF_VIEW_CMD_FUNCTION_NAME(name)) +typedef DF_VIEW_CMD_FUNCTION_SIG(DF_ViewCmdFunctionType); + +#define DF_VIEW_UI_FUNCTION_SIG(name) void name(struct DF_Window *ws, struct DF_Panel *panel, struct DF_View *view, Rng2F32 rect) +#define DF_VIEW_UI_FUNCTION_NAME(name) df_view_ui_##name +#define DF_VIEW_UI_FUNCTION_DEF(name) internal DF_VIEW_UI_FUNCTION_SIG(DF_VIEW_UI_FUNCTION_NAME(name)) +typedef DF_VIEW_UI_FUNCTION_SIG(DF_ViewUIFunctionType); + +//////////////////////////////// +//~ rjf: View Specification Types + +typedef U32 DF_ViewSpecFlags; +enum +{ + DF_ViewSpecFlag_ParameterizedByEntity = (1<<0), + DF_ViewSpecFlag_ProjectSpecific = (1<<1), + DF_ViewSpecFlag_CanSerialize = (1<<2), + DF_ViewSpecFlag_CanSerializeEntityPath = (1<<3), + DF_ViewSpecFlag_CanSerializeQuery = (1<<4), + DF_ViewSpecFlag_CanFilter = (1<<5), + DF_ViewSpecFlag_FilterIsCode = (1<<6), + DF_ViewSpecFlag_TypingAutomaticallyFilters = (1<<7), +}; + +typedef enum DF_NameKind +{ + DF_NameKind_Null, + DF_NameKind_EntityName, + DF_NameKind_COUNT +} +DF_NameKind; + +typedef struct DF_ViewSpecInfo DF_ViewSpecInfo; +struct DF_ViewSpecInfo +{ + DF_ViewSpecFlags flags; + String8 name; + String8 display_string; + DF_NameKind name_kind; + DF_IconKind icon_kind; + DF_ViewSetupFunctionType *setup_hook; + DF_ViewStringFromStateFunctionType *string_from_state_hook; + DF_ViewCmdFunctionType *cmd_hook; + DF_ViewUIFunctionType *ui_hook; +}; + +typedef struct DF_ViewSpec DF_ViewSpec; +struct DF_ViewSpec +{ + DF_ViewSpec *hash_next; + DF_ViewSpecInfo info; +}; + +typedef struct DF_ViewSpecInfoArray DF_ViewSpecInfoArray; +struct DF_ViewSpecInfoArray +{ + DF_ViewSpecInfo *v; + U64 count; +}; + +typedef struct DF_CmdParamSlotViewSpecRuleNode DF_CmdParamSlotViewSpecRuleNode; +struct DF_CmdParamSlotViewSpecRuleNode +{ + DF_CmdParamSlotViewSpecRuleNode *next; + DF_ViewSpec *view_spec; + DF_CmdSpec *cmd_spec; +}; + +typedef struct DF_CmdParamSlotViewSpecRuleList DF_CmdParamSlotViewSpecRuleList; +struct DF_CmdParamSlotViewSpecRuleList +{ + DF_CmdParamSlotViewSpecRuleNode *first; + DF_CmdParamSlotViewSpecRuleNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: View Types + +typedef struct DF_ArenaExt DF_ArenaExt; +struct DF_ArenaExt +{ + DF_ArenaExt *next; + Arena *arena; +}; + +typedef struct DF_View DF_View; +struct DF_View +{ + // rjf: ownership links ('owners' can have lists of views) + DF_View *next; + DF_View *prev; + + // rjf: allocation info + U64 generation; + + // rjf: loading animation state + F32 loading_t; + F32 loading_t_target; + U64 loading_progress_v; + U64 loading_progress_v_target; + + // rjf: view state + UI_ScrollPt2 scroll_pos; + TxtPt cursor; + TxtPt mark; + + // rjf: ctrl context overrides + DF_CtrlCtx ctrl_ctx_overrides; + + // rjf: allocation & user data extensions + Arena *arena; + DF_ArenaExt *first_arena_ext; + DF_ArenaExt *last_arena_ext; + void *user_data; + + // rjf: view kind info + DF_ViewSpec *spec; + DF_Handle entity; + DF_Handle project; + + // rjf: filter mode + B32 is_filtering; + F32 is_filtering_t; + + // rjf: query -> params data + TxtPt query_cursor; + TxtPt query_mark; + U8 query_buffer[1024]; + U64 query_string_size; +}; + +//////////////////////////////// +//~ rjf: Panel Types + +typedef struct DF_Panel DF_Panel; +struct DF_Panel +{ + // rjf: tree links/data + DF_Panel *first; + DF_Panel *last; + DF_Panel *next; + DF_Panel *prev; + DF_Panel *parent; + U64 child_count; + + // rjf: allocation data + U64 generation; + + // rjf: split data + Axis2 split_axis; + F32 pct_of_parent; + + // rjf: animated rectangle data + Rng2F32 animated_rect_pct; + + // rjf: tab params + Side tab_side; + + // rjf: stable views (tabs) + DF_View *first_tab_view; + DF_View *last_tab_view; + U64 tab_view_count; + DF_Handle selected_tab_view; +}; + +typedef struct DF_PanelRec DF_PanelRec; +struct DF_PanelRec +{ + DF_Panel *next; + int push_count; + int pop_count; +}; + +//////////////////////////////// +//~ rjf: Drag/Drop Types + +typedef enum DF_DragDropState +{ + DF_DragDropState_Null, + DF_DragDropState_Dragging, + DF_DragDropState_Dropping, + DF_DragDropState_COUNT +} +DF_DragDropState; + +typedef struct DF_DragDropPayload DF_DragDropPayload; +struct DF_DragDropPayload +{ + UI_Key key; + DF_Handle panel; + DF_Handle view; + DF_Handle entity; + TxtPt text_point; +}; + +//////////////////////////////// +//~ rjf: Rich Hover Types + +typedef struct DF_RichHoverInfo DF_RichHoverInfo; +struct DF_RichHoverInfo +{ + DF_Handle process; + Rng1U64 vaddr_range; + DF_Handle module; + Rng1U64 voff_range; + DI_Key dbgi_key; +}; + +//////////////////////////////// +//~ rjf: View Rule Spec Types + +typedef U32 DF_GfxViewRuleSpecInfoFlags; // NOTE(rjf): see @view_rule_info +enum +{ + DF_GfxViewRuleSpecInfoFlag_VizRowProd = (1<<0), + DF_GfxViewRuleSpecInfoFlag_LineStringize = (1<<1), + DF_GfxViewRuleSpecInfoFlag_RowUI = (1<<2), + DF_GfxViewRuleSpecInfoFlag_BlockUI = (1<<3), +}; + +#define DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_SIG(name) void name(void) +#define DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_NAME(name) df_gfx_view_rule_viz_row_prod__##name +#define DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_DEF(name) internal DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_SIG(DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_NAME(name)) + +#define DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_SIG(name) void name(void) +#define DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_NAME(name) df_gfx_view_rule_line_stringize__##name +#define DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_DEF(name) internal DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_SIG(DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_NAME(name)) + +#define DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_SIG(name) void name(struct DF_Window *ws, DF_ExpandKey key, DF_Eval eval, DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, struct DF_CfgNode *cfg) +#define DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_NAME(name) df_gfx_view_rule_row_ui__##name +#define DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_DEF(name) DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_SIG(DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_NAME(name)) + +#define DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_SIG(name) void name(struct DF_Window *ws, DF_ExpandKey key, DF_Eval eval, String8 string, DI_Scope *di_scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, struct DF_CfgNode *cfg, Vec2F32 dim) +#define DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_NAME(name) df_gfx_view_rule_block_ui__##name +#define DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_DEF(name) DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_SIG(DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_NAME(name)) + +typedef DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_SIG(DF_GfxViewRuleVizRowProdHookFunctionType); +typedef DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_SIG(DF_GfxViewRuleLineStringizeHookFunctionType); +typedef DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_SIG(DF_GfxViewRuleRowUIFunctionType); +typedef DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_SIG(DF_GfxViewRuleBlockUIFunctionType); + +typedef struct DF_GfxViewRuleSpecInfo DF_GfxViewRuleSpecInfo; +struct DF_GfxViewRuleSpecInfo +{ + String8 string; + DF_GfxViewRuleSpecInfoFlags flags; + DF_GfxViewRuleVizRowProdHookFunctionType *viz_row_prod; + DF_GfxViewRuleLineStringizeHookFunctionType *line_stringize; + DF_GfxViewRuleRowUIFunctionType *row_ui; + DF_GfxViewRuleBlockUIFunctionType *block_ui; + String8 tab_view_spec_name; +}; + +typedef struct DF_GfxViewRuleSpecInfoArray DF_GfxViewRuleSpecInfoArray; +struct DF_GfxViewRuleSpecInfoArray +{ + DF_GfxViewRuleSpecInfo *v; + U64 count; +}; + +typedef struct DF_GfxViewRuleSpec DF_GfxViewRuleSpec; +struct DF_GfxViewRuleSpec +{ + DF_GfxViewRuleSpec *hash_next; + DF_GfxViewRuleSpecInfo info; +}; + +//////////////////////////////// +//~ rjf: Generated Code + +#include "generated/df_gfx.meta.h" + +//////////////////////////////// +//~ rjf: Theme Types + +typedef struct DF_Theme DF_Theme; +struct DF_Theme +{ + Vec4F32 colors[DF_ThemeColor_COUNT]; +}; + +typedef enum DF_FontSlot +{ + DF_FontSlot_Main, + DF_FontSlot_Code, + DF_FontSlot_Icons, + DF_FontSlot_COUNT +} +DF_FontSlot; + +typedef enum DF_PaletteCode +{ + DF_PaletteCode_Base, + DF_PaletteCode_MenuBar, + DF_PaletteCode_Floating, + DF_PaletteCode_ImplicitButton, + DF_PaletteCode_PlainButton, + DF_PaletteCode_PositivePopButton, + DF_PaletteCode_NegativePopButton, + DF_PaletteCode_NeutralPopButton, + DF_PaletteCode_ScrollBarButton, + DF_PaletteCode_Tab, + DF_PaletteCode_TabInactive, + DF_PaletteCode_DropSiteOverlay, + DF_PaletteCode_COUNT +} +DF_PaletteCode; + +//////////////////////////////// +//~ rjf: UI Helper & Widget Types + +//- rjf: line edits + +typedef U32 DF_LineEditFlags; +enum +{ + DF_LineEditFlag_Expander = (1<<0), + DF_LineEditFlag_ExpanderSpace = (1<<1), + DF_LineEditFlag_ExpanderPlaceholder = (1<<2), + DF_LineEditFlag_DisableEdit = (1<<3), + DF_LineEditFlag_CodeContents = (1<<4), + DF_LineEditFlag_Border = (1<<5), + DF_LineEditFlag_NoBackground = (1<<6), + DF_LineEditFlag_PreferDisplayString = (1<<7), + DF_LineEditFlag_DisplayStringIsCode = (1<<8), +}; + +//- rjf: code viewing/editing widgets + +typedef U32 DF_CodeSliceFlags; +enum +{ + DF_CodeSliceFlag_Clickable = (1<<0), + DF_CodeSliceFlag_PriorityMargin = (1<<1), + DF_CodeSliceFlag_CatchallMargin = (1<<2), + DF_CodeSliceFlag_LineNums = (1<<3), +}; + +typedef struct DF_CodeSliceParams DF_CodeSliceParams; +struct DF_CodeSliceParams +{ + // rjf: content + DF_CodeSliceFlags flags; + Rng1S64 line_num_range; + String8 *line_text; + Rng1U64 *line_ranges; + TXT_TokenArray *line_tokens; + DF_EntityList *line_bps; + DF_EntityList *line_ips; + DF_EntityList *line_pins; + U64 *line_vaddrs; + DF_LineList *line_infos; + DI_KeyList relevant_dbgi_keys; + + // rjf: visual parameters + F_Tag font; + F32 font_size; + F32 tab_size; + String8 search_query; + F32 line_height_px; + F32 priority_margin_width_px; + F32 catchall_margin_width_px; + F32 line_num_width_px; + F32 line_text_max_width_px; + F32 margin_float_off_px; +}; + +typedef struct DF_CodeSliceSignal DF_CodeSliceSignal; +struct DF_CodeSliceSignal +{ + UI_Signal base; + TxtPt mouse_pt; + TxtRng mouse_expr_rng; + Vec2F32 mouse_expr_baseline_pos; + S64 clicked_margin_line_num; + DF_Entity *dropped_entity; + S64 dropped_entity_line_num; + TxtRng copy_range; + B32 toggle_cursor_watch; + S64 set_next_statement_line_num; + S64 run_to_line_num; + S64 goto_disasm_line_num; + S64 goto_src_line_num; +}; + +//////////////////////////////// +//~ rjf: Auto-Complete Lister Types + +typedef U32 DF_AutoCompListerFlags; +enum +{ + DF_AutoCompListerFlag_Locals = (1<<0), + DF_AutoCompListerFlag_Registers = (1<<1), + DF_AutoCompListerFlag_ViewRules = (1<<2), + DF_AutoCompListerFlag_ViewRuleParams= (1<<3), + DF_AutoCompListerFlag_Members = (1<<4), + DF_AutoCompListerFlag_Languages = (1<<5), + DF_AutoCompListerFlag_Architectures = (1<<6), + DF_AutoCompListerFlag_Tex2DFormats = (1<<7), +}; + +typedef struct DF_AutoCompListerItem DF_AutoCompListerItem; +struct DF_AutoCompListerItem +{ + String8 string; + String8 kind_string; + FuzzyMatchRangeList matches; +}; + +typedef struct DF_AutoCompListerItemChunkNode DF_AutoCompListerItemChunkNode; +struct DF_AutoCompListerItemChunkNode +{ + DF_AutoCompListerItemChunkNode *next; + DF_AutoCompListerItem *v; + U64 count; + U64 cap; +}; + +typedef struct DF_AutoCompListerItemChunkList DF_AutoCompListerItemChunkList; +struct DF_AutoCompListerItemChunkList +{ + DF_AutoCompListerItemChunkNode *first; + DF_AutoCompListerItemChunkNode *last; + U64 chunk_count; + U64 total_count; +}; + +typedef struct DF_AutoCompListerItemArray DF_AutoCompListerItemArray; +struct DF_AutoCompListerItemArray +{ + DF_AutoCompListerItem *v; + U64 count; +}; + +typedef struct DF_AutoCompListerParams DF_AutoCompListerParams; +struct DF_AutoCompListerParams +{ + DF_AutoCompListerFlags flags; + String8List strings; +}; + +//////////////////////////////// +//~ rjf: Per-Window State + +typedef struct DF_Window DF_Window; +struct DF_Window +{ + // rjf: links & metadata + DF_Window *next; + DF_Window *prev; + U64 gen; + U64 frames_alive; + DF_CfgSrc cfg_src; + + // rjf: top-level info & handles + Arena *arena; + OS_Handle os; + R_Handle r; + UI_State *ui; + F32 last_dpi; + B32 window_temporarily_focused_ipc; + + // rjf: config/settings + DF_SettingVal setting_vals[DF_SettingCode_COUNT]; + UI_Palette cfg_palettes[DF_PaletteCode_COUNT]; // derivative from theme + + // rjf: view state delta history + DF_StateDeltaHistory *view_state_hist; + + // rjf: dev interface state + B32 dev_menu_is_open; + + // rjf: menu bar state + B32 menu_bar_focused; + B32 menu_bar_focused_on_press; + B32 menu_bar_key_held; + B32 menu_bar_focus_press_started; + + // rjf: code context menu state + Arena *code_ctx_menu_arena; + UI_Key code_ctx_menu_key; + DF_Handle code_ctx_menu_file; + U128 code_ctx_menu_text_key; + TXT_LangKind code_ctx_menu_lang_kind; + TxtRng code_ctx_menu_range; + U64 code_ctx_menu_vaddr; + DF_LineList code_ctx_menu_lines; + + // rjf: entity context menu state + UI_Key entity_ctx_menu_key; + DF_Handle entity_ctx_menu_entity; + U8 entity_ctx_menu_input_buffer[1024]; + U64 entity_ctx_menu_input_size; + TxtPt entity_ctx_menu_input_cursor; + TxtPt entity_ctx_menu_input_mark; + + // rjf: tab context menu state + UI_Key tab_ctx_menu_key; + DF_Handle tab_ctx_menu_panel; + DF_Handle tab_ctx_menu_view; + + // rjf: autocomplete lister state + U64 autocomp_last_frame_idx; + B32 autocomp_force_closed; + B32 autocomp_query_dirty; + UI_Key autocomp_root_key; + DF_CtrlCtx autocomp_ctrl_ctx; + Arena *autocomp_lister_params_arena; + DF_AutoCompListerParams autocomp_lister_params; + U64 autocomp_cursor_off; + U8 autocomp_lister_query_buffer[1024]; + U64 autocomp_lister_query_size; + F32 autocomp_open_t; + F32 autocomp_num_visible_rows_t; + S64 autocomp_cursor_num; + + // rjf: query view stack + Arena *query_cmd_arena; + DF_CmdSpec *query_cmd_spec; + DF_CmdParams query_cmd_params; + DF_View *query_view_stack_top; + B32 query_view_selected; + F32 query_view_selected_t; + F32 query_view_t; + + // rjf: hover eval stable state + B32 hover_eval_focused; + TxtPt hover_eval_txt_cursor; + TxtPt hover_eval_txt_mark; + U8 hover_eval_txt_buffer[1024]; + U64 hover_eval_txt_size; + Arena *hover_eval_arena; + Vec2F32 hover_eval_spawn_pos; + String8 hover_eval_string; + + // rjf: hover eval timer + U64 hover_eval_first_frame_idx; + U64 hover_eval_last_frame_idx; + + // rjf: hover eval params + DF_CtrlCtx hover_eval_ctrl_ctx; + DF_Handle hover_eval_file; + TxtPt hover_eval_file_pt; + U64 hover_eval_vaddr; + F32 hover_eval_open_t; + F32 hover_eval_num_visible_rows_t; + + // rjf: error state + U8 error_buffer[512]; + U64 error_string_size; + F32 error_t; + + // rjf: context overrides + DF_CtrlCtx ctrl_ctx_overrides; + + // rjf: panel state + DF_Panel *root_panel; + DF_Panel *free_panel; + DF_Panel *focused_panel; + + // rjf: per-frame drawing state + D_Bucket *draw_bucket; +}; + +//////////////////////////////// +//~ rjf: View Rule Block State Types + +typedef struct DF_ViewRuleBlockArenaExt DF_ViewRuleBlockArenaExt; +struct DF_ViewRuleBlockArenaExt +{ + DF_ViewRuleBlockArenaExt *next; + Arena *arena; +}; + +typedef struct DF_ViewRuleBlockNode DF_ViewRuleBlockNode; +struct DF_ViewRuleBlockNode +{ + DF_ViewRuleBlockNode *next; + DF_ExpandKey key; + DF_ViewRuleBlockArenaExt *first_arena_ext; + DF_ViewRuleBlockArenaExt *last_arena_ext; + Arena *user_state_arena; + void *user_state; + U64 user_state_size; +}; + +typedef struct DF_ViewRuleBlockSlot DF_ViewRuleBlockSlot; +struct DF_ViewRuleBlockSlot +{ + DF_ViewRuleBlockNode *first; + DF_ViewRuleBlockNode *last; +}; + +//////////////////////////////// +//~ rjf: Main Per-Process Graphical State + +typedef struct DF_String2ViewNode DF_String2ViewNode; +struct DF_String2ViewNode +{ + DF_String2ViewNode *hash_next; + String8 string; + String8 view_name; +}; + +typedef struct DF_String2ViewSlot DF_String2ViewSlot; +struct DF_String2ViewSlot +{ + DF_String2ViewNode *first; + DF_String2ViewNode *last; +}; + +typedef struct DF_GfxState DF_GfxState; +struct DF_GfxState +{ + // rjf: arenas + Arena *arena; + + // rjf: frame request state + U64 num_frames_requested; + + // rjf: history cache + DF_StateDeltaHistory *hist; + + // rjf: key map table + Arena *key_map_arena; + U64 key_map_table_size; + DF_KeyMapSlot *key_map_table; + DF_KeyMapNode *free_key_map_node; + U64 key_map_total_count; + + // rjf: bind change + B32 bind_change_active; + DF_CmdSpec *bind_change_cmd_spec; + DF_Binding bind_change_binding; + + // rjf: confirmation popup state + UI_Key confirm_key; + B32 confirm_active; + F32 confirm_t; + Arena *confirm_arena; + DF_CmdList confirm_cmds; + String8 confirm_title; + String8 confirm_msg; + + // rjf: string search state + Arena *string_search_arena; + String8 string_search_string; + + // rjf: view specs + U64 view_spec_table_size; + DF_ViewSpec **view_spec_table; + + // rjf: view rule specs + U64 view_rule_spec_table_size; + DF_GfxViewRuleSpec **view_rule_spec_table; + + // rjf: view rule block state + U64 view_rule_block_slots_count; + DF_ViewRuleBlockSlot *view_rule_block_slots; + DF_ViewRuleBlockNode *free_view_rule_block_node; + + // rjf: cmd param slot -> view spec rule table + DF_CmdParamSlotViewSpecRuleList cmd_param_slot_view_spec_table[DF_CmdParamSlot_COUNT]; + + // rjf: windows + OS_WindowRepaintFunctionType *repaint_hook; + DF_Window *first_window; + DF_Window *last_window; + DF_Window *free_window; + U64 window_count; + B32 last_window_queued_save; + + // rjf: view state + DF_View *free_view; + U64 free_view_count; + U64 allocated_view_count; + + // rjf: drag/drop state machine + DF_DragDropState drag_drop_state; + + // rjf: rich hover info + Arena *rich_hover_info_next_arena; + Arena *rich_hover_info_current_arena; + DF_RichHoverInfo rich_hover_info_next; + DF_RichHoverInfo rich_hover_info_current; + + // rjf: running theme state + DF_Theme cfg_theme_target; + DF_Theme cfg_theme; + Arena *cfg_main_font_path_arena; + Arena *cfg_code_font_path_arena; + String8 cfg_main_font_path; + String8 cfg_code_font_path; + F_Tag cfg_font_tags[DF_FontSlot_COUNT]; // derivative from font paths + + // rjf: global settings + DF_SettingVal cfg_setting_vals[DF_CfgSrc_COUNT][DF_SettingCode_COUNT]; + + // rjf: icon texture + R_Handle icon_texture; +}; + +//////////////////////////////// +//~ rjf: Globals + +read_only global DF_ViewSpec df_g_nil_view_spec = +{ + &df_g_nil_view_spec, + { + 0, + {0}, + {0}, + DF_NameKind_Null, + DF_IconKind_Null, + DF_VIEW_SETUP_FUNCTION_NAME(Null), + DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(Null), + DF_VIEW_CMD_FUNCTION_NAME(Null), + DF_VIEW_UI_FUNCTION_NAME(Null), + }, +}; + +read_only global DF_GfxViewRuleSpec df_g_nil_gfx_view_rule_spec = +{ + &df_g_nil_gfx_view_rule_spec, +}; + +read_only global DF_View df_g_nil_view = +{ + &df_g_nil_view, + &df_g_nil_view, + 0, + 0, + 0, + 0, + 0, + {0}, + {0}, + {0}, + {0}, + 0, + 0, + 0, + 0, + &df_g_nil_view_spec, + {0}, +}; + +read_only global DF_Panel df_g_nil_panel = +{ + &df_g_nil_panel, + &df_g_nil_panel, + &df_g_nil_panel, + &df_g_nil_panel, + &df_g_nil_panel, + 0, +}; + +global DF_GfxState *df_gfx_state = 0; +global DF_DragDropPayload df_g_drag_drop_payload = {0}; +global DF_Handle df_g_last_drag_drop_panel = {0}; +global DF_Handle df_g_last_drag_drop_prev_tab = {0}; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal DF_PathQuery df_path_query_from_string(String8 string); + +//////////////////////////////// +//~ rjf: View Type Functions + +internal B32 df_view_is_nil(DF_View *view); +internal B32 df_view_is_project_filtered(DF_View *view); +internal DF_Handle df_handle_from_view(DF_View *view); +internal DF_View *df_view_from_handle(DF_Handle handle); + +//////////////////////////////// +//~ rjf: View Spec Type Functions + +internal DF_GfxViewKind df_gfx_view_kind_from_string(String8 string); + +//////////////////////////////// +//~ rjf: Panel Type Functions + +//- rjf: basic type functions +internal B32 df_panel_is_nil(DF_Panel *panel); +internal DF_Handle df_handle_from_panel(DF_Panel *panel); +internal DF_Panel *df_panel_from_handle(DF_Handle handle); +internal UI_Key df_ui_key_from_panel(DF_Panel *panel); + +//- rjf: panel tree mutation notification +internal void df_panel_notify_mutation(DF_Window *window, DF_Panel *panel); + +//- rjf: tree construction +internal void df_panel_insert(DF_Panel *parent, DF_Panel *prev_child, DF_Panel *new_child); +internal void df_panel_remove(DF_Panel *parent, DF_Panel *child); + +//- rjf: tree walk +internal DF_PanelRec df_panel_rec_df(DF_Panel *panel, U64 sib_off, U64 child_off); +#define df_panel_rec_df_pre(panel) df_panel_rec_df(panel, OffsetOf(DF_Panel, next), OffsetOf(DF_Panel, first)) +#define df_panel_rec_df_post(panel) df_panel_rec_df(panel, OffsetOf(DF_Panel, prev), OffsetOf(DF_Panel, last)) + +//- rjf: panel -> rect calculations +internal Rng2F32 df_target_rect_from_panel_child(Rng2F32 parent_rect, DF_Panel *parent, DF_Panel *panel); +internal Rng2F32 df_target_rect_from_panel(Rng2F32 root_rect, DF_Panel *root, DF_Panel *panel); + +//- rjf: view ownership insertion/removal +internal void df_panel_insert_tab_view(DF_Panel *panel, DF_View *prev_view, DF_View *view); +internal void df_panel_remove_tab_view(DF_Panel *panel, DF_View *view); +internal DF_View *df_selected_tab_from_panel(DF_Panel *panel); + +//- rjf: icons & display strings +internal String8 df_display_string_from_view(Arena *arena, DF_CtrlCtx ctrl_ctx, DF_View *view); +internal DF_IconKind df_icon_kind_from_view(DF_View *view); + +//////////////////////////////// +//~ rjf: Window Type Functions + +internal DF_Handle df_handle_from_window(DF_Window *window); +internal DF_Window *df_window_from_handle(DF_Handle handle); + +//////////////////////////////// +//~ rjf: Control Context + +internal DF_CtrlCtx df_ctrl_ctx_from_window(DF_Window *ws); +internal DF_CtrlCtx df_ctrl_ctx_from_view(DF_Window *ws, DF_View *view); + +//////////////////////////////// +//~ rjf: Command Parameters From Context + +internal DF_CmdParams df_cmd_params_from_gfx(void); +internal B32 df_prefer_dasm_from_window(DF_Window *window); +internal DF_CmdParams df_cmd_params_from_window(DF_Window *window); +internal DF_CmdParams df_cmd_params_from_panel(DF_Window *window, DF_Panel *panel); +internal DF_CmdParams df_cmd_params_from_view(DF_Window *window, DF_Panel *panel, DF_View *view); +internal DF_CmdParams df_cmd_params_copy(Arena *arena, DF_CmdParams *src); + +//////////////////////////////// +//~ rjf: Global Cross-Window UI Interaction State Functions + +internal B32 df_drag_is_active(void); +internal void df_drag_begin(DF_DragDropPayload *payload); +internal B32 df_drag_drop(DF_DragDropPayload *out_payload); +internal void df_drag_kill(void); +internal void df_queue_drag_drop(void); + +internal void df_set_rich_hover_info(DF_RichHoverInfo *info); +internal DF_RichHoverInfo df_get_rich_hover_info(void); + +//////////////////////////////// +//~ rjf: View Spec State Functions + +internal void df_register_view_specs(DF_ViewSpecInfoArray specs); +internal DF_ViewSpec *df_view_spec_from_string(String8 string); +internal DF_ViewSpec *df_view_spec_from_gfx_view_kind(DF_GfxViewKind gfx_view_kind); +internal DF_ViewSpec *df_view_spec_from_cmd_param_slot_spec(DF_CmdParamSlot slot, DF_CmdSpec *cmd_spec); + +//////////////////////////////// +//~ rjf: View Rule Spec State Functions + +internal void df_register_gfx_view_rule_specs(DF_GfxViewRuleSpecInfoArray specs); +internal DF_GfxViewRuleSpec *df_gfx_view_rule_spec_from_string(String8 string); +internal DF_ViewSpec *df_tab_view_spec_from_gfx_view_rule_spec(DF_GfxViewRuleSpec *spec); + +//////////////////////////////// +//~ rjf: View State Functions + +internal DF_View *df_view_alloc(void); +internal void df_view_release(DF_View *view); +internal void df_view_equip_spec(DF_Window *window, DF_View *view, DF_ViewSpec *spec, DF_Entity *entity, String8 default_query, DF_CfgNode *cfg_root); +internal void df_view_equip_loading_info(DF_View *view, B32 is_loading, U64 progress_v, U64 progress_target); +internal void df_view_clear_user_state(DF_View *view); +internal void *df_view_get_or_push_user_state(DF_View *view, U64 size); +internal Arena *df_view_push_arena_ext(DF_View *view); +#define df_view_user_state(view, type) (type *)df_view_get_or_push_user_state((view), sizeof(type)) + +//////////////////////////////// +//~ rjf: View Rule Instance State Functions + +internal void *df_view_rule_block_get_or_push_user_state(DF_ExpandKey key, U64 size); +#define df_view_rule_block_user_state(key, type) (type *)df_view_rule_block_get_or_push_user_state(key, sizeof(type)) +internal Arena *df_view_rule_block_push_arena_ext(DF_ExpandKey key); + +//////////////////////////////// +//~ rjf: Panel State Functions + +internal DF_Panel *df_panel_alloc(DF_Window *ws); +internal void df_panel_release(DF_Window *ws, DF_Panel *panel); +internal void df_panel_release_all_views(DF_Panel *panel); + +//////////////////////////////// +//~ rjf: Window State Functions + +internal DF_Window *df_window_open(Vec2F32 size, OS_Handle preferred_monitor, DF_CfgSrc cfg_src); + +internal DF_Window *df_window_from_os_handle(OS_Handle os); + +internal void df_window_update_and_render(Arena *arena, DF_Window *ws, DF_CmdList *cmds); + +//////////////////////////////// +//~ rjf: Eval Viz + +internal String8 df_eval_escaped_from_raw_string(Arena *arena, String8 raw); +internal String8List df_single_line_eval_value_strings_from_eval(Arena *arena, DF_EvalVizStringFlags flags, TG_Graph *graph, RDI_Parsed *rdi, DF_CtrlCtx *ctrl_ctx, U32 default_radix, F_Tag font, F32 font_size, F32 max_size, S32 depth, DF_Eval eval, TG_Member *opt_member, DF_CfgTable *cfg_table); +internal DF_EvalVizWindowedRowList df_eval_viz_windowed_row_list_from_viz_block_list(Arena *arena, DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, DF_EvalView *eval_view, U32 default_radix, F_Tag font, F32 font_size, Rng1S64 visible_range, DF_EvalVizBlockList *blocks); + +//////////////////////////////// +//~ rjf: Hover Eval + +internal void df_set_hover_eval(DF_Window *ws, Vec2F32 pos, DF_CtrlCtx ctrl_ctx, DF_Entity *file, TxtPt pt, U64 vaddr, String8 string); + +//////////////////////////////// +//~ rjf: Auto-Complete Lister + +internal void df_autocomp_lister_item_chunk_list_push(Arena *arena, DF_AutoCompListerItemChunkList *list, U64 cap, DF_AutoCompListerItem *item); +internal DF_AutoCompListerItemArray df_autocomp_lister_item_array_from_chunk_list(Arena *arena, DF_AutoCompListerItemChunkList *list); +internal int df_autocomp_lister_item_qsort_compare(DF_AutoCompListerItem *a, DF_AutoCompListerItem *b); +internal void df_autocomp_lister_item_array_sort__in_place(DF_AutoCompListerItemArray *array); + +internal String8 df_autocomp_query_word_from_input_string_off(String8 input, U64 cursor_off); +internal DF_AutoCompListerParams df_view_rule_autocomp_lister_params_from_input_cursor(Arena *arena, String8 string, U64 cursor_off); +internal void df_set_autocomp_lister_query(DF_Window *ws, UI_Key root_key, DF_CtrlCtx ctrl_ctx, DF_AutoCompListerParams *params, String8 input, U64 cursor_off); + +//////////////////////////////// +//~ rjf: Search Strings + +internal void df_set_search_string(String8 string); +internal String8 df_push_search_string(Arena *arena); + +//////////////////////////////// +//~ rjf: Colors, Fonts, Config + +//- rjf: keybindings +internal OS_Key df_os_key_from_cfg_string(String8 string); +internal void df_clear_bindings(void); +internal DF_BindingList df_bindings_from_spec(Arena *arena, DF_CmdSpec *spec); +internal void df_bind_spec(DF_CmdSpec *spec, DF_Binding binding); +internal void df_unbind_spec(DF_CmdSpec *spec, DF_Binding binding); +internal DF_CmdSpecList df_cmd_spec_list_from_binding(Arena *arena, DF_Binding binding); +internal DF_CmdSpecList df_cmd_spec_list_from_event_flags(Arena *arena, OS_EventFlags flags); + +//- rjf: colors +internal Vec4F32 df_rgba_from_theme_color(DF_ThemeColor color); +internal DF_ThemeColor df_theme_color_from_txt_token_kind(TXT_TokenKind kind); + +//- rjf: code -> palette +internal UI_Palette *df_palette_from_code(DF_Window *ws, DF_PaletteCode code); + +//- rjf: fonts/sizes +internal F_Tag df_font_from_slot(DF_FontSlot slot); +internal F32 df_font_size_from_slot(DF_Window *ws, DF_FontSlot slot); +internal F_RasterFlags df_raster_flags_from_slot(DF_Window *ws, DF_FontSlot slot); + +//- rjf: settings +internal DF_SettingVal df_setting_val_from_code(DF_Window *optional_window, DF_SettingCode code); + +//- rjf: config serialization +internal int df_qsort_compare__cfg_string_bindings(DF_StringBindingPair *a, DF_StringBindingPair *b); +internal String8List df_cfg_strings_from_gfx(Arena *arena, String8 root_path, DF_CfgSrc source); + +//////////////////////////////// +//~ rjf: Process Control Info Stringification + +internal String8 df_string_from_exception_code(U32 code); +internal String8 df_stop_explanation_string_icon_from_ctrl_event(Arena *arena, CTRL_Event *event, DF_IconKind *icon_out); + +//////////////////////////////// +//~ rjf: UI Building Helpers + +#define DF_Palette(ws, code) UI_Palette(df_palette_from_code((ws), (code))) +#define DF_Font(ws, slot) UI_Font(df_font_from_slot(slot)) UI_TextRasterFlags(df_raster_flags_from_slot((ws), (slot))) + +//////////////////////////////// +//~ rjf: UI Widgets: Fancy Buttons + +internal void df_cmd_binding_buttons(DF_Window *ws, DF_CmdSpec *spec); +internal UI_Signal df_menu_bar_button(String8 string); +internal UI_Signal df_cmd_spec_button(DF_Window *ws, DF_CmdSpec *spec); +internal void df_cmd_list_menu_buttons(DF_Window *ws, U64 count, DF_CoreCmdKind *cmds, U32 *fastpath_codepoints); +internal UI_Signal df_icon_button(DF_Window *ws, DF_IconKind kind, FuzzyMatchRangeList *matches, String8 string); +internal UI_Signal df_icon_buttonf(DF_Window *ws, DF_IconKind kind, FuzzyMatchRangeList *matches, char *fmt, ...); +internal void df_entity_tooltips(DF_Window *ws, DF_Entity *entity); +internal UI_Signal df_entity_desc_button(DF_Window *ws, DF_Entity *entity, FuzzyMatchRangeList *name_matches, String8 fuzzy_query, B32 is_implicit); +internal void df_entity_src_loc_button(DF_Window *ws, DF_Entity *entity, TxtPt point); + +//////////////////////////////// +//~ rjf: UI Widgets: Text View + +internal UI_BOX_CUSTOM_DRAW(df_thread_box_draw_extensions); +internal UI_BOX_CUSTOM_DRAW(df_bp_box_draw_extensions); +internal DF_CodeSliceSignal df_code_slice(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *preferred_column, String8 string); +internal DF_CodeSliceSignal df_code_slicef(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *preferred_column, char *fmt, ...); + +internal B32 df_do_txt_controls(TXT_TextInfo *info, String8 data, U64 line_count_per_page, TxtPt *cursor, TxtPt *mark, S64 *preferred_column); + +//////////////////////////////// +//~ rjf: UI Widgets: Fancy Labels + +internal UI_Signal df_error_label(String8 string); +internal B32 df_help_label(String8 string); +internal D_FancyStringList df_fancy_string_list_from_code_string(Arena *arena, F32 alpha, B32 indirection_size_change, Vec4F32 base_color, String8 string); +internal UI_Box *df_code_label(F32 alpha, B32 indirection_size_change, Vec4F32 base_color, String8 string); + +//////////////////////////////// +//~ rjf: UI Widgets: Line Edit + +internal UI_Signal df_line_edit(DF_Window *ws, DF_LineEditFlags flags, S32 depth, FuzzyMatchRangeList *matches, TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, B32 *expanded_out, String8 pre_edit_value, String8 string); +internal UI_Signal df_line_editf(DF_Window *ws, DF_LineEditFlags flags, S32 depth, FuzzyMatchRangeList *matches, TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, B32 *expanded_out, String8 pre_edit_value, char *fmt, ...); + +//////////////////////////////// +//~ rjf: Continuous Frame Requests + +internal void df_gfx_request_frame(void); + +//////////////////////////////// +//~ rjf: Main Layer Top-Level Calls + +internal void df_gfx_init(OS_WindowRepaintFunctionType *window_repaint_entry_point, DF_StateDeltaHistory *hist); +internal void df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds); +internal void df_gfx_end_frame(void); + +#endif // DF_GFX_H diff --git a/src/df/gfx/df_gfx.mdesk b/src/df/gfx/df_gfx.mdesk index fe6a258a..4a41b6fc 100644 --- a/src/df/gfx/df_gfx.mdesk +++ b/src/df/gfx/df_gfx.mdesk @@ -1,729 +1,729 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Embedded Data - -@embed_file df_g_icon_font_bytes: "../data/icons.ttf" -@embed_file df_g_default_main_font_bytes: "../data/Roboto-Regular.ttf" -@embed_file df_g_default_code_font_bytes: "../data/liberation-mono.ttf" -//@embed_file df_g_default_code_font_bytes: "../data/Inconsolata-Regular.ttf" -@embed_file df_g_icon_file_bytes: "../data/logo.ico" - -//////////////////////////////// -//~ rjf: Default Bindings - -@table(name key ctrl shift alt) -DF_DefaultBindingTable: -{ - //- rjf: low-level target control operations - { "kill_all" F5 0 shift 0 } - { "step_into_inst" F11 0 0 alt } - { "step_over_inst" F10 0 0 alt } - { "step_out" F11 0 shift 0 } - { "halt" X ctrl shift 0 } - { "halt" Pause 0 0 0 } - { "soft_halt_refresh" R 0 0 alt } - - //- rjf: high-level composite target control operations - { "run" F5 0 0 0 } - { "restart" F5 ctrl shift 0 } - { "step_into" F11 0 0 0 } - { "step_over" F10 0 0 0 } - { "run_to_cursor" F10 ctrl 0 0 } - { "set_next_statement" F10 ctrl shift 0 } - - //- rjf: font sizes - { "inc_ui_font_scale" Equal 0 0 alt } - { "dec_ui_font_scale" Minus 0 0 alt } - { "inc_code_font_scale" Equal 0 shift alt } - { "dec_code_font_scale" Minus 0 shift alt } - - //- rjf: windows - { "window" N ctrl shift 0 } - { "toggle_fullscreen" Return ctrl 0 0 } - - //- rjf: panel splitting - { "new_panel_right" P ctrl 0 0 } - { "new_panel_down" Minus ctrl 0 0 } - - //- rjf: panel rotation - { "rotate_panel_columns" 2 ctrl 0 0 } - - //- rjf: focused panel changing - { "next_panel" Comma ctrl 0 0 } - { "prev_panel" Comma ctrl shift 0 } - { "focus_panel_right" Right ctrl 0 alt } - { "focus_panel_left" Left ctrl 0 alt } - { "focus_panel_up" Up ctrl 0 alt } - { "focus_panel_down" Down ctrl 0 alt } - - //- rjf: undo/redo - //{ "undo" Z ctrl 0 0 } - //{ "redo" Y ctrl 0 0 } - - //- rjf: focus history - //{ "go_back" Left 0 0 alt } - //{ "go_forward" Right 0 0 alt } - - //- rjf: panel removal - { "close_panel" P ctrl shift 0 } - - //- rjf: panel tab - { "next_tab" PageDown ctrl 0 0 } - { "prev_tab" PageUp ctrl 0 0 } - { "next_tab" Tab ctrl 0 0 } - { "prev_tab" Tab ctrl shift 0 } - { "move_tab_right" PageDown ctrl shift 0 } - { "move_tab_left" PageUp ctrl shift 0 } - { "close_tab" W ctrl 0 0 } - { "tab_bar_top" Up ctrl shift alt } - { "tab_bar_bottom" Down ctrl shift alt } - - //- rjf: files - { "open" O ctrl 0 0 } - { "reload_active" R ctrl shift 0 } - { "switch" I ctrl 0 0 } - { "switch_to_partner_file" O 0 0 alt } - - //- rjf: setting config paths - { "open_user" O ctrl shift alt } - { "open_project" O ctrl 0 alt } - - //- rjf: meta controls - { "edit" F2 0 0 0 } - { "accept" Return 0 0 0 } - { "cancel" Esc 0 0 0 } - - //- rjf: directional movement & text controls - { "move_left" Left 0 0 0 } - { "move_right" Right 0 0 0 } - { "move_up" Up 0 0 0 } - { "move_down" Down 0 0 0 } - { "move_left_select" Left 0 shift 0 } - { "move_right_select" Right 0 shift 0 } - { "move_up_select" Up 0 shift 0 } - { "move_down_select" Down 0 shift 0 } - { "move_left_chunk" Left ctrl 0 0 } - { "move_right_chunk" Right ctrl 0 0 } - { "move_up_chunk" Up ctrl 0 0 } - { "move_down_chunk" Down ctrl 0 0 } - { "move_up_page" PageUp 0 0 0 } - { "move_down_page" PageDown 0 0 0 } - { "move_up_whole" Home ctrl 0 0 } - { "move_down_whole" End ctrl 0 0 } - { "move_left_chunk_select" Left ctrl shift 0 } - { "move_right_chunk_select" Right ctrl shift 0 } - { "move_up_chunk_select" Up ctrl shift 0 } - { "move_down_chunk_select" Down ctrl shift 0 } - { "move_up_page_select" PageUp 0 shift 0 } - { "move_down_page_select" PageDown 0 shift 0 } - { "move_up_whole_select" Home ctrl shift 0 } - { "move_down_whole_select" End ctrl shift 0 } - { "move_up_reorder" Up 0 0 alt } - { "move_down_reorder" Down 0 0 alt } - { "move_home" Home 0 0 0 } - { "move_end" End 0 0 0 } - { "move_home_select" Home 0 shift 0 } - { "move_end_select" End 0 shift 0 } - { "select_all" A ctrl 0 0 } - { "delete_single" Delete 0 0 0 } - { "delete_chunk" Delete ctrl 0 0 } - { "backspace_single" Backspace 0 0 0 } - { "backspace_chunk" Backspace ctrl 0 0 } - { "copy" C ctrl 0 0 } - { "copy" Insert ctrl 0 0 } - { "cut" X ctrl 0 0 } - { "paste" V ctrl 0 0 } - { "paste" Insert 0 shift 0 } - { "insert_text" Null 0 0 0 } - - //- rjf: code navigation - { "goto_line" G ctrl 0 0 } - { "goto_address" G 0 0 alt } - { "find_text_forward" F ctrl 0 0 } - { "find_text_backward" R ctrl 0 0 } - { "find_next" F3 0 0 0 } - { "find_prev" F3 shift 0 0 } - - //- rjf: thread finding - { "find_selected_thread" F4 0 0 0 } - - //- rjf: name finding - { "goto_name" J ctrl 0 0 } - { "goto_name_at_cursor" F12 0 0 0 } - - //- rjf: watch expressions - { "toggle_watch_expr_at_cursor" W 0 0 alt } - { "toggle_watch_expr_at_mouse" D ctrl 0 0 } - { "toggle_watch_pin_at_cursor" F9 ctrl 0 0 } - - //- rjf: breakpoints - { "toggle_breakpoint_cursor" F9 0 0 0 } - - //- rjf: targets - { "add_target" T ctrl 0 0 } - - //- rjf: attaching - { "attach" F6 0 shift 0 } - - //- rjf: filtering - { "filter" Slash ctrl 0 0 } - - //- rjf: command lister - { "run_command" F1 0 0 0 } - - //- rjf: developer commands - { "log_marker" M ctrl shift alt } -} - -@data(DF_StringBindingPair) df_g_default_binding_table: -{ - @expand(DF_DefaultBindingTable a) ```{str8_lit_comp("$(a.name)"), {OS_Key_$(a.key), 0 $(a.ctrl != 0 -> `|OS_EventFlag_Ctrl`) $(a.shift != 0 -> `|OS_EventFlag_Shift`) $(a.alt != 0 -> `|OS_EventFlag_Alt`)}}```; -} - -//////////////////////////////// -//~ rjf: Binding Version Remap Table - -@table(old_name new_name) -DF_BindingVersionRemapTable: -{ - {"commands" "run_command"} - {"load_user" "open_user"} - {"load_profile" "open_profile"} - {"load_project" "open_project"} - {"open_profile" "open_project"} -} - -@data(String8) df_g_binding_version_remap_old_name_table: -{ - @expand(DF_BindingVersionRemapTable a) `str8_lit_comp("$(a.old_name)")` -} - -@data(String8) df_g_binding_version_remap_new_name_table: -{ - @expand(DF_BindingVersionRemapTable a) `str8_lit_comp("$(a.new_name)")` -} - -//////////////////////////////// -//~ rjf: Gfx Layer View Kinds - -@table(name, name_lower, display_string, name_kind, icon, parameterized_by_entity, project_specific, can_serialize, can_serialize_entity_path, can_filter, filter_is_code, typing_automatically_filters, inc_in_docs, docs_desc) -DF_GfxViewTable: -{ - { Null "null" "" Null Null 0 0 0 0 0 0 0 0 "" } - { Empty "empty" "" Null Null 0 0 0 0 0 0 0 0 "" } - { GettingStarted "getting_started" "Getting Started" Null QuestionMark 0 0 1 0 0 0 0 0 "" } - { Commands "commands" "Commands" Null List 0 0 0 0 0 0 0 0 "" } - { FileSystem "file_system" "File System" Null FileOutline 0 0 0 0 0 0 0 0 "" } - { SystemProcesses "system_processes" "System Processes" Null Null 0 0 0 0 0 0 0 0 "" } - { EntityLister "entity_lister" "Entity List" Null Null 0 0 0 0 0 0 0 0 "" } - { SymbolLister "symbol_lister" "Symbols" Null Null 0 0 0 0 0 0 0 0 "" } - { Target "target" "Target" EntityName Target 1 0 0 0 0 0 0 0 "" } - { Targets "targets" "Targets" Null Target 0 0 1 0 1 0 1 1 "Displays a list of all targets, as well as controls for enabling, disabling, launching, editing, or deleting each target. For more information on targets, read the `Targets` section." } - { FilePathMap "file_path_map" "File Path Map" Null FileOutline 0 0 1 0 0 0 0 1 "Displays a table of *path maps*. Each path map is a pair of file or folder paths, one being a 'source' path, and one being a 'destination' path. These pairs are used by the debugger when automatically searching for specific files - for instance, when attempting to snap to a source code location specified by debug info. If debug info refers to a path on the machine on which a target executable was originally built, but that path is not valid on the debugger machine, but some alternative path exists, then path maps may be used to redirect the debugger from the debug info's specified paths to the associated appropriate debugger machine file paths." } - { AutoViewRules "auto_view_rules" "Auto View Rules" Null Binoculars 0 0 1 0 0 0 0 1 "Displays a table of *auto view rules*. Each *auto view rule* is a pair, with one element being a type, and the other being a view rule, which should be automatically applied to expressions of that type, when possible." } - { Scheduler "scheduler" "Scheduler" Null Scheduler 0 0 1 0 1 1 1 1 "Displays all processes and threads to which the debugger is currently attached, and contains controls for selecting and freezing threads." } - { CallStack "call_stack" "Call Stack" Null Thread 0 0 1 0 0 0 0 1 "Displays the call stack of the currently selected thread. Each frame in the call stack contains the associated module, function name, and return address. Allows selection of a particular call stack frame other than the top." } - { Modules "modules" "Modules" Null Module 0 0 1 0 1 0 1 1 "Displays a table of all modules currently loaded by any process to which the debugger is attached. This table displays each module's name, virtual address range in the containing process' address space, and which debug info file is being used by the debugger for the associated module." } - { PendingEntity "pending_entity" "Pending Entity" EntityName FileOutline 1 0 0 0 0 0 0 0 "" } - { Code "code" "Code" EntityName FileOutline 1 1 1 1 0 0 0 0 "" } - { Disassembly "disassembly" "Disassembly" Null Glasses 0 0 1 0 0 0 0 1 "Displays disassembled instructions in a textual form from the selected thread's containing process virtual address space." } - { Watch "watch" "Watch" Null Binoculars 0 0 1 0 1 1 1 1 "The familiar 'watch window' debugger interface. Allows the inputting of a number of expressions. Each expression in the table is evaluated within the context of the selected thread's selected call stack frame. If applicable (depending on visualization rules and the expression's type), these expressions may be hierarchically expanded, which displays children as more rows in the table. The values of these expressions may also be edited, and if possible, can be used to write to registers or memory in attached processes. Also contains a new *view rule* column, not found in other major debuggers, which allows per-row specification of various visualization rules. These view rules may be used to visualize and inspect the evaluation of expressions in a variety of ways. To learn more, read the 'View Rules' section." } - { Locals "locals" "Locals" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with local variables found within the selected call stack frame of the selected thread, according to the associated debug info. View rules and evaluation values can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } - { Registers "registers" "Registers" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all register names according to the selected thread's architecture. View rules and evaluation values can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } - { Globals "globals" "Globals" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all global variables within the selected thread's module. View rules and evaluation values can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } - { ThreadLocals "thread_locals" "Thread Locals" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all thread local variables within the selected thread's module. View rules and evaluation values can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } - { Types "types" "Types" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all types within the selected thread's module. View rules can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } - { Procedures "procedures" "Procedures" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all procedures within the selected thread's module. View rules can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } - { Output "output" "Output" Null List 0 0 1 0 0 0 0 1 "Displays textual output from the selected thread's containing process." } - { Memory "memory" "Memory" Null Grid 0 0 1 0 0 0 0 1 "A familiar hex-editor-like interface for viewing memory of attached processes." } - { Breakpoints "breakpoints" "Breakpoints" Null CircleFilled 0 0 1 0 1 0 1 1 "Displays a table of all breakpoints, containing information about each breakpoint's name, location, and hit count. Also contains per-breakpoint controls for enabling, deleting, or editing each breakpoint. For more information on breakpoints and their features, read the 'Breakpoints' section." } - { WatchPins "watch_pins" "Watch Pins" Null Pin 0 0 1 0 1 1 1 1 "Displays a table of all watch pins (watched expressions, like those found in `Watch`, but instead of being within a table, being pinned to some source code location, like breakpoints). This table contains each pin's name, location, and controls for editing or deleting each pin." } - { ExceptionFilters "exception_filters" "Exception Filters" Null Gear 0 0 1 0 1 0 1 1 "An interface which controls whether or not the debugger will halt attached processes upon encountering specific exception codes for the first time." } - { Settings "settings" "Settings" Null Gear 0 0 1 0 1 0 1 1 "An interface to modify general settings for the debugger's appearance and behavior." } -} - -@enum DF_GfxViewKind: -{ - @expand(DF_GfxViewTable a) `$(a.name)`, - COUNT, -} - -@gen -{ - @expand(DF_GfxViewTable a) `DF_VIEW_SETUP_FUNCTION_DEF($(a.name));`; - @expand(DF_GfxViewTable a) `DF_VIEW_STRING_FROM_STATE_FUNCTION_DEF($(a.name));`; - @expand(DF_GfxViewTable a) `DF_VIEW_CMD_FUNCTION_DEF($(a.name));`; - @expand(DF_GfxViewTable a) `DF_VIEW_UI_FUNCTION_DEF($(a.name));`; -} - -@data(DF_ViewSpecInfo) df_g_gfx_view_kind_spec_info_table: -{ - @expand(DF_GfxViewTable a) ```{(0|$(a.parameterized_by_entity)*DF_ViewSpecFlag_ParameterizedByEntity|$(a.project_specific)*DF_ViewSpecFlag_ProjectSpecific|$(a.can_serialize)*DF_ViewSpecFlag_CanSerialize|$(a.can_serialize_entity_path)*DF_ViewSpecFlag_CanSerializeEntityPath|$(a.can_filter)*DF_ViewSpecFlag_CanFilter|$(a.filter_is_code)*DF_ViewSpecFlag_FilterIsCode|$(a.typing_automatically_filters)*DF_ViewSpecFlag_TypingAutomaticallyFilters), str8_lit_comp("$(a.name_lower)"), str8_lit_comp("$(a.display_string)"), DF_NameKind_$(a.name_kind), DF_IconKind_$(a.icon), DF_VIEW_SETUP_FUNCTION_NAME($(a.name)), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME($(a.name)), DF_VIEW_CMD_FUNCTION_NAME($(a.name)), DF_VIEW_UI_FUNCTION_NAME($(a.name))}```; -} - -//////////////////////////////// -//~ rjf: Command Parameter Slot -> View - -@table(slot view_spec opt_cmd_spec) -DF_CmdParamSlot2ViewSpecMap: -{ - {Entity "entity_lister" "" } - {EntityList "entity_lister" "" } - {FilePath "file_system" "" } - {CmdSpec "commands" "" } - {ID "system_processes" "" } - {String "symbol_lister" "goto_name" } - {String "symbol_lister" "function_breakpoint" } -} - -@data(DF_CmdParamSlot) df_g_cmd_param_slot_2_view_spec_src_map: -{ - @expand(DF_CmdParamSlot2ViewSpecMap a) `DF_CmdParamSlot_$(a.slot)` -} - -@data(String8) df_g_cmd_param_slot_2_view_spec_dst_map: -{ - @expand(DF_CmdParamSlot2ViewSpecMap a) `str8_lit_comp("$(a.view_spec)")` -} - -@data(String8) df_g_cmd_param_slot_2_view_spec_cmd_map: -{ - @expand(DF_CmdParamSlot2ViewSpecMap a) `str8_lit_comp("$(a.opt_cmd_spec)")` -} - -//////////////////////////////// -//~ rjf: Built-In Graphical View Rule Extensions -// -// NOTE(rjf): see @view_rule_info - -@table(string vr ls ru bu tu tab_display_string) -DF_GfxViewRuleTable: -{ - {"array" - - - - - "" } - {"list" x - - - - "" } - {"dec" - x - - - "" } - {"bin" - x - - - "" } - {"oct" - x - - - "" } - {"hex" - x - - - "" } - {"only" x x - - - "" } - {"omit" x x - - - "" } - {"no_addr" - x - - - "" } - {"rgba" - - x x - "" } - {"text" - - - x x "Text" } - {"disasm" - - - x x "Disassembly" } - {"bitmap" - - x x x "Bitmap" } - {"geo" - - x x x "Geometry" } -} - -@gen -{ - ``; - @expand(DF_GfxViewRuleTable a) - `$(a.vr == "x" -> "DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_DEF(" .. a.name_lower .. ");")`; - @expand(DF_GfxViewRuleTable a) - `$(a.ls == "x" -> "DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_DEF(" .. a.name_lower .. ");")`; - @expand(DF_GfxViewRuleTable a) - `$(a.ru == "x" -> "DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_DEF(" .. a.name_lower .. ");")`; - @expand(DF_GfxViewRuleTable a) - `$(a.bu == "x" -> "DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_DEF(" .. a.name_lower .. ");")`; - @expand(DF_GfxViewRuleTable a) - `$(a.tu == "x" -> "DF_VIEW_SETUP_FUNCTION_DEF(" .. a.name_lower .. ");")`; - @expand(DF_GfxViewRuleTable a) - `$(a.tu == "x" -> "DF_VIEW_STRING_FROM_STATE_FUNCTION_DEF(" .. a.name_lower .. ");")`; - @expand(DF_GfxViewRuleTable a) - `$(a.tu == "x" -> "DF_VIEW_CMD_FUNCTION_DEF(" .. a.name_lower .. ");")`; - @expand(DF_GfxViewRuleTable a) - `$(a.tu == "x" -> "DF_VIEW_UI_FUNCTION_DEF(" .. a.name_lower .. ");")`; -} - -@data(DF_ViewSpecInfo) @c_file df_g_gfx_view_rule_tab_view_spec_info_table: -{ - @expand(DF_GfxViewRuleTable a) - ```$(a.tu == "x" -> '{ DF_ViewSpecFlag_CanSerialize|DF_ViewSpecFlag_CanSerializeQuery, str8_lit_comp("' .. a.string .. '_view_rule"), str8_lit_comp("' .. a.tab_display_string .. '"), DF_NameKind_Null, DF_IconKind_Binoculars, ' .. 'DF_VIEW_SETUP_FUNCTION_NAME(' .. a.string .. '), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(' .. a.string .. '), DF_VIEW_CMD_FUNCTION_NAME(' .. a.string .. '), DF_VIEW_UI_FUNCTION_NAME(' .. a.string .. ') }')```; -} - -@data(DF_GfxViewRuleSpecInfo) @c_file df_g_gfx_view_rule_spec_info_table: -{ - @expand(DF_GfxViewRuleTable a) - ```{ str8_lit_comp("$(a.string)"), (DF_GfxViewRuleSpecInfoFlag_VizRowProd*$(a.vr == "x"))|(DF_GfxViewRuleSpecInfoFlag_LineStringize*$(a.ls == "x"))|(DF_GfxViewRuleSpecInfoFlag_RowUI*$(a.ru == "x"))|(DF_GfxViewRuleSpecInfoFlag_BlockUI*$(a.bu == "x")), $(a.vr == "x" -> "DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_NAME("..a.name_lower..")") $(a.vr != "x" -> 0), $(a.ls == "x" -> "DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_NAME("..a.name_lower..")") $(a.ls != "x" -> 0), $(a.ru == "x" -> "DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_NAME("..a.name_lower..")") $(a.ru != "x" -> 0), $(a.bu == "x" -> "DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_NAME("..a.name_lower..")") $(a.bu != "x" -> 0), str8_lit_comp("$(a.tu == 'x' -> a.string..'_view_rule')") }```; -} - -//////////////////////////////// -//~ rjf: Theme Tables - -@table(name_upper name_lower display_string) -DF_ThemePresetTable: -{ - { DefaultDark default_dark "Default (Dark)" } - { DefaultLight default_light "Default (Light)" } - { VSDark vs_dark "VS (Dark)" } - { VSLight vs_light "VS (Light)" } - { SolarizedDark solarized_dark "Solarized (Dark)" } - { SolarizedLight solarized_light "Solarized (Light)" } - { HandmadeHero handmade_hero "Handmade Hero" } - { FourCoder four_coder "4coder" } - { FarManager far_manager "Far Manager" } -} - -@table(name display_name name_lower default_dark default_light vs_dark vs_light solarized_dark solarized_light handmade_hero four_coder far_manager desc) -DF_ThemeColorTable: -{ - {Null "Null" null 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff ""} - - //- rjf: global ui colors - {Text "Text" text 0xe5e5e5ff 0x4c4c4cff 0xe5e5e5ff 0x000000ff 0x999999ff 0x333333ff 0xa08462ff 0x90b080ff 0x00fefeff ""} - {TextPositive "Text (Positive)" text_positive 0x4dc221ff 0x4d9e2eff 0x4dc221ff 0x4dc221ff 0x4dc221ff 0x4dc221ff 0x4dc221ff 0x4dc221ff 0x4dc221ff ""} - {TextNegative "Text (Negative)" text_negative 0xc56452ff 0xbd371eff 0xc56452ff 0xc46451ff 0xc56452ff 0xc56452ff 0xc56452ff 0xc56452ff 0xc56452ff ""} - {TextNeutral "Text (Neutral)" text_neutral 0x307eb2ff 0x0064a7ff 0x307eb2ff 0x307eb2ff 0x307eb2ff 0x307eb2ff 0x307eb2ff 0x307eb2ff 0x307eb2ff ""} - {TextWeak "Text (Weak)" text_weak 0xa4a4a4fe 0x4c4c4cff 0xa4a4a4fe 0x0000007f 0x9999998a 0x818181ff 0x6e512eff 0x566e4bff 0x00a9a9ff ""} - {Cursor "Cursor" cursor 0x8aff00ff 0x699830ff 0x8aff00ff 0x000000ff 0x8aff00ff 0x586e75ff 0x8aff00ff 0x8aff00ff 0x8aff00ff ""} - {CursorInactive "Cursor (Inactive)" cursor_inactive 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff ""} - {Focus "Focus" focus 0xfda200ff 0x9c5900ff 0xfda200ff 0x002affff 0xfda200ff 0x92743dff 0xfda200ff 0xfda200ff 0x00fefeff ""} - {Hover "Hover" hover 0xffffffff 0xffffffff 0xffffffff 0x000000ff 0xffffffff 0x747474ff 0xffffffff 0xffffffff 0xffffffff ""} - {DropShadow "Drop Shadow" drop_shadow 0x0000007f 0x0000004c 0x0000007f 0xa3a3a37e 0x0000007f 0xc9bfa394 0x0000007f 0x0000007f 0x0000007f ""} - {DisabledOverlay "Disabled Overlay" disabled_overlay 0x0000003f 0xa6a6a63f 0x0000003f 0x0000003f 0x0000003f 0xe4dac090 0x0000003f 0x0000003f 0x0000003f ""} - {DropSiteOverlay "Drop Site Overlay" drop_site_overlay 0xffffff0c 0x4848480c 0xffffff0c 0x0000000c 0xffffff0c 0xffffff0c 0xffffff0c 0xffffff0c 0xffffff0c ""} - {InactivePanelOverlay "Inactive Panel Overlay" inactive_panel_overlay 0x0000003f 0xa4a4a43f 0x0000003f 0xfefefe53 0x0000003f 0x0000001c 0x0000003f 0x0000003f 0x0000003f ""} - {SelectionOverlay "Selection Overlay" selection_overlay 0x99ccff4c 0x003d7a48 0x99ccff4c 0x3d74ab4b 0x99ccff4c 0x678cb24c 0x99ccff4c 0x99ccff4c 0x99ccff4c ""} - {HighlightOverlay "Highlight Overlay" highlight_overlay 0xffffff1e 0xffffff1e 0xffffff1e 0x0000001e 0xffffff1e 0xffffff1e 0xffffff1e 0xffffff1e 0xffffff1e ""} - {HighlightOverlayError "Error Highlight Overlay" error_highlight_overlay 0x5f12005f 0xff30005f 0x5f12005f 0x5f12005f 0x5f12005f 0x5f12005f 0x5f12005f 0x5f12005f 0x5f12005f ""} - - //- rjf: base ui container colors - {BaseBackground "Base Background" base_background 0x1b1b1bfe 0xccccccfe 0x1b1b1bfe 0xfefefefe 0x002a35fe 0xfcf5e2fe 0x0c0c0cfe 0x0c0c0cfe 0x000081fe ""} - {BaseBackgroundAlt "Base Background (Alternate)" base_background_alt 0x2b2b2bfe 0x2b2b2bfe 0x1b1b1bfe 0xe7e7e7fe 0x2b2b2bfe 0x2b2b2bfe 0x2b2b2bfe 0x2b2b2bfe 0x2b2b2bfe ""} - {BaseBorder "Base Border" base_border 0x3f3f3ffe 0xa4a4a4fe 0x3f3f3ffe 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0x423525fe 0x3f3f3ffe 0x0000fffe ""} - - //- rjf: menu bar ui container colors - {MenuBarBackground "Menu Bar Background" menu_bar_background 0x3e4c577f 0xeaeaea7f 0x1b1b1bfd 0xffffff7f 0x00202bff 0xeee8d5ff 0x0c0c0cfe 0x0c0c0cfe 0x007d7dff ""} - {MenuBarBackgroundAlt "Menu Bar Background (Alternate)" menu_bar_background_alt 0x3e4c577f 0x3e4c577f 0x1b1b1bfd 0xffffff7f 0x3e4c577f 0x3e4c577f 0x3e4c577f 0x3e4c577f 0x007d7dff ""} - {MenuBarBorder "Menu Bar Border" menu_bar_border 0xffffff19 0xa4a4a4fe 0x3f3f3ffe 0xb6b6b6ff 0xffffff19 0xbebaabfe 0xffffff19 0xffffff19 0xfefefe00 ""} - - //- rjf: floating ui container colors - {FloatingBackground "Floating Background" floating_background 0x33333333 0xccccccc0 0x33333333 0xfefefec7 0x007fa14e 0xffffff7c 0x0c0c0c32 0x0c0c0c3e 0x007c7c55 ""} - {FloatingBackgroundAlt "Floating Background (Alternate)" floating_background_alt 0x33333333 0x33333333 0x33333333 0x33333333 0x33333333 0x33333333 0x33333333 0x33333333 0x33333333 ""} - {FloatingBorder "Floating Border" floating_border 0x3f3f3ffd 0xa4a4a4fe 0x3f3f3ffd 0xb6b6b6ff 0xfdfdfd3a 0xbebaabfe 0x423425fe 0x3f3f3ffd 0x00ffff55 ""} - - //- rjf: ui element colors - {ImplicitButtonBackground "Implicit Button Background" implicit_button_background 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 ""} - {ImplicitButtonBorder "Implicit Button Border" implicit_button_border 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0xbdb9aa00 0x00000000 0x00000000 0x00000000 ""} - {PlainButtonBackground "Plain Button Background" plain_button_background 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe ""} - {PlainButtonBorder "Plain Button Border" plain_button_border 0x3f3f3ffe 0x3f3f3ffe 0x3f3f3ffe 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0x3f3f3ffe 0x3f3f3ffe 0x3f3f3ffe ""} - {PositivePopButtonBackground "Positive Pop Button Background" positive_pop_button_background 0x2c5b36ff 0x65f534ff 0x2c5b36ff 0x84ce93ff 0x2c5b36ff 0xb6ddbeff 0x132e19ff 0x152f1bff 0x2c5b36ff ""} - {PositivePopButtonBorder "Positive Pop Button Border" positive_pop_button_border 0x3f3f3ffd 0x3f3f3ffd 0x3f3f3ffd 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0x3f3f3ffd 0x3f3f3ffd 0x3f3f3ffd ""} - {NegativePopButtonBackground "Negative Pop Button Background" negative_pop_button_background 0x803425ff 0xff694cff 0x803425ff 0xbd3e24ff 0x803425ff 0xf8b0a1ff 0x803425ff 0x43150cff 0x803425ff ""} - {NegativePopButtonBorder "Negative Pop Button Border" negative_pop_button_border 0x3f3f3ffd 0x3f3f3ffd 0x3f3f3ffd 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0x3f3f3ffd 0x3f3f3ffd 0x3f3f3ffd ""} - {NeutralPopButtonBackground "Neutral Pop Button Background" neutral_pop_button_background 0x355b6eff 0xa6becaff 0x355b6eff 0x6e9db5ff 0x355b6eff 0xb2d3e3ff 0x15445cff 0x1b323eff 0x933100ff ""} - {NeutralPopButtonBorder "Neutral Pop Button Border" neutral_pop_button_border 0x3f3f3ffd 0xa6a6a6fd 0x3f3f3ffd 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0x3f3f3ffd 0x3f3f3ffd 0x3f3f3ffd ""} - {ScrollBarButtonBackground "Scroll Bar Button Background" scroll_bar_button_background 0x2b2b2bfe 0xa9a9a9fe 0x2b2b2bfe 0xe8e8e8fe 0x005e77fe 0xe3dbc7fe 0x1f1f27fe 0x212721fe 0x007d7dff ""} - {ScrollBarButtonBorder "Scroll Bar Button Border" scroll_bar_button_border 0x3f3f3ffe 0xc0c0c0fe 0x3f3f3ffe 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0xfefefe4d 0x3f3f3ffe 0x3f3f3ffe ""} - {TabBackground "Tab Background" tab_background 0x6f5135fe 0xa98b6fff 0x0079ccff 0xfffffffe 0x005e77fe 0xfdf6e3ff 0x1f1f27fe 0x212721fe 0x007d7dff ""} - {TabBorder "Tab Border" tab_border 0xfefefe4d 0xffffff4d 0xfefefe4d 0xb6b6b6ff 0xfefefe4d 0xbebaabfe 0xfefefe4d 0xfefefe4d 0xfefefe4d ""} - {TabBackgroundInactive "Tab Background (Inactive)" tab_background_inactive 0x3e4c577f 0x8282827f 0xfefefe14 0xcdd4dc7f 0x3e4c577f 0xd4cfc0fe 0x131315ee 0x3a3a3a7f 0x3e4c577f ""} - {TabBorderInactive "Tab Border (Inactive)" tab_border_inactive 0xffffff19 0xffffff19 0xffffff00 0xb6b6b6ff 0xffffff19 0xbebaabfe 0xffffff19 0x00000019 0xfefefe19 ""} - - //- rjf: code colors - {CodeDefault "Code (Default)" code_default 0xcbcbcbff 0x4d4d4dff 0xcbcbcbff 0x000000ff 0xcbcbcbff 0x657b83ff 0xa08462ff 0x90b080ff 0x00fefeff ""} - {CodeSymbol "Code (Symbol)" code_symbol 0x42a2cffe 0x205670fe 0xdcdcaaff 0x000000ff 0xcb4a15ff 0xcb4a15ff 0xcc5634ff 0x42a2cffe 0x65b1ffff ""} - {CodeType "Code (Type)" code_type 0xfec746ff 0x996b00ff 0x4ec9afff 0xa33700ff 0xcb4a15ff 0xcb4a15ff 0xd8a51bff 0xfd7c52ff 0xfec746ff ""} - {CodeLocal "Code (Local)" code_local 0x98bc80ff 0x446a2bff 0x9cdbfeff 0x007666ff 0x98bc80ff 0x258ad2ff 0xc04047ff 0x98bc80ff 0x00ff00ff ""} - {CodeRegister "Code (Register)" code_register 0xb7afd5ff 0x4c35a1ff 0xb7afd5ff 0xb7afd5ff 0xb7afd5ff 0x373345ff 0xb7afd5ff 0xb7afd5ff 0xb7afd5ff ""} - {CodeKeyword "Code (Keyword)" code_keyword 0xb38d4cff 0x573700ff 0x569cd6ff 0x0000ffff 0x849803ff 0x586e75ff 0xac7a09ff 0xd08f1eff 0x00ffffff ""} - {CodeDelimiterOperator "Code (Delimiters/Operators)" code_delimiter_operator 0x767676ff 0x767676ff 0x767676ff 0x767676ff 0x767676ff 0x767676ff 0xa08462ff 0x90b080ff 0xffffffff ""} - {CodeNumeric "Code (Numeric)" code_numeric 0x98abb1ff 0x3f6e7dff 0xb5cea8ff 0x088658ff 0xd33582ff 0xd33482ef 0x698e21ff 0x4fff2eff 0x00ff00ff ""} - {CodeNumericAltDigitGroup "Code (Numeric, Alt. Digit Group)" code_numeric_alt_digit_group 0x738287ff 0x1f4450ff 0x729360ff 0x0c3828ff 0x902559ff 0x8e2659ff 0x3a4e11ff 0x3ccd21ff 0x738287ff ""} - {CodeString "Code (String)" code_string 0x98abb1ff 0x3c606bff 0xd59b85ff 0xa31414ff 0x1f9d91ff 0x29a198ff 0x6a8e22ff 0x4fff2eff 0x98abb1ff ""} - {CodeMeta "Code (Meta)" code_meta 0xd96759ff 0xad3627ff 0xd59c85ff 0x0000ffff 0x839802ff 0xd96759ff 0xdab98fff 0xa0b8a0ff 0xff0000ff ""} - {CodeComment "Code (Comment)" code_comment 0x717171ff 0x4b4b4bff 0x57a54aff 0x008000ff 0x556a6fff 0x93a1a1ff 0x686868ff 0x1e8fefff 0xffffffff ""} - {CodeLineNumbers "Code Line Numbers" code_line_numbers 0x7f7f7fff 0x4b4b4bff 0x2a91afff 0x227893ff 0x566c73ff 0x227893ef 0xa08462ff 0x7e7e7ffe 0x007d7dff ""} - {CodeLineNumbersSelected "Code Line Numbers (Selected)" code_line_numbers_selected 0xbebebeff 0x000000ff 0x9ddaecff 0x123d4bfe 0xa2aaacff 0x111e22ef 0xc8b399ff 0xbebebeff 0x00fefeff ""} - - //- rjf: debugging colors - {LineInfoBackground0 "Line Info Background 0" line_info_background_0 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f ""} - {LineInfoBackground1 "Line Info Background 1" line_info_background_1 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f ""} - {LineInfoBackground2 "Line Info Background 2" line_info_background_2 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f ""} - {LineInfoBackground3 "Line Info Background 3" line_info_background_3 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f ""} - {LineInfoBackground4 "Line Info Background 4" line_info_background_4 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f ""} - {LineInfoBackground5 "Line Info Background 5" line_info_background_5 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f ""} - {LineInfoBackground6 "Line Info Background 6" line_info_background_6 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f ""} - {LineInfoBackground7 "Line Info Background 7" line_info_background_7 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f ""} - {Thread0 "Thread 0" thread_0 0xffcb7fff 0x945800ff 0xffcb7fff 0x945800ff 0xffcb7fff 0x945800ff 0xffcb7fff 0xffcb7fff 0xffcb7fff ""} - {Thread1 "Thread 1" thread_1 0xb2ff65ff 0x3f5b23ff 0xb2ff65ff 0x3f5b23ff 0xb2ff65ff 0x3f5b23ff 0xb2ff65ff 0xb2ff65ff 0xb2ff65ff ""} - {Thread2 "Thread 2" thread_2 0xff99e5ff 0x642a55ff 0xff99e5ff 0x642a55ff 0xff99e5ff 0x642a55ff 0xff99e5ff 0xff99e5ff 0xff99e5ff ""} - {Thread3 "Thread 3" thread_3 0x6598ffff 0x30456fff 0x6598ffff 0x30456fff 0x6598ffff 0x30456fff 0x6598ffff 0x6598ffff 0x6598ffff ""} - {Thread4 "Thread 4" thread_4 0x65ffcbff 0x264f41ff 0x65ffcbff 0x264f41ff 0x65ffcbff 0x264f41ff 0x65ffcbff 0x65ffcbff 0x65ffcbff ""} - {Thread5 "Thread 5" thread_5 0xff9819ff 0x736a5fff 0xff9819ff 0x736a5fff 0xff9819ff 0x736a5fff 0xff9819ff 0xff9819ff 0xff9819ff ""} - {Thread6 "Thread 6" thread_6 0x9932ffff 0x472f5eff 0x9932ffff 0x472f5eff 0x9932ffff 0x472f5eff 0x9932ffff 0x9932ffff 0x9932ffff ""} - {Thread7 "Thread 7" thread_7 0x65ff4cff 0x405d3bff 0x65ff4cff 0x405d3bff 0x65ff4cff 0x405d3bff 0x65ff4cff 0x65ff4cff 0x65ff4cff ""} - {ThreadUnwound "Thread (Unwound)" thread_unwound 0xb2ccd8ff 0x49606aff 0xb2ccd8ff 0x49606aff 0xb2ccd8ff 0x49606aff 0xb2ccd8ff 0xb2ccd8ff 0xb2ccd8ff ""} - {ThreadError "Thread (Error)" thread_error 0xb23219ff 0xb23219ff 0xb23219ff 0xb23219ff 0xb23219ff 0xb23218ff 0xb23219ff 0xb23219ff 0xb23219ff ""} - {Breakpoint "Breakpoint" breakpoint 0xa72911ff 0xff2800ff 0xa72911ff 0xa72911ff 0xa72911ff 0xff684bff 0xa72911ff 0xa72911ff 0xff2800ff ""} -} - -@table(old_name new_name) -DF_ThemeColorVersionRemapTable: -{ - {plain_text text} - {plain_background base_background} - {plain_border base_border} - {plain_overlay drop_site_overlay} - {code_function code_symbol} - {code_symbol code_delimiter_operator} - {code_numeric code_numeric_alt_digit_group} - {line_info_0 line_info_background_0} - {line_info_1 line_info_background_1} - {line_info_2 line_info_background_2} - {line_info_3 line_info_background_3} - {alt_background menu_bar_background} - {alt_border menu_bar_border} - {tab_inactive tab_background_inactive} - {tab_active tab_background} - {weak_text text_weak} - {text_selection selection} - {cursor cursor} - {highlight_0 focus} - {success_background positive_pop_button_background} - {failure_background negative_pop_button_background} - {action_background neutral_pop_button_background} -} - -@enum DF_ThemeColor: -{ - @expand(DF_ThemeColorTable a) `$(a.name)`, - COUNT, -} - -@enum DF_ThemePreset: -{ - @expand(DF_ThemePresetTable a) `$(a.name)`, - COUNT, -} - -@data(String8) df_g_theme_preset_display_string_table: -{ - @expand(DF_ThemePresetTable a) `str8_lit_comp("$(a.display_string)")`, -} - -@data(String8) df_g_theme_preset_code_string_table: -{ - @expand(DF_ThemePresetTable a) `str8_lit_comp("$(a.name_lower)")`, -} - -@data(String8) df_g_theme_color_version_remap_old_name_table: -{ - @expand(DF_ThemeColorVersionRemapTable a) `str8_lit_comp("$(a.old_name)")` -} - -@data(String8) df_g_theme_color_version_remap_new_name_table: -{ - @expand(DF_ThemeColorVersionRemapTable a) `str8_lit_comp("$(a.new_name)")` -} - -@data(Vec4F32) df_g_theme_preset_colors__default_dark: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.default_dark))`} -@data(Vec4F32) df_g_theme_preset_colors__default_light: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.default_light))`} -@data(Vec4F32) df_g_theme_preset_colors__vs_dark: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.vs_dark))`} -@data(Vec4F32) df_g_theme_preset_colors__vs_light: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.vs_light))`} -@data(Vec4F32) df_g_theme_preset_colors__solarized_dark: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.solarized_dark))`,} -@data(Vec4F32) df_g_theme_preset_colors__solarized_light:{@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.solarized_light))`,} -@data(Vec4F32) df_g_theme_preset_colors__handmade_hero: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.handmade_hero))`,} -@data(Vec4F32) df_g_theme_preset_colors__four_coder: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.four_coder))`,} -@data(Vec4F32) df_g_theme_preset_colors__far_manager: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.far_manager))`;} -@data(`Vec4F32*`) df_g_theme_preset_colors_table: -{ - @expand(DF_ThemePresetTable a) `df_g_theme_preset_colors__$(a.name_lower)`, -} - -@data(String8) df_g_theme_color_display_string_table: -{ - @expand(DF_ThemeColorTable a) `str8_lit_comp("$(a.display_name)")` -} - -@data(String8) df_g_theme_color_cfg_string_table: -{ - @expand(DF_ThemeColorTable a) `str8_lit_comp("$(a.name_lower)")` -} - -//////////////////////////////// -//~ rjf: Settings - -@table(name name_lower display_string default_per_window default_s32 s32_min s32_max) -DF_SettingTable: -{ - {HoverAnimations hover_animations "Hover Animations" 0 1 0 1 } - {PressAnimations press_animations "Press Animations" 0 1 0 1 } - {FocusAnimations focus_animations "Focus Animations" 0 1 0 1 } - {TooltipAnimations tooltip_animations "Tooltip Animations" 0 1 0 1 } - {MenuAnimations menu_animations "Menu Animations" 0 1 0 1 } - {ScrollingAnimations scrolling_animations "Scrolling Animations" 0 1 0 1 } - {BackgroundBlur background_blur "Background Blur" 0 1 0 1 } - {ThreadLines thread_lines "Thread Lines" 0 1 0 1 } - {BreakpointLines breakpoint_lines "Breakpoint Lines" 0 1 0 1 } - {ThreadGlow thread_glow "Thread Glow" 0 1 0 1 } - {BreakpointGlow breakpoint_glow "Breakpoint Glow" 0 1 0 1 } - {OpaqueBackgrounds opaque_backgrounds "Opaque Backgrounds" 0 0 0 1 } - {TabWidth tab_width "Tab Width" 0 4 1 32 } - {MainFontSize main_font_size "Main Font Size" 1 12 6 72 } - {CodeFontSize code_font_size "Code Font Size" 1 12 6 72 } - {SmoothUIText smooth_ui_text "Smooth UI Text" 1 1 0 1 } - {SmoothCodeText smooth_code_text "Smooth Code Text" 1 0 0 1 } - {HintUIText hint_ui_text "Hint UI Text" 1 1 0 1 } - {HintCodeText hint_code_text "Hint Code Text" 1 1 0 1 } -} - -@enum DF_SettingCode: -{ - @expand(DF_SettingTable a) `$(a.name)`, - COUNT -} - -@data(String8) df_g_setting_code_display_string_table: -{ - @expand(DF_SettingTable a) `str8_lit_comp("$(a.display_string)")` -} - -@data(String8) df_g_setting_code_lower_string_table: -{ - @expand(DF_SettingTable a) `str8_lit_comp("$(a.name_lower)")` -} - -@data(B8) df_g_setting_code_default_is_per_window_table: -{ - @expand(DF_SettingTable a) `$(a.default_per_window)` -} - -@data(DF_SettingVal) df_g_setting_code_default_val_table: -{ - @expand(DF_SettingTable a) `{1, $(a.default_s32)}` -} - -@data(Rng1S32) df_g_setting_code_s32_range_table: -{ - @expand(DF_SettingTable a) `{$(a.s32_min), $(a.s32_max)}` -} - -//////////////////////////////// -//~ rjf: Help/Docs/README - -@markdown -raddbg_readme: -{ - @title "The RAD Debugger (ALPHA)"; - @p "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."; - - @subtitle "Getting Started"; - @p "To launch the RAD Debugger with your executable and command line arguments, run `raddbg` from the command line like so:"; - @p "```raddbg my_program.exe --foo --bar --baz```"; - @p "For more information, see the 'Command-Line Usage' section."; - @p "Default keyboard shortcuts for common debugger controls include:"; - @unordered_list - { - @p "**Ctrl + O**: Open Source Code File"; - @p "**F10**: Step Over"; - @p "**F11**: Step Into"; - @p "**Shift + F11**: Step Out"; - @p "**F5**: Run"; - @p "**Ctrl + Shift + X**, or **Pause**: Halt All Processes"; - @p "**Shift + F5**: Kill All Processes"; - @p "**Shift + F6**: Attach To Process"; - @p "**Ctrl + F**: Search For Text (Forwards)"; - @p "**F9**: Toggle Breakpoint At Cursor"; - @p "**Ctrl + Comma**: Focus Next Panel"; - @p "**Ctrl + Shift + Comma**: Focus Previous Panel"; - @p "**Ctrl + Shift + Alt + Arrow Key**: Focus Panel In Direction"; - @p "**Ctrl + Tab**: Focus Next Tab"; - @p "**Ctrl + Shift + Tab**: Focus Previous Tab"; - @p "**Ctrl + W**: Close Tab"; - @p "**F1**: Open Command Palette"; - } - @p "For more information, see the 'Commands' section."; - @p "View rules can be used to visualize expressions differently in the watch window. Here are some examples:"; - @unordered_list - { - @p "`array:16`: Visualize a pointer as pointing to a 16-element array."; - @p "`array:(count*2)`: Visualize a pointer as pointing to a `count*2`-element array."; - @p "`list:next`: Visualize a linked list flatly, where each node has a `next` pointer, which points to the next node in the list."; - @p "`hex`: Visualize numeric literals as base-16 (hexadecimal)."; - @p "`dec`: Visualize numeric literals as base-10 (decimal)."; - @p "`oct`: Visualize numeric literals as base-8 (octal)."; - @p "`bin`: Visualize numeric literals as base-2 (binary)."; - @p "`omit:(foo bar baz)`: Prohibits members named `foo`, `bar`, and `baz` from being displayed."; - @p "`only:(foo bar baz)`: Only allows members named `foo`, `bar`, and `baz` to be displayed."; - } - @p "Multiple view rules can be specified on one line, so they can be combined like so:"; - @p "```list:next, hex, omit:next```"; - @p "For more information, see the 'View Rules' section."; - - @subtitle "Command-Line Usage"; - @p "When run normally, either by launching through a file explorer or running from a command line without arguments, `raddbg` will open a new instance of the debugger. But it also supports a number of command line options for a number of other purposes. These options are specified with a `-` or `--` prefix, followed by the name of the option, and if the option requires a parameter, followed by a `:` or `=`, followed by the parameter's content. A list of the possible options follows:"; - @unordered_list - { - @p "`--help` Displays a help menu which documents the possible command line options."; - @p "`--user:` Specifies a path to the user file which the debugger should use instead of the default. The default user file is stored at `%appdata%/raddbg/default.raddbg_user`. For more information on user files, read the 'User & Profile Files' section."; - @p "`--project:` Specifies a path to the project file which the debugger should use instead of the default. The default project file is stored at `%appdata%/raddbg/default.raddbg_project`. For more information on project files, read the 'User & Project Files' section."; - @p "`--auto_run` Specifies that the debugger should immediately run its selected targets upon launching."; - @p "`--auto_step` Specifies that the debugger should immediately step into its selected targets upon launching."; - //@p "`--ipc` Specifies that the launched debugger instance is for communicating a command to another instance of the debugger. In this mode, any non-argument command line contents will be used to express a command. For more information on commands, read the 'Commands' section. For more information on driving another debugger instance with this argument, read the 'Driving Another Debugger Instance' section." - } - @p "On the command line, non-options (meaning any command line arguments *not* prefixed with a `-` or `--`) can also be specified. with normal usage, they are interpreted as the command line for a target (see the 'Targets' section)." - // add when --ipc support is ready: "When driving another debugger instance (using the `--ipc` argument), this additional command line text is used to encode a debugger command."; - @p "The debugger will stop parsing `-` and `--` prefixes as arguments after seeing a standalone `--`, *or* after seeing the first non-option argument, when reading the command line left-to-right. Some examples of command line usage and their interpretations are below:"; - @unordered_list - { - @p "`raddbg --foo --bar --a:b --c=d test.exe` All options are used to configure `raddbg`. `test.exe` is interpreted as a target executable. `b` is interpreted as the parameter for the `a` option. `d` is interpreted as the parameter for the `c` option."; - @p "`raddbg test.exe --foo --bar` `test.exe` is interpreted as a target executable. `--foo --bar` is interpreted as arguments for `test.exe`, and thus are *not* used to configure `raddbg`."; - @p "`raddbg -- test.exe` `test.exe` is interpreted as a target executable."; - //@p "`raddbg --ipc find_code_location \"c:/foo/bar/baz.c:123:1\"` `--ipc` configures `raddbg` to drive another instance of `raddbg`. The remainder of the text is interpreted as a command."; - @p "`raddbg \"C:/path with spaces/test.exe\" --foo --bar` A target is formed from the `test.exe` path, and `--foo --bar` are interpreted as arguments to the `test.exe` target."; - } - - @subtitle "Windows, Panels, & Tabs"; - @p "Each opened *window* in the debugger frontend is subdivided into *panels*. Panels subdivide regions of their window without overlapping. Each panel can contain multiple *tabs*, and can have one tab selected at any time. Tabs can be dragged and dropped between panels. Each tab is used to view one of the many supported debugger interfaces, including source code, disassembly, memory, or watches. When a tab is selected, that interface will fill the tab's containing panel's region of the containing window."; - @p "There are no 'special' windows, panels, or tabs; the debugger is written such that the number of windows, each window's panel organization, and the placement and arrangement of tabs can all be organized in a large variety of ways."; - @p "A list of debugger interfaces, which can occupy tabs, are below:"; - @unordered_list - { - @expand(DF_GfxViewTable a) @p "$(a.inc_in_docs -> '`'..a.display_string..'` '..a.docs_desc)"; - } - - @subtitle "Commands"; - @p "The debugger is operated with *commands*. Commands may be manually executed in the debugger UI through the `Commands` menu (which you can open either in the `View` menu bar list, or by using the keybinding, which is F1 by default). Operations in the debugger UI are implemented with commands, so if it's ever unclear how to accomplish some operation through the UI, a useful fallback is searching for and running the command through the command menu."; - //@p "Commands are also how a debugger instance launched with `--ipc` may communicate with a primary debugger instance."; - //@p "A list of commands, how they're referred to textually (for the purposes of `--ipc` debugger instances), and their descriptions are below:"; - @p "A list of commands and their descriptions are below:"; - @unordered_list - { - @expand(DF_CoreCmdTable a) @p "$(a.lister_omit == 0 -> '`'..a.display_name..'` '..'(`'..a.string..'`) '..a.desc)"; - } - - @subtitle "Targets"; - @p "A *target* is one executable and configuration for launching that executable, including command line arguments and working directory (the directory from which the executable is launched). Each target may also have a custom label (replaces the executable path when visualizing the target), and the name of a custom entry point function (when the default entry points - `main`, `WinMain`, etc. - are not desired when stepping into the program upon launch). The debugger can have several targets at once. Each target can also be enabled or disabled. Some operations work on all enabled targets - for instance, the `Run` or `Kill All` commands (standardly bound as F5 or Shift + F5). Enabling and disabling targets allows one to filter which targets are currently being worked with."; - @p "To add a target, you can run the `Add Target` command. A target is also created automatically from command line arguments - the rules for how this happens can be found in the `Command-Line Usage` section."; - @p "Targets created through command line usage are temporary, meaning they are not persistently saved across runs of the debugger. To change this, you can right click the command-line-created target in the `Targets` view, and click `Save To Project`. After doing so, the target will be restored across runs, and will no longer need to be specified on the command-line."; - - @subtitle "View Rules"; - @p "*View Rules* are used to transform the way that evaluations in the debugger are visualized. An evaluation is produced by taking an expression string - for instance, the name of a variable - and using debug info and information from an attached process' live runtime (memory, registers, and so on) to interpret it."; - @p "Evaluations may be visualized in a variety of ways. A 64-bit unsigned integer may be visualized as a textual representation of the value with a radix of 10. A 32-bit floating-point value may be visualized as a textual representation of the value. An array of 32-bit floating-point values can be visualized as a list of textual representations of those values."; - @p "But all of these cases may be visualized in a number of other ways, as well. A 64-bit unsigned integer may be more usefully represented with a radix of 16, 8, or 2. An array of 32-bit floating-point values may encode the R, G, B, and A components of a color, or vertex positions for 3D geometry, or samples for a waveform. An array of bytes may encode raw pixel data for an image, or image data in a compressed format. A struct may have several members which are not useful to look at all the time. A struct may form the head of a linked list, and a flat linked list representation may be more preferable than the traditional watch view representation, which adds an additional layer of hierarchical nesting with the expansion of each 'next' pointer in a linked list. When designing the debugger, we felt that the traditional memory view and watch view representations of data in a debugged-process were not sufficient. View rules were added to the traditional watch view structure to allow per-row specification of extra visualization parameters."; - @p "View rules are specified with the name of a view rule, and depending on the view rule, a `:`, followed by parameters for the view rule. These parameters may be whitespace delimited, but importantly, multiple view rules may be specified per-row in a watch view. To explicitly separate the parameters of one view rule from the name of another - for instance, in a case like `array:16 bin`, where `bin` will not be interpreted as a view rule, but as a parameter of `array` - then commas and semicolons may be used to separate the two view rules (`array:16, bin`), or parentheses/braces/brackets may also be used to explicitly delimit the view rule parameters (`array:(16) bin`)."; - @p "A list of currently-supported view rules are below:"; - @unordered_list - { - @expand(DF_CoreViewRuleTable a) @p "$(a.docs == 'x' -> '`'..a.string..'` ('..a.display_name..') '..a.description)"; - } - - @subtitle "Breakpoints"; - @p "Breakpoints interrupt execution of attached processes. They may be placed on specific code addresses, lines of source code, on specific symbol names. In the latter two cases, the higher level locations are resolved to code addresses. If there is no code associated with a line of source code, then the resolution path chooses to use the next closest line of source code in the same file. A symbol name breakpoint will only work if the symbol name is found within loaded debug info."; - @p "Breakpoints may have stop conditions attached to them. When a breakpoint is hit by a thread, before it stops execution, the stop condition is evaluated, and if it evaluates to a nonzero value, only then is execution stopped."; - @p "Each breakpoint has a hit count. Every time a breakpoint causes execution to stop, this counter is increased."; - @p "Processor breakpoints are not currently supported, but planned to be in the future."; - - @subtitle "User & Project Files"; - @p "Applicable state controlling the debugger's appearance, behavior, targets, breakpoints, and other configurations is saved and reloaded across runs of the debugger through both *user files* and *project files*. These files are auto-saved. These files are written in a textual format which can be hand-edited as necessary, but they're also continuously re-read and re-written by the debugger. By default, the debugger uses `%appdata%/raddbg/default.raddbg_user` for its user file path, and `%appdata%/raddbg/default.raddbg_project` for its project file path. These paths can be overridden on the command line (see the 'Command-Line Usage' section)."; - @p "The *user file* defaultly stores file path maps, windows (including their preferred monitor, placement, and size), each window's panel layout and tabs, keybindings, theme colors, and fonts."; - @p "The *project file* defaultly stores targets, breakpoints, watch pins, and exception code filters."; - @p "Because both can be hand-edited, however, if you want to store something normally stored in a user file in a project file, or vice versa, this can be done by hand transferring the textual data from one file to another. There is no path in the debugger's UI to support this transfer, currently, although this is planned."; - - //@subtitle "Driving Another Debugger Instance"; - //@p "When the debugger is launched with the `--ipc` command-line argument, it does not launch another instance of the graphical debugger. Instead, it launches, sends a string encoding a command to a running instance of the graphical debugger, and then terminates. The set of commands which can be sent are identical to those which can be run from the debugger's UI itself, but these commands must be encoded textually (through the other command-line arguments). These commands are described in the 'Commands' section."; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Embedded Data + +@embed_file df_g_icon_font_bytes: "../data/icons.ttf" +@embed_file df_g_default_main_font_bytes: "../data/Roboto-Regular.ttf" +@embed_file df_g_default_code_font_bytes: "../data/liberation-mono.ttf" +//@embed_file df_g_default_code_font_bytes: "../data/Inconsolata-Regular.ttf" +@embed_file df_g_icon_file_bytes: "../data/logo.ico" + +//////////////////////////////// +//~ rjf: Default Bindings + +@table(name key ctrl shift alt) +DF_DefaultBindingTable: +{ + //- rjf: low-level target control operations + { "kill_all" F5 0 shift 0 } + { "step_into_inst" F11 0 0 alt } + { "step_over_inst" F10 0 0 alt } + { "step_out" F11 0 shift 0 } + { "halt" X ctrl shift 0 } + { "halt" Pause 0 0 0 } + { "soft_halt_refresh" R 0 0 alt } + + //- rjf: high-level composite target control operations + { "run" F5 0 0 0 } + { "restart" F5 ctrl shift 0 } + { "step_into" F11 0 0 0 } + { "step_over" F10 0 0 0 } + { "run_to_cursor" F10 ctrl 0 0 } + { "set_next_statement" F10 ctrl shift 0 } + + //- rjf: font sizes + { "inc_ui_font_scale" Equal 0 0 alt } + { "dec_ui_font_scale" Minus 0 0 alt } + { "inc_code_font_scale" Equal 0 shift alt } + { "dec_code_font_scale" Minus 0 shift alt } + + //- rjf: windows + { "window" N ctrl shift 0 } + { "toggle_fullscreen" Return ctrl 0 0 } + + //- rjf: panel splitting + { "new_panel_right" P ctrl 0 0 } + { "new_panel_down" Minus ctrl 0 0 } + + //- rjf: panel rotation + { "rotate_panel_columns" 2 ctrl 0 0 } + + //- rjf: focused panel changing + { "next_panel" Comma ctrl 0 0 } + { "prev_panel" Comma ctrl shift 0 } + { "focus_panel_right" Right ctrl 0 alt } + { "focus_panel_left" Left ctrl 0 alt } + { "focus_panel_up" Up ctrl 0 alt } + { "focus_panel_down" Down ctrl 0 alt } + + //- rjf: undo/redo + //{ "undo" Z ctrl 0 0 } + //{ "redo" Y ctrl 0 0 } + + //- rjf: focus history + //{ "go_back" Left 0 0 alt } + //{ "go_forward" Right 0 0 alt } + + //- rjf: panel removal + { "close_panel" P ctrl shift 0 } + + //- rjf: panel tab + { "next_tab" PageDown ctrl 0 0 } + { "prev_tab" PageUp ctrl 0 0 } + { "next_tab" Tab ctrl 0 0 } + { "prev_tab" Tab ctrl shift 0 } + { "move_tab_right" PageDown ctrl shift 0 } + { "move_tab_left" PageUp ctrl shift 0 } + { "close_tab" W ctrl 0 0 } + { "tab_bar_top" Up ctrl shift alt } + { "tab_bar_bottom" Down ctrl shift alt } + + //- rjf: files + { "open" O ctrl 0 0 } + { "reload_active" R ctrl shift 0 } + { "switch" I ctrl 0 0 } + { "switch_to_partner_file" O 0 0 alt } + + //- rjf: setting config paths + { "open_user" O ctrl shift alt } + { "open_project" O ctrl 0 alt } + + //- rjf: meta controls + { "edit" F2 0 0 0 } + { "accept" Return 0 0 0 } + { "cancel" Esc 0 0 0 } + + //- rjf: directional movement & text controls + { "move_left" Left 0 0 0 } + { "move_right" Right 0 0 0 } + { "move_up" Up 0 0 0 } + { "move_down" Down 0 0 0 } + { "move_left_select" Left 0 shift 0 } + { "move_right_select" Right 0 shift 0 } + { "move_up_select" Up 0 shift 0 } + { "move_down_select" Down 0 shift 0 } + { "move_left_chunk" Left ctrl 0 0 } + { "move_right_chunk" Right ctrl 0 0 } + { "move_up_chunk" Up ctrl 0 0 } + { "move_down_chunk" Down ctrl 0 0 } + { "move_up_page" PageUp 0 0 0 } + { "move_down_page" PageDown 0 0 0 } + { "move_up_whole" Home ctrl 0 0 } + { "move_down_whole" End ctrl 0 0 } + { "move_left_chunk_select" Left ctrl shift 0 } + { "move_right_chunk_select" Right ctrl shift 0 } + { "move_up_chunk_select" Up ctrl shift 0 } + { "move_down_chunk_select" Down ctrl shift 0 } + { "move_up_page_select" PageUp 0 shift 0 } + { "move_down_page_select" PageDown 0 shift 0 } + { "move_up_whole_select" Home ctrl shift 0 } + { "move_down_whole_select" End ctrl shift 0 } + { "move_up_reorder" Up 0 0 alt } + { "move_down_reorder" Down 0 0 alt } + { "move_home" Home 0 0 0 } + { "move_end" End 0 0 0 } + { "move_home_select" Home 0 shift 0 } + { "move_end_select" End 0 shift 0 } + { "select_all" A ctrl 0 0 } + { "delete_single" Delete 0 0 0 } + { "delete_chunk" Delete ctrl 0 0 } + { "backspace_single" Backspace 0 0 0 } + { "backspace_chunk" Backspace ctrl 0 0 } + { "copy" C ctrl 0 0 } + { "copy" Insert ctrl 0 0 } + { "cut" X ctrl 0 0 } + { "paste" V ctrl 0 0 } + { "paste" Insert 0 shift 0 } + { "insert_text" Null 0 0 0 } + + //- rjf: code navigation + { "goto_line" G ctrl 0 0 } + { "goto_address" G 0 0 alt } + { "find_text_forward" F ctrl 0 0 } + { "find_text_backward" R ctrl 0 0 } + { "find_next" F3 0 0 0 } + { "find_prev" F3 shift 0 0 } + + //- rjf: thread finding + { "find_selected_thread" F4 0 0 0 } + + //- rjf: name finding + { "goto_name" J ctrl 0 0 } + { "goto_name_at_cursor" F12 0 0 0 } + + //- rjf: watch expressions + { "toggle_watch_expr_at_cursor" W 0 0 alt } + { "toggle_watch_expr_at_mouse" D ctrl 0 0 } + { "toggle_watch_pin_at_cursor" F9 ctrl 0 0 } + + //- rjf: breakpoints + { "toggle_breakpoint_cursor" F9 0 0 0 } + + //- rjf: targets + { "add_target" T ctrl 0 0 } + + //- rjf: attaching + { "attach" F6 0 shift 0 } + + //- rjf: filtering + { "filter" Slash ctrl 0 0 } + + //- rjf: command lister + { "run_command" F1 0 0 0 } + + //- rjf: developer commands + { "log_marker" M ctrl shift alt } +} + +@data(DF_StringBindingPair) df_g_default_binding_table: +{ + @expand(DF_DefaultBindingTable a) ```{str8_lit_comp("$(a.name)"), {OS_Key_$(a.key), 0 $(a.ctrl != 0 -> `|OS_EventFlag_Ctrl`) $(a.shift != 0 -> `|OS_EventFlag_Shift`) $(a.alt != 0 -> `|OS_EventFlag_Alt`)}}```; +} + +//////////////////////////////// +//~ rjf: Binding Version Remap Table + +@table(old_name new_name) +DF_BindingVersionRemapTable: +{ + {"commands" "run_command"} + {"load_user" "open_user"} + {"load_profile" "open_profile"} + {"load_project" "open_project"} + {"open_profile" "open_project"} +} + +@data(String8) df_g_binding_version_remap_old_name_table: +{ + @expand(DF_BindingVersionRemapTable a) `str8_lit_comp("$(a.old_name)")` +} + +@data(String8) df_g_binding_version_remap_new_name_table: +{ + @expand(DF_BindingVersionRemapTable a) `str8_lit_comp("$(a.new_name)")` +} + +//////////////////////////////// +//~ rjf: Gfx Layer View Kinds + +@table(name, name_lower, display_string, name_kind, icon, parameterized_by_entity, project_specific, can_serialize, can_serialize_entity_path, can_filter, filter_is_code, typing_automatically_filters, inc_in_docs, docs_desc) +DF_GfxViewTable: +{ + { Null "null" "" Null Null 0 0 0 0 0 0 0 0 "" } + { Empty "empty" "" Null Null 0 0 0 0 0 0 0 0 "" } + { GettingStarted "getting_started" "Getting Started" Null QuestionMark 0 0 1 0 0 0 0 0 "" } + { Commands "commands" "Commands" Null List 0 0 0 0 0 0 0 0 "" } + { FileSystem "file_system" "File System" Null FileOutline 0 0 0 0 0 0 0 0 "" } + { SystemProcesses "system_processes" "System Processes" Null Null 0 0 0 0 0 0 0 0 "" } + { EntityLister "entity_lister" "Entity List" Null Null 0 0 0 0 0 0 0 0 "" } + { SymbolLister "symbol_lister" "Symbols" Null Null 0 0 0 0 0 0 0 0 "" } + { Target "target" "Target" EntityName Target 1 0 0 0 0 0 0 0 "" } + { Targets "targets" "Targets" Null Target 0 0 1 0 1 0 1 1 "Displays a list of all targets, as well as controls for enabling, disabling, launching, editing, or deleting each target. For more information on targets, read the `Targets` section." } + { FilePathMap "file_path_map" "File Path Map" Null FileOutline 0 0 1 0 0 0 0 1 "Displays a table of *path maps*. Each path map is a pair of file or folder paths, one being a 'source' path, and one being a 'destination' path. These pairs are used by the debugger when automatically searching for specific files - for instance, when attempting to snap to a source code location specified by debug info. If debug info refers to a path on the machine on which a target executable was originally built, but that path is not valid on the debugger machine, but some alternative path exists, then path maps may be used to redirect the debugger from the debug info's specified paths to the associated appropriate debugger machine file paths." } + { AutoViewRules "auto_view_rules" "Auto View Rules" Null Binoculars 0 0 1 0 0 0 0 1 "Displays a table of *auto view rules*. Each *auto view rule* is a pair, with one element being a type, and the other being a view rule, which should be automatically applied to expressions of that type, when possible." } + { Scheduler "scheduler" "Scheduler" Null Scheduler 0 0 1 0 1 1 1 1 "Displays all processes and threads to which the debugger is currently attached, and contains controls for selecting and freezing threads." } + { CallStack "call_stack" "Call Stack" Null Thread 0 0 1 0 0 0 0 1 "Displays the call stack of the currently selected thread. Each frame in the call stack contains the associated module, function name, and return address. Allows selection of a particular call stack frame other than the top." } + { Modules "modules" "Modules" Null Module 0 0 1 0 1 0 1 1 "Displays a table of all modules currently loaded by any process to which the debugger is attached. This table displays each module's name, virtual address range in the containing process' address space, and which debug info file is being used by the debugger for the associated module." } + { PendingEntity "pending_entity" "Pending Entity" EntityName FileOutline 1 0 0 0 0 0 0 0 "" } + { Code "code" "Code" EntityName FileOutline 1 1 1 1 0 0 0 0 "" } + { Disassembly "disassembly" "Disassembly" Null Glasses 0 0 1 0 0 0 0 1 "Displays disassembled instructions in a textual form from the selected thread's containing process virtual address space." } + { Watch "watch" "Watch" Null Binoculars 0 0 1 0 1 1 1 1 "The familiar 'watch window' debugger interface. Allows the inputting of a number of expressions. Each expression in the table is evaluated within the context of the selected thread's selected call stack frame. If applicable (depending on visualization rules and the expression's type), these expressions may be hierarchically expanded, which displays children as more rows in the table. The values of these expressions may also be edited, and if possible, can be used to write to registers or memory in attached processes. Also contains a new *view rule* column, not found in other major debuggers, which allows per-row specification of various visualization rules. These view rules may be used to visualize and inspect the evaluation of expressions in a variety of ways. To learn more, read the 'View Rules' section." } + { Locals "locals" "Locals" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with local variables found within the selected call stack frame of the selected thread, according to the associated debug info. View rules and evaluation values can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } + { Registers "registers" "Registers" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all register names according to the selected thread's architecture. View rules and evaluation values can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } + { Globals "globals" "Globals" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all global variables within the selected thread's module. View rules and evaluation values can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } + { ThreadLocals "thread_locals" "Thread Locals" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all thread local variables within the selected thread's module. View rules and evaluation values can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } + { Types "types" "Types" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all types within the selected thread's module. View rules can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } + { Procedures "procedures" "Procedures" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all procedures within the selected thread's module. View rules can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } + { Output "output" "Output" Null List 0 0 1 0 0 0 0 1 "Displays textual output from the selected thread's containing process." } + { Memory "memory" "Memory" Null Grid 0 0 1 0 0 0 0 1 "A familiar hex-editor-like interface for viewing memory of attached processes." } + { Breakpoints "breakpoints" "Breakpoints" Null CircleFilled 0 0 1 0 1 0 1 1 "Displays a table of all breakpoints, containing information about each breakpoint's name, location, and hit count. Also contains per-breakpoint controls for enabling, deleting, or editing each breakpoint. For more information on breakpoints and their features, read the 'Breakpoints' section." } + { WatchPins "watch_pins" "Watch Pins" Null Pin 0 0 1 0 1 1 1 1 "Displays a table of all watch pins (watched expressions, like those found in `Watch`, but instead of being within a table, being pinned to some source code location, like breakpoints). This table contains each pin's name, location, and controls for editing or deleting each pin." } + { ExceptionFilters "exception_filters" "Exception Filters" Null Gear 0 0 1 0 1 0 1 1 "An interface which controls whether or not the debugger will halt attached processes upon encountering specific exception codes for the first time." } + { Settings "settings" "Settings" Null Gear 0 0 1 0 1 0 1 1 "An interface to modify general settings for the debugger's appearance and behavior." } +} + +@enum DF_GfxViewKind: +{ + @expand(DF_GfxViewTable a) `$(a.name)`, + COUNT, +} + +@gen +{ + @expand(DF_GfxViewTable a) `DF_VIEW_SETUP_FUNCTION_DEF($(a.name));`; + @expand(DF_GfxViewTable a) `DF_VIEW_STRING_FROM_STATE_FUNCTION_DEF($(a.name));`; + @expand(DF_GfxViewTable a) `DF_VIEW_CMD_FUNCTION_DEF($(a.name));`; + @expand(DF_GfxViewTable a) `DF_VIEW_UI_FUNCTION_DEF($(a.name));`; +} + +@data(DF_ViewSpecInfo) df_g_gfx_view_kind_spec_info_table: +{ + @expand(DF_GfxViewTable a) ```{(0|$(a.parameterized_by_entity)*DF_ViewSpecFlag_ParameterizedByEntity|$(a.project_specific)*DF_ViewSpecFlag_ProjectSpecific|$(a.can_serialize)*DF_ViewSpecFlag_CanSerialize|$(a.can_serialize_entity_path)*DF_ViewSpecFlag_CanSerializeEntityPath|$(a.can_filter)*DF_ViewSpecFlag_CanFilter|$(a.filter_is_code)*DF_ViewSpecFlag_FilterIsCode|$(a.typing_automatically_filters)*DF_ViewSpecFlag_TypingAutomaticallyFilters), str8_lit_comp("$(a.name_lower)"), str8_lit_comp("$(a.display_string)"), DF_NameKind_$(a.name_kind), DF_IconKind_$(a.icon), DF_VIEW_SETUP_FUNCTION_NAME($(a.name)), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME($(a.name)), DF_VIEW_CMD_FUNCTION_NAME($(a.name)), DF_VIEW_UI_FUNCTION_NAME($(a.name))}```; +} + +//////////////////////////////// +//~ rjf: Command Parameter Slot -> View + +@table(slot view_spec opt_cmd_spec) +DF_CmdParamSlot2ViewSpecMap: +{ + {Entity "entity_lister" "" } + {EntityList "entity_lister" "" } + {FilePath "file_system" "" } + {CmdSpec "commands" "" } + {ID "system_processes" "" } + {String "symbol_lister" "goto_name" } + {String "symbol_lister" "function_breakpoint" } +} + +@data(DF_CmdParamSlot) df_g_cmd_param_slot_2_view_spec_src_map: +{ + @expand(DF_CmdParamSlot2ViewSpecMap a) `DF_CmdParamSlot_$(a.slot)` +} + +@data(String8) df_g_cmd_param_slot_2_view_spec_dst_map: +{ + @expand(DF_CmdParamSlot2ViewSpecMap a) `str8_lit_comp("$(a.view_spec)")` +} + +@data(String8) df_g_cmd_param_slot_2_view_spec_cmd_map: +{ + @expand(DF_CmdParamSlot2ViewSpecMap a) `str8_lit_comp("$(a.opt_cmd_spec)")` +} + +//////////////////////////////// +//~ rjf: Built-In Graphical View Rule Extensions +// +// NOTE(rjf): see @view_rule_info + +@table(string vr ls ru bu tu tab_display_string) +DF_GfxViewRuleTable: +{ + {"array" - - - - - "" } + {"list" x - - - - "" } + {"dec" - x - - - "" } + {"bin" - x - - - "" } + {"oct" - x - - - "" } + {"hex" - x - - - "" } + {"only" x x - - - "" } + {"omit" x x - - - "" } + {"no_addr" - x - - - "" } + {"rgba" - - x x - "" } + {"text" - - - x x "Text" } + {"disasm" - - - x x "Disassembly" } + {"bitmap" - - x x x "Bitmap" } + {"geo" - - x x x "Geometry" } +} + +@gen +{ + ``; + @expand(DF_GfxViewRuleTable a) + `$(a.vr == "x" -> "DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_DEF(" .. a.name_lower .. ");")`; + @expand(DF_GfxViewRuleTable a) + `$(a.ls == "x" -> "DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_DEF(" .. a.name_lower .. ");")`; + @expand(DF_GfxViewRuleTable a) + `$(a.ru == "x" -> "DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_DEF(" .. a.name_lower .. ");")`; + @expand(DF_GfxViewRuleTable a) + `$(a.bu == "x" -> "DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_DEF(" .. a.name_lower .. ");")`; + @expand(DF_GfxViewRuleTable a) + `$(a.tu == "x" -> "DF_VIEW_SETUP_FUNCTION_DEF(" .. a.name_lower .. ");")`; + @expand(DF_GfxViewRuleTable a) + `$(a.tu == "x" -> "DF_VIEW_STRING_FROM_STATE_FUNCTION_DEF(" .. a.name_lower .. ");")`; + @expand(DF_GfxViewRuleTable a) + `$(a.tu == "x" -> "DF_VIEW_CMD_FUNCTION_DEF(" .. a.name_lower .. ");")`; + @expand(DF_GfxViewRuleTable a) + `$(a.tu == "x" -> "DF_VIEW_UI_FUNCTION_DEF(" .. a.name_lower .. ");")`; +} + +@data(DF_ViewSpecInfo) @c_file df_g_gfx_view_rule_tab_view_spec_info_table: +{ + @expand(DF_GfxViewRuleTable a) + ```$(a.tu == "x" -> '{ DF_ViewSpecFlag_CanSerialize|DF_ViewSpecFlag_CanSerializeQuery, str8_lit_comp("' .. a.string .. '_view_rule"), str8_lit_comp("' .. a.tab_display_string .. '"), DF_NameKind_Null, DF_IconKind_Binoculars, ' .. 'DF_VIEW_SETUP_FUNCTION_NAME(' .. a.string .. '), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(' .. a.string .. '), DF_VIEW_CMD_FUNCTION_NAME(' .. a.string .. '), DF_VIEW_UI_FUNCTION_NAME(' .. a.string .. ') }')```; +} + +@data(DF_GfxViewRuleSpecInfo) @c_file df_g_gfx_view_rule_spec_info_table: +{ + @expand(DF_GfxViewRuleTable a) + ```{ str8_lit_comp("$(a.string)"), (DF_GfxViewRuleSpecInfoFlag_VizRowProd*$(a.vr == "x"))|(DF_GfxViewRuleSpecInfoFlag_LineStringize*$(a.ls == "x"))|(DF_GfxViewRuleSpecInfoFlag_RowUI*$(a.ru == "x"))|(DF_GfxViewRuleSpecInfoFlag_BlockUI*$(a.bu == "x")), $(a.vr == "x" -> "DF_GFX_VIEW_RULE_VIZ_ROW_PROD_FUNCTION_NAME("..a.name_lower..")") $(a.vr != "x" -> 0), $(a.ls == "x" -> "DF_GFX_VIEW_RULE_LINE_STRINGIZE_FUNCTION_NAME("..a.name_lower..")") $(a.ls != "x" -> 0), $(a.ru == "x" -> "DF_GFX_VIEW_RULE_ROW_UI_FUNCTION_NAME("..a.name_lower..")") $(a.ru != "x" -> 0), $(a.bu == "x" -> "DF_GFX_VIEW_RULE_BLOCK_UI_FUNCTION_NAME("..a.name_lower..")") $(a.bu != "x" -> 0), str8_lit_comp("$(a.tu == 'x' -> a.string..'_view_rule')") }```; +} + +//////////////////////////////// +//~ rjf: Theme Tables + +@table(name_upper name_lower display_string) +DF_ThemePresetTable: +{ + { DefaultDark default_dark "Default (Dark)" } + { DefaultLight default_light "Default (Light)" } + { VSDark vs_dark "VS (Dark)" } + { VSLight vs_light "VS (Light)" } + { SolarizedDark solarized_dark "Solarized (Dark)" } + { SolarizedLight solarized_light "Solarized (Light)" } + { HandmadeHero handmade_hero "Handmade Hero" } + { FourCoder four_coder "4coder" } + { FarManager far_manager "Far Manager" } +} + +@table(name display_name name_lower default_dark default_light vs_dark vs_light solarized_dark solarized_light handmade_hero four_coder far_manager desc) +DF_ThemeColorTable: +{ + {Null "Null" null 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff 0xff00ffff ""} + + //- rjf: global ui colors + {Text "Text" text 0xe5e5e5ff 0x4c4c4cff 0xe5e5e5ff 0x000000ff 0x999999ff 0x333333ff 0xa08462ff 0x90b080ff 0x00fefeff ""} + {TextPositive "Text (Positive)" text_positive 0x4dc221ff 0x4d9e2eff 0x4dc221ff 0x4dc221ff 0x4dc221ff 0x4dc221ff 0x4dc221ff 0x4dc221ff 0x4dc221ff ""} + {TextNegative "Text (Negative)" text_negative 0xc56452ff 0xbd371eff 0xc56452ff 0xc46451ff 0xc56452ff 0xc56452ff 0xc56452ff 0xc56452ff 0xc56452ff ""} + {TextNeutral "Text (Neutral)" text_neutral 0x307eb2ff 0x0064a7ff 0x307eb2ff 0x307eb2ff 0x307eb2ff 0x307eb2ff 0x307eb2ff 0x307eb2ff 0x307eb2ff ""} + {TextWeak "Text (Weak)" text_weak 0xa4a4a4fe 0x4c4c4cff 0xa4a4a4fe 0x0000007f 0x9999998a 0x818181ff 0x6e512eff 0x566e4bff 0x00a9a9ff ""} + {Cursor "Cursor" cursor 0x8aff00ff 0x699830ff 0x8aff00ff 0x000000ff 0x8aff00ff 0x586e75ff 0x8aff00ff 0x8aff00ff 0x8aff00ff ""} + {CursorInactive "Cursor (Inactive)" cursor_inactive 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff 0xb23217ff ""} + {Focus "Focus" focus 0xfda200ff 0x9c5900ff 0xfda200ff 0x002affff 0xfda200ff 0x92743dff 0xfda200ff 0xfda200ff 0x00fefeff ""} + {Hover "Hover" hover 0xffffffff 0xffffffff 0xffffffff 0x000000ff 0xffffffff 0x747474ff 0xffffffff 0xffffffff 0xffffffff ""} + {DropShadow "Drop Shadow" drop_shadow 0x0000007f 0x0000004c 0x0000007f 0xa3a3a37e 0x0000007f 0xc9bfa394 0x0000007f 0x0000007f 0x0000007f ""} + {DisabledOverlay "Disabled Overlay" disabled_overlay 0x0000003f 0xa6a6a63f 0x0000003f 0x0000003f 0x0000003f 0xe4dac090 0x0000003f 0x0000003f 0x0000003f ""} + {DropSiteOverlay "Drop Site Overlay" drop_site_overlay 0xffffff0c 0x4848480c 0xffffff0c 0x0000000c 0xffffff0c 0xffffff0c 0xffffff0c 0xffffff0c 0xffffff0c ""} + {InactivePanelOverlay "Inactive Panel Overlay" inactive_panel_overlay 0x0000003f 0xa4a4a43f 0x0000003f 0xfefefe53 0x0000003f 0x0000001c 0x0000003f 0x0000003f 0x0000003f ""} + {SelectionOverlay "Selection Overlay" selection_overlay 0x99ccff4c 0x003d7a48 0x99ccff4c 0x3d74ab4b 0x99ccff4c 0x678cb24c 0x99ccff4c 0x99ccff4c 0x99ccff4c ""} + {HighlightOverlay "Highlight Overlay" highlight_overlay 0xffffff1e 0xffffff1e 0xffffff1e 0x0000001e 0xffffff1e 0xffffff1e 0xffffff1e 0xffffff1e 0xffffff1e ""} + {HighlightOverlayError "Error Highlight Overlay" error_highlight_overlay 0x5f12005f 0xff30005f 0x5f12005f 0x5f12005f 0x5f12005f 0x5f12005f 0x5f12005f 0x5f12005f 0x5f12005f ""} + + //- rjf: base ui container colors + {BaseBackground "Base Background" base_background 0x1b1b1bfe 0xccccccfe 0x1b1b1bfe 0xfefefefe 0x002a35fe 0xfcf5e2fe 0x0c0c0cfe 0x0c0c0cfe 0x000081fe ""} + {BaseBackgroundAlt "Base Background (Alternate)" base_background_alt 0x2b2b2bfe 0x2b2b2bfe 0x1b1b1bfe 0xe7e7e7fe 0x2b2b2bfe 0x2b2b2bfe 0x2b2b2bfe 0x2b2b2bfe 0x2b2b2bfe ""} + {BaseBorder "Base Border" base_border 0x3f3f3ffe 0xa4a4a4fe 0x3f3f3ffe 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0x423525fe 0x3f3f3ffe 0x0000fffe ""} + + //- rjf: menu bar ui container colors + {MenuBarBackground "Menu Bar Background" menu_bar_background 0x3e4c577f 0xeaeaea7f 0x1b1b1bfd 0xffffff7f 0x00202bff 0xeee8d5ff 0x0c0c0cfe 0x0c0c0cfe 0x007d7dff ""} + {MenuBarBackgroundAlt "Menu Bar Background (Alternate)" menu_bar_background_alt 0x3e4c577f 0x3e4c577f 0x1b1b1bfd 0xffffff7f 0x3e4c577f 0x3e4c577f 0x3e4c577f 0x3e4c577f 0x007d7dff ""} + {MenuBarBorder "Menu Bar Border" menu_bar_border 0xffffff19 0xa4a4a4fe 0x3f3f3ffe 0xb6b6b6ff 0xffffff19 0xbebaabfe 0xffffff19 0xffffff19 0xfefefe00 ""} + + //- rjf: floating ui container colors + {FloatingBackground "Floating Background" floating_background 0x33333333 0xccccccc0 0x33333333 0xfefefec7 0x007fa14e 0xffffff7c 0x0c0c0c32 0x0c0c0c3e 0x007c7c55 ""} + {FloatingBackgroundAlt "Floating Background (Alternate)" floating_background_alt 0x33333333 0x33333333 0x33333333 0x33333333 0x33333333 0x33333333 0x33333333 0x33333333 0x33333333 ""} + {FloatingBorder "Floating Border" floating_border 0x3f3f3ffd 0xa4a4a4fe 0x3f3f3ffd 0xb6b6b6ff 0xfdfdfd3a 0xbebaabfe 0x423425fe 0x3f3f3ffd 0x00ffff55 ""} + + //- rjf: ui element colors + {ImplicitButtonBackground "Implicit Button Background" implicit_button_background 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 ""} + {ImplicitButtonBorder "Implicit Button Border" implicit_button_border 0x00000000 0x00000000 0x00000000 0x00000000 0x00000000 0xbdb9aa00 0x00000000 0x00000000 0x00000000 ""} + {PlainButtonBackground "Plain Button Background" plain_button_background 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe 0x1b1b1bfe ""} + {PlainButtonBorder "Plain Button Border" plain_button_border 0x3f3f3ffe 0x3f3f3ffe 0x3f3f3ffe 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0x3f3f3ffe 0x3f3f3ffe 0x3f3f3ffe ""} + {PositivePopButtonBackground "Positive Pop Button Background" positive_pop_button_background 0x2c5b36ff 0x65f534ff 0x2c5b36ff 0x84ce93ff 0x2c5b36ff 0xb6ddbeff 0x132e19ff 0x152f1bff 0x2c5b36ff ""} + {PositivePopButtonBorder "Positive Pop Button Border" positive_pop_button_border 0x3f3f3ffd 0x3f3f3ffd 0x3f3f3ffd 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0x3f3f3ffd 0x3f3f3ffd 0x3f3f3ffd ""} + {NegativePopButtonBackground "Negative Pop Button Background" negative_pop_button_background 0x803425ff 0xff694cff 0x803425ff 0xbd3e24ff 0x803425ff 0xf8b0a1ff 0x803425ff 0x43150cff 0x803425ff ""} + {NegativePopButtonBorder "Negative Pop Button Border" negative_pop_button_border 0x3f3f3ffd 0x3f3f3ffd 0x3f3f3ffd 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0x3f3f3ffd 0x3f3f3ffd 0x3f3f3ffd ""} + {NeutralPopButtonBackground "Neutral Pop Button Background" neutral_pop_button_background 0x355b6eff 0xa6becaff 0x355b6eff 0x6e9db5ff 0x355b6eff 0xb2d3e3ff 0x15445cff 0x1b323eff 0x933100ff ""} + {NeutralPopButtonBorder "Neutral Pop Button Border" neutral_pop_button_border 0x3f3f3ffd 0xa6a6a6fd 0x3f3f3ffd 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0x3f3f3ffd 0x3f3f3ffd 0x3f3f3ffd ""} + {ScrollBarButtonBackground "Scroll Bar Button Background" scroll_bar_button_background 0x2b2b2bfe 0xa9a9a9fe 0x2b2b2bfe 0xe8e8e8fe 0x005e77fe 0xe3dbc7fe 0x1f1f27fe 0x212721fe 0x007d7dff ""} + {ScrollBarButtonBorder "Scroll Bar Button Border" scroll_bar_button_border 0x3f3f3ffe 0xc0c0c0fe 0x3f3f3ffe 0xb6b6b6ff 0xfefefe3a 0xbebaabfe 0xfefefe4d 0x3f3f3ffe 0x3f3f3ffe ""} + {TabBackground "Tab Background" tab_background 0x6f5135fe 0xa98b6fff 0x0079ccff 0xfffffffe 0x005e77fe 0xfdf6e3ff 0x1f1f27fe 0x212721fe 0x007d7dff ""} + {TabBorder "Tab Border" tab_border 0xfefefe4d 0xffffff4d 0xfefefe4d 0xb6b6b6ff 0xfefefe4d 0xbebaabfe 0xfefefe4d 0xfefefe4d 0xfefefe4d ""} + {TabBackgroundInactive "Tab Background (Inactive)" tab_background_inactive 0x3e4c577f 0x8282827f 0xfefefe14 0xcdd4dc7f 0x3e4c577f 0xd4cfc0fe 0x131315ee 0x3a3a3a7f 0x3e4c577f ""} + {TabBorderInactive "Tab Border (Inactive)" tab_border_inactive 0xffffff19 0xffffff19 0xffffff00 0xb6b6b6ff 0xffffff19 0xbebaabfe 0xffffff19 0x00000019 0xfefefe19 ""} + + //- rjf: code colors + {CodeDefault "Code (Default)" code_default 0xcbcbcbff 0x4d4d4dff 0xcbcbcbff 0x000000ff 0xcbcbcbff 0x657b83ff 0xa08462ff 0x90b080ff 0x00fefeff ""} + {CodeSymbol "Code (Symbol)" code_symbol 0x42a2cffe 0x205670fe 0xdcdcaaff 0x000000ff 0xcb4a15ff 0xcb4a15ff 0xcc5634ff 0x42a2cffe 0x65b1ffff ""} + {CodeType "Code (Type)" code_type 0xfec746ff 0x996b00ff 0x4ec9afff 0xa33700ff 0xcb4a15ff 0xcb4a15ff 0xd8a51bff 0xfd7c52ff 0xfec746ff ""} + {CodeLocal "Code (Local)" code_local 0x98bc80ff 0x446a2bff 0x9cdbfeff 0x007666ff 0x98bc80ff 0x258ad2ff 0xc04047ff 0x98bc80ff 0x00ff00ff ""} + {CodeRegister "Code (Register)" code_register 0xb7afd5ff 0x4c35a1ff 0xb7afd5ff 0xb7afd5ff 0xb7afd5ff 0x373345ff 0xb7afd5ff 0xb7afd5ff 0xb7afd5ff ""} + {CodeKeyword "Code (Keyword)" code_keyword 0xb38d4cff 0x573700ff 0x569cd6ff 0x0000ffff 0x849803ff 0x586e75ff 0xac7a09ff 0xd08f1eff 0x00ffffff ""} + {CodeDelimiterOperator "Code (Delimiters/Operators)" code_delimiter_operator 0x767676ff 0x767676ff 0x767676ff 0x767676ff 0x767676ff 0x767676ff 0xa08462ff 0x90b080ff 0xffffffff ""} + {CodeNumeric "Code (Numeric)" code_numeric 0x98abb1ff 0x3f6e7dff 0xb5cea8ff 0x088658ff 0xd33582ff 0xd33482ef 0x698e21ff 0x4fff2eff 0x00ff00ff ""} + {CodeNumericAltDigitGroup "Code (Numeric, Alt. Digit Group)" code_numeric_alt_digit_group 0x738287ff 0x1f4450ff 0x729360ff 0x0c3828ff 0x902559ff 0x8e2659ff 0x3a4e11ff 0x3ccd21ff 0x738287ff ""} + {CodeString "Code (String)" code_string 0x98abb1ff 0x3c606bff 0xd59b85ff 0xa31414ff 0x1f9d91ff 0x29a198ff 0x6a8e22ff 0x4fff2eff 0x98abb1ff ""} + {CodeMeta "Code (Meta)" code_meta 0xd96759ff 0xad3627ff 0xd59c85ff 0x0000ffff 0x839802ff 0xd96759ff 0xdab98fff 0xa0b8a0ff 0xff0000ff ""} + {CodeComment "Code (Comment)" code_comment 0x717171ff 0x4b4b4bff 0x57a54aff 0x008000ff 0x556a6fff 0x93a1a1ff 0x686868ff 0x1e8fefff 0xffffffff ""} + {CodeLineNumbers "Code Line Numbers" code_line_numbers 0x7f7f7fff 0x4b4b4bff 0x2a91afff 0x227893ff 0x566c73ff 0x227893ef 0xa08462ff 0x7e7e7ffe 0x007d7dff ""} + {CodeLineNumbersSelected "Code Line Numbers (Selected)" code_line_numbers_selected 0xbebebeff 0x000000ff 0x9ddaecff 0x123d4bfe 0xa2aaacff 0x111e22ef 0xc8b399ff 0xbebebeff 0x00fefeff ""} + + //- rjf: debugging colors + {LineInfoBackground0 "Line Info Background 0" line_info_background_0 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f ""} + {LineInfoBackground1 "Line Info Background 1" line_info_background_1 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f ""} + {LineInfoBackground2 "Line Info Background 2" line_info_background_2 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f ""} + {LineInfoBackground3 "Line Info Background 3" line_info_background_3 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f ""} + {LineInfoBackground4 "Line Info Background 4" line_info_background_4 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f 0x99503d3f ""} + {LineInfoBackground5 "Line Info Background 5" line_info_background_5 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f 0xfe82493f ""} + {LineInfoBackground6 "Line Info Background 6" line_info_background_6 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f 0xffba173f ""} + {LineInfoBackground7 "Line Info Background 7" line_info_background_7 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f 0xcefd693f ""} + {Thread0 "Thread 0" thread_0 0xffcb7fff 0x945800ff 0xffcb7fff 0x945800ff 0xffcb7fff 0x945800ff 0xffcb7fff 0xffcb7fff 0xffcb7fff ""} + {Thread1 "Thread 1" thread_1 0xb2ff65ff 0x3f5b23ff 0xb2ff65ff 0x3f5b23ff 0xb2ff65ff 0x3f5b23ff 0xb2ff65ff 0xb2ff65ff 0xb2ff65ff ""} + {Thread2 "Thread 2" thread_2 0xff99e5ff 0x642a55ff 0xff99e5ff 0x642a55ff 0xff99e5ff 0x642a55ff 0xff99e5ff 0xff99e5ff 0xff99e5ff ""} + {Thread3 "Thread 3" thread_3 0x6598ffff 0x30456fff 0x6598ffff 0x30456fff 0x6598ffff 0x30456fff 0x6598ffff 0x6598ffff 0x6598ffff ""} + {Thread4 "Thread 4" thread_4 0x65ffcbff 0x264f41ff 0x65ffcbff 0x264f41ff 0x65ffcbff 0x264f41ff 0x65ffcbff 0x65ffcbff 0x65ffcbff ""} + {Thread5 "Thread 5" thread_5 0xff9819ff 0x736a5fff 0xff9819ff 0x736a5fff 0xff9819ff 0x736a5fff 0xff9819ff 0xff9819ff 0xff9819ff ""} + {Thread6 "Thread 6" thread_6 0x9932ffff 0x472f5eff 0x9932ffff 0x472f5eff 0x9932ffff 0x472f5eff 0x9932ffff 0x9932ffff 0x9932ffff ""} + {Thread7 "Thread 7" thread_7 0x65ff4cff 0x405d3bff 0x65ff4cff 0x405d3bff 0x65ff4cff 0x405d3bff 0x65ff4cff 0x65ff4cff 0x65ff4cff ""} + {ThreadUnwound "Thread (Unwound)" thread_unwound 0xb2ccd8ff 0x49606aff 0xb2ccd8ff 0x49606aff 0xb2ccd8ff 0x49606aff 0xb2ccd8ff 0xb2ccd8ff 0xb2ccd8ff ""} + {ThreadError "Thread (Error)" thread_error 0xb23219ff 0xb23219ff 0xb23219ff 0xb23219ff 0xb23219ff 0xb23218ff 0xb23219ff 0xb23219ff 0xb23219ff ""} + {Breakpoint "Breakpoint" breakpoint 0xa72911ff 0xff2800ff 0xa72911ff 0xa72911ff 0xa72911ff 0xff684bff 0xa72911ff 0xa72911ff 0xff2800ff ""} +} + +@table(old_name new_name) +DF_ThemeColorVersionRemapTable: +{ + {plain_text text} + {plain_background base_background} + {plain_border base_border} + {plain_overlay drop_site_overlay} + {code_function code_symbol} + {code_symbol code_delimiter_operator} + {code_numeric code_numeric_alt_digit_group} + {line_info_0 line_info_background_0} + {line_info_1 line_info_background_1} + {line_info_2 line_info_background_2} + {line_info_3 line_info_background_3} + {alt_background menu_bar_background} + {alt_border menu_bar_border} + {tab_inactive tab_background_inactive} + {tab_active tab_background} + {weak_text text_weak} + {text_selection selection} + {cursor cursor} + {highlight_0 focus} + {success_background positive_pop_button_background} + {failure_background negative_pop_button_background} + {action_background neutral_pop_button_background} +} + +@enum DF_ThemeColor: +{ + @expand(DF_ThemeColorTable a) `$(a.name)`, + COUNT, +} + +@enum DF_ThemePreset: +{ + @expand(DF_ThemePresetTable a) `$(a.name)`, + COUNT, +} + +@data(String8) df_g_theme_preset_display_string_table: +{ + @expand(DF_ThemePresetTable a) `str8_lit_comp("$(a.display_string)")`, +} + +@data(String8) df_g_theme_preset_code_string_table: +{ + @expand(DF_ThemePresetTable a) `str8_lit_comp("$(a.name_lower)")`, +} + +@data(String8) df_g_theme_color_version_remap_old_name_table: +{ + @expand(DF_ThemeColorVersionRemapTable a) `str8_lit_comp("$(a.old_name)")` +} + +@data(String8) df_g_theme_color_version_remap_new_name_table: +{ + @expand(DF_ThemeColorVersionRemapTable a) `str8_lit_comp("$(a.new_name)")` +} + +@data(Vec4F32) df_g_theme_preset_colors__default_dark: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.default_dark))`} +@data(Vec4F32) df_g_theme_preset_colors__default_light: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.default_light))`} +@data(Vec4F32) df_g_theme_preset_colors__vs_dark: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.vs_dark))`} +@data(Vec4F32) df_g_theme_preset_colors__vs_light: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.vs_light))`} +@data(Vec4F32) df_g_theme_preset_colors__solarized_dark: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.solarized_dark))`,} +@data(Vec4F32) df_g_theme_preset_colors__solarized_light:{@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.solarized_light))`,} +@data(Vec4F32) df_g_theme_preset_colors__handmade_hero: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.handmade_hero))`,} +@data(Vec4F32) df_g_theme_preset_colors__four_coder: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.four_coder))`,} +@data(Vec4F32) df_g_theme_preset_colors__far_manager: {@expand(DF_ThemeColorTable a) `rgba_from_u32_lit_comp($(a.far_manager))`;} +@data(`Vec4F32*`) df_g_theme_preset_colors_table: +{ + @expand(DF_ThemePresetTable a) `df_g_theme_preset_colors__$(a.name_lower)`, +} + +@data(String8) df_g_theme_color_display_string_table: +{ + @expand(DF_ThemeColorTable a) `str8_lit_comp("$(a.display_name)")` +} + +@data(String8) df_g_theme_color_cfg_string_table: +{ + @expand(DF_ThemeColorTable a) `str8_lit_comp("$(a.name_lower)")` +} + +//////////////////////////////// +//~ rjf: Settings + +@table(name name_lower display_string default_per_window default_s32 s32_min s32_max) +DF_SettingTable: +{ + {HoverAnimations hover_animations "Hover Animations" 0 1 0 1 } + {PressAnimations press_animations "Press Animations" 0 1 0 1 } + {FocusAnimations focus_animations "Focus Animations" 0 1 0 1 } + {TooltipAnimations tooltip_animations "Tooltip Animations" 0 1 0 1 } + {MenuAnimations menu_animations "Menu Animations" 0 1 0 1 } + {ScrollingAnimations scrolling_animations "Scrolling Animations" 0 1 0 1 } + {BackgroundBlur background_blur "Background Blur" 0 1 0 1 } + {ThreadLines thread_lines "Thread Lines" 0 1 0 1 } + {BreakpointLines breakpoint_lines "Breakpoint Lines" 0 1 0 1 } + {ThreadGlow thread_glow "Thread Glow" 0 1 0 1 } + {BreakpointGlow breakpoint_glow "Breakpoint Glow" 0 1 0 1 } + {OpaqueBackgrounds opaque_backgrounds "Opaque Backgrounds" 0 0 0 1 } + {TabWidth tab_width "Tab Width" 0 4 1 32 } + {MainFontSize main_font_size "Main Font Size" 1 12 6 72 } + {CodeFontSize code_font_size "Code Font Size" 1 12 6 72 } + {SmoothUIText smooth_ui_text "Smooth UI Text" 1 1 0 1 } + {SmoothCodeText smooth_code_text "Smooth Code Text" 1 0 0 1 } + {HintUIText hint_ui_text "Hint UI Text" 1 1 0 1 } + {HintCodeText hint_code_text "Hint Code Text" 1 1 0 1 } +} + +@enum DF_SettingCode: +{ + @expand(DF_SettingTable a) `$(a.name)`, + COUNT +} + +@data(String8) df_g_setting_code_display_string_table: +{ + @expand(DF_SettingTable a) `str8_lit_comp("$(a.display_string)")` +} + +@data(String8) df_g_setting_code_lower_string_table: +{ + @expand(DF_SettingTable a) `str8_lit_comp("$(a.name_lower)")` +} + +@data(B8) df_g_setting_code_default_is_per_window_table: +{ + @expand(DF_SettingTable a) `$(a.default_per_window)` +} + +@data(DF_SettingVal) df_g_setting_code_default_val_table: +{ + @expand(DF_SettingTable a) `{1, $(a.default_s32)}` +} + +@data(Rng1S32) df_g_setting_code_s32_range_table: +{ + @expand(DF_SettingTable a) `{$(a.s32_min), $(a.s32_max)}` +} + +//////////////////////////////// +//~ rjf: Help/Docs/README + +@markdown +raddbg_readme: +{ + @title "The RAD Debugger (ALPHA)"; + @p "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."; + + @subtitle "Getting Started"; + @p "To launch the RAD Debugger with your executable and command line arguments, run `raddbg` from the command line like so:"; + @p "```raddbg my_program.exe --foo --bar --baz```"; + @p "For more information, see the 'Command-Line Usage' section."; + @p "Default keyboard shortcuts for common debugger controls include:"; + @unordered_list + { + @p "**Ctrl + O**: Open Source Code File"; + @p "**F10**: Step Over"; + @p "**F11**: Step Into"; + @p "**Shift + F11**: Step Out"; + @p "**F5**: Run"; + @p "**Ctrl + Shift + X**, or **Pause**: Halt All Processes"; + @p "**Shift + F5**: Kill All Processes"; + @p "**Shift + F6**: Attach To Process"; + @p "**Ctrl + F**: Search For Text (Forwards)"; + @p "**F9**: Toggle Breakpoint At Cursor"; + @p "**Ctrl + Comma**: Focus Next Panel"; + @p "**Ctrl + Shift + Comma**: Focus Previous Panel"; + @p "**Ctrl + Shift + Alt + Arrow Key**: Focus Panel In Direction"; + @p "**Ctrl + Tab**: Focus Next Tab"; + @p "**Ctrl + Shift + Tab**: Focus Previous Tab"; + @p "**Ctrl + W**: Close Tab"; + @p "**F1**: Open Command Palette"; + } + @p "For more information, see the 'Commands' section."; + @p "View rules can be used to visualize expressions differently in the watch window. Here are some examples:"; + @unordered_list + { + @p "`array:16`: Visualize a pointer as pointing to a 16-element array."; + @p "`array:(count*2)`: Visualize a pointer as pointing to a `count*2`-element array."; + @p "`list:next`: Visualize a linked list flatly, where each node has a `next` pointer, which points to the next node in the list."; + @p "`hex`: Visualize numeric literals as base-16 (hexadecimal)."; + @p "`dec`: Visualize numeric literals as base-10 (decimal)."; + @p "`oct`: Visualize numeric literals as base-8 (octal)."; + @p "`bin`: Visualize numeric literals as base-2 (binary)."; + @p "`omit:(foo bar baz)`: Prohibits members named `foo`, `bar`, and `baz` from being displayed."; + @p "`only:(foo bar baz)`: Only allows members named `foo`, `bar`, and `baz` to be displayed."; + } + @p "Multiple view rules can be specified on one line, so they can be combined like so:"; + @p "```list:next, hex, omit:next```"; + @p "For more information, see the 'View Rules' section."; + + @subtitle "Command-Line Usage"; + @p "When run normally, either by launching through a file explorer or running from a command line without arguments, `raddbg` will open a new instance of the debugger. But it also supports a number of command line options for a number of other purposes. These options are specified with a `-` or `--` prefix, followed by the name of the option, and if the option requires a parameter, followed by a `:` or `=`, followed by the parameter's content. A list of the possible options follows:"; + @unordered_list + { + @p "`--help` Displays a help menu which documents the possible command line options."; + @p "`--user:` Specifies a path to the user file which the debugger should use instead of the default. The default user file is stored at `%appdata%/raddbg/default.raddbg_user`. For more information on user files, read the 'User & Profile Files' section."; + @p "`--project:` Specifies a path to the project file which the debugger should use instead of the default. The default project file is stored at `%appdata%/raddbg/default.raddbg_project`. For more information on project files, read the 'User & Project Files' section."; + @p "`--auto_run` Specifies that the debugger should immediately run its selected targets upon launching."; + @p "`--auto_step` Specifies that the debugger should immediately step into its selected targets upon launching."; + //@p "`--ipc` Specifies that the launched debugger instance is for communicating a command to another instance of the debugger. In this mode, any non-argument command line contents will be used to express a command. For more information on commands, read the 'Commands' section. For more information on driving another debugger instance with this argument, read the 'Driving Another Debugger Instance' section." + } + @p "On the command line, non-options (meaning any command line arguments *not* prefixed with a `-` or `--`) can also be specified. with normal usage, they are interpreted as the command line for a target (see the 'Targets' section)." + // add when --ipc support is ready: "When driving another debugger instance (using the `--ipc` argument), this additional command line text is used to encode a debugger command."; + @p "The debugger will stop parsing `-` and `--` prefixes as arguments after seeing a standalone `--`, *or* after seeing the first non-option argument, when reading the command line left-to-right. Some examples of command line usage and their interpretations are below:"; + @unordered_list + { + @p "`raddbg --foo --bar --a:b --c=d test.exe` All options are used to configure `raddbg`. `test.exe` is interpreted as a target executable. `b` is interpreted as the parameter for the `a` option. `d` is interpreted as the parameter for the `c` option."; + @p "`raddbg test.exe --foo --bar` `test.exe` is interpreted as a target executable. `--foo --bar` is interpreted as arguments for `test.exe`, and thus are *not* used to configure `raddbg`."; + @p "`raddbg -- test.exe` `test.exe` is interpreted as a target executable."; + //@p "`raddbg --ipc find_code_location \"c:/foo/bar/baz.c:123:1\"` `--ipc` configures `raddbg` to drive another instance of `raddbg`. The remainder of the text is interpreted as a command."; + @p "`raddbg \"C:/path with spaces/test.exe\" --foo --bar` A target is formed from the `test.exe` path, and `--foo --bar` are interpreted as arguments to the `test.exe` target."; + } + + @subtitle "Windows, Panels, & Tabs"; + @p "Each opened *window* in the debugger frontend is subdivided into *panels*. Panels subdivide regions of their window without overlapping. Each panel can contain multiple *tabs*, and can have one tab selected at any time. Tabs can be dragged and dropped between panels. Each tab is used to view one of the many supported debugger interfaces, including source code, disassembly, memory, or watches. When a tab is selected, that interface will fill the tab's containing panel's region of the containing window."; + @p "There are no 'special' windows, panels, or tabs; the debugger is written such that the number of windows, each window's panel organization, and the placement and arrangement of tabs can all be organized in a large variety of ways."; + @p "A list of debugger interfaces, which can occupy tabs, are below:"; + @unordered_list + { + @expand(DF_GfxViewTable a) @p "$(a.inc_in_docs -> '`'..a.display_string..'` '..a.docs_desc)"; + } + + @subtitle "Commands"; + @p "The debugger is operated with *commands*. Commands may be manually executed in the debugger UI through the `Commands` menu (which you can open either in the `View` menu bar list, or by using the keybinding, which is F1 by default). Operations in the debugger UI are implemented with commands, so if it's ever unclear how to accomplish some operation through the UI, a useful fallback is searching for and running the command through the command menu."; + //@p "Commands are also how a debugger instance launched with `--ipc` may communicate with a primary debugger instance."; + //@p "A list of commands, how they're referred to textually (for the purposes of `--ipc` debugger instances), and their descriptions are below:"; + @p "A list of commands and their descriptions are below:"; + @unordered_list + { + @expand(DF_CoreCmdTable a) @p "$(a.lister_omit == 0 -> '`'..a.display_name..'` '..'(`'..a.string..'`) '..a.desc)"; + } + + @subtitle "Targets"; + @p "A *target* is one executable and configuration for launching that executable, including command line arguments and working directory (the directory from which the executable is launched). Each target may also have a custom label (replaces the executable path when visualizing the target), and the name of a custom entry point function (when the default entry points - `main`, `WinMain`, etc. - are not desired when stepping into the program upon launch). The debugger can have several targets at once. Each target can also be enabled or disabled. Some operations work on all enabled targets - for instance, the `Run` or `Kill All` commands (standardly bound as F5 or Shift + F5). Enabling and disabling targets allows one to filter which targets are currently being worked with."; + @p "To add a target, you can run the `Add Target` command. A target is also created automatically from command line arguments - the rules for how this happens can be found in the `Command-Line Usage` section."; + @p "Targets created through command line usage are temporary, meaning they are not persistently saved across runs of the debugger. To change this, you can right click the command-line-created target in the `Targets` view, and click `Save To Project`. After doing so, the target will be restored across runs, and will no longer need to be specified on the command-line."; + + @subtitle "View Rules"; + @p "*View Rules* are used to transform the way that evaluations in the debugger are visualized. An evaluation is produced by taking an expression string - for instance, the name of a variable - and using debug info and information from an attached process' live runtime (memory, registers, and so on) to interpret it."; + @p "Evaluations may be visualized in a variety of ways. A 64-bit unsigned integer may be visualized as a textual representation of the value with a radix of 10. A 32-bit floating-point value may be visualized as a textual representation of the value. An array of 32-bit floating-point values can be visualized as a list of textual representations of those values."; + @p "But all of these cases may be visualized in a number of other ways, as well. A 64-bit unsigned integer may be more usefully represented with a radix of 16, 8, or 2. An array of 32-bit floating-point values may encode the R, G, B, and A components of a color, or vertex positions for 3D geometry, or samples for a waveform. An array of bytes may encode raw pixel data for an image, or image data in a compressed format. A struct may have several members which are not useful to look at all the time. A struct may form the head of a linked list, and a flat linked list representation may be more preferable than the traditional watch view representation, which adds an additional layer of hierarchical nesting with the expansion of each 'next' pointer in a linked list. When designing the debugger, we felt that the traditional memory view and watch view representations of data in a debugged-process were not sufficient. View rules were added to the traditional watch view structure to allow per-row specification of extra visualization parameters."; + @p "View rules are specified with the name of a view rule, and depending on the view rule, a `:`, followed by parameters for the view rule. These parameters may be whitespace delimited, but importantly, multiple view rules may be specified per-row in a watch view. To explicitly separate the parameters of one view rule from the name of another - for instance, in a case like `array:16 bin`, where `bin` will not be interpreted as a view rule, but as a parameter of `array` - then commas and semicolons may be used to separate the two view rules (`array:16, bin`), or parentheses/braces/brackets may also be used to explicitly delimit the view rule parameters (`array:(16) bin`)."; + @p "A list of currently-supported view rules are below:"; + @unordered_list + { + @expand(DF_CoreViewRuleTable a) @p "$(a.docs == 'x' -> '`'..a.string..'` ('..a.display_name..') '..a.description)"; + } + + @subtitle "Breakpoints"; + @p "Breakpoints interrupt execution of attached processes. They may be placed on specific code addresses, lines of source code, on specific symbol names. In the latter two cases, the higher level locations are resolved to code addresses. If there is no code associated with a line of source code, then the resolution path chooses to use the next closest line of source code in the same file. A symbol name breakpoint will only work if the symbol name is found within loaded debug info."; + @p "Breakpoints may have stop conditions attached to them. When a breakpoint is hit by a thread, before it stops execution, the stop condition is evaluated, and if it evaluates to a nonzero value, only then is execution stopped."; + @p "Each breakpoint has a hit count. Every time a breakpoint causes execution to stop, this counter is increased."; + @p "Processor breakpoints are not currently supported, but planned to be in the future."; + + @subtitle "User & Project Files"; + @p "Applicable state controlling the debugger's appearance, behavior, targets, breakpoints, and other configurations is saved and reloaded across runs of the debugger through both *user files* and *project files*. These files are auto-saved. These files are written in a textual format which can be hand-edited as necessary, but they're also continuously re-read and re-written by the debugger. By default, the debugger uses `%appdata%/raddbg/default.raddbg_user` for its user file path, and `%appdata%/raddbg/default.raddbg_project` for its project file path. These paths can be overridden on the command line (see the 'Command-Line Usage' section)."; + @p "The *user file* defaultly stores file path maps, windows (including their preferred monitor, placement, and size), each window's panel layout and tabs, keybindings, theme colors, and fonts."; + @p "The *project file* defaultly stores targets, breakpoints, watch pins, and exception code filters."; + @p "Because both can be hand-edited, however, if you want to store something normally stored in a user file in a project file, or vice versa, this can be done by hand transferring the textual data from one file to another. There is no path in the debugger's UI to support this transfer, currently, although this is planned."; + + //@subtitle "Driving Another Debugger Instance"; + //@p "When the debugger is launched with the `--ipc` command-line argument, it does not launch another instance of the graphical debugger. Instead, it launches, sends a string encoding a command to a running instance of the graphical debugger, and then terminates. The set of commands which can be sent are identical to those which can be run from the debugger's UI itself, but these commands must be encoded textually (through the other command-line arguments). These commands are described in the 'Commands' section."; +} diff --git a/src/df/gfx/df_view_rules.h b/src/df/gfx/df_view_rules.h index 9d895ed1..13cfde74 100644 --- a/src/df/gfx/df_view_rules.h +++ b/src/df/gfx/df_view_rules.h @@ -1,144 +1,144 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DF_VIEW_RULES_H -#define DF_VIEW_RULES_H - -//////////////////////////////// -//~ rjf: "rgba" - -typedef struct DF_VR_RGBAState DF_VR_RGBAState; -struct DF_VR_RGBAState -{ - Vec4F32 hsva; - U64 memgen_idx; -}; - -internal Vec4F32 df_vr_rgba_from_eval(DF_Eval eval, TG_Graph *graph, RDI_Parsed *raddbg, DF_Entity *process); -internal void df_vr_eval_commit_rgba(DF_Eval eval, TG_Graph *graph, RDI_Parsed *raddbg, DF_CtrlCtx *ctrl_ctx, Vec4F32 rgba); - -//////////////////////////////// -//~ rjf: "text" - -typedef struct DF_TxtTopologyInfo DF_TxtTopologyInfo; -struct DF_TxtTopologyInfo -{ - TXT_LangKind lang; - U64 size_cap; -}; - -typedef struct DF_VR_TextState DF_VR_TextState; -struct DF_VR_TextState -{ - B32 initialized; - TxtPt cursor; - TxtPt mark; - S64 preferred_column; - U64 last_open_frame_idx; - F32 loaded_t; -}; - -internal DF_TxtTopologyInfo df_vr_txt_topology_info_from_cfg(DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, DF_CfgNode *cfg); - -//////////////////////////////// -//~ rjf: "disasm" - -typedef struct DF_DisasmTopologyInfo DF_DisasmTopologyInfo; -struct DF_DisasmTopologyInfo -{ - Architecture arch; - U64 size_cap; -}; - -typedef struct DF_VR_DisasmState DF_VR_DisasmState; -struct DF_VR_DisasmState -{ - B32 initialized; - TxtPt cursor; - TxtPt mark; - S64 preferred_column; - U64 last_open_frame_idx; - F32 loaded_t; -}; - -internal DF_DisasmTopologyInfo df_vr_disasm_topology_info_from_cfg(DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, DF_CfgNode *cfg); - -//////////////////////////////// -//~ rjf: "bitmap" - -typedef struct DF_BitmapTopologyInfo DF_BitmapTopologyInfo; -struct DF_BitmapTopologyInfo -{ - U64 width; - U64 height; - R_Tex2DFormat fmt; -}; - -typedef struct DF_BitmapViewState DF_BitmapViewState; -struct DF_BitmapViewState -{ - Vec2F32 view_center_pos; - F32 zoom; - DF_BitmapTopologyInfo top; -}; - -typedef struct DF_VR_BitmapState DF_VR_BitmapState; -struct DF_VR_BitmapState -{ - U64 last_open_frame_idx; - F32 loaded_t; -}; - -typedef struct DF_VR_BitmapBoxDrawData DF_VR_BitmapBoxDrawData; -struct DF_VR_BitmapBoxDrawData -{ - Rng2F32 src; - R_Handle texture; - F32 loaded_t; - B32 hovered; - Vec2S32 mouse_px; - F32 ui_per_bmp_px; -}; - -internal Vec2F32 df_bitmap_view_state__screen_from_canvas_pos(DF_BitmapViewState *bvs, Rng2F32 rect, Vec2F32 cvs); -internal Rng2F32 df_bitmap_view_state__screen_from_canvas_rect(DF_BitmapViewState *bvs, Rng2F32 rect, Rng2F32 cvs); -internal Vec2F32 df_bitmap_view_state__canvas_from_screen_pos(DF_BitmapViewState *bvs, Rng2F32 rect, Vec2F32 scr); -internal Rng2F32 df_bitmap_view_state__canvas_from_screen_rect(DF_BitmapViewState *bvs, Rng2F32 rect, Rng2F32 scr); -internal DF_BitmapTopologyInfo df_vr_bitmap_topology_info_from_cfg(DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, DF_CfgNode *cfg); - -//////////////////////////////// -//~ rjf: "geo" - -typedef struct DF_GeoTopologyInfo DF_GeoTopologyInfo; -struct DF_GeoTopologyInfo -{ - U64 index_count; - Rng1U64 vertices_vaddr_range; -}; - -typedef struct DF_VR_GeoState DF_VR_GeoState; -struct DF_VR_GeoState -{ - B32 initialized; - U64 last_open_frame_idx; - F32 loaded_t; - F32 pitch; - F32 pitch_target; - F32 yaw; - F32 yaw_target; - F32 zoom; - F32 zoom_target; -}; - -typedef struct DF_VR_GeoBoxDrawData DF_VR_GeoBoxDrawData; -struct DF_VR_GeoBoxDrawData -{ - DF_ExpandKey key; - R_Handle vertex_buffer; - R_Handle index_buffer; - F32 loaded_t; -}; - -internal DF_GeoTopologyInfo df_vr_geo_topology_info_from_cfg(DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, DF_CfgNode *cfg); - -#endif // DF_VIEW_RULES_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DF_VIEW_RULES_H +#define DF_VIEW_RULES_H + +//////////////////////////////// +//~ rjf: "rgba" + +typedef struct DF_VR_RGBAState DF_VR_RGBAState; +struct DF_VR_RGBAState +{ + Vec4F32 hsva; + U64 memgen_idx; +}; + +internal Vec4F32 df_vr_rgba_from_eval(DF_Eval eval, TG_Graph *graph, RDI_Parsed *raddbg, DF_Entity *process); +internal void df_vr_eval_commit_rgba(DF_Eval eval, TG_Graph *graph, RDI_Parsed *raddbg, DF_CtrlCtx *ctrl_ctx, Vec4F32 rgba); + +//////////////////////////////// +//~ rjf: "text" + +typedef struct DF_TxtTopologyInfo DF_TxtTopologyInfo; +struct DF_TxtTopologyInfo +{ + TXT_LangKind lang; + U64 size_cap; +}; + +typedef struct DF_VR_TextState DF_VR_TextState; +struct DF_VR_TextState +{ + B32 initialized; + TxtPt cursor; + TxtPt mark; + S64 preferred_column; + U64 last_open_frame_idx; + F32 loaded_t; +}; + +internal DF_TxtTopologyInfo df_vr_txt_topology_info_from_cfg(DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, DF_CfgNode *cfg); + +//////////////////////////////// +//~ rjf: "disasm" + +typedef struct DF_DisasmTopologyInfo DF_DisasmTopologyInfo; +struct DF_DisasmTopologyInfo +{ + Architecture arch; + U64 size_cap; +}; + +typedef struct DF_VR_DisasmState DF_VR_DisasmState; +struct DF_VR_DisasmState +{ + B32 initialized; + TxtPt cursor; + TxtPt mark; + S64 preferred_column; + U64 last_open_frame_idx; + F32 loaded_t; +}; + +internal DF_DisasmTopologyInfo df_vr_disasm_topology_info_from_cfg(DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, DF_CfgNode *cfg); + +//////////////////////////////// +//~ rjf: "bitmap" + +typedef struct DF_BitmapTopologyInfo DF_BitmapTopologyInfo; +struct DF_BitmapTopologyInfo +{ + U64 width; + U64 height; + R_Tex2DFormat fmt; +}; + +typedef struct DF_BitmapViewState DF_BitmapViewState; +struct DF_BitmapViewState +{ + Vec2F32 view_center_pos; + F32 zoom; + DF_BitmapTopologyInfo top; +}; + +typedef struct DF_VR_BitmapState DF_VR_BitmapState; +struct DF_VR_BitmapState +{ + U64 last_open_frame_idx; + F32 loaded_t; +}; + +typedef struct DF_VR_BitmapBoxDrawData DF_VR_BitmapBoxDrawData; +struct DF_VR_BitmapBoxDrawData +{ + Rng2F32 src; + R_Handle texture; + F32 loaded_t; + B32 hovered; + Vec2S32 mouse_px; + F32 ui_per_bmp_px; +}; + +internal Vec2F32 df_bitmap_view_state__screen_from_canvas_pos(DF_BitmapViewState *bvs, Rng2F32 rect, Vec2F32 cvs); +internal Rng2F32 df_bitmap_view_state__screen_from_canvas_rect(DF_BitmapViewState *bvs, Rng2F32 rect, Rng2F32 cvs); +internal Vec2F32 df_bitmap_view_state__canvas_from_screen_pos(DF_BitmapViewState *bvs, Rng2F32 rect, Vec2F32 scr); +internal Rng2F32 df_bitmap_view_state__canvas_from_screen_rect(DF_BitmapViewState *bvs, Rng2F32 rect, Rng2F32 scr); +internal DF_BitmapTopologyInfo df_vr_bitmap_topology_info_from_cfg(DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, DF_CfgNode *cfg); + +//////////////////////////////// +//~ rjf: "geo" + +typedef struct DF_GeoTopologyInfo DF_GeoTopologyInfo; +struct DF_GeoTopologyInfo +{ + U64 index_count; + Rng1U64 vertices_vaddr_range; +}; + +typedef struct DF_VR_GeoState DF_VR_GeoState; +struct DF_VR_GeoState +{ + B32 initialized; + U64 last_open_frame_idx; + F32 loaded_t; + F32 pitch; + F32 pitch_target; + F32 yaw; + F32 yaw_target; + F32 zoom; + F32 zoom_target; +}; + +typedef struct DF_VR_GeoBoxDrawData DF_VR_GeoBoxDrawData; +struct DF_VR_GeoBoxDrawData +{ + DF_ExpandKey key; + R_Handle vertex_buffer; + R_Handle index_buffer; + F32 loaded_t; +}; + +internal DF_GeoTopologyInfo df_vr_geo_topology_info_from_cfg(DI_Scope *scope, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, EVAL_String2ExprMap *macro_map, DF_CfgNode *cfg); + +#endif // DF_VIEW_RULES_H diff --git a/src/draw/draw.h b/src/draw/draw.h index f93627f8..f967779e 100644 --- a/src/draw/draw.h +++ b/src/draw/draw.h @@ -1,191 +1,191 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef DRAW_H -#define DRAW_H - -//////////////////////////////// -//~ rjf: Fancy String Types - -typedef struct D_FancyString D_FancyString; -struct D_FancyString -{ - F_Tag font; - String8 string; - Vec4F32 color; - F32 size; - F32 underline_thickness; - F32 strikethrough_thickness; -}; - -typedef struct D_FancyStringNode D_FancyStringNode; -struct D_FancyStringNode -{ - D_FancyStringNode *next; - D_FancyString v; -}; - -typedef struct D_FancyStringList D_FancyStringList; -struct D_FancyStringList -{ - D_FancyStringNode *first; - D_FancyStringNode *last; - U64 node_count; - U64 total_size; -}; - -typedef struct D_FancyRun D_FancyRun; -struct D_FancyRun -{ - F_Run run; - Vec4F32 color; - F32 underline_thickness; - F32 strikethrough_thickness; -}; - -typedef struct D_FancyRunNode D_FancyRunNode; -struct D_FancyRunNode -{ - D_FancyRunNode *next; - D_FancyRun v; -}; - -typedef struct D_FancyRunList D_FancyRunList; -struct D_FancyRunList -{ - D_FancyRunNode *first; - D_FancyRunNode *last; - U64 node_count; - Vec2F32 dim; -}; - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/draw.meta.h" - -//////////////////////////////// -//~ rjf: Draw Bucket Types - -typedef struct D_Bucket D_Bucket; -struct D_Bucket -{ - R_PassList passes; - U64 stack_gen; - U64 last_cmd_stack_gen; - D_BucketStackDecls; -}; - -//////////////////////////////// -//~ rjf: Thread Context - -typedef struct D_BucketSelectionNode D_BucketSelectionNode; -struct D_BucketSelectionNode -{ - D_BucketSelectionNode *next; - D_Bucket *bucket; -}; - -typedef struct D_ThreadCtx D_ThreadCtx; -struct D_ThreadCtx -{ - Arena *arena; - U64 arena_frame_start_pos; - D_BucketSelectionNode *top_bucket; - D_BucketSelectionNode *free_bucket_selection; -}; - -//////////////////////////////// -//~ rjf: Globals - -thread_static D_ThreadCtx *d_thread_ctx = 0; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal U64 d_hash_from_string(String8 string); - -//////////////////////////////// -//~ rjf: Fancy String Type Functions - -internal void d_fancy_string_list_push(Arena *arena, D_FancyStringList *list, D_FancyString *str); -internal void d_fancy_string_list_concat_in_place(D_FancyStringList *dst, D_FancyStringList *to_push); -internal String8 d_string_from_fancy_string_list(Arena *arena, D_FancyStringList *list); -internal D_FancyRunList d_fancy_run_list_from_fancy_string_list(Arena *arena, F32 tab_size_px, F_RasterFlags flags, D_FancyStringList *strs); -internal D_FancyRunList d_fancy_run_list_copy(Arena *arena, D_FancyRunList *src); - -//////////////////////////////// -//~ rjf: Top-Level API -// -// (Frame boundaries & bucket submission) - -internal void d_begin_frame(void); -internal void d_submit_bucket(OS_Handle os_window, R_Handle r_window, D_Bucket *bucket); - -//////////////////////////////// -//~ rjf: Bucket Construction & Selection API -// -// (Bucket: Handle to sequence of many render passes, constructed by this layer) - -internal D_Bucket *d_bucket_make(void); -internal void d_push_bucket(D_Bucket *bucket); -internal void d_pop_bucket(void); -internal D_Bucket *d_top_bucket(void); -#define D_BucketScope(b) DeferLoop(d_push_bucket(b), d_pop_bucket()) - -//////////////////////////////// -//~ rjf: Bucket Stacks -// -// (Pushing/popping implicit draw parameters) - -internal R_Tex2DSampleKind d_push_tex2d_sample_kind(R_Tex2DSampleKind v); -internal Mat3x3F32 d_push_xform2d(Mat3x3F32 v); -internal Rng2F32 d_push_clip(Rng2F32 v); -internal F32 d_push_transparency(F32 v); -internal R_Tex2DSampleKind d_pop_tex2d_sample_kind(void); -internal Mat3x3F32 d_pop_xform2d(void); -internal Rng2F32 d_pop_clip(void); -internal F32 d_pop_transparency(void); -internal R_Tex2DSampleKind d_top_tex2d_sample_kind(void); -internal Mat3x3F32 d_top_xform2d(void); -internal Rng2F32 d_top_clip(void); -internal F32 d_top_transparency(void); - -#define D_Tex2DSampleKindScope(v) DeferLoop(d_push_tex2d_sample_kind(v), d_pop_tex2d_sample_kind()) -#define D_XForm2DScope(v) DeferLoop(d_push_xform2d(v), d_pop_xform2d()) -#define D_ClipScope(v) DeferLoop(d_push_clip(v), d_pop_clip()) -#define D_TransparencyScope(v) DeferLoop(d_push_transparency(v), d_pop_transparency()) - -//////////////////////////////// -//~ rjf: Core Draw Calls -// -// (Apply to the calling thread's currently selected bucket) - -//- rjf: rectangles -internal inline R_Rect2DInst *d_rect(Rng2F32 dst, Vec4F32 color, F32 corner_radius, F32 border_thickness, F32 edge_softness); - -//- rjf: images -internal inline R_Rect2DInst *d_img(Rng2F32 dst, Rng2F32 src, R_Handle texture, Vec4F32 color, F32 corner_radius, F32 border_thickness, F32 edge_softness); - -//- rjf: blurs -internal R_PassParams_Blur *d_blur(Rng2F32 rect, F32 blur_size, F32 corner_radius); - -//- rjf: 3d rendering pass params -internal R_PassParams_Geo3D *d_geo3d_begin(Rng2F32 viewport, Mat4x4F32 view, Mat4x4F32 projection); - -//- rjf: meshes -internal R_Mesh3DInst *d_mesh(R_Handle mesh_vertices, R_Handle mesh_indices, R_GeoTopologyKind mesh_geo_topology, R_GeoVertexFlags mesh_geo_vertex_flags, R_Handle albedo_tex, Mat4x4F32 inst_xform); - -//- rjf: collating one pre-prepped bucket into parent bucket -internal void d_sub_bucket(D_Bucket *bucket); - -//////////////////////////////// -//~ rjf: Draw Call Helpers - -//- rjf: text -internal void d_truncated_fancy_run_list(Vec2F32 p, D_FancyRunList *list, F32 max_x, F_Run trailer_run); -internal void d_truncated_fancy_run_fuzzy_matches(Vec2F32 p, D_FancyRunList *list, F32 max_x, FuzzyMatchRangeList *ranges, Vec4F32 color); -internal void d_text_run(Vec2F32 p, Vec4F32 color, F_Run run); -internal void d_text(F_Tag font, F32 size, F32 base_align_px, F32 tab_size_px, F_RasterFlags flags, Vec2F32 p, Vec4F32 color, String8 string); - -#endif // DRAW_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef DRAW_H +#define DRAW_H + +//////////////////////////////// +//~ rjf: Fancy String Types + +typedef struct D_FancyString D_FancyString; +struct D_FancyString +{ + F_Tag font; + String8 string; + Vec4F32 color; + F32 size; + F32 underline_thickness; + F32 strikethrough_thickness; +}; + +typedef struct D_FancyStringNode D_FancyStringNode; +struct D_FancyStringNode +{ + D_FancyStringNode *next; + D_FancyString v; +}; + +typedef struct D_FancyStringList D_FancyStringList; +struct D_FancyStringList +{ + D_FancyStringNode *first; + D_FancyStringNode *last; + U64 node_count; + U64 total_size; +}; + +typedef struct D_FancyRun D_FancyRun; +struct D_FancyRun +{ + F_Run run; + Vec4F32 color; + F32 underline_thickness; + F32 strikethrough_thickness; +}; + +typedef struct D_FancyRunNode D_FancyRunNode; +struct D_FancyRunNode +{ + D_FancyRunNode *next; + D_FancyRun v; +}; + +typedef struct D_FancyRunList D_FancyRunList; +struct D_FancyRunList +{ + D_FancyRunNode *first; + D_FancyRunNode *last; + U64 node_count; + Vec2F32 dim; +}; + +//////////////////////////////// +//~ rjf: Generated Code + +#include "generated/draw.meta.h" + +//////////////////////////////// +//~ rjf: Draw Bucket Types + +typedef struct D_Bucket D_Bucket; +struct D_Bucket +{ + R_PassList passes; + U64 stack_gen; + U64 last_cmd_stack_gen; + D_BucketStackDecls; +}; + +//////////////////////////////// +//~ rjf: Thread Context + +typedef struct D_BucketSelectionNode D_BucketSelectionNode; +struct D_BucketSelectionNode +{ + D_BucketSelectionNode *next; + D_Bucket *bucket; +}; + +typedef struct D_ThreadCtx D_ThreadCtx; +struct D_ThreadCtx +{ + Arena *arena; + U64 arena_frame_start_pos; + D_BucketSelectionNode *top_bucket; + D_BucketSelectionNode *free_bucket_selection; +}; + +//////////////////////////////// +//~ rjf: Globals + +thread_static D_ThreadCtx *d_thread_ctx = 0; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal U64 d_hash_from_string(String8 string); + +//////////////////////////////// +//~ rjf: Fancy String Type Functions + +internal void d_fancy_string_list_push(Arena *arena, D_FancyStringList *list, D_FancyString *str); +internal void d_fancy_string_list_concat_in_place(D_FancyStringList *dst, D_FancyStringList *to_push); +internal String8 d_string_from_fancy_string_list(Arena *arena, D_FancyStringList *list); +internal D_FancyRunList d_fancy_run_list_from_fancy_string_list(Arena *arena, F32 tab_size_px, F_RasterFlags flags, D_FancyStringList *strs); +internal D_FancyRunList d_fancy_run_list_copy(Arena *arena, D_FancyRunList *src); + +//////////////////////////////// +//~ rjf: Top-Level API +// +// (Frame boundaries & bucket submission) + +internal void d_begin_frame(void); +internal void d_submit_bucket(OS_Handle os_window, R_Handle r_window, D_Bucket *bucket); + +//////////////////////////////// +//~ rjf: Bucket Construction & Selection API +// +// (Bucket: Handle to sequence of many render passes, constructed by this layer) + +internal D_Bucket *d_bucket_make(void); +internal void d_push_bucket(D_Bucket *bucket); +internal void d_pop_bucket(void); +internal D_Bucket *d_top_bucket(void); +#define D_BucketScope(b) DeferLoop(d_push_bucket(b), d_pop_bucket()) + +//////////////////////////////// +//~ rjf: Bucket Stacks +// +// (Pushing/popping implicit draw parameters) + +internal R_Tex2DSampleKind d_push_tex2d_sample_kind(R_Tex2DSampleKind v); +internal Mat3x3F32 d_push_xform2d(Mat3x3F32 v); +internal Rng2F32 d_push_clip(Rng2F32 v); +internal F32 d_push_transparency(F32 v); +internal R_Tex2DSampleKind d_pop_tex2d_sample_kind(void); +internal Mat3x3F32 d_pop_xform2d(void); +internal Rng2F32 d_pop_clip(void); +internal F32 d_pop_transparency(void); +internal R_Tex2DSampleKind d_top_tex2d_sample_kind(void); +internal Mat3x3F32 d_top_xform2d(void); +internal Rng2F32 d_top_clip(void); +internal F32 d_top_transparency(void); + +#define D_Tex2DSampleKindScope(v) DeferLoop(d_push_tex2d_sample_kind(v), d_pop_tex2d_sample_kind()) +#define D_XForm2DScope(v) DeferLoop(d_push_xform2d(v), d_pop_xform2d()) +#define D_ClipScope(v) DeferLoop(d_push_clip(v), d_pop_clip()) +#define D_TransparencyScope(v) DeferLoop(d_push_transparency(v), d_pop_transparency()) + +//////////////////////////////// +//~ rjf: Core Draw Calls +// +// (Apply to the calling thread's currently selected bucket) + +//- rjf: rectangles +internal inline R_Rect2DInst *d_rect(Rng2F32 dst, Vec4F32 color, F32 corner_radius, F32 border_thickness, F32 edge_softness); + +//- rjf: images +internal inline R_Rect2DInst *d_img(Rng2F32 dst, Rng2F32 src, R_Handle texture, Vec4F32 color, F32 corner_radius, F32 border_thickness, F32 edge_softness); + +//- rjf: blurs +internal R_PassParams_Blur *d_blur(Rng2F32 rect, F32 blur_size, F32 corner_radius); + +//- rjf: 3d rendering pass params +internal R_PassParams_Geo3D *d_geo3d_begin(Rng2F32 viewport, Mat4x4F32 view, Mat4x4F32 projection); + +//- rjf: meshes +internal R_Mesh3DInst *d_mesh(R_Handle mesh_vertices, R_Handle mesh_indices, R_GeoTopologyKind mesh_geo_topology, R_GeoVertexFlags mesh_geo_vertex_flags, R_Handle albedo_tex, Mat4x4F32 inst_xform); + +//- rjf: collating one pre-prepped bucket into parent bucket +internal void d_sub_bucket(D_Bucket *bucket); + +//////////////////////////////// +//~ rjf: Draw Call Helpers + +//- rjf: text +internal void d_truncated_fancy_run_list(Vec2F32 p, D_FancyRunList *list, F32 max_x, F_Run trailer_run); +internal void d_truncated_fancy_run_fuzzy_matches(Vec2F32 p, D_FancyRunList *list, F32 max_x, FuzzyMatchRangeList *ranges, Vec4F32 color); +internal void d_text_run(Vec2F32 p, Vec4F32 color, F_Run run); +internal void d_text(F_Tag font, F32 size, F32 base_align_px, F32 tab_size_px, F_RasterFlags flags, Vec2F32 p, Vec4F32 color, String8 string); + +#endif // DRAW_H diff --git a/src/draw/draw.mdesk b/src/draw/draw.mdesk index cc4be4f2..5de25390 100644 --- a/src/draw/draw.mdesk +++ b/src/draw/draw.mdesk @@ -1,58 +1,58 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -@table(name, name_lower, type, default_init) -D_StackTable: -{ - {Tex2DSampleKind tex2d_sample_kind R_Tex2DSampleKind `R_Tex2DSampleKind_Nearest` } - {XForm2D xform2d Mat3x3F32 `{1, 0, 0, 0, 1, 0, 0, 0, 1}` } - {Clip clip Rng2F32 `{0}` } - {Transparency transparency F32 `0` } -} - -@gen -{ - @expand(D_StackTable a) `typedef struct D_$(a.name)Node D_$(a.name)Node; struct D_$(a.name)Node {D_$(a.name)Node *next; $(a.type) v;};`; -} - -@gen -{ - `#define D_BucketStackDecls struct{\\`; - @expand(D_StackTable a) `D_$(a.name)Node *top_$(a.name_lower);\\`; - `}`; -} - -@gen -{ - @expand(D_StackTable a) `read_only global D_$(a.name)Node d_nil_$(a.name_lower) = {0, $(a.default_init)};`; -} - -@gen -{ - `#define D_BucketStackInits(b) do{\\`; - @expand(D_StackTable a) `(b)->top_$(a.name_lower) = &d_nil_$(a.name_lower);\\`; - `}while(0)`; -} - -@gen -{ - `#if 0`; - @expand(D_StackTable a) `internal $(a.type) $(=>35) d_push_$(a.name_lower)($(a.type) v);`; - @expand(D_StackTable a) `internal $(a.type) $(=>35) d_pop_$(a.name_lower)(void);`; - @expand(D_StackTable a) `internal $(a.type) $(=>35) d_top_$(a.name_lower)(void);`; - `#endif`; -} - -@gen @c_file -{ - @expand(D_StackTable a) `internal $(a.type) $(=>35) d_push_$(a.name_lower)($(a.type) v) {D_StackPushImpl($(a.name), $(a.name_lower), $(a.type), v);}`; - @expand(D_StackTable a) `internal $(a.type) $(=>35) d_pop_$(a.name_lower)(void) {D_StackPopImpl($(a.name), $(a.name_lower), $(a.type));}`; - @expand(D_StackTable a) `internal $(a.type) $(=>35) d_top_$(a.name_lower)(void) {D_StackTopImpl($(a.name), $(a.name_lower), $(a.type));}`; -} - -@gen -{ - `#if 0`; - @expand(D_StackTable a) `#define D_$(a.name)Scope(v) $(=>35) DeferLoop(d_push_$(a.name_lower)(v), d_pop_$(a.name_lower)())`; - `#endif`; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +@table(name, name_lower, type, default_init) +D_StackTable: +{ + {Tex2DSampleKind tex2d_sample_kind R_Tex2DSampleKind `R_Tex2DSampleKind_Nearest` } + {XForm2D xform2d Mat3x3F32 `{1, 0, 0, 0, 1, 0, 0, 0, 1}` } + {Clip clip Rng2F32 `{0}` } + {Transparency transparency F32 `0` } +} + +@gen +{ + @expand(D_StackTable a) `typedef struct D_$(a.name)Node D_$(a.name)Node; struct D_$(a.name)Node {D_$(a.name)Node *next; $(a.type) v;};`; +} + +@gen +{ + `#define D_BucketStackDecls struct{\\`; + @expand(D_StackTable a) `D_$(a.name)Node *top_$(a.name_lower);\\`; + `}`; +} + +@gen +{ + @expand(D_StackTable a) `read_only global D_$(a.name)Node d_nil_$(a.name_lower) = {0, $(a.default_init)};`; +} + +@gen +{ + `#define D_BucketStackInits(b) do{\\`; + @expand(D_StackTable a) `(b)->top_$(a.name_lower) = &d_nil_$(a.name_lower);\\`; + `}while(0)`; +} + +@gen +{ + `#if 0`; + @expand(D_StackTable a) `internal $(a.type) $(=>35) d_push_$(a.name_lower)($(a.type) v);`; + @expand(D_StackTable a) `internal $(a.type) $(=>35) d_pop_$(a.name_lower)(void);`; + @expand(D_StackTable a) `internal $(a.type) $(=>35) d_top_$(a.name_lower)(void);`; + `#endif`; +} + +@gen @c_file +{ + @expand(D_StackTable a) `internal $(a.type) $(=>35) d_push_$(a.name_lower)($(a.type) v) {D_StackPushImpl($(a.name), $(a.name_lower), $(a.type), v);}`; + @expand(D_StackTable a) `internal $(a.type) $(=>35) d_pop_$(a.name_lower)(void) {D_StackPopImpl($(a.name), $(a.name_lower), $(a.type));}`; + @expand(D_StackTable a) `internal $(a.type) $(=>35) d_top_$(a.name_lower)(void) {D_StackTopImpl($(a.name), $(a.name_lower), $(a.type));}`; +} + +@gen +{ + `#if 0`; + @expand(D_StackTable a) `#define D_$(a.name)Scope(v) $(=>35) DeferLoop(d_push_$(a.name_lower)(v), d_pop_$(a.name_lower)())`; + `#endif`; +} diff --git a/src/eval/eval.mdesk b/src/eval/eval.mdesk index 56c26b51..59671cb1 100644 --- a/src/eval/eval.mdesk +++ b/src/eval/eval.mdesk @@ -1,106 +1,106 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -@table(name num_children op_string) -// num_children - # of children packed after this node kind -// op_string - string for quick display of the operator -EVAL_ExprKindTable: -{ - { Nil 0 "" } - - { ArrayIndex 2 "[]" } - { MemberAccess 2 "." } - { Deref 1 "*" } - { Address 1 "&" } - - { Cast 2 "cast" } - { Sizeof 1 "sizeof" } - - { Neg 1 "-" } - { LogNot 1 "!" } - { BitNot 1 "~" } - { Mul 2 "*" } - { Div 2 "/" } - { Mod 2 "%" } - { Add 2 "+" } - { Sub 2 "-" } - { LShift 2 "<<" } - { RShift 2 ">>" } - { Less 2 "<" } - { LsEq 2 "<=" } - { Grtr 2 ">" } - { GrEq 2 ">=" } - { EqEq 2 "==" } - { NtEq 2 "!=" } - - { BitAnd 2 "&" } - { BitXor 2 "^" } - { BitOr 2 "|" } - { LogAnd 2 "&&" } - { LogOr 2 "||" } - - { Ternary 3 "? " } - - { LeafBytecode 0 "bytecode" } - { LeafMember 0 "member" } - { LeafU64 0 "U64" } - { LeafF64 0 "F64" } - { LeafF32 0 "F32" } - - { TypeIdent 0 "type_ident" } - { Ptr 1 "ptr" } - { Array 2 "array" } - { Func 1 "function" } - - { Define 2 "=" } - { LeafIdent 0 "leaf_ident" } -} - -@table(name display_string) -EVAL_ResultCodeTable: -{ - { Good "" } - { DivideByZero "Cannot divide by zero." } - { BadOp "Invalid operation." } - { BadOpTypes "Invalid operation types." } - { BadMemRead "Failed memory read." } - { BadRegRead "Failed register read." } - { BadFrameBase "Invalid frame base address." } - { BadModuleBase "Invalid module base address." } - { BadTLSBase "Invalid thread-local storage base address." } - { InsufficientStackSpace "Insufficient evaluation machine stack space." } - { MalformedBytecode "Malformed bytecode." } -} - -@enum(U32) EVAL_ExprKind: -{ - @expand(EVAL_ExprKindTable a) `$(a.name)`, - COUNT, -} - -@enum EVAL_ResultCode: -{ - @expand(EVAL_ResultCodeTable a) `$(a.name)`, - COUNT, -} - -@data(U8) eval_expr_kind_child_counts: -{ - @expand(EVAL_ExprKindTable a) `$(a.num_children)` -} - -@data(String8) -eval_expr_kind_strings: -{ - @expand(EVAL_ExprKindTable a) `str8_lit_comp("$(a.name)")` -} - -@data(String8) eval_result_code_display_strings: -{ - @expand(EVAL_ResultCodeTable a) `str8_lit_comp("$(a.display_string)")` -} - -@data(String8) eval_expr_op_strings: -{ - @expand(EVAL_ExprKindTable a) `str8_lit_comp("$(a.op_string)")` -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +@table(name num_children op_string) +// num_children - # of children packed after this node kind +// op_string - string for quick display of the operator +EVAL_ExprKindTable: +{ + { Nil 0 "" } + + { ArrayIndex 2 "[]" } + { MemberAccess 2 "." } + { Deref 1 "*" } + { Address 1 "&" } + + { Cast 2 "cast" } + { Sizeof 1 "sizeof" } + + { Neg 1 "-" } + { LogNot 1 "!" } + { BitNot 1 "~" } + { Mul 2 "*" } + { Div 2 "/" } + { Mod 2 "%" } + { Add 2 "+" } + { Sub 2 "-" } + { LShift 2 "<<" } + { RShift 2 ">>" } + { Less 2 "<" } + { LsEq 2 "<=" } + { Grtr 2 ">" } + { GrEq 2 ">=" } + { EqEq 2 "==" } + { NtEq 2 "!=" } + + { BitAnd 2 "&" } + { BitXor 2 "^" } + { BitOr 2 "|" } + { LogAnd 2 "&&" } + { LogOr 2 "||" } + + { Ternary 3 "? " } + + { LeafBytecode 0 "bytecode" } + { LeafMember 0 "member" } + { LeafU64 0 "U64" } + { LeafF64 0 "F64" } + { LeafF32 0 "F32" } + + { TypeIdent 0 "type_ident" } + { Ptr 1 "ptr" } + { Array 2 "array" } + { Func 1 "function" } + + { Define 2 "=" } + { LeafIdent 0 "leaf_ident" } +} + +@table(name display_string) +EVAL_ResultCodeTable: +{ + { Good "" } + { DivideByZero "Cannot divide by zero." } + { BadOp "Invalid operation." } + { BadOpTypes "Invalid operation types." } + { BadMemRead "Failed memory read." } + { BadRegRead "Failed register read." } + { BadFrameBase "Invalid frame base address." } + { BadModuleBase "Invalid module base address." } + { BadTLSBase "Invalid thread-local storage base address." } + { InsufficientStackSpace "Insufficient evaluation machine stack space." } + { MalformedBytecode "Malformed bytecode." } +} + +@enum(U32) EVAL_ExprKind: +{ + @expand(EVAL_ExprKindTable a) `$(a.name)`, + COUNT, +} + +@enum EVAL_ResultCode: +{ + @expand(EVAL_ResultCodeTable a) `$(a.name)`, + COUNT, +} + +@data(U8) eval_expr_kind_child_counts: +{ + @expand(EVAL_ExprKindTable a) `$(a.num_children)` +} + +@data(String8) +eval_expr_kind_strings: +{ + @expand(EVAL_ExprKindTable a) `str8_lit_comp("$(a.name)")` +} + +@data(String8) eval_result_code_display_strings: +{ + @expand(EVAL_ResultCodeTable a) `str8_lit_comp("$(a.display_string)")` +} + +@data(String8) eval_expr_op_strings: +{ + @expand(EVAL_ExprKindTable a) `str8_lit_comp("$(a.op_string)")` +} diff --git a/src/eval/eval_compiler.c b/src/eval/eval_compiler.c index 16f9a6de..d7342817 100644 --- a/src/eval/eval_compiler.c +++ b/src/eval/eval_compiler.c @@ -1,1641 +1,1641 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ allen: EVAL Bytecode Helpers - -internal String8 -eval_bytecode_from_oplist(Arena *arena, EVAL_OpList *list){ - ProfBeginFunction(); - // allocate output - U64 size = list->encoded_size; - U8 *str = push_array_no_zero(arena, U8, size); - - // iterate loose op nodes - U8 *ptr = str; - U8 *opl = str + size; - for (EVAL_Op *op = list->first_op; - op != 0; - op = op->next){ - U32 opcode = op->opcode; - - switch (opcode){ - default: - { - // compute bytecode advance - U8 ctrlbits = rdi_eval_op_ctrlbits_table[opcode]; - U64 extra_byte_count = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); - - U8 *next_ptr = ptr + 1 + extra_byte_count; - Assert(next_ptr <= opl); - - // fill bytecode - ptr[0] = opcode; - MemoryCopy(ptr + 1, &op->p, extra_byte_count); - - // advance output pointer - ptr = next_ptr; - }break; - - case EVAL_IRExtKind_Bytecode: - { - // compute bytecode advance - U64 size = op->bytecode.size; - U8 *next_ptr = ptr + size; - Assert(next_ptr <= opl); - - // fill bytecode - MemoryCopy(ptr, op->bytecode.str, size); - - // advance output pointer - ptr = next_ptr; - }break; - } - } - - // fill result - String8 result = {0}; - result.size = size; - result.str = str; - ProfEnd(); - return(result); -} - -internal void -eval_oplist_push_op(Arena *arena, EVAL_OpList *list, RDI_EvalOp opcode, U64 p){ - U8 ctrlbits = rdi_eval_op_ctrlbits_table[opcode]; - U32 p_size = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); - - EVAL_Op *node = push_array_no_zero(arena, EVAL_Op, 1); - node->opcode = opcode; - node->p = p; - - SLLQueuePush(list->first_op, list->last_op, node); - list->op_count += 1; - list->encoded_size += 1 + p_size; -} - -internal void -eval_oplist_push_uconst(Arena *arena, EVAL_OpList *list, U64 x){ - if (x <= 0xFF){ - eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU8, x); - } - else if (x <= 0xFFFF){ - eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU16, x); - } - else if (x <= 0xFFFFFFFF){ - eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU32, x); - } - else{ - eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU64, x); - } -} - -internal void -eval_oplist_push_sconst(Arena *arena, EVAL_OpList *list, S64 x){ - if (-0x80 <= x && x <= 0x7F){ - eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU8, (U64)x); - eval_oplist_push_op(arena, list, RDI_EvalOp_TruncSigned, 8); - } - else if (-0x8000 <= x && x <= 0x7FFF){ - eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU16, (U64)x); - eval_oplist_push_op(arena, list, RDI_EvalOp_TruncSigned, 16); - } - else if (-0x80000000ll <= x && x <= 0x7FFFFFFFll){ - eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU32, (U64)x); - eval_oplist_push_op(arena, list, RDI_EvalOp_TruncSigned, 32); - } - else{ - eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU64, (U64)x); - } -} - -internal void -eval_oplist_push_bytecode(Arena *arena, EVAL_OpList *list, String8 bytecode){ - EVAL_Op *node = push_array_no_zero(arena, EVAL_Op, 1); - node->opcode = EVAL_IRExtKind_Bytecode; - node->bytecode = bytecode; - SLLQueuePush(list->first_op, list->last_op, node); - list->op_count += 1; - list->encoded_size += bytecode.size; -} - -internal void -eval_oplist_concat_in_place(EVAL_OpList *left_dst, EVAL_OpList *right_destroyed){ - if (right_destroyed->first_op != 0){ - if (left_dst->first_op == 0){ - MemoryCopyStruct(left_dst, right_destroyed); - } - else{ - left_dst->last_op = right_destroyed->last_op; - left_dst->op_count += right_destroyed->op_count; - left_dst->encoded_size += right_destroyed->encoded_size; - } - MemoryZeroStruct(right_destroyed); - } -} - -//////////////////////////////// -//~ allen: EVAL Expression Info Functions - -internal RDI_EvalOp -eval_opcode_from_expr_kind(EVAL_ExprKind kind){ - RDI_EvalOp result = RDI_EvalOp_Stop; - switch (kind){ - case EVAL_ExprKind_Neg: result = RDI_EvalOp_Neg; break; - case EVAL_ExprKind_LogNot: result = RDI_EvalOp_LogNot; break; - case EVAL_ExprKind_BitNot: result = RDI_EvalOp_BitNot; break; - case EVAL_ExprKind_Mul: result = RDI_EvalOp_Mul; break; - case EVAL_ExprKind_Div: result = RDI_EvalOp_Div; break; - case EVAL_ExprKind_Mod: result = RDI_EvalOp_Mod; break; - case EVAL_ExprKind_Add: result = RDI_EvalOp_Add; break; - case EVAL_ExprKind_Sub: result = RDI_EvalOp_Sub; break; - case EVAL_ExprKind_LShift: result = RDI_EvalOp_LShift; break; - case EVAL_ExprKind_RShift: result = RDI_EvalOp_RShift; break; - case EVAL_ExprKind_Less: result = RDI_EvalOp_Less; break; - case EVAL_ExprKind_LsEq: result = RDI_EvalOp_LsEq; break; - case EVAL_ExprKind_Grtr: result = RDI_EvalOp_Grtr; break; - case EVAL_ExprKind_GrEq: result = RDI_EvalOp_GrEq; break; - case EVAL_ExprKind_EqEq: result = RDI_EvalOp_EqEq; break; - case EVAL_ExprKind_NtEq: result = RDI_EvalOp_NtEq; break; - case EVAL_ExprKind_BitAnd: result = RDI_EvalOp_BitAnd; break; - case EVAL_ExprKind_BitXor: result = RDI_EvalOp_BitXor; break; - case EVAL_ExprKind_BitOr: result = RDI_EvalOp_BitOr; break; - case EVAL_ExprKind_LogAnd: result = RDI_EvalOp_LogAnd; break; - case EVAL_ExprKind_LogOr: result = RDI_EvalOp_LogOr; break; - } - return(result); -} - -internal B32 -eval_expr_kind_is_comparison(EVAL_ExprKind kind){ - B32 result = 0; - switch (kind){ - case EVAL_ExprKind_EqEq: - case EVAL_ExprKind_NtEq: - case EVAL_ExprKind_Less: - case EVAL_ExprKind_Grtr: - case EVAL_ExprKind_LsEq: - case EVAL_ExprKind_GrEq: - { - result = 1; - }break; - } - return(result); -} - -//////////////////////////////// -//~ allen: EVAL Expression Constructors - -internal EVAL_Expr* -eval_expr(Arena *arena, EVAL_ExprKind kind, void *location, - EVAL_Expr *c0, EVAL_Expr *c1, EVAL_Expr *c2){ - EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); - result->kind = kind; - result->location = location; - result->children[0] = c0; - result->children[1] = c1; - result->children[2] = c2; - return(result); -} - -internal EVAL_Expr* -eval_expr_u64(Arena *arena, void *location, U64 u64){ - EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); - result->kind = EVAL_ExprKind_LeafU64; - result->location = location; - result->u64 = u64; - return(result); -} - -internal EVAL_Expr* -eval_expr_f64(Arena *arena, void *location, F64 f64){ - EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); - result->kind = EVAL_ExprKind_LeafF64; - result->location = location; - result->f64 = f64; - return(result); -} - -internal EVAL_Expr* -eval_expr_f32(Arena *arena, void *location, F32 f32){ - EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); - result->kind = EVAL_ExprKind_LeafF32; - result->location = location; - result->f32 = f32; - return(result); -} - -internal EVAL_Expr* -eval_expr_child_and_u64(Arena *arena, EVAL_ExprKind kind, void *location, - EVAL_Expr *child, U64 u64){ - EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); - result->kind = kind; - result->location = location; - result->child_and_constant.child = child; - result->child_and_constant.u64 = u64; - return(result); -} - -internal EVAL_Expr* -eval_expr_leaf_member(Arena *arena, void *location, String8 name){ - EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); - result->location = location; - result->kind = EVAL_ExprKind_LeafMember; - result->name = name; - return(result); -} - -internal EVAL_Expr* -eval_expr_leaf_ident(Arena *arena, void *location, String8 name) -{ - EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); - result->location = location; - result->kind = EVAL_ExprKind_LeafIdent; - result->name = name; - return(result); -} - -internal EVAL_Expr* -eval_expr_leaf_bytecode(Arena *arena, void *location, TG_Key type_key, String8 bytecode, EVAL_EvalMode mode){ - EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); - result->location = location; - result->kind = EVAL_ExprKind_LeafBytecode; - result->type_key = type_key; - result->bytecode = bytecode; - result->mode = mode; - return(result); -} - -internal EVAL_Expr* -eval_expr_leaf_op_list(Arena *arena, void *location, TG_Key type_key, EVAL_OpList *ops, EVAL_EvalMode mode){ - String8 bytecode = eval_bytecode_from_oplist(arena, ops); - EVAL_Expr *result = eval_expr_leaf_bytecode(arena, location, type_key, bytecode, mode); - return(result); -} - -internal EVAL_Expr* -eval_expr_leaf_type(Arena *arena, void *location, TG_Key type_key){ - EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); - result->location = location; - result->kind = EVAL_ExprKind_TypeIdent; - result->type_key = type_key; - return(result); -} - -//////////////////////////////// -//~ allen: EVAL Type Information Transformers - -internal RDI_EvalTypeGroup -eval_type_group_from_kind(TG_Kind kind){ - RDI_EvalTypeGroup result = 0; - switch (kind){ - default:{}break; - - case TG_Kind_Null: case TG_Kind_Void: - case TG_Kind_F16: case TG_Kind_F32PP: case TG_Kind_F48: - case TG_Kind_F80: case TG_Kind_F128: - case TG_Kind_ComplexF32: case TG_Kind_ComplexF64: - case TG_Kind_ComplexF80: case TG_Kind_ComplexF128: - case TG_Kind_Modifier: case TG_Kind_Array: - case TG_Kind_Struct: case TG_Kind_Class: case TG_Kind_Union: - case TG_Kind_Enum: case TG_Kind_Alias: - case TG_Kind_IncompleteStruct: case TG_Kind_IncompleteClass: - case TG_Kind_IncompleteUnion: case TG_Kind_IncompleteEnum: - case TG_Kind_Bitfield: case TG_Kind_Variadic: - result = RDI_EvalTypeGroup_Other; break; - - case TG_Kind_Handle: - case TG_Kind_UChar8: case TG_Kind_UChar16: case TG_Kind_UChar32: - case TG_Kind_U8: case TG_Kind_U16: case TG_Kind_U32: - case TG_Kind_U64: case TG_Kind_U128: case TG_Kind_U256: - case TG_Kind_U512: - case TG_Kind_Ptr: case TG_Kind_LRef: case TG_Kind_RRef: - case TG_Kind_Function: case TG_Kind_Method: case TG_Kind_MemberPtr: - result = RDI_EvalTypeGroup_U; break; - - case TG_Kind_Char8: case TG_Kind_Char16: case TG_Kind_Char32: - case TG_Kind_S8: case TG_Kind_S16: case TG_Kind_S32: - case TG_Kind_S64: case TG_Kind_S128: case TG_Kind_S256: - case TG_Kind_S512: - case TG_Kind_Bool: - result = RDI_EvalTypeGroup_S; break; - - case TG_Kind_F32: - result = RDI_EvalTypeGroup_F32; break; - - case TG_Kind_F64: - result = RDI_EvalTypeGroup_F64; break; - } - return(result); -} - -internal TG_Key -eval_type_unwrap_enum(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - TG_Key result = key; - for(B32 good = 1; good;) - { - TG_Kind kind = tg_kind_from_key(result); - if(kind == TG_Kind_Enum) - { - result = tg_direct_from_graph_rdi_key(graph, rdi, result); - } - else - { - good = 0; - } - } - return result; -} - -internal TG_Key -eval_type_promote(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key){ - TG_Key result = key; - TG_Kind kind = tg_kind_from_key(key); - if(kind == TG_Kind_Bool || - kind == TG_Kind_S8 || - kind == TG_Kind_S16 || - kind == TG_Kind_U8 || - kind == TG_Kind_U16) - { - result = tg_key_basic(TG_Kind_S32); - } - return result; -} - -internal TG_Key -eval_type_coerce(TG_Graph *graph, RDI_Parsed *rdi, TG_Key l, TG_Key r){ - Assert(eval_kind_is_basic_or_enum(tg_kind_from_key(l)) && - eval_kind_is_basic_or_enum(tg_kind_from_key(r))); - - // replace enums with corresponding ints - TG_Key lt = eval_type_unwrap_enum(graph, rdi, l); - TG_Key rt = eval_type_unwrap_enum(graph, rdi, r); - - // first promote each - TG_Key lp = eval_type_promote(graph, rdi, lt); - TG_Key rp = eval_type_promote(graph, rdi, rt); - TG_Kind lk = tg_kind_from_key(lp); - TG_Kind rk = tg_kind_from_key(rp); - - TG_Key result = l; - return(result); -} - -internal B32 -eval_type_match(TG_Graph *graph, RDI_Parsed *rdi, TG_Key l, TG_Key r){ - B32 result = 0; - - // unwrap - TG_Key lu = tg_unwrapped_from_graph_rdi_key(graph, rdi, l); - TG_Key ru = tg_unwrapped_from_graph_rdi_key(graph, rdi, r); - - if (tg_key_match(lu, ru)){ - result = 1; - } - else{ - TG_Kind luk = tg_kind_from_key(lu); - TG_Kind ruk = tg_kind_from_key(ru); - if (luk == ruk){ - switch (luk){ - default: - { - result = 1; - }break; - - case TG_Kind_Ptr: - case TG_Kind_LRef: - case TG_Kind_RRef: - { - TG_Key lud = tg_direct_from_graph_rdi_key(graph, rdi, lu); - TG_Key rud = tg_direct_from_graph_rdi_key(graph, rdi, ru); - if (eval_type_match(graph, rdi, lud, rud)){ - result = 1; - } - }break; - - case TG_Kind_MemberPtr: - { - TG_Key lud = tg_direct_from_graph_rdi_key(graph, rdi, lu); - TG_Key rud = tg_direct_from_graph_rdi_key(graph, rdi, ru); - TG_Key luo = tg_owner_from_graph_rdi_key(graph, rdi, lu); - TG_Key ruo = tg_owner_from_graph_rdi_key(graph, rdi, ru); - if (eval_type_match(graph, rdi, lud, rud) && - eval_type_match(graph, rdi, luo, ruo)){ - result = 1; - } - }break; - - case TG_Kind_Array: - { - Temp scratch = scratch_begin(0, 0); - TG_Type *lt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, l); - TG_Type *rt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, r); - if(lt->count == rt->count && eval_type_match(graph, rdi, lt->direct_type_key, rt->direct_type_key)) - { - result = 1; - } - scratch_end(scratch); - }break; - - case TG_Kind_Function: - { - Temp scratch = scratch_begin(0, 0); - TG_Type *lt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, l); - TG_Type *rt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, r); - if (lt->count == rt->count && eval_type_match(graph, rdi, lt->direct_type_key, rt->direct_type_key)) - { - B32 params_match = 1; - TG_Key *lp = lt->param_type_keys; - TG_Key *rp = rt->param_type_keys; - U64 count = lt->count; - for(U64 i = 0; i < count; i += 1, lp += 1, rp += 1) - { - if(!eval_type_match(graph, rdi, *lp, *rp)) - { - params_match = 0; - break; - } - } - result = params_match; - } - scratch_end(scratch); - }break; - - case TG_Kind_Method: - { - Temp scratch = scratch_begin(0, 0); - TG_Type *lt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, l); - TG_Type *rt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, r); - if (lt->count == rt->count && - eval_type_match(graph, rdi, lt->direct_type_key, rt->direct_type_key) && - eval_type_match(graph, rdi, lt->owner_type_key, rt->owner_type_key)) - { - B32 params_match = 1; - TG_Key *lp = lt->param_type_keys; - TG_Key *rp = rt->param_type_keys; - U64 count = lt->count; - for(U64 i = 0; i < count; i += 1, lp += 1, rp += 1) - { - if(!eval_type_match(graph, rdi, *lp, *rp)) - { - params_match = 0; - break; - } - } - result = params_match; - } - scratch_end(scratch); - }break; - } - } - } - - return(result); -} - -internal B32 -eval_kind_is_integer(TG_Kind kind){ - B32 result = (TG_Kind_FirstInteger <= kind && kind <= TG_Kind_LastInteger); - return(result); -} - -internal B32 -eval_kind_is_signed(TG_Kind kind){ - B32 result = ((TG_Kind_FirstSigned1 <= kind && kind <= TG_Kind_LastSigned1) || - (TG_Kind_FirstSigned2 <= kind && kind <= TG_Kind_LastSigned2)); - return(result); -} - -internal B32 -eval_kind_is_basic_or_enum(TG_Kind kind){ - B32 result = ((TG_Kind_FirstBasic <= kind && kind <= TG_Kind_LastBasic) || - kind == TG_Kind_Enum); - return(result); -} - - -//////////////////////////////// -//~ allen: EVAL IR-Tree Constructors - -internal EVAL_IRTree* -eval_irtree_const_u(Arena *arena, U64 v){ - // choose encoding op - RDI_EvalOp op = RDI_EvalOp_ConstU64; - if (v < 0x100){ - op = RDI_EvalOp_ConstU8; - } - else if (v < 0x10000){ - op = RDI_EvalOp_ConstU16; - } - else if (v < 0x100000000){ - op = RDI_EvalOp_ConstU32; - } - - // make the tree node - EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); - result->op = op; - result->p = v; - return(result); -} - -internal EVAL_IRTree* -eval_irtree_unary_op(Arena *arena, RDI_EvalOp op, - RDI_EvalTypeGroup group, EVAL_IRTree *c){ - EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); - result->op = op; - result->p = group; - result->children[0] = c; - return(result); -} - -internal EVAL_IRTree* -eval_irtree_binary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, - EVAL_IRTree *l, EVAL_IRTree *r){ - EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); - result->op = op; - result->p = group; - result->children[0] = l; - result->children[1] = r; - return(result); -} - -internal EVAL_IRTree* -eval_irtree_binary_op_u(Arena *arena, RDI_EvalOp op, EVAL_IRTree *l, EVAL_IRTree *r){ - EVAL_IRTree *result = eval_irtree_binary_op(arena, op, RDI_EvalTypeGroup_U, l, r); - return(result); -} - -internal EVAL_IRTree* -eval_irtree_conditional(Arena *arena, EVAL_IRTree *c, EVAL_IRTree *l, EVAL_IRTree *r){ - EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); - result->op = RDI_EvalOp_Cond; - result->children[0] = c; - result->children[1] = l; - result->children[2] = r; - return(result); -} - -internal EVAL_IRTree* -eval_irtree_bytecode_no_copy(Arena *arena, String8 bytecode){ - EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); - result->op = EVAL_IRExtKind_Bytecode; - result->bytecode = bytecode; - return(result); -} - -//////////////////////////////// -//~ allen: EVAL IR-Tree High Level Helpers - -internal EVAL_IRTree* -eval_irtree_mem_read_type(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key type_key){ - U64 byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, type_key); - EVAL_IRTree *result = &eval_irtree_nil; - if (0 < byte_size && byte_size <= 8){ - // build the read node - EVAL_IRTree *read_node = push_array(arena, EVAL_IRTree, 1); - read_node->op = RDI_EvalOp_MemRead; - read_node->p = byte_size; - read_node->children[0] = c; - - // build a signed trunc node if needed - U64 bit_size = byte_size << 3; - EVAL_IRTree *with_trunc = read_node; - TG_Kind kind = tg_kind_from_key(type_key); - if (bit_size < 64 && eval_kind_is_signed(kind)){ - with_trunc = push_array(arena, EVAL_IRTree, 1); - with_trunc->op = RDI_EvalOp_TruncSigned; - with_trunc->p = bit_size; - with_trunc->children[0] = read_node; - } - - // set result - result = with_trunc; - } - else{ - // TODO: unexpected path - } - return(result); -} - -internal EVAL_IRTree* -eval_irtree_convert_lo(Arena *arena, EVAL_IRTree *c, RDI_EvalTypeGroup out, RDI_EvalTypeGroup in){ - EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); - result->op = RDI_EvalOp_Convert; - result->p = in | (out << 8); - result->children[0] = c; - return(result); -} - -internal EVAL_IRTree* -eval_irtree_trunc(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key type_key){ - EVAL_IRTree *result = c; - U64 byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, type_key); - if (byte_size < 64){ - RDI_EvalOp op = RDI_EvalOp_Trunc; - TG_Kind kind = tg_kind_from_key(type_key); - if (eval_kind_is_signed(kind)){ - op = RDI_EvalOp_TruncSigned; - } - U64 bit_size = byte_size << 3; - result = push_array(arena, EVAL_IRTree, 1); - result->op = op; - result->p = bit_size; - result->children[0] = c; - } - return(result); -} - -internal EVAL_IRTree* -eval_irtree_convert_hi(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key out, TG_Key in){ - EVAL_IRTree *result = c; - TG_Kind in_kind = tg_kind_from_key(in); - TG_Kind out_kind = tg_kind_from_key(out); - U8 in_group = eval_type_group_from_kind(in_kind); - U8 out_group = eval_type_group_from_kind(out_kind); - U32 conversion_rule = rdi_eval_conversion_kind_from_typegroups(in_group, out_group); - if(conversion_rule == RDI_EvalConversionKind_Legal) - { - result = eval_irtree_convert_lo(arena, result, out_group, in_group); - } - U64 in_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, in); - U64 out_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, out); - if(out_byte_size < in_byte_size && eval_kind_is_integer(out_kind)) - { - result = eval_irtree_trunc(arena, graph, rdi, result, out); - } - return(result); -} - -internal EVAL_IRTree* -eval_irtree_resolve_to_value(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_EvalMode from_mode, - EVAL_IRTree *tree, TG_Key type_key){ - EVAL_IRTree *result = tree; - switch (from_mode){ - default:{}break; - case EVAL_EvalMode_Addr: - { - result = eval_irtree_mem_read_type(arena, graph, rdi, tree, type_key); - }break; - case EVAL_EvalMode_Reg: - { - result = eval_irtree_unary_op(arena, RDI_EvalOp_RegReadDyn, RDI_EvalTypeGroup_U, tree); - }break; - } - return(result); -} - -//////////////////////////////// -//~ allen: EVAL Compiler Phases - -internal void -eval_push_leaf_ident_exprs_from_expr__in_place(Arena *arena, EVAL_String2ExprMap *map, EVAL_Expr *expr, EVAL_ErrorList *eout) -{ - switch(expr->kind) - { - default: - { - U64 children_count = eval_expr_kind_child_counts[expr->kind]; - for(U64 idx = 0; idx < children_count; idx += 1) - { - eval_push_leaf_ident_exprs_from_expr__in_place(arena, map, expr->children[idx], eout); - } - }break; - case EVAL_ExprKind_Define: - { - EVAL_Expr *exprl = expr->children[0]; - EVAL_Expr *exprr = expr->children[1]; - if(exprl->kind == EVAL_ExprKind_LeafIdent) - { - eval_string2expr_map_insert(arena, map, exprl->name, exprr); - } - }break; - } -} - -internal TG_Key -eval_type_from_type_expr(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_Expr *expr, EVAL_ErrorList *eout){ - TG_Key result = zero_struct; - - EVAL_ExprKind kind = expr->kind; - switch (kind){ - default: - { - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Expected type expression."); - }break; - - case EVAL_ExprKind_TypeIdent: - { - result = expr->type_key; - }break; - - case EVAL_ExprKind_Ptr: - { - TG_Key direct_type_key = eval_type_from_type_expr(arena, graph, rdi, expr->children[0], eout); - result = tg_cons_type_make(graph, TG_Kind_Ptr, direct_type_key, 0); - }break; - - case EVAL_ExprKind_Array: - { - EVAL_Expr *child_expr = expr->child_and_constant.child; - TG_Key direct_type_key = eval_type_from_type_expr(arena, graph, rdi, child_expr, eout); - result = tg_cons_type_make(graph, TG_Kind_Array, direct_type_key, expr->child_and_constant.u64); - }break; - - case EVAL_ExprKind_Func: - { - // TODO(rjf): old type graph code is below: -#if 0 - TG_Type *ret_type = eval_type_from_type_expr(arena, graph, expr->children[0], eout); - // TODO(allen): decision: do we do the extra work to preserve full function type info? - result = tg_type_func(graph, ret_type, 0, 0); -#endif - }break; - } - - return(result); -} - -internal EVAL_IRTreeAndType -eval_irtree_and_type_from_expr(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_String2ExprMap *leaf_ident_expr_map, EVAL_Expr *expr, EVAL_ErrorList *eout) -{ - ProfBeginFunction(); - EVAL_IRTreeAndType result = {0}; - result.tree = &eval_irtree_nil; - - EVAL_ExprKind kind = expr->kind; - switch(kind) - { - default: - { - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "(internal) Undefined expression kind (%u).", kind); - }break; - - case EVAL_ExprKind_ArrayIndex: - { - EVAL_Expr *exprl = expr->children[0]; - EVAL_Expr *exprr = expr->children[1]; - - EVAL_IRTreeAndType l = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprl, eout); - EVAL_IRTreeAndType r = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprr, eout); - - if (l.tree->op != 0 && r.tree->op != 0){ - TG_Key l_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, l.type_key); - TG_Key r_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, r.type_key); - TG_Kind l_restype_kind = tg_kind_from_key(l_restype); - TG_Kind r_restype_kind = tg_kind_from_key(r_restype); - if(eval_kind_is_basic_or_enum(r_restype_kind)) - { - r_restype = eval_type_unwrap_enum(graph, rdi, r_restype); - r_restype_kind = tg_kind_from_key(r_restype); - } - - // analyze situation - B32 can_generate = 0; - B32 l_resolve = 0; - TG_Key direct_type = zero_struct; - U64 direct_type_size = 0; - if (!eval_kind_is_integer(r_restype_kind)){ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprr->location, "Cannot index with this type."); - } - else{ - direct_type = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, l_restype); - direct_type_size = tg_byte_size_from_graph_rdi_key(graph, rdi, direct_type); - if (l_restype_kind == TG_Kind_Ptr){ - if (direct_type_size == 0){ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprr->location, "Cannot index into pointers of zero-sized types."); - } - else{ - can_generate = 1; - if (l.mode != EVAL_EvalMode_Value){ - l_resolve = 1; - } - } - } - else if (l_restype_kind == TG_Kind_Array){ - if (l.mode == EVAL_EvalMode_Addr){ - if (direct_type_size == 0){ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprl->location, "Cannot index into arrays of zero-sized types."); - } - else{ - can_generate = 1; - } - } - else{ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprl->location, "(Not supported) Cannot index into array without base address."); - } - } - else{ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprl->location, "Cannot index into this type."); - } - } - - // generate ir tree - if (can_generate){ - // how to compute the index - EVAL_IRTree *index_tree = eval_irtree_resolve_to_value(arena, graph, rdi, r.mode, r.tree, r_restype); - if (direct_type_size > 1){ - EVAL_IRTree *const_tree = eval_irtree_const_u(arena, direct_type_size); - index_tree = eval_irtree_binary_op_u(arena, RDI_EvalOp_Mul, index_tree, const_tree); - } - - // how to compute the base address - EVAL_IRTree *base_tree = l.tree; - if (l_resolve){ - base_tree = eval_irtree_resolve_to_value(arena, graph, rdi, l.mode, base_tree, l_restype); - } - - // how to compute the final address - EVAL_IRTree *new_tree = eval_irtree_binary_op_u(arena, RDI_EvalOp_Add, index_tree, base_tree); - - // fill result - result.tree = new_tree; - result.type_key = direct_type; - result.mode = EVAL_EvalMode_Addr; - } - } - }break; - - case EVAL_ExprKind_MemberAccess: - { - EVAL_Expr *exprl = expr->children[0]; - EVAL_Expr *exprr = expr->children[1]; - - EVAL_IRTreeAndType l = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprl, eout); - - if (l.tree->op != 0 && !tg_key_match(tg_key_zero(), l.type_key)){ - TG_Key l_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, l.type_key); - TG_Kind l_restype_kind = tg_kind_from_key(l_restype); - - // determine which type to use - TG_Key check_type_key = l_restype; - TG_Kind check_type_kind = l_restype_kind; - if (l_restype_kind == TG_Kind_Ptr || l_restype_kind == TG_Kind_LRef || l_restype_kind == TG_Kind_RRef){ - check_type_key = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, l_restype); - check_type_kind = tg_kind_from_key(check_type_key); - } - - // switch to handle - switch(check_type_kind) - { - default: - { - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprl->location, "Cannot perform member access on this type."); - }break; - - case TG_Kind_Struct: - case TG_Kind_Class: - case TG_Kind_Union: - { - // analyze situation - B32 can_generate = 0; - TG_Key r_type = zero_struct; - U32 r_off = 0; - B32 l_resolve = 0; - - // determine how to treat left - B32 l_good = 0; - if (l_restype_kind == TG_Kind_Ptr || l_restype_kind == TG_Kind_LRef || l_restype_kind == TG_Kind_RRef){ - l_good = 1; - l_resolve = 1; - } - else{ - if (l.mode == EVAL_EvalMode_Addr){ - l_good = 1; - } - else if(l.mode == EVAL_EvalMode_Reg) - { - l_good = 1; - } - else{ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprl->location, - "(Not supported) Cannot access member without a base address or register location."); - } - } - - // right must be identifier - B32 r_good = 0; - if (exprr->kind == EVAL_ExprKind_LeafMember){ - r_good = 1; - } - else{ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprr->location, - "(internal) Expected a leaf member in member access."); - } - - if (l_good && r_good){ - Temp scratch = scratch_begin(&arena, 1); - TG_MemberArray check_type_members = tg_data_members_from_graph_rdi_key(scratch.arena, graph, rdi, check_type_key); - - // lookup member - String8 member_name = exprr->name; - TG_Member *match = 0; - for(U64 member_idx = 0; member_idx < check_type_members.count; member_idx += 1) - { - TG_Member *member = &check_type_members.v[member_idx]; - if(str8_match(member->name, member_name, 0)) - { - match = member; - break; - } - } - - // extract member info - if (match != 0){ - can_generate = 1; - r_type = match->type_key; - r_off = match->off; - } - else{ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprr->location, "Could not find a member named '%S' in type.", member_name); - } - - scratch_end(scratch); - } - - // generate ir tree - if (can_generate){ - EVAL_IRTree *new_tree = l.tree; - EVAL_EvalMode mode = l.mode; - if (l_resolve){ - new_tree = eval_irtree_resolve_to_value(arena, graph, rdi, l.mode, new_tree, l_restype); - mode = EVAL_EvalMode_Addr; - } - if (r_off != 0){ - EVAL_IRTree *const_tree = eval_irtree_const_u(arena, r_off); - new_tree = eval_irtree_binary_op_u(arena, RDI_EvalOp_Add, new_tree, const_tree); - } - - // fill result - result.tree = new_tree; - result.type_key = r_type; - result.mode = mode; - } - }break; - } - - } - }break; - - case EVAL_ExprKind_Deref: - { - EVAL_Expr *exprc = expr->children[0]; - - EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprc, eout); - - if (c.tree->op != 0){ - TG_Key c_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, c.type_key); - TG_Kind c_restype_kind = tg_kind_from_key(c_restype); - TG_Key c_restype_direct = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, c_restype); - U64 c_restype_direct_size = tg_byte_size_from_graph_rdi_key(graph, rdi, c_restype_direct); - - // analyze situation - B32 can_generate = 0; - B32 c_resolve = 0; - if (c_restype_kind == TG_Kind_Ptr || - c_restype_kind == TG_Kind_LRef || - c_restype_kind == TG_Kind_RRef){ - if (c_restype_direct_size == 0){ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprc->location, "Cannot dereference pointers of zero-sized types."); - } - else{ - can_generate = 1; - c_resolve = 1; - } - } - else if (c_restype_kind == TG_Kind_Array){ - if (c.mode == EVAL_EvalMode_Addr){ - if (c_restype_direct_size == 0){ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprc->location, "Cannot dereference arrays of zero-sized types."); - } - else{ - can_generate = 1; - } - } - else{ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprc->location, "(Not supported) Cannot dereference array without base address."); - } - } - else{ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprc->location, "Cannot dereference this type."); - } - - // generate ir tree - if (can_generate){ - EVAL_IRTree *new_tree = c.tree; - if (c_resolve){ - new_tree = eval_irtree_resolve_to_value(arena, graph, rdi, c.mode, c.tree, c_restype); - } - - // fill result - result.tree = new_tree; - result.type_key = c_restype_direct; - result.mode = EVAL_EvalMode_Addr; - } - } - }break; - - case EVAL_ExprKind_Address: - { - EVAL_Expr *exprc = expr->children[0]; - EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprc, eout); - if(c.tree->op != 0 && !tg_key_match(c.type_key, tg_key_zero())) - { - TG_Key c_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, c.type_key); - TG_Kind c_restype_kind = tg_kind_from_key(c_restype); - - // analyze situation - B32 can_generate = 0; - if(c.mode == EVAL_EvalMode_Addr) - { - can_generate = 1; - } - - // generate ir tree - if(can_generate) - { - EVAL_IRTree *new_tree = c.tree; - TG_Key ptr_type = tg_cons_type_make(graph, TG_Kind_Ptr, c_restype, 0); - - // fill result - result.tree = new_tree; - result.type_key = ptr_type; - result.mode = EVAL_EvalMode_Value; - } - } - }break; - - case EVAL_ExprKind_Cast: - { - EVAL_Expr *exprl = expr->children[0]; - EVAL_Expr *exprr = expr->children[1]; - - TG_Key cast_type_key = eval_type_from_type_expr(arena, graph, rdi, exprl, eout); - TG_Kind cast_type_kind = tg_kind_from_key(cast_type_key); - EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprr, eout); - - if(cast_type_kind != TG_Kind_Null && c.tree->op != 0) - { - TG_Key c_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, c.type_key); - TG_Kind c_restype_kind = tg_kind_from_key(c_restype); - U64 c_restype_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, c_restype); - U64 cast_type_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, cast_type_key); - - // analyze situation - U8 in_group = eval_type_group_from_kind(c_restype_kind); - U8 out_group = eval_type_group_from_kind(cast_type_kind); - RDI_EvalConversionKind conversion_rule = rdi_eval_conversion_kind_from_typegroups(in_group, out_group); - - // generate tree - switch(conversion_rule) - { - case RDI_EvalConversionKind_Noop: - case RDI_EvalConversionKind_Legal: - { - EVAL_IRTree *in_tree = eval_irtree_resolve_to_value(arena, graph, rdi, c.mode, c.tree, c_restype); - - EVAL_IRTree *new_tree = in_tree; - if (conversion_rule == RDI_EvalConversionKind_Legal){ - new_tree = eval_irtree_convert_lo(arena, in_tree, out_group, in_group); - } - if (cast_type_byte_size < c_restype_byte_size && eval_kind_is_integer(cast_type_kind)){ - new_tree = eval_irtree_trunc(arena, graph, rdi, in_tree, cast_type_key); - } - - result.tree = new_tree; - result.type_key = cast_type_key; - result.mode = EVAL_EvalMode_Value; - }break; - - default: - { - String8 text = str8_lit("(internal) unknown conversion rule"); - if (conversion_rule < RDI_EvalConversionKind_COUNT){ - text.str = rdi_explanation_string_from_eval_conversion_kind(conversion_rule, &text.size); - } - eval_error(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, text); - }break; - } - } - }break; - - case EVAL_ExprKind_Sizeof: - { - EVAL_Expr *exprc = expr->children[0]; - - // analyze situation - TG_Key type_key = zero_struct; - switch (exprc->kind){ - // size of type expression - case EVAL_ExprKind_TypeIdent: - case EVAL_ExprKind_Ptr: - case EVAL_ExprKind_Array: - case EVAL_ExprKind_Func: - { - type_key = eval_type_from_type_expr(arena, graph, rdi, exprc, eout); - }break; - - // size of value expression - default: - { - EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprc, eout); - type_key = c.type_key; - }break; - } - B32 can_generate = 0; - U64 size = 0; - TG_Kind type_kind = tg_kind_from_key(type_key); - if (type_kind != TG_Kind_Null){ - can_generate = 1; - size = tg_byte_size_from_graph_rdi_key(graph, rdi, type_key); - } - - // generate ir tree - if(can_generate) - { - EVAL_IRTree *new_tree = eval_irtree_const_u(arena, size); - result.tree = new_tree; - result.type_key = tg_key_basic(TG_Kind_U64); - result.mode = EVAL_EvalMode_Value; - } - }break; - - case EVAL_ExprKind_Neg: - case EVAL_ExprKind_LogNot: - case EVAL_ExprKind_BitNot: - { - EVAL_Expr *exprc = expr->children[0]; - - EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprc, eout); - if (c.tree->op != 0){ - TG_Key c_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, c.type_key); - TG_Key p_type = eval_type_promote(graph, rdi, c_restype); - TG_Kind c_restype_kind = tg_kind_from_key(c_restype); - - // analyze situation - B32 can_generate = 0; - RDI_EvalOp op = eval_opcode_from_expr_kind(kind); - U8 c_group = eval_type_group_from_kind(c_restype_kind); - if (!rdi_eval_op_typegroup_are_compatible(op, c_group)){ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Cannot use this operator on this type."); - } - else{ - can_generate = 1; - } - - // generate ir tree - if (can_generate){ - EVAL_IRTree *in_tree = eval_irtree_resolve_to_value(arena, graph, rdi, c.mode, c.tree, c_restype); - in_tree = eval_irtree_convert_hi(arena, graph, rdi, in_tree, p_type, c_restype); - - EVAL_IRTree *new_tree = eval_irtree_unary_op(arena, op, c_group, in_tree); - - result.tree = new_tree; - result.type_key = p_type; - result.mode = EVAL_EvalMode_Value; - } - } - }break; - - case EVAL_ExprKind_Mul: - case EVAL_ExprKind_Div: - case EVAL_ExprKind_Mod: - case EVAL_ExprKind_Add: - case EVAL_ExprKind_Sub: - case EVAL_ExprKind_LShift: - case EVAL_ExprKind_RShift: - case EVAL_ExprKind_Less: - case EVAL_ExprKind_LsEq: - case EVAL_ExprKind_Grtr: - case EVAL_ExprKind_GrEq: - case EVAL_ExprKind_EqEq: - case EVAL_ExprKind_NtEq: - case EVAL_ExprKind_BitAnd: - case EVAL_ExprKind_BitXor: - case EVAL_ExprKind_BitOr: - case EVAL_ExprKind_LogAnd: - case EVAL_ExprKind_LogOr: - { - //- setup & dispatch - EVAL_Expr *exprl = expr->children[0]; - EVAL_Expr *exprr = expr->children[1]; - - EVAL_IRTreeAndType l = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprl, eout); - EVAL_IRTreeAndType r = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprr, eout); - - if (l.tree->op != 0 && r.tree->op != 0){ - TG_Key l_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, l.type_key); - TG_Key r_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, r.type_key); - TG_Kind l_restype_kind = tg_kind_from_key(l_restype); - TG_Kind r_restype_kind = tg_kind_from_key(r_restype); - - //- rjf: decay register types to basics - if(l_restype.kind == TG_KeyKind_Reg) - { - l_restype = tg_key_basic(TG_Kind_U64); - l_restype_kind = tg_kind_from_key(l_restype); - } - if(r_restype.kind == TG_KeyKind_Reg) - { - r_restype = tg_key_basic(TG_Kind_U64); - r_restype_kind = tg_kind_from_key(r_restype); - } - - RDI_EvalOp op = eval_opcode_from_expr_kind(kind); - - //- pointer decay - B32 l_is_pointer = (l_restype_kind == TG_Kind_Ptr); - B32 l_is_decay = (l_restype_kind == TG_Kind_Array && l.mode == EVAL_EvalMode_Addr); - B32 l_is_pointer_like = (l_is_pointer || l_is_decay); - - B32 r_is_pointer = (r_restype_kind == TG_Kind_Ptr); - B32 r_is_decay = (r_restype_kind == TG_Kind_Array && r.mode == EVAL_EvalMode_Addr); - B32 r_is_pointer_like = (r_is_pointer || r_is_decay); - - //- determine arithmetic path -#define EVAL_ArithPath_Normal 0 -#define EVAL_ArithPath_PtrAdd 1 -#define EVAL_ArithPath_PtrSub 2 - - B32 ptr_arithmetic_mul_rptr = 0; - U32 arith_path = EVAL_ArithPath_Normal; - if (kind == EVAL_ExprKind_Add){ - if (l_is_pointer_like && eval_kind_is_integer(r_restype_kind)){ - arith_path = EVAL_ArithPath_PtrAdd; - } - if (l_is_pointer_like && eval_kind_is_integer(l_restype_kind)){ - arith_path = EVAL_ArithPath_PtrAdd; - ptr_arithmetic_mul_rptr = 1; - } - } - else if (kind == EVAL_ExprKind_Sub){ - if (l_is_pointer_like && eval_kind_is_integer(r_restype_kind)){ - arith_path = EVAL_ArithPath_PtrAdd; - } - if (l_is_pointer_like && r_is_pointer_like){ - TG_Key l_restype_direct = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, l_restype); - TG_Key r_restype_direct = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, r_restype); - U64 l_restype_direct_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, l_restype_direct); - U64 r_restype_direct_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, r_restype_direct); - if (l_restype_direct_byte_size == r_restype_direct_byte_size){ - arith_path = EVAL_ArithPath_PtrSub; - } - } - } - - //- specific arithmetic handlers - - switch (arith_path){ - case EVAL_ArithPath_Normal: - { - // analyze situation - B32 is_comparison = eval_expr_kind_is_comparison(kind); - B32 both_basic = (eval_kind_is_basic_or_enum(l_restype_kind) && - eval_kind_is_basic_or_enum(r_restype_kind)); - - TG_Key cv_type_key = zero_struct; - if (both_basic){ - cv_type_key = eval_type_coerce(graph, rdi, l_restype, r_restype); - } - else if (is_comparison && eval_type_match(graph, rdi, l_restype, r_restype)){ - cv_type_key = l_restype; - } - - TG_Kind cv_type_kind = tg_kind_from_key(cv_type_key); - U8 cv_group = eval_type_group_from_kind(cv_type_kind); - - B32 can_generate = 0; - if (rdi_eval_op_typegroup_are_compatible(op, cv_group)){ - can_generate = 1; - } - else{ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Cannot use this operator on this type."); - } - - // generate ir tree - if (can_generate){ - TG_Key final_type_key = cv_type_key; - if (is_comparison){ - final_type_key = tg_key_basic(TG_Kind_Bool); - } - - EVAL_IRTree *l_tree = eval_irtree_resolve_to_value(arena, graph, rdi, l.mode, l.tree, l_restype); - l_tree = eval_irtree_convert_hi(arena, graph, rdi, l_tree, cv_type_key, l_restype); - - EVAL_IRTree *r_tree = eval_irtree_resolve_to_value(arena, graph, rdi, r.mode, r.tree, r_restype); - r_tree = eval_irtree_convert_hi(arena, graph, rdi, r_tree, cv_type_key, r_restype); - - EVAL_IRTree *new_tree = eval_irtree_binary_op(arena, op, cv_group, l_tree, r_tree); - - result.tree = new_tree; - result.type_key = final_type_key; - result.mode = EVAL_EvalMode_Value; - } - }break; - - case EVAL_ArithPath_PtrAdd: - { - // setup which side is the pointer - EVAL_IRTreeAndType *ptr = &l; - EVAL_IRTreeAndType *integer = &r; - B32 ptr_is_decay = l_is_decay; - if (ptr_arithmetic_mul_rptr){ - ptr = &r; - integer = &l; - ptr_is_decay = r_is_decay; - } - - TG_Key direct = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, ptr->type_key); - U64 direct_type_size = tg_byte_size_from_graph_rdi_key(graph, rdi, direct); - - // generate ir tree - EVAL_IRTree *ptr_tree = ptr->tree; - if (!ptr_is_decay){ - ptr_tree = eval_irtree_resolve_to_value(arena, graph, rdi, ptr->mode, ptr_tree, ptr->type_key); - } - - EVAL_IRTree *integer_tree = eval_irtree_resolve_to_value(arena, graph, rdi, integer->mode, integer->tree, integer->type_key); - if (direct_type_size > 1){ - EVAL_IRTree *const_tree = eval_irtree_const_u(arena, direct_type_size); - integer_tree = eval_irtree_binary_op_u(arena, RDI_EvalOp_Mul, integer_tree, const_tree); - } - - TG_Key ptr_type = ptr->type_key; - if (ptr_is_decay){ - ptr_type = tg_cons_type_make(graph, TG_Kind_Ptr, direct, 0); - } - - EVAL_IRTree *new_tree = eval_irtree_binary_op(arena, op, RDI_EvalTypeGroup_U, ptr_tree, integer_tree); - - result.tree = new_tree; - result.type_key = ptr_type; - result.mode = EVAL_EvalMode_Value; - }break; - - case EVAL_ArithPath_PtrSub: - { - TG_Key direct = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, l_restype); - U64 direct_type_size = tg_byte_size_from_graph_rdi_key(graph, rdi, direct); - - // generate ir tree - EVAL_IRTree *l_tree = l.tree; - if (!l_is_decay){ - l_tree = eval_irtree_resolve_to_value(arena, graph, rdi, l.mode, l.tree, l_restype); - } - - EVAL_IRTree *r_tree = r.tree; - if (!r_is_decay){ - r_tree = eval_irtree_resolve_to_value(arena, graph, rdi, r.mode, r.tree, r_restype); - } - - EVAL_IRTree *op_tree = eval_irtree_binary_op(arena, op, RDI_EvalTypeGroup_U, l_tree, r_tree); - - EVAL_IRTree *new_tree = op_tree; - if (direct_type_size > 1){ - EVAL_IRTree *const_tree = eval_irtree_const_u(arena, direct_type_size); - new_tree = eval_irtree_binary_op(arena, RDI_EvalOp_Div, RDI_EvalTypeGroup_U, new_tree, const_tree); - } - - result.tree = new_tree; - result.type_key = tg_key_basic(TG_Kind_U64); - result.mode = EVAL_EvalMode_Value; - }break; - } - } - }break; - - case EVAL_ExprKind_Ternary: - { - EVAL_Expr *exprc = expr->children[0]; - EVAL_Expr *exprl = expr->children[1]; - EVAL_Expr *exprr = expr->children[2]; - - EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprc, eout); - EVAL_IRTreeAndType l = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprl, eout); - EVAL_IRTreeAndType r = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprr, eout); - - if (l.tree->op != 0 && r.tree->op != 0 && c.tree->op != 0){ - - TG_Key c_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, c.type_key); - TG_Key l_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, l.type_key); - TG_Key r_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, r.type_key); - TG_Kind c_restype_kind = tg_kind_from_key(c_restype); - TG_Kind l_restype_kind = tg_kind_from_key(l_restype); - TG_Kind r_restype_kind = tg_kind_from_key(r_restype); - - // analyze situation - B32 can_generate = 0; - TG_Key final_type = zero_struct; - if (eval_kind_is_integer(c_restype_kind)){ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Conditional term must be an integer type."); - } - else{ - if (eval_kind_is_basic_or_enum(l_restype_kind) && - eval_kind_is_basic_or_enum(r_restype_kind)){ - can_generate = 1; - final_type = eval_type_coerce(graph, rdi, l_restype, r_restype); - } - else{ - if (eval_type_match(graph, rdi, l_restype, r_restype)){ - if (l_restype_kind == TG_Kind_Ptr){ - can_generate = 1; - final_type = l_restype; - } - else{ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "(Not supported) Conditional value not basic type or pointer."); - } - } - else{ - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Left and right terms must have matching types."); - } - } - } - - // generate ir tree - if (can_generate){ - EVAL_IRTree *c_tree = eval_irtree_resolve_to_value(arena, graph, rdi, c.mode, c.tree, c_restype); - - EVAL_IRTree *l_tree = eval_irtree_resolve_to_value(arena, graph, rdi, l.mode, l.tree, l_restype); - l_tree = eval_irtree_convert_hi(arena, graph, rdi, l_tree, final_type, l_restype); - - EVAL_IRTree *r_tree = eval_irtree_resolve_to_value(arena, graph, rdi, r.mode, r.tree, r_restype); - r_tree = eval_irtree_convert_hi(arena, graph, rdi, r_tree, final_type, r_restype); - - EVAL_IRTree *new_tree = eval_irtree_conditional(arena, c_tree, l_tree, r_tree); - - result.tree = new_tree; - result.type_key = final_type; - result.mode = EVAL_EvalMode_Value; - } - } - }break; - - case EVAL_ExprKind_LeafBytecode: - { - EVAL_IRTree *new_tree = eval_irtree_bytecode_no_copy(arena, expr->bytecode); - TG_Key final_type_key = expr->type_key; - EVAL_EvalMode mode = expr->mode; - - result.tree = new_tree; - result.type_key = final_type_key; - result.mode = mode; - }break; - - case EVAL_ExprKind_LeafMember: - { - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "(internal) Leaf member not expected here."); - }break; - - case EVAL_ExprKind_LeafU64: - { - U64 val = expr->u64; - EVAL_IRTree *new_tree = eval_irtree_const_u(arena, val); - - TG_Key type_key = zero_struct; - if (val <= max_S32){ - type_key = tg_key_basic(TG_Kind_S32); - } - else if (val <= max_S64){ - type_key = tg_key_basic(TG_Kind_S64); - } - else{ - type_key = tg_key_basic(TG_Kind_U64); - } - - result.tree = new_tree; - result.type_key = type_key; - result.mode = EVAL_EvalMode_Value; - }break; - - case EVAL_ExprKind_LeafF64: - { - U64 val = expr->u64; - EVAL_IRTree *new_tree = eval_irtree_const_u(arena, val); - - result.tree = new_tree; - result.type_key = tg_key_basic(TG_Kind_F64); - result.mode = EVAL_EvalMode_Value; - }break; - - case EVAL_ExprKind_LeafF32: - { - U32 val = expr->u32; - EVAL_IRTree *new_tree = eval_irtree_const_u(arena, val); - - result.tree = new_tree; - result.type_key = tg_key_basic(TG_Kind_F32); - result.mode = EVAL_EvalMode_Value; - }break; - - case EVAL_ExprKind_TypeIdent: - { - result.tree = &eval_irtree_nil; - result.type_key = expr->type_key; - result.mode = EVAL_EvalMode_NULL; - }break; - case EVAL_ExprKind_Ptr: - case EVAL_ExprKind_Array: - case EVAL_ExprKind_Func: - { - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Type expression not expected."); - }break; - - case EVAL_ExprKind_Define: - { - if(expr->children[0]->kind != EVAL_ExprKind_LeafIdent) - { - eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Left side of assignment must be an identifier."); - } - result = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, expr->children[1], eout); - }break; - case EVAL_ExprKind_LeafIdent: - { - String8 name = expr->name; - EVAL_Expr *leaf_ident_expr = eval_expr_from_string(leaf_ident_expr_map, name); - if(leaf_ident_expr == &eval_expr_nil) - { - eval_errorf(arena, eout, EVAL_ErrorKind_ResolutionFailure, expr->location, "\"%S\" could not be found.", name); - } - else - { - eval_string2expr_map_inc_poison(leaf_ident_expr_map, name); - result = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, leaf_ident_expr, eout); - eval_string2expr_map_dec_poison(leaf_ident_expr_map, name); - } - }break; - } - - ProfEnd(); - return(result); -} - -internal void -eval_oplist_from_irtree(Arena *arena, EVAL_IRTree *tree, EVAL_OpList *out){ - ProfBeginFunction(); - U32 op = tree->op; - switch (op){ - case RDI_EvalOp_Stop: - case RDI_EvalOp_Skip: - { - // TODO: error - invalid ir-tree op - }break; - - case EVAL_IRExtKind_Bytecode: - { - eval_oplist_push_bytecode(arena, out, tree->bytecode); - }break; - - case RDI_EvalOp_Cond: - { - // split out each of the children - EVAL_OpList prt_cond = {0}; - eval_oplist_from_irtree(arena, tree->children[0], &prt_cond); - - EVAL_OpList prt_left = {0}; - eval_oplist_from_irtree(arena, tree->children[1], &prt_left); - - EVAL_OpList prt_right = {0}; - eval_oplist_from_irtree(arena, tree->children[2], &prt_right); - - // put together like so: - // 1. , Op_Cond (sizeof(2)) - // 2. , Op_Skip (sizeof(3)) - // 3. - // 4. - - // modify prt_right in place to create step 2 - eval_oplist_push_op(arena, &prt_right, RDI_EvalOp_Skip, prt_left.encoded_size); - - // merge 1 into out - eval_oplist_concat_in_place(out, &prt_cond); - eval_oplist_push_op(arena, out, RDI_EvalOp_Cond, prt_right.encoded_size); - - // merge 2 into out - eval_oplist_concat_in_place(out, &prt_right); - - // merge 3 into out - eval_oplist_concat_in_place(out, &prt_left); - }break; - - default: - { - if (op >= RDI_EvalOp_COUNT){ - // TODO: error - invalid ir-tree op - } - else{ - // handle all children - U8 ctrlbits = rdi_eval_op_ctrlbits_table[op]; - U64 child_count = RDI_POPN_FROM_CTRLBITS(ctrlbits); - EVAL_IRTree**child = tree->children; - for (U64 i = 0; i < child_count; i += 1, child += 1){ - eval_oplist_from_irtree(arena, *child, out); - } - - // emit op to compute this node - eval_oplist_push_op(arena, out, (RDI_EvalOp)tree->op, tree->p); - } - }break; - } - ProfEnd(); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ allen: EVAL Bytecode Helpers + +internal String8 +eval_bytecode_from_oplist(Arena *arena, EVAL_OpList *list){ + ProfBeginFunction(); + // allocate output + U64 size = list->encoded_size; + U8 *str = push_array_no_zero(arena, U8, size); + + // iterate loose op nodes + U8 *ptr = str; + U8 *opl = str + size; + for (EVAL_Op *op = list->first_op; + op != 0; + op = op->next){ + U32 opcode = op->opcode; + + switch (opcode){ + default: + { + // compute bytecode advance + U8 ctrlbits = rdi_eval_op_ctrlbits_table[opcode]; + U64 extra_byte_count = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); + + U8 *next_ptr = ptr + 1 + extra_byte_count; + Assert(next_ptr <= opl); + + // fill bytecode + ptr[0] = opcode; + MemoryCopy(ptr + 1, &op->p, extra_byte_count); + + // advance output pointer + ptr = next_ptr; + }break; + + case EVAL_IRExtKind_Bytecode: + { + // compute bytecode advance + U64 size = op->bytecode.size; + U8 *next_ptr = ptr + size; + Assert(next_ptr <= opl); + + // fill bytecode + MemoryCopy(ptr, op->bytecode.str, size); + + // advance output pointer + ptr = next_ptr; + }break; + } + } + + // fill result + String8 result = {0}; + result.size = size; + result.str = str; + ProfEnd(); + return(result); +} + +internal void +eval_oplist_push_op(Arena *arena, EVAL_OpList *list, RDI_EvalOp opcode, U64 p){ + U8 ctrlbits = rdi_eval_op_ctrlbits_table[opcode]; + U32 p_size = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); + + EVAL_Op *node = push_array_no_zero(arena, EVAL_Op, 1); + node->opcode = opcode; + node->p = p; + + SLLQueuePush(list->first_op, list->last_op, node); + list->op_count += 1; + list->encoded_size += 1 + p_size; +} + +internal void +eval_oplist_push_uconst(Arena *arena, EVAL_OpList *list, U64 x){ + if (x <= 0xFF){ + eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU8, x); + } + else if (x <= 0xFFFF){ + eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU16, x); + } + else if (x <= 0xFFFFFFFF){ + eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU32, x); + } + else{ + eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU64, x); + } +} + +internal void +eval_oplist_push_sconst(Arena *arena, EVAL_OpList *list, S64 x){ + if (-0x80 <= x && x <= 0x7F){ + eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU8, (U64)x); + eval_oplist_push_op(arena, list, RDI_EvalOp_TruncSigned, 8); + } + else if (-0x8000 <= x && x <= 0x7FFF){ + eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU16, (U64)x); + eval_oplist_push_op(arena, list, RDI_EvalOp_TruncSigned, 16); + } + else if (-0x80000000ll <= x && x <= 0x7FFFFFFFll){ + eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU32, (U64)x); + eval_oplist_push_op(arena, list, RDI_EvalOp_TruncSigned, 32); + } + else{ + eval_oplist_push_op(arena, list, RDI_EvalOp_ConstU64, (U64)x); + } +} + +internal void +eval_oplist_push_bytecode(Arena *arena, EVAL_OpList *list, String8 bytecode){ + EVAL_Op *node = push_array_no_zero(arena, EVAL_Op, 1); + node->opcode = EVAL_IRExtKind_Bytecode; + node->bytecode = bytecode; + SLLQueuePush(list->first_op, list->last_op, node); + list->op_count += 1; + list->encoded_size += bytecode.size; +} + +internal void +eval_oplist_concat_in_place(EVAL_OpList *left_dst, EVAL_OpList *right_destroyed){ + if (right_destroyed->first_op != 0){ + if (left_dst->first_op == 0){ + MemoryCopyStruct(left_dst, right_destroyed); + } + else{ + left_dst->last_op = right_destroyed->last_op; + left_dst->op_count += right_destroyed->op_count; + left_dst->encoded_size += right_destroyed->encoded_size; + } + MemoryZeroStruct(right_destroyed); + } +} + +//////////////////////////////// +//~ allen: EVAL Expression Info Functions + +internal RDI_EvalOp +eval_opcode_from_expr_kind(EVAL_ExprKind kind){ + RDI_EvalOp result = RDI_EvalOp_Stop; + switch (kind){ + case EVAL_ExprKind_Neg: result = RDI_EvalOp_Neg; break; + case EVAL_ExprKind_LogNot: result = RDI_EvalOp_LogNot; break; + case EVAL_ExprKind_BitNot: result = RDI_EvalOp_BitNot; break; + case EVAL_ExprKind_Mul: result = RDI_EvalOp_Mul; break; + case EVAL_ExprKind_Div: result = RDI_EvalOp_Div; break; + case EVAL_ExprKind_Mod: result = RDI_EvalOp_Mod; break; + case EVAL_ExprKind_Add: result = RDI_EvalOp_Add; break; + case EVAL_ExprKind_Sub: result = RDI_EvalOp_Sub; break; + case EVAL_ExprKind_LShift: result = RDI_EvalOp_LShift; break; + case EVAL_ExprKind_RShift: result = RDI_EvalOp_RShift; break; + case EVAL_ExprKind_Less: result = RDI_EvalOp_Less; break; + case EVAL_ExprKind_LsEq: result = RDI_EvalOp_LsEq; break; + case EVAL_ExprKind_Grtr: result = RDI_EvalOp_Grtr; break; + case EVAL_ExprKind_GrEq: result = RDI_EvalOp_GrEq; break; + case EVAL_ExprKind_EqEq: result = RDI_EvalOp_EqEq; break; + case EVAL_ExprKind_NtEq: result = RDI_EvalOp_NtEq; break; + case EVAL_ExprKind_BitAnd: result = RDI_EvalOp_BitAnd; break; + case EVAL_ExprKind_BitXor: result = RDI_EvalOp_BitXor; break; + case EVAL_ExprKind_BitOr: result = RDI_EvalOp_BitOr; break; + case EVAL_ExprKind_LogAnd: result = RDI_EvalOp_LogAnd; break; + case EVAL_ExprKind_LogOr: result = RDI_EvalOp_LogOr; break; + } + return(result); +} + +internal B32 +eval_expr_kind_is_comparison(EVAL_ExprKind kind){ + B32 result = 0; + switch (kind){ + case EVAL_ExprKind_EqEq: + case EVAL_ExprKind_NtEq: + case EVAL_ExprKind_Less: + case EVAL_ExprKind_Grtr: + case EVAL_ExprKind_LsEq: + case EVAL_ExprKind_GrEq: + { + result = 1; + }break; + } + return(result); +} + +//////////////////////////////// +//~ allen: EVAL Expression Constructors + +internal EVAL_Expr* +eval_expr(Arena *arena, EVAL_ExprKind kind, void *location, + EVAL_Expr *c0, EVAL_Expr *c1, EVAL_Expr *c2){ + EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); + result->kind = kind; + result->location = location; + result->children[0] = c0; + result->children[1] = c1; + result->children[2] = c2; + return(result); +} + +internal EVAL_Expr* +eval_expr_u64(Arena *arena, void *location, U64 u64){ + EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); + result->kind = EVAL_ExprKind_LeafU64; + result->location = location; + result->u64 = u64; + return(result); +} + +internal EVAL_Expr* +eval_expr_f64(Arena *arena, void *location, F64 f64){ + EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); + result->kind = EVAL_ExprKind_LeafF64; + result->location = location; + result->f64 = f64; + return(result); +} + +internal EVAL_Expr* +eval_expr_f32(Arena *arena, void *location, F32 f32){ + EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); + result->kind = EVAL_ExprKind_LeafF32; + result->location = location; + result->f32 = f32; + return(result); +} + +internal EVAL_Expr* +eval_expr_child_and_u64(Arena *arena, EVAL_ExprKind kind, void *location, + EVAL_Expr *child, U64 u64){ + EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); + result->kind = kind; + result->location = location; + result->child_and_constant.child = child; + result->child_and_constant.u64 = u64; + return(result); +} + +internal EVAL_Expr* +eval_expr_leaf_member(Arena *arena, void *location, String8 name){ + EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); + result->location = location; + result->kind = EVAL_ExprKind_LeafMember; + result->name = name; + return(result); +} + +internal EVAL_Expr* +eval_expr_leaf_ident(Arena *arena, void *location, String8 name) +{ + EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); + result->location = location; + result->kind = EVAL_ExprKind_LeafIdent; + result->name = name; + return(result); +} + +internal EVAL_Expr* +eval_expr_leaf_bytecode(Arena *arena, void *location, TG_Key type_key, String8 bytecode, EVAL_EvalMode mode){ + EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); + result->location = location; + result->kind = EVAL_ExprKind_LeafBytecode; + result->type_key = type_key; + result->bytecode = bytecode; + result->mode = mode; + return(result); +} + +internal EVAL_Expr* +eval_expr_leaf_op_list(Arena *arena, void *location, TG_Key type_key, EVAL_OpList *ops, EVAL_EvalMode mode){ + String8 bytecode = eval_bytecode_from_oplist(arena, ops); + EVAL_Expr *result = eval_expr_leaf_bytecode(arena, location, type_key, bytecode, mode); + return(result); +} + +internal EVAL_Expr* +eval_expr_leaf_type(Arena *arena, void *location, TG_Key type_key){ + EVAL_Expr *result = push_array(arena, EVAL_Expr, 1); + result->location = location; + result->kind = EVAL_ExprKind_TypeIdent; + result->type_key = type_key; + return(result); +} + +//////////////////////////////// +//~ allen: EVAL Type Information Transformers + +internal RDI_EvalTypeGroup +eval_type_group_from_kind(TG_Kind kind){ + RDI_EvalTypeGroup result = 0; + switch (kind){ + default:{}break; + + case TG_Kind_Null: case TG_Kind_Void: + case TG_Kind_F16: case TG_Kind_F32PP: case TG_Kind_F48: + case TG_Kind_F80: case TG_Kind_F128: + case TG_Kind_ComplexF32: case TG_Kind_ComplexF64: + case TG_Kind_ComplexF80: case TG_Kind_ComplexF128: + case TG_Kind_Modifier: case TG_Kind_Array: + case TG_Kind_Struct: case TG_Kind_Class: case TG_Kind_Union: + case TG_Kind_Enum: case TG_Kind_Alias: + case TG_Kind_IncompleteStruct: case TG_Kind_IncompleteClass: + case TG_Kind_IncompleteUnion: case TG_Kind_IncompleteEnum: + case TG_Kind_Bitfield: case TG_Kind_Variadic: + result = RDI_EvalTypeGroup_Other; break; + + case TG_Kind_Handle: + case TG_Kind_UChar8: case TG_Kind_UChar16: case TG_Kind_UChar32: + case TG_Kind_U8: case TG_Kind_U16: case TG_Kind_U32: + case TG_Kind_U64: case TG_Kind_U128: case TG_Kind_U256: + case TG_Kind_U512: + case TG_Kind_Ptr: case TG_Kind_LRef: case TG_Kind_RRef: + case TG_Kind_Function: case TG_Kind_Method: case TG_Kind_MemberPtr: + result = RDI_EvalTypeGroup_U; break; + + case TG_Kind_Char8: case TG_Kind_Char16: case TG_Kind_Char32: + case TG_Kind_S8: case TG_Kind_S16: case TG_Kind_S32: + case TG_Kind_S64: case TG_Kind_S128: case TG_Kind_S256: + case TG_Kind_S512: + case TG_Kind_Bool: + result = RDI_EvalTypeGroup_S; break; + + case TG_Kind_F32: + result = RDI_EvalTypeGroup_F32; break; + + case TG_Kind_F64: + result = RDI_EvalTypeGroup_F64; break; + } + return(result); +} + +internal TG_Key +eval_type_unwrap_enum(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + TG_Key result = key; + for(B32 good = 1; good;) + { + TG_Kind kind = tg_kind_from_key(result); + if(kind == TG_Kind_Enum) + { + result = tg_direct_from_graph_rdi_key(graph, rdi, result); + } + else + { + good = 0; + } + } + return result; +} + +internal TG_Key +eval_type_promote(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key){ + TG_Key result = key; + TG_Kind kind = tg_kind_from_key(key); + if(kind == TG_Kind_Bool || + kind == TG_Kind_S8 || + kind == TG_Kind_S16 || + kind == TG_Kind_U8 || + kind == TG_Kind_U16) + { + result = tg_key_basic(TG_Kind_S32); + } + return result; +} + +internal TG_Key +eval_type_coerce(TG_Graph *graph, RDI_Parsed *rdi, TG_Key l, TG_Key r){ + Assert(eval_kind_is_basic_or_enum(tg_kind_from_key(l)) && + eval_kind_is_basic_or_enum(tg_kind_from_key(r))); + + // replace enums with corresponding ints + TG_Key lt = eval_type_unwrap_enum(graph, rdi, l); + TG_Key rt = eval_type_unwrap_enum(graph, rdi, r); + + // first promote each + TG_Key lp = eval_type_promote(graph, rdi, lt); + TG_Key rp = eval_type_promote(graph, rdi, rt); + TG_Kind lk = tg_kind_from_key(lp); + TG_Kind rk = tg_kind_from_key(rp); + + TG_Key result = l; + return(result); +} + +internal B32 +eval_type_match(TG_Graph *graph, RDI_Parsed *rdi, TG_Key l, TG_Key r){ + B32 result = 0; + + // unwrap + TG_Key lu = tg_unwrapped_from_graph_rdi_key(graph, rdi, l); + TG_Key ru = tg_unwrapped_from_graph_rdi_key(graph, rdi, r); + + if (tg_key_match(lu, ru)){ + result = 1; + } + else{ + TG_Kind luk = tg_kind_from_key(lu); + TG_Kind ruk = tg_kind_from_key(ru); + if (luk == ruk){ + switch (luk){ + default: + { + result = 1; + }break; + + case TG_Kind_Ptr: + case TG_Kind_LRef: + case TG_Kind_RRef: + { + TG_Key lud = tg_direct_from_graph_rdi_key(graph, rdi, lu); + TG_Key rud = tg_direct_from_graph_rdi_key(graph, rdi, ru); + if (eval_type_match(graph, rdi, lud, rud)){ + result = 1; + } + }break; + + case TG_Kind_MemberPtr: + { + TG_Key lud = tg_direct_from_graph_rdi_key(graph, rdi, lu); + TG_Key rud = tg_direct_from_graph_rdi_key(graph, rdi, ru); + TG_Key luo = tg_owner_from_graph_rdi_key(graph, rdi, lu); + TG_Key ruo = tg_owner_from_graph_rdi_key(graph, rdi, ru); + if (eval_type_match(graph, rdi, lud, rud) && + eval_type_match(graph, rdi, luo, ruo)){ + result = 1; + } + }break; + + case TG_Kind_Array: + { + Temp scratch = scratch_begin(0, 0); + TG_Type *lt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, l); + TG_Type *rt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, r); + if(lt->count == rt->count && eval_type_match(graph, rdi, lt->direct_type_key, rt->direct_type_key)) + { + result = 1; + } + scratch_end(scratch); + }break; + + case TG_Kind_Function: + { + Temp scratch = scratch_begin(0, 0); + TG_Type *lt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, l); + TG_Type *rt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, r); + if (lt->count == rt->count && eval_type_match(graph, rdi, lt->direct_type_key, rt->direct_type_key)) + { + B32 params_match = 1; + TG_Key *lp = lt->param_type_keys; + TG_Key *rp = rt->param_type_keys; + U64 count = lt->count; + for(U64 i = 0; i < count; i += 1, lp += 1, rp += 1) + { + if(!eval_type_match(graph, rdi, *lp, *rp)) + { + params_match = 0; + break; + } + } + result = params_match; + } + scratch_end(scratch); + }break; + + case TG_Kind_Method: + { + Temp scratch = scratch_begin(0, 0); + TG_Type *lt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, l); + TG_Type *rt = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, r); + if (lt->count == rt->count && + eval_type_match(graph, rdi, lt->direct_type_key, rt->direct_type_key) && + eval_type_match(graph, rdi, lt->owner_type_key, rt->owner_type_key)) + { + B32 params_match = 1; + TG_Key *lp = lt->param_type_keys; + TG_Key *rp = rt->param_type_keys; + U64 count = lt->count; + for(U64 i = 0; i < count; i += 1, lp += 1, rp += 1) + { + if(!eval_type_match(graph, rdi, *lp, *rp)) + { + params_match = 0; + break; + } + } + result = params_match; + } + scratch_end(scratch); + }break; + } + } + } + + return(result); +} + +internal B32 +eval_kind_is_integer(TG_Kind kind){ + B32 result = (TG_Kind_FirstInteger <= kind && kind <= TG_Kind_LastInteger); + return(result); +} + +internal B32 +eval_kind_is_signed(TG_Kind kind){ + B32 result = ((TG_Kind_FirstSigned1 <= kind && kind <= TG_Kind_LastSigned1) || + (TG_Kind_FirstSigned2 <= kind && kind <= TG_Kind_LastSigned2)); + return(result); +} + +internal B32 +eval_kind_is_basic_or_enum(TG_Kind kind){ + B32 result = ((TG_Kind_FirstBasic <= kind && kind <= TG_Kind_LastBasic) || + kind == TG_Kind_Enum); + return(result); +} + + +//////////////////////////////// +//~ allen: EVAL IR-Tree Constructors + +internal EVAL_IRTree* +eval_irtree_const_u(Arena *arena, U64 v){ + // choose encoding op + RDI_EvalOp op = RDI_EvalOp_ConstU64; + if (v < 0x100){ + op = RDI_EvalOp_ConstU8; + } + else if (v < 0x10000){ + op = RDI_EvalOp_ConstU16; + } + else if (v < 0x100000000){ + op = RDI_EvalOp_ConstU32; + } + + // make the tree node + EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); + result->op = op; + result->p = v; + return(result); +} + +internal EVAL_IRTree* +eval_irtree_unary_op(Arena *arena, RDI_EvalOp op, + RDI_EvalTypeGroup group, EVAL_IRTree *c){ + EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); + result->op = op; + result->p = group; + result->children[0] = c; + return(result); +} + +internal EVAL_IRTree* +eval_irtree_binary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, + EVAL_IRTree *l, EVAL_IRTree *r){ + EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); + result->op = op; + result->p = group; + result->children[0] = l; + result->children[1] = r; + return(result); +} + +internal EVAL_IRTree* +eval_irtree_binary_op_u(Arena *arena, RDI_EvalOp op, EVAL_IRTree *l, EVAL_IRTree *r){ + EVAL_IRTree *result = eval_irtree_binary_op(arena, op, RDI_EvalTypeGroup_U, l, r); + return(result); +} + +internal EVAL_IRTree* +eval_irtree_conditional(Arena *arena, EVAL_IRTree *c, EVAL_IRTree *l, EVAL_IRTree *r){ + EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); + result->op = RDI_EvalOp_Cond; + result->children[0] = c; + result->children[1] = l; + result->children[2] = r; + return(result); +} + +internal EVAL_IRTree* +eval_irtree_bytecode_no_copy(Arena *arena, String8 bytecode){ + EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); + result->op = EVAL_IRExtKind_Bytecode; + result->bytecode = bytecode; + return(result); +} + +//////////////////////////////// +//~ allen: EVAL IR-Tree High Level Helpers + +internal EVAL_IRTree* +eval_irtree_mem_read_type(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key type_key){ + U64 byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, type_key); + EVAL_IRTree *result = &eval_irtree_nil; + if (0 < byte_size && byte_size <= 8){ + // build the read node + EVAL_IRTree *read_node = push_array(arena, EVAL_IRTree, 1); + read_node->op = RDI_EvalOp_MemRead; + read_node->p = byte_size; + read_node->children[0] = c; + + // build a signed trunc node if needed + U64 bit_size = byte_size << 3; + EVAL_IRTree *with_trunc = read_node; + TG_Kind kind = tg_kind_from_key(type_key); + if (bit_size < 64 && eval_kind_is_signed(kind)){ + with_trunc = push_array(arena, EVAL_IRTree, 1); + with_trunc->op = RDI_EvalOp_TruncSigned; + with_trunc->p = bit_size; + with_trunc->children[0] = read_node; + } + + // set result + result = with_trunc; + } + else{ + // TODO: unexpected path + } + return(result); +} + +internal EVAL_IRTree* +eval_irtree_convert_lo(Arena *arena, EVAL_IRTree *c, RDI_EvalTypeGroup out, RDI_EvalTypeGroup in){ + EVAL_IRTree *result = push_array(arena, EVAL_IRTree, 1); + result->op = RDI_EvalOp_Convert; + result->p = in | (out << 8); + result->children[0] = c; + return(result); +} + +internal EVAL_IRTree* +eval_irtree_trunc(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key type_key){ + EVAL_IRTree *result = c; + U64 byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, type_key); + if (byte_size < 64){ + RDI_EvalOp op = RDI_EvalOp_Trunc; + TG_Kind kind = tg_kind_from_key(type_key); + if (eval_kind_is_signed(kind)){ + op = RDI_EvalOp_TruncSigned; + } + U64 bit_size = byte_size << 3; + result = push_array(arena, EVAL_IRTree, 1); + result->op = op; + result->p = bit_size; + result->children[0] = c; + } + return(result); +} + +internal EVAL_IRTree* +eval_irtree_convert_hi(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key out, TG_Key in){ + EVAL_IRTree *result = c; + TG_Kind in_kind = tg_kind_from_key(in); + TG_Kind out_kind = tg_kind_from_key(out); + U8 in_group = eval_type_group_from_kind(in_kind); + U8 out_group = eval_type_group_from_kind(out_kind); + U32 conversion_rule = rdi_eval_conversion_kind_from_typegroups(in_group, out_group); + if(conversion_rule == RDI_EvalConversionKind_Legal) + { + result = eval_irtree_convert_lo(arena, result, out_group, in_group); + } + U64 in_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, in); + U64 out_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, out); + if(out_byte_size < in_byte_size && eval_kind_is_integer(out_kind)) + { + result = eval_irtree_trunc(arena, graph, rdi, result, out); + } + return(result); +} + +internal EVAL_IRTree* +eval_irtree_resolve_to_value(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_EvalMode from_mode, + EVAL_IRTree *tree, TG_Key type_key){ + EVAL_IRTree *result = tree; + switch (from_mode){ + default:{}break; + case EVAL_EvalMode_Addr: + { + result = eval_irtree_mem_read_type(arena, graph, rdi, tree, type_key); + }break; + case EVAL_EvalMode_Reg: + { + result = eval_irtree_unary_op(arena, RDI_EvalOp_RegReadDyn, RDI_EvalTypeGroup_U, tree); + }break; + } + return(result); +} + +//////////////////////////////// +//~ allen: EVAL Compiler Phases + +internal void +eval_push_leaf_ident_exprs_from_expr__in_place(Arena *arena, EVAL_String2ExprMap *map, EVAL_Expr *expr, EVAL_ErrorList *eout) +{ + switch(expr->kind) + { + default: + { + U64 children_count = eval_expr_kind_child_counts[expr->kind]; + for(U64 idx = 0; idx < children_count; idx += 1) + { + eval_push_leaf_ident_exprs_from_expr__in_place(arena, map, expr->children[idx], eout); + } + }break; + case EVAL_ExprKind_Define: + { + EVAL_Expr *exprl = expr->children[0]; + EVAL_Expr *exprr = expr->children[1]; + if(exprl->kind == EVAL_ExprKind_LeafIdent) + { + eval_string2expr_map_insert(arena, map, exprl->name, exprr); + } + }break; + } +} + +internal TG_Key +eval_type_from_type_expr(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_Expr *expr, EVAL_ErrorList *eout){ + TG_Key result = zero_struct; + + EVAL_ExprKind kind = expr->kind; + switch (kind){ + default: + { + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Expected type expression."); + }break; + + case EVAL_ExprKind_TypeIdent: + { + result = expr->type_key; + }break; + + case EVAL_ExprKind_Ptr: + { + TG_Key direct_type_key = eval_type_from_type_expr(arena, graph, rdi, expr->children[0], eout); + result = tg_cons_type_make(graph, TG_Kind_Ptr, direct_type_key, 0); + }break; + + case EVAL_ExprKind_Array: + { + EVAL_Expr *child_expr = expr->child_and_constant.child; + TG_Key direct_type_key = eval_type_from_type_expr(arena, graph, rdi, child_expr, eout); + result = tg_cons_type_make(graph, TG_Kind_Array, direct_type_key, expr->child_and_constant.u64); + }break; + + case EVAL_ExprKind_Func: + { + // TODO(rjf): old type graph code is below: +#if 0 + TG_Type *ret_type = eval_type_from_type_expr(arena, graph, expr->children[0], eout); + // TODO(allen): decision: do we do the extra work to preserve full function type info? + result = tg_type_func(graph, ret_type, 0, 0); +#endif + }break; + } + + return(result); +} + +internal EVAL_IRTreeAndType +eval_irtree_and_type_from_expr(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_String2ExprMap *leaf_ident_expr_map, EVAL_Expr *expr, EVAL_ErrorList *eout) +{ + ProfBeginFunction(); + EVAL_IRTreeAndType result = {0}; + result.tree = &eval_irtree_nil; + + EVAL_ExprKind kind = expr->kind; + switch(kind) + { + default: + { + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "(internal) Undefined expression kind (%u).", kind); + }break; + + case EVAL_ExprKind_ArrayIndex: + { + EVAL_Expr *exprl = expr->children[0]; + EVAL_Expr *exprr = expr->children[1]; + + EVAL_IRTreeAndType l = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprl, eout); + EVAL_IRTreeAndType r = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprr, eout); + + if (l.tree->op != 0 && r.tree->op != 0){ + TG_Key l_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, l.type_key); + TG_Key r_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, r.type_key); + TG_Kind l_restype_kind = tg_kind_from_key(l_restype); + TG_Kind r_restype_kind = tg_kind_from_key(r_restype); + if(eval_kind_is_basic_or_enum(r_restype_kind)) + { + r_restype = eval_type_unwrap_enum(graph, rdi, r_restype); + r_restype_kind = tg_kind_from_key(r_restype); + } + + // analyze situation + B32 can_generate = 0; + B32 l_resolve = 0; + TG_Key direct_type = zero_struct; + U64 direct_type_size = 0; + if (!eval_kind_is_integer(r_restype_kind)){ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprr->location, "Cannot index with this type."); + } + else{ + direct_type = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, l_restype); + direct_type_size = tg_byte_size_from_graph_rdi_key(graph, rdi, direct_type); + if (l_restype_kind == TG_Kind_Ptr){ + if (direct_type_size == 0){ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprr->location, "Cannot index into pointers of zero-sized types."); + } + else{ + can_generate = 1; + if (l.mode != EVAL_EvalMode_Value){ + l_resolve = 1; + } + } + } + else if (l_restype_kind == TG_Kind_Array){ + if (l.mode == EVAL_EvalMode_Addr){ + if (direct_type_size == 0){ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprl->location, "Cannot index into arrays of zero-sized types."); + } + else{ + can_generate = 1; + } + } + else{ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprl->location, "(Not supported) Cannot index into array without base address."); + } + } + else{ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprl->location, "Cannot index into this type."); + } + } + + // generate ir tree + if (can_generate){ + // how to compute the index + EVAL_IRTree *index_tree = eval_irtree_resolve_to_value(arena, graph, rdi, r.mode, r.tree, r_restype); + if (direct_type_size > 1){ + EVAL_IRTree *const_tree = eval_irtree_const_u(arena, direct_type_size); + index_tree = eval_irtree_binary_op_u(arena, RDI_EvalOp_Mul, index_tree, const_tree); + } + + // how to compute the base address + EVAL_IRTree *base_tree = l.tree; + if (l_resolve){ + base_tree = eval_irtree_resolve_to_value(arena, graph, rdi, l.mode, base_tree, l_restype); + } + + // how to compute the final address + EVAL_IRTree *new_tree = eval_irtree_binary_op_u(arena, RDI_EvalOp_Add, index_tree, base_tree); + + // fill result + result.tree = new_tree; + result.type_key = direct_type; + result.mode = EVAL_EvalMode_Addr; + } + } + }break; + + case EVAL_ExprKind_MemberAccess: + { + EVAL_Expr *exprl = expr->children[0]; + EVAL_Expr *exprr = expr->children[1]; + + EVAL_IRTreeAndType l = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprl, eout); + + if (l.tree->op != 0 && !tg_key_match(tg_key_zero(), l.type_key)){ + TG_Key l_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, l.type_key); + TG_Kind l_restype_kind = tg_kind_from_key(l_restype); + + // determine which type to use + TG_Key check_type_key = l_restype; + TG_Kind check_type_kind = l_restype_kind; + if (l_restype_kind == TG_Kind_Ptr || l_restype_kind == TG_Kind_LRef || l_restype_kind == TG_Kind_RRef){ + check_type_key = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, l_restype); + check_type_kind = tg_kind_from_key(check_type_key); + } + + // switch to handle + switch(check_type_kind) + { + default: + { + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprl->location, "Cannot perform member access on this type."); + }break; + + case TG_Kind_Struct: + case TG_Kind_Class: + case TG_Kind_Union: + { + // analyze situation + B32 can_generate = 0; + TG_Key r_type = zero_struct; + U32 r_off = 0; + B32 l_resolve = 0; + + // determine how to treat left + B32 l_good = 0; + if (l_restype_kind == TG_Kind_Ptr || l_restype_kind == TG_Kind_LRef || l_restype_kind == TG_Kind_RRef){ + l_good = 1; + l_resolve = 1; + } + else{ + if (l.mode == EVAL_EvalMode_Addr){ + l_good = 1; + } + else if(l.mode == EVAL_EvalMode_Reg) + { + l_good = 1; + } + else{ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprl->location, + "(Not supported) Cannot access member without a base address or register location."); + } + } + + // right must be identifier + B32 r_good = 0; + if (exprr->kind == EVAL_ExprKind_LeafMember){ + r_good = 1; + } + else{ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprr->location, + "(internal) Expected a leaf member in member access."); + } + + if (l_good && r_good){ + Temp scratch = scratch_begin(&arena, 1); + TG_MemberArray check_type_members = tg_data_members_from_graph_rdi_key(scratch.arena, graph, rdi, check_type_key); + + // lookup member + String8 member_name = exprr->name; + TG_Member *match = 0; + for(U64 member_idx = 0; member_idx < check_type_members.count; member_idx += 1) + { + TG_Member *member = &check_type_members.v[member_idx]; + if(str8_match(member->name, member_name, 0)) + { + match = member; + break; + } + } + + // extract member info + if (match != 0){ + can_generate = 1; + r_type = match->type_key; + r_off = match->off; + } + else{ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprr->location, "Could not find a member named '%S' in type.", member_name); + } + + scratch_end(scratch); + } + + // generate ir tree + if (can_generate){ + EVAL_IRTree *new_tree = l.tree; + EVAL_EvalMode mode = l.mode; + if (l_resolve){ + new_tree = eval_irtree_resolve_to_value(arena, graph, rdi, l.mode, new_tree, l_restype); + mode = EVAL_EvalMode_Addr; + } + if (r_off != 0){ + EVAL_IRTree *const_tree = eval_irtree_const_u(arena, r_off); + new_tree = eval_irtree_binary_op_u(arena, RDI_EvalOp_Add, new_tree, const_tree); + } + + // fill result + result.tree = new_tree; + result.type_key = r_type; + result.mode = mode; + } + }break; + } + + } + }break; + + case EVAL_ExprKind_Deref: + { + EVAL_Expr *exprc = expr->children[0]; + + EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprc, eout); + + if (c.tree->op != 0){ + TG_Key c_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, c.type_key); + TG_Kind c_restype_kind = tg_kind_from_key(c_restype); + TG_Key c_restype_direct = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, c_restype); + U64 c_restype_direct_size = tg_byte_size_from_graph_rdi_key(graph, rdi, c_restype_direct); + + // analyze situation + B32 can_generate = 0; + B32 c_resolve = 0; + if (c_restype_kind == TG_Kind_Ptr || + c_restype_kind == TG_Kind_LRef || + c_restype_kind == TG_Kind_RRef){ + if (c_restype_direct_size == 0){ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprc->location, "Cannot dereference pointers of zero-sized types."); + } + else{ + can_generate = 1; + c_resolve = 1; + } + } + else if (c_restype_kind == TG_Kind_Array){ + if (c.mode == EVAL_EvalMode_Addr){ + if (c_restype_direct_size == 0){ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprc->location, "Cannot dereference arrays of zero-sized types."); + } + else{ + can_generate = 1; + } + } + else{ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprc->location, "(Not supported) Cannot dereference array without base address."); + } + } + else{ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, exprc->location, "Cannot dereference this type."); + } + + // generate ir tree + if (can_generate){ + EVAL_IRTree *new_tree = c.tree; + if (c_resolve){ + new_tree = eval_irtree_resolve_to_value(arena, graph, rdi, c.mode, c.tree, c_restype); + } + + // fill result + result.tree = new_tree; + result.type_key = c_restype_direct; + result.mode = EVAL_EvalMode_Addr; + } + } + }break; + + case EVAL_ExprKind_Address: + { + EVAL_Expr *exprc = expr->children[0]; + EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprc, eout); + if(c.tree->op != 0 && !tg_key_match(c.type_key, tg_key_zero())) + { + TG_Key c_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, c.type_key); + TG_Kind c_restype_kind = tg_kind_from_key(c_restype); + + // analyze situation + B32 can_generate = 0; + if(c.mode == EVAL_EvalMode_Addr) + { + can_generate = 1; + } + + // generate ir tree + if(can_generate) + { + EVAL_IRTree *new_tree = c.tree; + TG_Key ptr_type = tg_cons_type_make(graph, TG_Kind_Ptr, c_restype, 0); + + // fill result + result.tree = new_tree; + result.type_key = ptr_type; + result.mode = EVAL_EvalMode_Value; + } + } + }break; + + case EVAL_ExprKind_Cast: + { + EVAL_Expr *exprl = expr->children[0]; + EVAL_Expr *exprr = expr->children[1]; + + TG_Key cast_type_key = eval_type_from_type_expr(arena, graph, rdi, exprl, eout); + TG_Kind cast_type_kind = tg_kind_from_key(cast_type_key); + EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprr, eout); + + if(cast_type_kind != TG_Kind_Null && c.tree->op != 0) + { + TG_Key c_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, c.type_key); + TG_Kind c_restype_kind = tg_kind_from_key(c_restype); + U64 c_restype_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, c_restype); + U64 cast_type_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, cast_type_key); + + // analyze situation + U8 in_group = eval_type_group_from_kind(c_restype_kind); + U8 out_group = eval_type_group_from_kind(cast_type_kind); + RDI_EvalConversionKind conversion_rule = rdi_eval_conversion_kind_from_typegroups(in_group, out_group); + + // generate tree + switch(conversion_rule) + { + case RDI_EvalConversionKind_Noop: + case RDI_EvalConversionKind_Legal: + { + EVAL_IRTree *in_tree = eval_irtree_resolve_to_value(arena, graph, rdi, c.mode, c.tree, c_restype); + + EVAL_IRTree *new_tree = in_tree; + if (conversion_rule == RDI_EvalConversionKind_Legal){ + new_tree = eval_irtree_convert_lo(arena, in_tree, out_group, in_group); + } + if (cast_type_byte_size < c_restype_byte_size && eval_kind_is_integer(cast_type_kind)){ + new_tree = eval_irtree_trunc(arena, graph, rdi, in_tree, cast_type_key); + } + + result.tree = new_tree; + result.type_key = cast_type_key; + result.mode = EVAL_EvalMode_Value; + }break; + + default: + { + String8 text = str8_lit("(internal) unknown conversion rule"); + if (conversion_rule < RDI_EvalConversionKind_COUNT){ + text.str = rdi_explanation_string_from_eval_conversion_kind(conversion_rule, &text.size); + } + eval_error(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, text); + }break; + } + } + }break; + + case EVAL_ExprKind_Sizeof: + { + EVAL_Expr *exprc = expr->children[0]; + + // analyze situation + TG_Key type_key = zero_struct; + switch (exprc->kind){ + // size of type expression + case EVAL_ExprKind_TypeIdent: + case EVAL_ExprKind_Ptr: + case EVAL_ExprKind_Array: + case EVAL_ExprKind_Func: + { + type_key = eval_type_from_type_expr(arena, graph, rdi, exprc, eout); + }break; + + // size of value expression + default: + { + EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprc, eout); + type_key = c.type_key; + }break; + } + B32 can_generate = 0; + U64 size = 0; + TG_Kind type_kind = tg_kind_from_key(type_key); + if (type_kind != TG_Kind_Null){ + can_generate = 1; + size = tg_byte_size_from_graph_rdi_key(graph, rdi, type_key); + } + + // generate ir tree + if(can_generate) + { + EVAL_IRTree *new_tree = eval_irtree_const_u(arena, size); + result.tree = new_tree; + result.type_key = tg_key_basic(TG_Kind_U64); + result.mode = EVAL_EvalMode_Value; + } + }break; + + case EVAL_ExprKind_Neg: + case EVAL_ExprKind_LogNot: + case EVAL_ExprKind_BitNot: + { + EVAL_Expr *exprc = expr->children[0]; + + EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprc, eout); + if (c.tree->op != 0){ + TG_Key c_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, c.type_key); + TG_Key p_type = eval_type_promote(graph, rdi, c_restype); + TG_Kind c_restype_kind = tg_kind_from_key(c_restype); + + // analyze situation + B32 can_generate = 0; + RDI_EvalOp op = eval_opcode_from_expr_kind(kind); + U8 c_group = eval_type_group_from_kind(c_restype_kind); + if (!rdi_eval_op_typegroup_are_compatible(op, c_group)){ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Cannot use this operator on this type."); + } + else{ + can_generate = 1; + } + + // generate ir tree + if (can_generate){ + EVAL_IRTree *in_tree = eval_irtree_resolve_to_value(arena, graph, rdi, c.mode, c.tree, c_restype); + in_tree = eval_irtree_convert_hi(arena, graph, rdi, in_tree, p_type, c_restype); + + EVAL_IRTree *new_tree = eval_irtree_unary_op(arena, op, c_group, in_tree); + + result.tree = new_tree; + result.type_key = p_type; + result.mode = EVAL_EvalMode_Value; + } + } + }break; + + case EVAL_ExprKind_Mul: + case EVAL_ExprKind_Div: + case EVAL_ExprKind_Mod: + case EVAL_ExprKind_Add: + case EVAL_ExprKind_Sub: + case EVAL_ExprKind_LShift: + case EVAL_ExprKind_RShift: + case EVAL_ExprKind_Less: + case EVAL_ExprKind_LsEq: + case EVAL_ExprKind_Grtr: + case EVAL_ExprKind_GrEq: + case EVAL_ExprKind_EqEq: + case EVAL_ExprKind_NtEq: + case EVAL_ExprKind_BitAnd: + case EVAL_ExprKind_BitXor: + case EVAL_ExprKind_BitOr: + case EVAL_ExprKind_LogAnd: + case EVAL_ExprKind_LogOr: + { + //- setup & dispatch + EVAL_Expr *exprl = expr->children[0]; + EVAL_Expr *exprr = expr->children[1]; + + EVAL_IRTreeAndType l = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprl, eout); + EVAL_IRTreeAndType r = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprr, eout); + + if (l.tree->op != 0 && r.tree->op != 0){ + TG_Key l_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, l.type_key); + TG_Key r_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, r.type_key); + TG_Kind l_restype_kind = tg_kind_from_key(l_restype); + TG_Kind r_restype_kind = tg_kind_from_key(r_restype); + + //- rjf: decay register types to basics + if(l_restype.kind == TG_KeyKind_Reg) + { + l_restype = tg_key_basic(TG_Kind_U64); + l_restype_kind = tg_kind_from_key(l_restype); + } + if(r_restype.kind == TG_KeyKind_Reg) + { + r_restype = tg_key_basic(TG_Kind_U64); + r_restype_kind = tg_kind_from_key(r_restype); + } + + RDI_EvalOp op = eval_opcode_from_expr_kind(kind); + + //- pointer decay + B32 l_is_pointer = (l_restype_kind == TG_Kind_Ptr); + B32 l_is_decay = (l_restype_kind == TG_Kind_Array && l.mode == EVAL_EvalMode_Addr); + B32 l_is_pointer_like = (l_is_pointer || l_is_decay); + + B32 r_is_pointer = (r_restype_kind == TG_Kind_Ptr); + B32 r_is_decay = (r_restype_kind == TG_Kind_Array && r.mode == EVAL_EvalMode_Addr); + B32 r_is_pointer_like = (r_is_pointer || r_is_decay); + + //- determine arithmetic path +#define EVAL_ArithPath_Normal 0 +#define EVAL_ArithPath_PtrAdd 1 +#define EVAL_ArithPath_PtrSub 2 + + B32 ptr_arithmetic_mul_rptr = 0; + U32 arith_path = EVAL_ArithPath_Normal; + if (kind == EVAL_ExprKind_Add){ + if (l_is_pointer_like && eval_kind_is_integer(r_restype_kind)){ + arith_path = EVAL_ArithPath_PtrAdd; + } + if (l_is_pointer_like && eval_kind_is_integer(l_restype_kind)){ + arith_path = EVAL_ArithPath_PtrAdd; + ptr_arithmetic_mul_rptr = 1; + } + } + else if (kind == EVAL_ExprKind_Sub){ + if (l_is_pointer_like && eval_kind_is_integer(r_restype_kind)){ + arith_path = EVAL_ArithPath_PtrAdd; + } + if (l_is_pointer_like && r_is_pointer_like){ + TG_Key l_restype_direct = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, l_restype); + TG_Key r_restype_direct = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, r_restype); + U64 l_restype_direct_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, l_restype_direct); + U64 r_restype_direct_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, r_restype_direct); + if (l_restype_direct_byte_size == r_restype_direct_byte_size){ + arith_path = EVAL_ArithPath_PtrSub; + } + } + } + + //- specific arithmetic handlers + + switch (arith_path){ + case EVAL_ArithPath_Normal: + { + // analyze situation + B32 is_comparison = eval_expr_kind_is_comparison(kind); + B32 both_basic = (eval_kind_is_basic_or_enum(l_restype_kind) && + eval_kind_is_basic_or_enum(r_restype_kind)); + + TG_Key cv_type_key = zero_struct; + if (both_basic){ + cv_type_key = eval_type_coerce(graph, rdi, l_restype, r_restype); + } + else if (is_comparison && eval_type_match(graph, rdi, l_restype, r_restype)){ + cv_type_key = l_restype; + } + + TG_Kind cv_type_kind = tg_kind_from_key(cv_type_key); + U8 cv_group = eval_type_group_from_kind(cv_type_kind); + + B32 can_generate = 0; + if (rdi_eval_op_typegroup_are_compatible(op, cv_group)){ + can_generate = 1; + } + else{ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Cannot use this operator on this type."); + } + + // generate ir tree + if (can_generate){ + TG_Key final_type_key = cv_type_key; + if (is_comparison){ + final_type_key = tg_key_basic(TG_Kind_Bool); + } + + EVAL_IRTree *l_tree = eval_irtree_resolve_to_value(arena, graph, rdi, l.mode, l.tree, l_restype); + l_tree = eval_irtree_convert_hi(arena, graph, rdi, l_tree, cv_type_key, l_restype); + + EVAL_IRTree *r_tree = eval_irtree_resolve_to_value(arena, graph, rdi, r.mode, r.tree, r_restype); + r_tree = eval_irtree_convert_hi(arena, graph, rdi, r_tree, cv_type_key, r_restype); + + EVAL_IRTree *new_tree = eval_irtree_binary_op(arena, op, cv_group, l_tree, r_tree); + + result.tree = new_tree; + result.type_key = final_type_key; + result.mode = EVAL_EvalMode_Value; + } + }break; + + case EVAL_ArithPath_PtrAdd: + { + // setup which side is the pointer + EVAL_IRTreeAndType *ptr = &l; + EVAL_IRTreeAndType *integer = &r; + B32 ptr_is_decay = l_is_decay; + if (ptr_arithmetic_mul_rptr){ + ptr = &r; + integer = &l; + ptr_is_decay = r_is_decay; + } + + TG_Key direct = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, ptr->type_key); + U64 direct_type_size = tg_byte_size_from_graph_rdi_key(graph, rdi, direct); + + // generate ir tree + EVAL_IRTree *ptr_tree = ptr->tree; + if (!ptr_is_decay){ + ptr_tree = eval_irtree_resolve_to_value(arena, graph, rdi, ptr->mode, ptr_tree, ptr->type_key); + } + + EVAL_IRTree *integer_tree = eval_irtree_resolve_to_value(arena, graph, rdi, integer->mode, integer->tree, integer->type_key); + if (direct_type_size > 1){ + EVAL_IRTree *const_tree = eval_irtree_const_u(arena, direct_type_size); + integer_tree = eval_irtree_binary_op_u(arena, RDI_EvalOp_Mul, integer_tree, const_tree); + } + + TG_Key ptr_type = ptr->type_key; + if (ptr_is_decay){ + ptr_type = tg_cons_type_make(graph, TG_Kind_Ptr, direct, 0); + } + + EVAL_IRTree *new_tree = eval_irtree_binary_op(arena, op, RDI_EvalTypeGroup_U, ptr_tree, integer_tree); + + result.tree = new_tree; + result.type_key = ptr_type; + result.mode = EVAL_EvalMode_Value; + }break; + + case EVAL_ArithPath_PtrSub: + { + TG_Key direct = tg_unwrapped_direct_from_graph_rdi_key(graph, rdi, l_restype); + U64 direct_type_size = tg_byte_size_from_graph_rdi_key(graph, rdi, direct); + + // generate ir tree + EVAL_IRTree *l_tree = l.tree; + if (!l_is_decay){ + l_tree = eval_irtree_resolve_to_value(arena, graph, rdi, l.mode, l.tree, l_restype); + } + + EVAL_IRTree *r_tree = r.tree; + if (!r_is_decay){ + r_tree = eval_irtree_resolve_to_value(arena, graph, rdi, r.mode, r.tree, r_restype); + } + + EVAL_IRTree *op_tree = eval_irtree_binary_op(arena, op, RDI_EvalTypeGroup_U, l_tree, r_tree); + + EVAL_IRTree *new_tree = op_tree; + if (direct_type_size > 1){ + EVAL_IRTree *const_tree = eval_irtree_const_u(arena, direct_type_size); + new_tree = eval_irtree_binary_op(arena, RDI_EvalOp_Div, RDI_EvalTypeGroup_U, new_tree, const_tree); + } + + result.tree = new_tree; + result.type_key = tg_key_basic(TG_Kind_U64); + result.mode = EVAL_EvalMode_Value; + }break; + } + } + }break; + + case EVAL_ExprKind_Ternary: + { + EVAL_Expr *exprc = expr->children[0]; + EVAL_Expr *exprl = expr->children[1]; + EVAL_Expr *exprr = expr->children[2]; + + EVAL_IRTreeAndType c = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprc, eout); + EVAL_IRTreeAndType l = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprl, eout); + EVAL_IRTreeAndType r = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, exprr, eout); + + if (l.tree->op != 0 && r.tree->op != 0 && c.tree->op != 0){ + + TG_Key c_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, c.type_key); + TG_Key l_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, l.type_key); + TG_Key r_restype = tg_unwrapped_from_graph_rdi_key(graph, rdi, r.type_key); + TG_Kind c_restype_kind = tg_kind_from_key(c_restype); + TG_Kind l_restype_kind = tg_kind_from_key(l_restype); + TG_Kind r_restype_kind = tg_kind_from_key(r_restype); + + // analyze situation + B32 can_generate = 0; + TG_Key final_type = zero_struct; + if (eval_kind_is_integer(c_restype_kind)){ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Conditional term must be an integer type."); + } + else{ + if (eval_kind_is_basic_or_enum(l_restype_kind) && + eval_kind_is_basic_or_enum(r_restype_kind)){ + can_generate = 1; + final_type = eval_type_coerce(graph, rdi, l_restype, r_restype); + } + else{ + if (eval_type_match(graph, rdi, l_restype, r_restype)){ + if (l_restype_kind == TG_Kind_Ptr){ + can_generate = 1; + final_type = l_restype; + } + else{ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "(Not supported) Conditional value not basic type or pointer."); + } + } + else{ + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Left and right terms must have matching types."); + } + } + } + + // generate ir tree + if (can_generate){ + EVAL_IRTree *c_tree = eval_irtree_resolve_to_value(arena, graph, rdi, c.mode, c.tree, c_restype); + + EVAL_IRTree *l_tree = eval_irtree_resolve_to_value(arena, graph, rdi, l.mode, l.tree, l_restype); + l_tree = eval_irtree_convert_hi(arena, graph, rdi, l_tree, final_type, l_restype); + + EVAL_IRTree *r_tree = eval_irtree_resolve_to_value(arena, graph, rdi, r.mode, r.tree, r_restype); + r_tree = eval_irtree_convert_hi(arena, graph, rdi, r_tree, final_type, r_restype); + + EVAL_IRTree *new_tree = eval_irtree_conditional(arena, c_tree, l_tree, r_tree); + + result.tree = new_tree; + result.type_key = final_type; + result.mode = EVAL_EvalMode_Value; + } + } + }break; + + case EVAL_ExprKind_LeafBytecode: + { + EVAL_IRTree *new_tree = eval_irtree_bytecode_no_copy(arena, expr->bytecode); + TG_Key final_type_key = expr->type_key; + EVAL_EvalMode mode = expr->mode; + + result.tree = new_tree; + result.type_key = final_type_key; + result.mode = mode; + }break; + + case EVAL_ExprKind_LeafMember: + { + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "(internal) Leaf member not expected here."); + }break; + + case EVAL_ExprKind_LeafU64: + { + U64 val = expr->u64; + EVAL_IRTree *new_tree = eval_irtree_const_u(arena, val); + + TG_Key type_key = zero_struct; + if (val <= max_S32){ + type_key = tg_key_basic(TG_Kind_S32); + } + else if (val <= max_S64){ + type_key = tg_key_basic(TG_Kind_S64); + } + else{ + type_key = tg_key_basic(TG_Kind_U64); + } + + result.tree = new_tree; + result.type_key = type_key; + result.mode = EVAL_EvalMode_Value; + }break; + + case EVAL_ExprKind_LeafF64: + { + U64 val = expr->u64; + EVAL_IRTree *new_tree = eval_irtree_const_u(arena, val); + + result.tree = new_tree; + result.type_key = tg_key_basic(TG_Kind_F64); + result.mode = EVAL_EvalMode_Value; + }break; + + case EVAL_ExprKind_LeafF32: + { + U32 val = expr->u32; + EVAL_IRTree *new_tree = eval_irtree_const_u(arena, val); + + result.tree = new_tree; + result.type_key = tg_key_basic(TG_Kind_F32); + result.mode = EVAL_EvalMode_Value; + }break; + + case EVAL_ExprKind_TypeIdent: + { + result.tree = &eval_irtree_nil; + result.type_key = expr->type_key; + result.mode = EVAL_EvalMode_NULL; + }break; + case EVAL_ExprKind_Ptr: + case EVAL_ExprKind_Array: + case EVAL_ExprKind_Func: + { + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Type expression not expected."); + }break; + + case EVAL_ExprKind_Define: + { + if(expr->children[0]->kind != EVAL_ExprKind_LeafIdent) + { + eval_errorf(arena, eout, EVAL_ErrorKind_MalformedInput, expr->location, "Left side of assignment must be an identifier."); + } + result = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, expr->children[1], eout); + }break; + case EVAL_ExprKind_LeafIdent: + { + String8 name = expr->name; + EVAL_Expr *leaf_ident_expr = eval_expr_from_string(leaf_ident_expr_map, name); + if(leaf_ident_expr == &eval_expr_nil) + { + eval_errorf(arena, eout, EVAL_ErrorKind_ResolutionFailure, expr->location, "\"%S\" could not be found.", name); + } + else + { + eval_string2expr_map_inc_poison(leaf_ident_expr_map, name); + result = eval_irtree_and_type_from_expr(arena, graph, rdi, leaf_ident_expr_map, leaf_ident_expr, eout); + eval_string2expr_map_dec_poison(leaf_ident_expr_map, name); + } + }break; + } + + ProfEnd(); + return(result); +} + +internal void +eval_oplist_from_irtree(Arena *arena, EVAL_IRTree *tree, EVAL_OpList *out){ + ProfBeginFunction(); + U32 op = tree->op; + switch (op){ + case RDI_EvalOp_Stop: + case RDI_EvalOp_Skip: + { + // TODO: error - invalid ir-tree op + }break; + + case EVAL_IRExtKind_Bytecode: + { + eval_oplist_push_bytecode(arena, out, tree->bytecode); + }break; + + case RDI_EvalOp_Cond: + { + // split out each of the children + EVAL_OpList prt_cond = {0}; + eval_oplist_from_irtree(arena, tree->children[0], &prt_cond); + + EVAL_OpList prt_left = {0}; + eval_oplist_from_irtree(arena, tree->children[1], &prt_left); + + EVAL_OpList prt_right = {0}; + eval_oplist_from_irtree(arena, tree->children[2], &prt_right); + + // put together like so: + // 1. , Op_Cond (sizeof(2)) + // 2. , Op_Skip (sizeof(3)) + // 3. + // 4. + + // modify prt_right in place to create step 2 + eval_oplist_push_op(arena, &prt_right, RDI_EvalOp_Skip, prt_left.encoded_size); + + // merge 1 into out + eval_oplist_concat_in_place(out, &prt_cond); + eval_oplist_push_op(arena, out, RDI_EvalOp_Cond, prt_right.encoded_size); + + // merge 2 into out + eval_oplist_concat_in_place(out, &prt_right); + + // merge 3 into out + eval_oplist_concat_in_place(out, &prt_left); + }break; + + default: + { + if (op >= RDI_EvalOp_COUNT){ + // TODO: error - invalid ir-tree op + } + else{ + // handle all children + U8 ctrlbits = rdi_eval_op_ctrlbits_table[op]; + U64 child_count = RDI_POPN_FROM_CTRLBITS(ctrlbits); + EVAL_IRTree**child = tree->children; + for (U64 i = 0; i < child_count; i += 1, child += 1){ + eval_oplist_from_irtree(arena, *child, out); + } + + // emit op to compute this node + eval_oplist_push_op(arena, out, (RDI_EvalOp)tree->op, tree->p); + } + }break; + } + ProfEnd(); +} diff --git a/src/eval/eval_compiler.h b/src/eval/eval_compiler.h index 4f44ee55..50aa2885 100644 --- a/src/eval/eval_compiler.h +++ b/src/eval/eval_compiler.h @@ -1,82 +1,82 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef EVAL_COMPILER_H -#define EVAL_COMPILER_H - -//////////////////////////////// -//~ allen: EVAL Bytecode Helpers - -internal String8 eval_bytecode_from_oplist(Arena *arena, EVAL_OpList *list); - -internal void eval_oplist_push_op(Arena *arena, EVAL_OpList *list, RDI_EvalOp op, U64 p); -internal void eval_oplist_push_uconst(Arena *arena, EVAL_OpList *list, U64 x); -internal void eval_oplist_push_sconst(Arena *arena, EVAL_OpList *list, S64 x); - -internal void eval_oplist_push_bytecode(Arena *arena, EVAL_OpList *list, String8 bytecode); - -internal void eval_oplist_concat_in_place(EVAL_OpList *left_dst, EVAL_OpList *right_destroyed); - -//////////////////////////////// -//~ allen: EVAL Expression Info Functions - -internal RDI_EvalOp eval_opcode_from_expr_kind(EVAL_ExprKind kind); -internal B32 eval_expr_kind_is_comparison(EVAL_ExprKind kind); - -//////////////////////////////// -//~ allen: EVAL Expression Constructors - -internal EVAL_Expr* eval_expr(Arena *arena, EVAL_ExprKind kind, void *location, EVAL_Expr *c0, EVAL_Expr *c1, EVAL_Expr *c2); -internal EVAL_Expr* eval_expr_u64(Arena *arena, void *location, U64 u64); -internal EVAL_Expr* eval_expr_f64(Arena *arena, void *location, F64 f64); -internal EVAL_Expr* eval_expr_f32(Arena *arena, void *location, F32 f32); -internal EVAL_Expr* eval_expr_child_and_u64(Arena *arena, EVAL_ExprKind kind, void *location, EVAL_Expr *child, U64 u64); -internal EVAL_Expr* eval_expr_leaf_member(Arena *arena, void *location, String8 name); -internal EVAL_Expr* eval_expr_leaf_ident(Arena *arena, void *location, String8 name); -internal EVAL_Expr* eval_expr_leaf_bytecode(Arena *arena, void *location, TG_Key type_key, String8 bytecode, EVAL_EvalMode mode); -internal EVAL_Expr* eval_expr_leaf_op_list(Arena *arena, void *location, TG_Key type_key, EVAL_OpList *ops, EVAL_EvalMode mode); -internal EVAL_Expr* eval_expr_leaf_type(Arena *arena, void *location, TG_Key type_key); - -//////////////////////////////// -//~ allen: EVAL Type Information Transformers - -internal RDI_EvalTypeGroup eval_type_group_from_kind(TG_Kind kind); - -internal TG_Key eval_type_unwrap_enum(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal TG_Key eval_type_promote(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal TG_Key eval_type_coerce(TG_Graph *graph, RDI_Parsed *rdi, TG_Key l, TG_Key r); - -internal B32 eval_type_match(TG_Graph *graph, RDI_Parsed *rdi, TG_Key l, TG_Key r); - -internal B32 eval_kind_is_integer(TG_Kind kind); -internal B32 eval_kind_is_signed(TG_Kind kind); -internal B32 eval_kind_is_basic_or_enum(TG_Kind kind); - -//////////////////////////////// -//~ allen: EVAL IR-Tree Constructors - -internal EVAL_IRTree* eval_irtree_const_u(Arena *arena, U64 v); -internal EVAL_IRTree* eval_irtree_unary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, EVAL_IRTree *c); -internal EVAL_IRTree* eval_irtree_binary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, EVAL_IRTree *l, EVAL_IRTree *r); -internal EVAL_IRTree* eval_irtree_binary_op_u(Arena *arena, RDI_EvalOp op, EVAL_IRTree *l, EVAL_IRTree *r); -internal EVAL_IRTree* eval_irtree_conditional(Arena *arena, EVAL_IRTree *c, EVAL_IRTree *l, EVAL_IRTree *r); -internal EVAL_IRTree* eval_irtree_bytecode_no_copy(Arena *arena, String8 bytecode); - -//////////////////////////////// -//~ allen: EVAL IR-Tree High Level Helpers - -internal EVAL_IRTree* eval_irtree_mem_read_type(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key type_key); -internal EVAL_IRTree* eval_irtree_convert_lo(Arena *arena, EVAL_IRTree *c, RDI_EvalTypeGroup out, RDI_EvalTypeGroup in); -internal EVAL_IRTree* eval_irtree_trunc(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key type_key); -internal EVAL_IRTree* eval_irtree_convert_hi(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key out, TG_Key in); -internal EVAL_IRTree* eval_irtree_resolve_to_value(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_EvalMode from_mode, EVAL_IRTree *tree, TG_Key type_key); - -//////////////////////////////// -//~ allen: EVAL Compiler Phases - -internal void eval_push_leaf_ident_exprs_from_expr__in_place(Arena *arena, EVAL_String2ExprMap *map, EVAL_Expr *expr, EVAL_ErrorList *eout); -internal TG_Key eval_type_from_type_expr(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_Expr *expr, EVAL_ErrorList *eout); -internal EVAL_IRTreeAndType eval_irtree_and_type_from_expr(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_String2ExprMap *leaf_ident_expr_map, EVAL_Expr *expr, EVAL_ErrorList *eout); -internal void eval_oplist_from_irtree(Arena *arena, EVAL_IRTree *tree, EVAL_OpList *out); - -#endif //EVAL_COMPILER_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef EVAL_COMPILER_H +#define EVAL_COMPILER_H + +//////////////////////////////// +//~ allen: EVAL Bytecode Helpers + +internal String8 eval_bytecode_from_oplist(Arena *arena, EVAL_OpList *list); + +internal void eval_oplist_push_op(Arena *arena, EVAL_OpList *list, RDI_EvalOp op, U64 p); +internal void eval_oplist_push_uconst(Arena *arena, EVAL_OpList *list, U64 x); +internal void eval_oplist_push_sconst(Arena *arena, EVAL_OpList *list, S64 x); + +internal void eval_oplist_push_bytecode(Arena *arena, EVAL_OpList *list, String8 bytecode); + +internal void eval_oplist_concat_in_place(EVAL_OpList *left_dst, EVAL_OpList *right_destroyed); + +//////////////////////////////// +//~ allen: EVAL Expression Info Functions + +internal RDI_EvalOp eval_opcode_from_expr_kind(EVAL_ExprKind kind); +internal B32 eval_expr_kind_is_comparison(EVAL_ExprKind kind); + +//////////////////////////////// +//~ allen: EVAL Expression Constructors + +internal EVAL_Expr* eval_expr(Arena *arena, EVAL_ExprKind kind, void *location, EVAL_Expr *c0, EVAL_Expr *c1, EVAL_Expr *c2); +internal EVAL_Expr* eval_expr_u64(Arena *arena, void *location, U64 u64); +internal EVAL_Expr* eval_expr_f64(Arena *arena, void *location, F64 f64); +internal EVAL_Expr* eval_expr_f32(Arena *arena, void *location, F32 f32); +internal EVAL_Expr* eval_expr_child_and_u64(Arena *arena, EVAL_ExprKind kind, void *location, EVAL_Expr *child, U64 u64); +internal EVAL_Expr* eval_expr_leaf_member(Arena *arena, void *location, String8 name); +internal EVAL_Expr* eval_expr_leaf_ident(Arena *arena, void *location, String8 name); +internal EVAL_Expr* eval_expr_leaf_bytecode(Arena *arena, void *location, TG_Key type_key, String8 bytecode, EVAL_EvalMode mode); +internal EVAL_Expr* eval_expr_leaf_op_list(Arena *arena, void *location, TG_Key type_key, EVAL_OpList *ops, EVAL_EvalMode mode); +internal EVAL_Expr* eval_expr_leaf_type(Arena *arena, void *location, TG_Key type_key); + +//////////////////////////////// +//~ allen: EVAL Type Information Transformers + +internal RDI_EvalTypeGroup eval_type_group_from_kind(TG_Kind kind); + +internal TG_Key eval_type_unwrap_enum(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal TG_Key eval_type_promote(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal TG_Key eval_type_coerce(TG_Graph *graph, RDI_Parsed *rdi, TG_Key l, TG_Key r); + +internal B32 eval_type_match(TG_Graph *graph, RDI_Parsed *rdi, TG_Key l, TG_Key r); + +internal B32 eval_kind_is_integer(TG_Kind kind); +internal B32 eval_kind_is_signed(TG_Kind kind); +internal B32 eval_kind_is_basic_or_enum(TG_Kind kind); + +//////////////////////////////// +//~ allen: EVAL IR-Tree Constructors + +internal EVAL_IRTree* eval_irtree_const_u(Arena *arena, U64 v); +internal EVAL_IRTree* eval_irtree_unary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, EVAL_IRTree *c); +internal EVAL_IRTree* eval_irtree_binary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, EVAL_IRTree *l, EVAL_IRTree *r); +internal EVAL_IRTree* eval_irtree_binary_op_u(Arena *arena, RDI_EvalOp op, EVAL_IRTree *l, EVAL_IRTree *r); +internal EVAL_IRTree* eval_irtree_conditional(Arena *arena, EVAL_IRTree *c, EVAL_IRTree *l, EVAL_IRTree *r); +internal EVAL_IRTree* eval_irtree_bytecode_no_copy(Arena *arena, String8 bytecode); + +//////////////////////////////// +//~ allen: EVAL IR-Tree High Level Helpers + +internal EVAL_IRTree* eval_irtree_mem_read_type(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key type_key); +internal EVAL_IRTree* eval_irtree_convert_lo(Arena *arena, EVAL_IRTree *c, RDI_EvalTypeGroup out, RDI_EvalTypeGroup in); +internal EVAL_IRTree* eval_irtree_trunc(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key type_key); +internal EVAL_IRTree* eval_irtree_convert_hi(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_IRTree *c, TG_Key out, TG_Key in); +internal EVAL_IRTree* eval_irtree_resolve_to_value(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_EvalMode from_mode, EVAL_IRTree *tree, TG_Key type_key); + +//////////////////////////////// +//~ allen: EVAL Compiler Phases + +internal void eval_push_leaf_ident_exprs_from_expr__in_place(Arena *arena, EVAL_String2ExprMap *map, EVAL_Expr *expr, EVAL_ErrorList *eout); +internal TG_Key eval_type_from_type_expr(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_Expr *expr, EVAL_ErrorList *eout); +internal EVAL_IRTreeAndType eval_irtree_and_type_from_expr(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, EVAL_String2ExprMap *leaf_ident_expr_map, EVAL_Expr *expr, EVAL_ErrorList *eout); +internal void eval_oplist_from_irtree(Arena *arena, EVAL_IRTree *tree, EVAL_OpList *out); + +#endif //EVAL_COMPILER_H diff --git a/src/eval/eval_core.c b/src/eval/eval_core.c index 3a237afe..23d14855 100644 --- a/src/eval/eval_core.c +++ b/src/eval/eval_core.c @@ -1,254 +1,254 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/eval.meta.c" - -//////////////////////////////// -//~ rjf: Basic Functions - -internal U64 -eval_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; -} - -//////////////////////////////// -//~ rjf: Error List Building Functions - -internal void -eval_error(Arena *arena, EVAL_ErrorList *list, EVAL_ErrorKind kind, void *location, String8 text){ - EVAL_Error *error = push_array_no_zero(arena, EVAL_Error, 1); - SLLQueuePush(list->first, list->last, error); - list->count += 1; - list->max_kind = Max(kind, list->max_kind); - error->kind = kind; - error->location = location; - error->text = text; -} - -internal void -eval_errorf(Arena *arena, EVAL_ErrorList *list, EVAL_ErrorKind kind, void *location, char *fmt, ...){ - va_list args; - va_start(args, fmt); - String8 text = push_str8fv(arena, fmt, args); - va_end(args); - eval_error(arena, list, kind, location, text); -} - -internal void -eval_error_list_concat_in_place(EVAL_ErrorList *dst, EVAL_ErrorList *to_push){ - if (dst->last != 0){ - if (to_push->last != 0){ - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->count += to_push->count; - } - } - else{ - *dst = *to_push; - } - MemoryZeroStruct(to_push); -} - -//////////////////////////////// -//~ rjf: Map Functions - -//- rjf: string -> num - -internal EVAL_String2NumMap -eval_string2num_map_make(Arena *arena, U64 slot_count) -{ - EVAL_String2NumMap map = {0}; - map.slots_count = slot_count; - map.slots = push_array(arena, EVAL_String2NumMapSlot, map.slots_count); - return map; -} - -internal void -eval_string2num_map_insert(Arena *arena, EVAL_String2NumMap *map, String8 string, U64 num) -{ - U64 hash = eval_hash_from_string(string); - U64 slot_idx = hash%map->slots_count; - EVAL_String2NumMapNode *existing_node = 0; - for(EVAL_String2NumMapNode *node = map->slots[slot_idx].first; node != 0; node = node->hash_next) - { - if(str8_match(node->string, string, 0) && node->num == num) - { - existing_node = node; - break; - } - } - if(existing_node == 0) - { - EVAL_String2NumMapNode *node = push_array(arena, EVAL_String2NumMapNode, 1); - SLLQueuePush_N(map->slots[slot_idx].first, map->slots[slot_idx].last, node, hash_next); - SLLQueuePush_N(map->first, map->last, node, order_next); - node->string = push_str8_copy(arena, string); - node->num = num; - map->node_count += 1; - } -} - -internal U64 -eval_num_from_string(EVAL_String2NumMap *map, String8 string) -{ - U64 num = 0; - if(map->slots_count != 0) - { - U64 hash = eval_hash_from_string(string); - U64 slot_idx = hash%map->slots_count; - EVAL_String2NumMapNode *existing_node = 0; - for(EVAL_String2NumMapNode *node = map->slots[slot_idx].first; node != 0; node = node->hash_next) - { - if(str8_match(node->string, string, 0)) - { - existing_node = node; - break; - } - } - if(existing_node != 0) - { - num = existing_node->num; - } - } - return num; -} - -internal EVAL_String2NumMapNodeArray -eval_string2num_map_node_array_from_map(Arena *arena, EVAL_String2NumMap *map) -{ - EVAL_String2NumMapNodeArray result = {0}; - result.count = map->node_count; - result.v = push_array(arena, EVAL_String2NumMapNode *, result.count); - U64 idx = 0; - for(EVAL_String2NumMapNode *n = map->first; n != 0; n = n->order_next, idx += 1) - { - result.v[idx] = n; - } - return result; -} - -internal int -eval_string2num_map_node_qsort_compare__num_ascending(EVAL_String2NumMapNode **a, EVAL_String2NumMapNode **b) -{ - int result = 0; - if(a[0]->num < b[0]->num) - { - result = -1; - } - else if(a[0]->num > b[0]->num) - { - result = +1; - } - return result; -} - -internal void -eval_string2num_map_node_array_sort__in_place(EVAL_String2NumMapNodeArray *array) -{ - quick_sort(array->v, array->count, sizeof(array->v[0]), eval_string2num_map_node_qsort_compare__num_ascending); -} - -//- rjf: string -> expr - -internal EVAL_String2ExprMap -eval_string2expr_map_make(Arena *arena, U64 slot_count) -{ - EVAL_String2ExprMap map = {0}; - map.slots_count = slot_count; - map.slots = push_array(arena, EVAL_String2ExprMapSlot, map.slots_count); - return map; -} - -internal void -eval_string2expr_map_insert(Arena *arena, EVAL_String2ExprMap *map, String8 string, EVAL_Expr *expr) -{ - U64 hash = eval_hash_from_string(string); - U64 slot_idx = hash%map->slots_count; - EVAL_String2ExprMapNode *existing_node = 0; - for(EVAL_String2ExprMapNode *node = map->slots[slot_idx].first; - node != 0; - node = node->hash_next) - { - if(str8_match(node->string, string, 0)) - { - existing_node = node; - break; - } - } - if(existing_node == 0) - { - EVAL_String2ExprMapNode *node = push_array(arena, EVAL_String2ExprMapNode, 1); - SLLQueuePush_N(map->slots[slot_idx].first, map->slots[slot_idx].last, node, hash_next); - node->string = push_str8_copy(arena, string); - existing_node = node; - } - existing_node->expr = expr; -} - -internal void -eval_string2expr_map_inc_poison(EVAL_String2ExprMap *map, String8 string) -{ - U64 hash = eval_hash_from_string(string); - U64 slot_idx = hash%map->slots_count; - for(EVAL_String2ExprMapNode *node = map->slots[slot_idx].first; - node != 0; - node = node->hash_next) - { - if(str8_match(node->string, string, 0)) - { - node->poison_count += 1; - break; - } - } -} - -internal void -eval_string2expr_map_dec_poison(EVAL_String2ExprMap *map, String8 string) -{ - U64 hash = eval_hash_from_string(string); - U64 slot_idx = hash%map->slots_count; - for(EVAL_String2ExprMapNode *node = map->slots[slot_idx].first; - node != 0; - node = node->hash_next) - { - if(str8_match(node->string, string, 0) && node->poison_count > 0) - { - node->poison_count -= 1; - break; - } - } -} - -internal EVAL_Expr * -eval_expr_from_string(EVAL_String2ExprMap *map, String8 string) -{ - EVAL_Expr *expr = &eval_expr_nil; - if(map->slots_count != 0) - { - U64 hash = eval_hash_from_string(string); - U64 slot_idx = hash%map->slots_count; - EVAL_String2ExprMapNode *existing_node = 0; - for(EVAL_String2ExprMapNode *node = map->slots[slot_idx].first; node != 0; node = node->hash_next) - { - if(str8_match(node->string, string, 0) && node->poison_count == 0) - { - existing_node = node; - break; - } - } - if(existing_node != 0) - { - expr = existing_node->expr; - } - } - return expr; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Generated Code + +#include "generated/eval.meta.c" + +//////////////////////////////// +//~ rjf: Basic Functions + +internal U64 +eval_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; +} + +//////////////////////////////// +//~ rjf: Error List Building Functions + +internal void +eval_error(Arena *arena, EVAL_ErrorList *list, EVAL_ErrorKind kind, void *location, String8 text){ + EVAL_Error *error = push_array_no_zero(arena, EVAL_Error, 1); + SLLQueuePush(list->first, list->last, error); + list->count += 1; + list->max_kind = Max(kind, list->max_kind); + error->kind = kind; + error->location = location; + error->text = text; +} + +internal void +eval_errorf(Arena *arena, EVAL_ErrorList *list, EVAL_ErrorKind kind, void *location, char *fmt, ...){ + va_list args; + va_start(args, fmt); + String8 text = push_str8fv(arena, fmt, args); + va_end(args); + eval_error(arena, list, kind, location, text); +} + +internal void +eval_error_list_concat_in_place(EVAL_ErrorList *dst, EVAL_ErrorList *to_push){ + if (dst->last != 0){ + if (to_push->last != 0){ + dst->last->next = to_push->first; + dst->last = to_push->last; + dst->count += to_push->count; + } + } + else{ + *dst = *to_push; + } + MemoryZeroStruct(to_push); +} + +//////////////////////////////// +//~ rjf: Map Functions + +//- rjf: string -> num + +internal EVAL_String2NumMap +eval_string2num_map_make(Arena *arena, U64 slot_count) +{ + EVAL_String2NumMap map = {0}; + map.slots_count = slot_count; + map.slots = push_array(arena, EVAL_String2NumMapSlot, map.slots_count); + return map; +} + +internal void +eval_string2num_map_insert(Arena *arena, EVAL_String2NumMap *map, String8 string, U64 num) +{ + U64 hash = eval_hash_from_string(string); + U64 slot_idx = hash%map->slots_count; + EVAL_String2NumMapNode *existing_node = 0; + for(EVAL_String2NumMapNode *node = map->slots[slot_idx].first; node != 0; node = node->hash_next) + { + if(str8_match(node->string, string, 0) && node->num == num) + { + existing_node = node; + break; + } + } + if(existing_node == 0) + { + EVAL_String2NumMapNode *node = push_array(arena, EVAL_String2NumMapNode, 1); + SLLQueuePush_N(map->slots[slot_idx].first, map->slots[slot_idx].last, node, hash_next); + SLLQueuePush_N(map->first, map->last, node, order_next); + node->string = push_str8_copy(arena, string); + node->num = num; + map->node_count += 1; + } +} + +internal U64 +eval_num_from_string(EVAL_String2NumMap *map, String8 string) +{ + U64 num = 0; + if(map->slots_count != 0) + { + U64 hash = eval_hash_from_string(string); + U64 slot_idx = hash%map->slots_count; + EVAL_String2NumMapNode *existing_node = 0; + for(EVAL_String2NumMapNode *node = map->slots[slot_idx].first; node != 0; node = node->hash_next) + { + if(str8_match(node->string, string, 0)) + { + existing_node = node; + break; + } + } + if(existing_node != 0) + { + num = existing_node->num; + } + } + return num; +} + +internal EVAL_String2NumMapNodeArray +eval_string2num_map_node_array_from_map(Arena *arena, EVAL_String2NumMap *map) +{ + EVAL_String2NumMapNodeArray result = {0}; + result.count = map->node_count; + result.v = push_array(arena, EVAL_String2NumMapNode *, result.count); + U64 idx = 0; + for(EVAL_String2NumMapNode *n = map->first; n != 0; n = n->order_next, idx += 1) + { + result.v[idx] = n; + } + return result; +} + +internal int +eval_string2num_map_node_qsort_compare__num_ascending(EVAL_String2NumMapNode **a, EVAL_String2NumMapNode **b) +{ + int result = 0; + if(a[0]->num < b[0]->num) + { + result = -1; + } + else if(a[0]->num > b[0]->num) + { + result = +1; + } + return result; +} + +internal void +eval_string2num_map_node_array_sort__in_place(EVAL_String2NumMapNodeArray *array) +{ + quick_sort(array->v, array->count, sizeof(array->v[0]), eval_string2num_map_node_qsort_compare__num_ascending); +} + +//- rjf: string -> expr + +internal EVAL_String2ExprMap +eval_string2expr_map_make(Arena *arena, U64 slot_count) +{ + EVAL_String2ExprMap map = {0}; + map.slots_count = slot_count; + map.slots = push_array(arena, EVAL_String2ExprMapSlot, map.slots_count); + return map; +} + +internal void +eval_string2expr_map_insert(Arena *arena, EVAL_String2ExprMap *map, String8 string, EVAL_Expr *expr) +{ + U64 hash = eval_hash_from_string(string); + U64 slot_idx = hash%map->slots_count; + EVAL_String2ExprMapNode *existing_node = 0; + for(EVAL_String2ExprMapNode *node = map->slots[slot_idx].first; + node != 0; + node = node->hash_next) + { + if(str8_match(node->string, string, 0)) + { + existing_node = node; + break; + } + } + if(existing_node == 0) + { + EVAL_String2ExprMapNode *node = push_array(arena, EVAL_String2ExprMapNode, 1); + SLLQueuePush_N(map->slots[slot_idx].first, map->slots[slot_idx].last, node, hash_next); + node->string = push_str8_copy(arena, string); + existing_node = node; + } + existing_node->expr = expr; +} + +internal void +eval_string2expr_map_inc_poison(EVAL_String2ExprMap *map, String8 string) +{ + U64 hash = eval_hash_from_string(string); + U64 slot_idx = hash%map->slots_count; + for(EVAL_String2ExprMapNode *node = map->slots[slot_idx].first; + node != 0; + node = node->hash_next) + { + if(str8_match(node->string, string, 0)) + { + node->poison_count += 1; + break; + } + } +} + +internal void +eval_string2expr_map_dec_poison(EVAL_String2ExprMap *map, String8 string) +{ + U64 hash = eval_hash_from_string(string); + U64 slot_idx = hash%map->slots_count; + for(EVAL_String2ExprMapNode *node = map->slots[slot_idx].first; + node != 0; + node = node->hash_next) + { + if(str8_match(node->string, string, 0) && node->poison_count > 0) + { + node->poison_count -= 1; + break; + } + } +} + +internal EVAL_Expr * +eval_expr_from_string(EVAL_String2ExprMap *map, String8 string) +{ + EVAL_Expr *expr = &eval_expr_nil; + if(map->slots_count != 0) + { + U64 hash = eval_hash_from_string(string); + U64 slot_idx = hash%map->slots_count; + EVAL_String2ExprMapNode *existing_node = 0; + for(EVAL_String2ExprMapNode *node = map->slots[slot_idx].first; node != 0; node = node->hash_next) + { + if(str8_match(node->string, string, 0) && node->poison_count == 0) + { + existing_node = node; + break; + } + } + if(existing_node != 0) + { + expr = existing_node->expr; + } + } + return expr; +} diff --git a/src/eval/eval_core.h b/src/eval/eval_core.h index 8f2f0c85..f9ae23c2 100644 --- a/src/eval/eval_core.h +++ b/src/eval/eval_core.h @@ -1,232 +1,232 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef EVAL_CORE_H -#define EVAL_CORE_H - -//////////////////////////////// -//~ rjf: Errors - -typedef enum EVAL_ErrorKind -{ - EVAL_ErrorKind_Null, - EVAL_ErrorKind_MalformedInput, - EVAL_ErrorKind_MissingInfo, - EVAL_ErrorKind_ResolutionFailure, - EVAL_ErrorKind_InterpretationError, - EVAL_ErrorKind_COUNT -} -EVAL_ErrorKind; - -typedef struct EVAL_Error EVAL_Error; -struct EVAL_Error -{ - EVAL_Error *next; - EVAL_ErrorKind kind; - void *location; - String8 text; -}; - -typedef struct EVAL_ErrorList EVAL_ErrorList; -struct EVAL_ErrorList -{ - EVAL_Error *first; - EVAL_Error *last; - EVAL_ErrorKind max_kind; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Operation Types - -enum -{ - EVAL_IRExtKind_Bytecode = RDI_EvalOp_COUNT, - EVAL_IRExtKind_COUNT -}; - -typedef struct EVAL_Op EVAL_Op; -struct EVAL_Op -{ - EVAL_Op *next; - RDI_EvalOp opcode; - union - { - U64 p; - String8 bytecode; - }; -}; - -typedef struct EVAL_OpList EVAL_OpList; -struct EVAL_OpList -{ - EVAL_Op *first_op; - EVAL_Op *last_op; - U32 op_count; - U32 encoded_size; -}; - -//////////////////////////////// -//~ rjf: Generated Code - -#include "eval/generated/eval.meta.h" - -//////////////////////////////// -//~ rjf: Expression Tree Types - -typedef enum EVAL_EvalMode -{ - EVAL_EvalMode_NULL, - EVAL_EvalMode_Value, - EVAL_EvalMode_Addr, - EVAL_EvalMode_Reg -} -EVAL_EvalMode; - -typedef struct EVAL_Expr EVAL_Expr; -struct EVAL_Expr -{ - EVAL_ExprKind kind; - void *location; - union - { - EVAL_Expr *children[3]; - U32 u32; - U64 u64; - F32 f32; - F64 f64; - struct - { - EVAL_Expr *child; - U64 u64; - } child_and_constant; - String8 name; - struct - { - TG_Key type_key; - String8 bytecode; - EVAL_EvalMode mode; - }; - }; -}; - -//////////////////////////////// -//~ rjf: IR Tree Types - -typedef struct EVAL_IRTree EVAL_IRTree; -struct EVAL_IRTree{ - RDI_EvalOp op; - EVAL_IRTree *children[3]; - union{ - U64 p; - String8 bytecode; - }; -}; - -typedef struct EVAL_IRTreeAndType EVAL_IRTreeAndType; -struct EVAL_IRTreeAndType{ - EVAL_IRTree *tree; - TG_Key type_key; - EVAL_EvalMode mode; -}; - -//////////////////////////////// -//~ rjf: Map Types - -//- rjf: string -> num - -typedef struct EVAL_String2NumMapNode EVAL_String2NumMapNode; -struct EVAL_String2NumMapNode -{ - EVAL_String2NumMapNode *order_next; - EVAL_String2NumMapNode *hash_next; - String8 string; - U64 num; -}; - -typedef struct EVAL_String2NumMapNodeArray EVAL_String2NumMapNodeArray; -struct EVAL_String2NumMapNodeArray -{ - EVAL_String2NumMapNode **v; - U64 count; -}; - -typedef struct EVAL_String2NumMapSlot EVAL_String2NumMapSlot; -struct EVAL_String2NumMapSlot -{ - EVAL_String2NumMapNode *first; - EVAL_String2NumMapNode *last; -}; - -typedef struct EVAL_String2NumMap EVAL_String2NumMap; -struct EVAL_String2NumMap -{ - U64 slots_count; - U64 node_count; - EVAL_String2NumMapSlot *slots; - EVAL_String2NumMapNode *first; - EVAL_String2NumMapNode *last; -}; - -//- rjf: string -> expr - -typedef struct EVAL_String2ExprMapNode EVAL_String2ExprMapNode; -struct EVAL_String2ExprMapNode -{ - EVAL_String2ExprMapNode *hash_next; - String8 string; - EVAL_Expr *expr; - U64 poison_count; -}; - -typedef struct EVAL_String2ExprMapSlot EVAL_String2ExprMapSlot; -struct EVAL_String2ExprMapSlot -{ - EVAL_String2ExprMapNode *first; - EVAL_String2ExprMapNode *last; -}; - -typedef struct EVAL_String2ExprMap EVAL_String2ExprMap; -struct EVAL_String2ExprMap -{ - U64 slots_count; - EVAL_String2ExprMapSlot *slots; -}; - -//////////////////////////////// -//~ rjf: Globals - -global read_only EVAL_Expr eval_expr_nil = {0}; -global read_only EVAL_IRTree eval_irtree_nil = {0}; - -//////////////////////////////// -//~ rjf: Basic Functions - -internal U64 eval_hash_from_string(String8 string); - -//////////////////////////////// -//~ rjf: Error List Building Functions - -internal void eval_error(Arena *arena, EVAL_ErrorList *list, EVAL_ErrorKind kind, void *location, String8 text); -internal void eval_errorf(Arena *arena, EVAL_ErrorList *list, EVAL_ErrorKind kind, void *location, char *fmt, ...); -internal void eval_error_list_concat_in_place(EVAL_ErrorList *dst, EVAL_ErrorList *to_push); - -//////////////////////////////// -//~ rjf: Map Functions - -//- rjf: string -> num -internal EVAL_String2NumMap eval_string2num_map_make(Arena *arena, U64 slot_count); -internal void eval_string2num_map_insert(Arena *arena, EVAL_String2NumMap *map, String8 string, U64 num); -internal U64 eval_num_from_string(EVAL_String2NumMap *map, String8 string); -internal EVAL_String2NumMapNodeArray eval_string2num_map_node_array_from_map(Arena *arena, EVAL_String2NumMap *map); -internal int eval_string2num_map_node_qsort_compare__num_ascending(EVAL_String2NumMapNode **a, EVAL_String2NumMapNode **b); -internal void eval_string2num_map_node_array_sort__in_place(EVAL_String2NumMapNodeArray *array); - -//- rjf: string -> expr -internal EVAL_String2ExprMap eval_string2expr_map_make(Arena *arena, U64 slot_count); -internal void eval_string2expr_map_insert(Arena *arena, EVAL_String2ExprMap *map, String8 string, EVAL_Expr *expr); -internal void eval_string2expr_map_inc_poison(EVAL_String2ExprMap *map, String8 string); -internal void eval_string2expr_map_dec_poison(EVAL_String2ExprMap *map, String8 string); -internal EVAL_Expr *eval_expr_from_string(EVAL_String2ExprMap *map, String8 string); - -#endif // EVAL_CORE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef EVAL_CORE_H +#define EVAL_CORE_H + +//////////////////////////////// +//~ rjf: Errors + +typedef enum EVAL_ErrorKind +{ + EVAL_ErrorKind_Null, + EVAL_ErrorKind_MalformedInput, + EVAL_ErrorKind_MissingInfo, + EVAL_ErrorKind_ResolutionFailure, + EVAL_ErrorKind_InterpretationError, + EVAL_ErrorKind_COUNT +} +EVAL_ErrorKind; + +typedef struct EVAL_Error EVAL_Error; +struct EVAL_Error +{ + EVAL_Error *next; + EVAL_ErrorKind kind; + void *location; + String8 text; +}; + +typedef struct EVAL_ErrorList EVAL_ErrorList; +struct EVAL_ErrorList +{ + EVAL_Error *first; + EVAL_Error *last; + EVAL_ErrorKind max_kind; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Operation Types + +enum +{ + EVAL_IRExtKind_Bytecode = RDI_EvalOp_COUNT, + EVAL_IRExtKind_COUNT +}; + +typedef struct EVAL_Op EVAL_Op; +struct EVAL_Op +{ + EVAL_Op *next; + RDI_EvalOp opcode; + union + { + U64 p; + String8 bytecode; + }; +}; + +typedef struct EVAL_OpList EVAL_OpList; +struct EVAL_OpList +{ + EVAL_Op *first_op; + EVAL_Op *last_op; + U32 op_count; + U32 encoded_size; +}; + +//////////////////////////////// +//~ rjf: Generated Code + +#include "eval/generated/eval.meta.h" + +//////////////////////////////// +//~ rjf: Expression Tree Types + +typedef enum EVAL_EvalMode +{ + EVAL_EvalMode_NULL, + EVAL_EvalMode_Value, + EVAL_EvalMode_Addr, + EVAL_EvalMode_Reg +} +EVAL_EvalMode; + +typedef struct EVAL_Expr EVAL_Expr; +struct EVAL_Expr +{ + EVAL_ExprKind kind; + void *location; + union + { + EVAL_Expr *children[3]; + U32 u32; + U64 u64; + F32 f32; + F64 f64; + struct + { + EVAL_Expr *child; + U64 u64; + } child_and_constant; + String8 name; + struct + { + TG_Key type_key; + String8 bytecode; + EVAL_EvalMode mode; + }; + }; +}; + +//////////////////////////////// +//~ rjf: IR Tree Types + +typedef struct EVAL_IRTree EVAL_IRTree; +struct EVAL_IRTree{ + RDI_EvalOp op; + EVAL_IRTree *children[3]; + union{ + U64 p; + String8 bytecode; + }; +}; + +typedef struct EVAL_IRTreeAndType EVAL_IRTreeAndType; +struct EVAL_IRTreeAndType{ + EVAL_IRTree *tree; + TG_Key type_key; + EVAL_EvalMode mode; +}; + +//////////////////////////////// +//~ rjf: Map Types + +//- rjf: string -> num + +typedef struct EVAL_String2NumMapNode EVAL_String2NumMapNode; +struct EVAL_String2NumMapNode +{ + EVAL_String2NumMapNode *order_next; + EVAL_String2NumMapNode *hash_next; + String8 string; + U64 num; +}; + +typedef struct EVAL_String2NumMapNodeArray EVAL_String2NumMapNodeArray; +struct EVAL_String2NumMapNodeArray +{ + EVAL_String2NumMapNode **v; + U64 count; +}; + +typedef struct EVAL_String2NumMapSlot EVAL_String2NumMapSlot; +struct EVAL_String2NumMapSlot +{ + EVAL_String2NumMapNode *first; + EVAL_String2NumMapNode *last; +}; + +typedef struct EVAL_String2NumMap EVAL_String2NumMap; +struct EVAL_String2NumMap +{ + U64 slots_count; + U64 node_count; + EVAL_String2NumMapSlot *slots; + EVAL_String2NumMapNode *first; + EVAL_String2NumMapNode *last; +}; + +//- rjf: string -> expr + +typedef struct EVAL_String2ExprMapNode EVAL_String2ExprMapNode; +struct EVAL_String2ExprMapNode +{ + EVAL_String2ExprMapNode *hash_next; + String8 string; + EVAL_Expr *expr; + U64 poison_count; +}; + +typedef struct EVAL_String2ExprMapSlot EVAL_String2ExprMapSlot; +struct EVAL_String2ExprMapSlot +{ + EVAL_String2ExprMapNode *first; + EVAL_String2ExprMapNode *last; +}; + +typedef struct EVAL_String2ExprMap EVAL_String2ExprMap; +struct EVAL_String2ExprMap +{ + U64 slots_count; + EVAL_String2ExprMapSlot *slots; +}; + +//////////////////////////////// +//~ rjf: Globals + +global read_only EVAL_Expr eval_expr_nil = {0}; +global read_only EVAL_IRTree eval_irtree_nil = {0}; + +//////////////////////////////// +//~ rjf: Basic Functions + +internal U64 eval_hash_from_string(String8 string); + +//////////////////////////////// +//~ rjf: Error List Building Functions + +internal void eval_error(Arena *arena, EVAL_ErrorList *list, EVAL_ErrorKind kind, void *location, String8 text); +internal void eval_errorf(Arena *arena, EVAL_ErrorList *list, EVAL_ErrorKind kind, void *location, char *fmt, ...); +internal void eval_error_list_concat_in_place(EVAL_ErrorList *dst, EVAL_ErrorList *to_push); + +//////////////////////////////// +//~ rjf: Map Functions + +//- rjf: string -> num +internal EVAL_String2NumMap eval_string2num_map_make(Arena *arena, U64 slot_count); +internal void eval_string2num_map_insert(Arena *arena, EVAL_String2NumMap *map, String8 string, U64 num); +internal U64 eval_num_from_string(EVAL_String2NumMap *map, String8 string); +internal EVAL_String2NumMapNodeArray eval_string2num_map_node_array_from_map(Arena *arena, EVAL_String2NumMap *map); +internal int eval_string2num_map_node_qsort_compare__num_ascending(EVAL_String2NumMapNode **a, EVAL_String2NumMapNode **b); +internal void eval_string2num_map_node_array_sort__in_place(EVAL_String2NumMapNodeArray *array); + +//- rjf: string -> expr +internal EVAL_String2ExprMap eval_string2expr_map_make(Arena *arena, U64 slot_count); +internal void eval_string2expr_map_insert(Arena *arena, EVAL_String2ExprMap *map, String8 string, EVAL_Expr *expr); +internal void eval_string2expr_map_inc_poison(EVAL_String2ExprMap *map, String8 string); +internal void eval_string2expr_map_dec_poison(EVAL_String2ExprMap *map, String8 string); +internal EVAL_Expr *eval_expr_from_string(EVAL_String2ExprMap *map, String8 string); + +#endif // EVAL_CORE_H diff --git a/src/eval/eval_decode.c b/src/eval/eval_decode.c index c465bef7..db66481f 100644 --- a/src/eval/eval_decode.c +++ b/src/eval/eval_decode.c @@ -1,50 +1,50 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -// NOTE(allen): Eval Decode Function - -internal void -eval_print_decode_from_bytecode(FILE *out, String8 bytecode){ - U8 *ptr = bytecode.str; - U8 *opl = bytecode.str + bytecode.size; - - for (;ptr < opl;){ - // consume opcode - SYMS_EvalOp op = (SYMS_EvalOp)*ptr; - if (op >= SYMS_EvalOp_COUNT){ - fprintf(out, "decode error: undefined op code\n"); - goto done; - } - U8 ctrlbits = syms_eval_opcode_ctrlbits[op]; - ptr += 1; - - // decode - U64 imm = 0; - U32 decode_size = (ctrlbits >> SYMS_EvalOpCtrlBits_DecodeShft)&SYMS_EvalOpCtrlBits_DecodeMask; - { - U8 *next_ptr = ptr + decode_size; - if (next_ptr > opl){ - fprintf(out, "decode error: expected constant goes past the end of bytecode\n"); - goto done; - } - // TODO(allen): to improve this: - // gaurantee 8 bytes padding after the end of serialized bytecode - // read 8 bytes and mask - switch (decode_size){ - case 1: imm = *ptr; break; - case 2: imm = *(U16*)ptr; break; - case 4: imm = *(U32*)ptr; break; - case 8: imm = *(U64*)ptr; break; - } - ptr = next_ptr; - } - - // op string & control bits - SYMS_String8 op_string = syms_eval_opcode_strings[op]; - - // print - fprintf(out, "%.*s 0x%llx\n", str8_varg(op_string), imm); - } - done:; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +// NOTE(allen): Eval Decode Function + +internal void +eval_print_decode_from_bytecode(FILE *out, String8 bytecode){ + U8 *ptr = bytecode.str; + U8 *opl = bytecode.str + bytecode.size; + + for (;ptr < opl;){ + // consume opcode + SYMS_EvalOp op = (SYMS_EvalOp)*ptr; + if (op >= SYMS_EvalOp_COUNT){ + fprintf(out, "decode error: undefined op code\n"); + goto done; + } + U8 ctrlbits = syms_eval_opcode_ctrlbits[op]; + ptr += 1; + + // decode + U64 imm = 0; + U32 decode_size = (ctrlbits >> SYMS_EvalOpCtrlBits_DecodeShft)&SYMS_EvalOpCtrlBits_DecodeMask; + { + U8 *next_ptr = ptr + decode_size; + if (next_ptr > opl){ + fprintf(out, "decode error: expected constant goes past the end of bytecode\n"); + goto done; + } + // TODO(allen): to improve this: + // gaurantee 8 bytes padding after the end of serialized bytecode + // read 8 bytes and mask + switch (decode_size){ + case 1: imm = *ptr; break; + case 2: imm = *(U16*)ptr; break; + case 4: imm = *(U32*)ptr; break; + case 8: imm = *(U64*)ptr; break; + } + ptr = next_ptr; + } + + // op string & control bits + SYMS_String8 op_string = syms_eval_opcode_strings[op]; + + // print + fprintf(out, "%.*s 0x%llx\n", str8_varg(op_string), imm); + } + done:; +} diff --git a/src/eval/eval_decode.h b/src/eval/eval_decode.h index c6ebc743..4db739e5 100644 --- a/src/eval/eval_decode.h +++ b/src/eval/eval_decode.h @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef EVAL_DECODE_H -#define EVAL_DECODE_H - -//////////////////////////////// -// NOTE(allen): Eval Decode Function - -internal void eval_print_decode_from_bytecode(FILE *out, String8 bytecode); - -#endif //EVAL_DECODE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef EVAL_DECODE_H +#define EVAL_DECODE_H + +//////////////////////////////// +// NOTE(allen): Eval Decode Function + +internal void eval_print_decode_from_bytecode(FILE *out, String8 bytecode); + +#endif //EVAL_DECODE_H diff --git a/src/eval/eval_inc.c b/src/eval/eval_inc.c index 791890a7..6a880fff 100644 --- a/src/eval/eval_inc.c +++ b/src/eval/eval_inc.c @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#include "eval/eval_core.c" -#include "eval/eval_compiler.c" -#include "eval/eval_machine.c" -#include "eval/eval_parser.c" +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "eval/eval_core.c" +#include "eval/eval_compiler.c" +#include "eval/eval_machine.c" +#include "eval/eval_parser.c" diff --git a/src/eval/eval_inc.h b/src/eval/eval_inc.h index 56c2c648..b0a74c09 100644 --- a/src/eval/eval_inc.h +++ b/src/eval/eval_inc.h @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef EVAL_INC_H -#define EVAL_INC_H - -#include "eval/eval_core.h" -#include "eval/eval_compiler.h" -#include "eval/eval_machine.h" -#include "eval/eval_parser.h" - -#endif // EVAL_INC_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef EVAL_INC_H +#define EVAL_INC_H + +#include "eval/eval_core.h" +#include "eval/eval_compiler.h" +#include "eval/eval_machine.h" +#include "eval/eval_parser.h" + +#endif // EVAL_INC_H diff --git a/src/eval/eval_machine.c b/src/eval/eval_machine.c index 5678fb87..2e1cff13 100644 --- a/src/eval/eval_machine.c +++ b/src/eval/eval_machine.c @@ -1,648 +1,648 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ allen: Eval Machine Functions - -internal EVAL_Result -eval_interpret(EVAL_Machine *machine, String8 bytecode) -{ - ProfBeginFunction(); - EVAL_Result result = {0}; - - // TODO(allen): We could scan the bytecode and figure out the - // maximum depth of the stack - Temp scratch = scratch_begin(0, 0); - U64 stack_cap = 128; - EVAL_Slot *stack = push_array_no_zero(scratch.arena, EVAL_Slot, stack_cap); - - U64 stack_count = 0; - U8 *ptr = bytecode.str; - U8 *opl = bytecode.str + bytecode.size; - - for (;ptr < opl;){ - // consume opcode - RDI_EvalOp op = (RDI_EvalOp)*ptr; - if (op >= RDI_EvalOp_COUNT){ - result.code = EVAL_ResultCode_BadOp; - goto done; - } - U8 ctrlbits = rdi_eval_op_ctrlbits_table[op]; - ptr += 1; - - // decode - U64 imm = 0; - { - U32 decode_size = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); - U8 *next_ptr = ptr + decode_size; - if (next_ptr > opl){ - result.code = EVAL_ResultCode_BadOp; - goto done; - } - // TODO(allen): to improve this: - // gaurantee 8 bytes padding after the end of serialized bytecode - // read 8 bytes and mask - switch (decode_size){ - case 1: imm = *ptr; break; - case 2: imm = *(U16*)ptr; break; - case 4: imm = *(U32*)ptr; break; - case 8: imm = *(U64*)ptr; break; - } - ptr = next_ptr; - } - - // pop - EVAL_Slot *svals = 0; - { - U32 pop_count = RDI_POPN_FROM_CTRLBITS(ctrlbits); - if (pop_count > stack_count){ - result.code = EVAL_ResultCode_BadOp; - goto done; - } - if (pop_count <= stack_count){ - stack_count -= pop_count; - svals = stack + stack_count; - } - } - - // interpret - EVAL_Slot nval = {0}; - switch (op){ - case RDI_EvalOp_Stop: - { - goto done; - }break; - - case RDI_EvalOp_Noop: - { - // do nothing - }break; - - case RDI_EvalOp_Cond: - { - if (svals[0].u64){ - ptr += imm; - } - }break; - - case RDI_EvalOp_Skip: - { - ptr += imm; - }break; - - case RDI_EvalOp_MemRead: - { - U64 addr = svals[0].u64; - U64 size = imm; - B32 good_read = 0; - if (machine->memory_read != 0 && - machine->memory_read(machine->u, &nval, addr, size)){ - good_read = 1; - } - if (!good_read){ - result.code = EVAL_ResultCode_BadMemRead; - goto done; - } - }break; - - case RDI_EvalOp_RegRead: - { - U8 rdi_reg_code = (imm&0x0000FF)>>0; - U8 byte_size = (imm&0x00FF00)>>8; - U8 byte_off = (imm&0xFF0000)>>16; - REGS_RegCode base_reg_code = regs_reg_code_from_arch_rdi_code(machine->arch, rdi_reg_code); - REGS_Rng rng = regs_reg_code_rng_table_from_architecture(machine->arch)[base_reg_code]; - U64 off = (U64)rng.byte_off + byte_off; - U64 size = (U64)byte_size; - if (off + size <= machine->reg_size){ - MemoryCopy(&nval, (U8*)machine->reg_data + off, size); - } - else{ - result.code = EVAL_ResultCode_BadRegRead; - goto done; - } - }break; - - case RDI_EvalOp_RegReadDyn: - { - U64 off = svals[0].u64; - U64 size = bit_size_from_arch(machine->arch)/8; - if (off + size <= machine->reg_size){ - MemoryCopy(&nval, (U8*)machine->reg_data + off, size); - } - else{ - result.code = EVAL_ResultCode_BadRegRead; - goto done; - } - }break; - - case RDI_EvalOp_FrameOff: - { - if (machine->frame_base != 0){ - nval.u64 = *machine->frame_base + imm; - } - else{ - result.code = EVAL_ResultCode_BadFrameBase; - goto done; - } - }break; - - case RDI_EvalOp_ModuleOff: - { - if (machine->module_base != 0){ - nval.u64 = *machine->module_base + imm; - } - else{ - result.code = EVAL_ResultCode_BadModuleBase; - goto done; - } - }break; - - case RDI_EvalOp_TLSOff: - { - if (machine->tls_base != 0){ - nval.u64 = *machine->tls_base + imm; - } - else{ - result.code = EVAL_ResultCode_BadTLSBase; - goto done; - } - }break; - - case RDI_EvalOp_ConstU8: - case RDI_EvalOp_ConstU16: - case RDI_EvalOp_ConstU32: - case RDI_EvalOp_ConstU64: - { - nval.u64 = imm; - }break; - - case RDI_EvalOp_Abs: - { - if (imm == RDI_EvalTypeGroup_F32){ - nval.f32 = svals[0].f32; - if (svals[0].f32 < 0){ - nval.f32 = -svals[0].f32; - } - } - else if (imm == RDI_EvalTypeGroup_F64){ - nval.f64 = svals[0].f64; - if (svals[0].f64 < 0){ - nval.f64 = -svals[0].f64; - } - } - else{ - nval.s64 = svals[0].s64; - if (svals[0].s64 < 0){ - nval.s64 = -svals[0].s64; - } - } - }break; - - case RDI_EvalOp_Neg: - { - if (imm == RDI_EvalTypeGroup_F32){ - nval.f32 = -svals[0].f32; - } - else if (imm == RDI_EvalTypeGroup_F64){ - nval.f64 = -svals[0].f64; - } - else{ - nval.u64 = (~svals[0].u64) + 1; - } - }break; - - case RDI_EvalOp_Add: - { - if (imm == RDI_EvalTypeGroup_F32){ - nval.f32 = svals[0].f32 + svals[1].f32; - } - else if (imm == RDI_EvalTypeGroup_F64){ - nval.f64 = svals[0].f64 + svals[1].f64; - } - else{ - nval.u64 = svals[0].u64 + svals[1].u64; - } - }break; - - case RDI_EvalOp_Sub: - { - if (imm == RDI_EvalTypeGroup_F32){ - nval.f32 = svals[0].f32 - svals[1].f32; - } - else if (imm == RDI_EvalTypeGroup_F64){ - nval.f64 = svals[0].f64 - svals[1].f64; - } - else{ - nval.u64 = svals[0].u64 - svals[1].u64; - } - }break; - - case RDI_EvalOp_Mul: - { - if (imm == RDI_EvalTypeGroup_F32){ - nval.f32 = svals[0].f32*svals[1].f32; - } - else if (imm == RDI_EvalTypeGroup_F64){ - nval.f64 = svals[0].f64*svals[1].f64; - } - else{ - nval.u64 = svals[0].u64*svals[1].u64; - } - }break; - - case RDI_EvalOp_Div: - { - if (imm == RDI_EvalTypeGroup_F32){ - if (svals[1].f32 != 0.f){ - nval.f32 = svals[0].f32/svals[1].f32; - } - else - { - result.code = EVAL_ResultCode_DivideByZero; - goto done; - } - } - else if (imm == RDI_EvalTypeGroup_F64){ - if (svals[1].f64 != 0.){ - nval.f64 = svals[0].f64/svals[1].f64; - } - else - { - result.code = EVAL_ResultCode_DivideByZero; - goto done; - } - } - else if (imm == RDI_EvalTypeGroup_U || - imm == RDI_EvalTypeGroup_S){ - if (svals[1].u64 != 0){ - nval.u64 = svals[0].u64/svals[1].u64; - } - else - { - result.code = EVAL_ResultCode_DivideByZero; - goto done; - } - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_Mod: - { - if (imm == RDI_EvalTypeGroup_U || - imm == RDI_EvalTypeGroup_S){ - if (svals[1].u64 != 0){ - nval.u64 = svals[0].u64%svals[1].u64; - } - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_LShift: - { - if (imm == RDI_EvalTypeGroup_U || - imm == RDI_EvalTypeGroup_S){ - nval.u64 = svals[0].u64 << svals[1].u64; - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_RShift: - { - if (imm == RDI_EvalTypeGroup_U){ - nval.u64 = svals[0].u64 >> svals[1].u64; - } - else if (imm == RDI_EvalTypeGroup_S){ - nval.u64 = svals[0].s64 >> svals[1].u64; - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_BitAnd: - { - if (imm == RDI_EvalTypeGroup_U || - imm == RDI_EvalTypeGroup_S){ - nval.u64 = svals[0].u64&svals[1].u64; - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_BitOr: - { - if (imm == RDI_EvalTypeGroup_U || - imm == RDI_EvalTypeGroup_S){ - nval.u64 = svals[0].u64|svals[1].u64; - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_BitXor: - { - if (imm == RDI_EvalTypeGroup_U || - imm == RDI_EvalTypeGroup_S){ - nval.u64 = svals[0].u64^svals[1].u64; - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_BitNot: - { - if (imm == RDI_EvalTypeGroup_U || - imm == RDI_EvalTypeGroup_S){ - nval.u64 = ~svals[0].u64; - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_LogAnd: - { - if (imm == RDI_EvalTypeGroup_U || - imm == RDI_EvalTypeGroup_S){ - nval.u64 = (svals[0].u64 && svals[1].u64); - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_LogOr: - { - if (imm == RDI_EvalTypeGroup_U || - imm == RDI_EvalTypeGroup_S){ - nval.u64 = (svals[0].u64 || svals[1].u64); - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_LogNot: - { - if (imm == RDI_EvalTypeGroup_U || - imm == RDI_EvalTypeGroup_S){ - nval.u64 = (!svals[0].u64); - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_EqEq: - { - nval.u64 = (svals[0].u64 == svals[1].u64); - }break; - - case RDI_EvalOp_NtEq: - { - nval.u64 = (svals[0].u64 != svals[1].u64); - }break; - - case RDI_EvalOp_LsEq: - { - if (imm == RDI_EvalTypeGroup_F32){ - nval.u64 = (svals[0].f32 <= svals[1].f32); - } - else if (imm == RDI_EvalTypeGroup_F64){ - nval.u64 = (svals[0].f64 <= svals[1].f64); - } - else if (imm == RDI_EvalTypeGroup_U){ - nval.u64 = (svals[0].u64 <= svals[1].u64); - } - else if (imm == RDI_EvalTypeGroup_S){ - nval.u64 = (svals[0].s64 <= svals[1].s64); - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_GrEq: - { - if (imm == RDI_EvalTypeGroup_F32){ - nval.u64 = (svals[0].f32 >= svals[1].f32); - } - else if (imm == RDI_EvalTypeGroup_F64){ - nval.u64 = (svals[0].f64 >= svals[1].f64); - } - else if (imm == RDI_EvalTypeGroup_U){ - nval.u64 = (svals[0].u64 >= svals[1].u64); - } - else if (imm == RDI_EvalTypeGroup_S){ - nval.u64 = (svals[0].s64 >= svals[1].s64); - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_Less: - { - if (imm == RDI_EvalTypeGroup_F32){ - nval.u64 = (svals[0].f32 < svals[1].f32); - } - else if (imm == RDI_EvalTypeGroup_F64){ - nval.u64 = (svals[0].f64 < svals[1].f64); - } - else if (imm == RDI_EvalTypeGroup_U){ - nval.u64 = (svals[0].u64 < svals[1].u64); - } - else if (imm == RDI_EvalTypeGroup_S){ - nval.u64 = (svals[0].s64 < svals[1].s64); - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_Grtr: - { - if (imm == RDI_EvalTypeGroup_F32){ - nval.u64 = (svals[0].f32 > svals[1].f32); - } - else if (imm == RDI_EvalTypeGroup_F64){ - nval.u64 = (svals[0].f64 > svals[1].f64); - } - else if (imm == RDI_EvalTypeGroup_U){ - nval.u64 = (svals[0].u64 > svals[1].u64); - } - else if (imm == RDI_EvalTypeGroup_S){ - nval.u64 = (svals[0].s64 > svals[1].s64); - } - else{ - result.code = EVAL_ResultCode_BadOpTypes; - goto done; - } - }break; - - case RDI_EvalOp_Trunc: - { - if (0 < imm){ - U64 mask = 0; - if (imm < 64){ - mask = max_U64 >> (64 - imm); - } - nval.u64 = svals[0].u64&mask; - } - }break; - - case RDI_EvalOp_TruncSigned: - { - if (0 < imm){ - U64 mask = 0; - if (imm < 64){ - mask = max_U64 >> (64 - imm); - } - U64 high = 0; - if (svals[0].u64 & (1 << (imm - 1))){ - high = ~mask; - } - nval.u64 = high|(svals[0].u64&mask); - } - }break; - - case RDI_EvalOp_Convert: - { - U32 in = imm&0xFF; - U32 out = (imm >> 8)&0xFF; - if (in != out){ - switch (in + out*RDI_EvalTypeGroup_COUNT){ - case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_U*RDI_EvalTypeGroup_COUNT: - { - nval.u64 = (U64)svals[0].f32; - }break; - case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_U*RDI_EvalTypeGroup_COUNT: - { - nval.u64 = (U64)svals[0].f64; - }break; - - case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_S*RDI_EvalTypeGroup_COUNT: - { - nval.s64 = (S64)svals[0].f32; - }break; - case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_S*RDI_EvalTypeGroup_COUNT: - { - nval.s64 = (S64)svals[0].f64; - }break; - - case RDI_EvalTypeGroup_U + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: - { - nval.f32 = (F32)svals[0].u64; - }break; - case RDI_EvalTypeGroup_S + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: - { - nval.f32 = (F32)svals[0].s64; - }break; - case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: - { - nval.f32 = (F32)svals[0].f64; - }break; - - case RDI_EvalTypeGroup_U + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: - { - nval.f64 = (F64)svals[0].u64; - }break; - case RDI_EvalTypeGroup_S + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: - { - nval.f64 = (F64)svals[0].s64; - }break; - case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: - { - nval.f64 = (F64)svals[0].f32; - }break; - } - } - }break; - - case RDI_EvalOp_Pick: - { - if (stack_count > imm){ - nval = stack[stack_count - imm - 1]; - } - else{ - result.code = EVAL_ResultCode_BadOp; - goto done; - } - }break; - - case RDI_EvalOp_Pop: - { - // do nothing - the pop is handled by the control bits - }break; - - case RDI_EvalOp_Insert: - { - if (stack_count > imm){ - if (imm > 0){ - EVAL_Slot tval = stack[stack_count - 1]; - EVAL_Slot *dst = stack + stack_count - 1 - imm; - EVAL_Slot *shift = dst + 1; - MemoryCopy(shift, dst, imm*sizeof(EVAL_Slot)); - *dst = tval; - } - } - else{ - result.code = EVAL_ResultCode_BadOp; - goto done; - } - }break; - } - - // push - { - U64 push_count = RDI_PUSHN_FROM_CTRLBITS(ctrlbits); - if (push_count == 1){ - if (stack_count < stack_cap){ - stack[stack_count] = nval; - stack_count += 1; - } - else{ - result.code = EVAL_ResultCode_InsufficientStackSpace; - goto done; - } - } - } - - } - done:; - - if (stack_count == 1){ - result.value = stack[0]; - } - else if(result.code == EVAL_ResultCode_Good){ - result.code = EVAL_ResultCode_MalformedBytecode; - } - - scratch_end(scratch); - ProfEnd(); - return(result); +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ allen: Eval Machine Functions + +internal EVAL_Result +eval_interpret(EVAL_Machine *machine, String8 bytecode) +{ + ProfBeginFunction(); + EVAL_Result result = {0}; + + // TODO(allen): We could scan the bytecode and figure out the + // maximum depth of the stack + Temp scratch = scratch_begin(0, 0); + U64 stack_cap = 128; + EVAL_Slot *stack = push_array_no_zero(scratch.arena, EVAL_Slot, stack_cap); + + U64 stack_count = 0; + U8 *ptr = bytecode.str; + U8 *opl = bytecode.str + bytecode.size; + + for (;ptr < opl;){ + // consume opcode + RDI_EvalOp op = (RDI_EvalOp)*ptr; + if (op >= RDI_EvalOp_COUNT){ + result.code = EVAL_ResultCode_BadOp; + goto done; + } + U8 ctrlbits = rdi_eval_op_ctrlbits_table[op]; + ptr += 1; + + // decode + U64 imm = 0; + { + U32 decode_size = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); + U8 *next_ptr = ptr + decode_size; + if (next_ptr > opl){ + result.code = EVAL_ResultCode_BadOp; + goto done; + } + // TODO(allen): to improve this: + // gaurantee 8 bytes padding after the end of serialized bytecode + // read 8 bytes and mask + switch (decode_size){ + case 1: imm = *ptr; break; + case 2: imm = *(U16*)ptr; break; + case 4: imm = *(U32*)ptr; break; + case 8: imm = *(U64*)ptr; break; + } + ptr = next_ptr; + } + + // pop + EVAL_Slot *svals = 0; + { + U32 pop_count = RDI_POPN_FROM_CTRLBITS(ctrlbits); + if (pop_count > stack_count){ + result.code = EVAL_ResultCode_BadOp; + goto done; + } + if (pop_count <= stack_count){ + stack_count -= pop_count; + svals = stack + stack_count; + } + } + + // interpret + EVAL_Slot nval = {0}; + switch (op){ + case RDI_EvalOp_Stop: + { + goto done; + }break; + + case RDI_EvalOp_Noop: + { + // do nothing + }break; + + case RDI_EvalOp_Cond: + { + if (svals[0].u64){ + ptr += imm; + } + }break; + + case RDI_EvalOp_Skip: + { + ptr += imm; + }break; + + case RDI_EvalOp_MemRead: + { + U64 addr = svals[0].u64; + U64 size = imm; + B32 good_read = 0; + if (machine->memory_read != 0 && + machine->memory_read(machine->u, &nval, addr, size)){ + good_read = 1; + } + if (!good_read){ + result.code = EVAL_ResultCode_BadMemRead; + goto done; + } + }break; + + case RDI_EvalOp_RegRead: + { + U8 rdi_reg_code = (imm&0x0000FF)>>0; + U8 byte_size = (imm&0x00FF00)>>8; + U8 byte_off = (imm&0xFF0000)>>16; + REGS_RegCode base_reg_code = regs_reg_code_from_arch_rdi_code(machine->arch, rdi_reg_code); + REGS_Rng rng = regs_reg_code_rng_table_from_architecture(machine->arch)[base_reg_code]; + U64 off = (U64)rng.byte_off + byte_off; + U64 size = (U64)byte_size; + if (off + size <= machine->reg_size){ + MemoryCopy(&nval, (U8*)machine->reg_data + off, size); + } + else{ + result.code = EVAL_ResultCode_BadRegRead; + goto done; + } + }break; + + case RDI_EvalOp_RegReadDyn: + { + U64 off = svals[0].u64; + U64 size = bit_size_from_arch(machine->arch)/8; + if (off + size <= machine->reg_size){ + MemoryCopy(&nval, (U8*)machine->reg_data + off, size); + } + else{ + result.code = EVAL_ResultCode_BadRegRead; + goto done; + } + }break; + + case RDI_EvalOp_FrameOff: + { + if (machine->frame_base != 0){ + nval.u64 = *machine->frame_base + imm; + } + else{ + result.code = EVAL_ResultCode_BadFrameBase; + goto done; + } + }break; + + case RDI_EvalOp_ModuleOff: + { + if (machine->module_base != 0){ + nval.u64 = *machine->module_base + imm; + } + else{ + result.code = EVAL_ResultCode_BadModuleBase; + goto done; + } + }break; + + case RDI_EvalOp_TLSOff: + { + if (machine->tls_base != 0){ + nval.u64 = *machine->tls_base + imm; + } + else{ + result.code = EVAL_ResultCode_BadTLSBase; + goto done; + } + }break; + + case RDI_EvalOp_ConstU8: + case RDI_EvalOp_ConstU16: + case RDI_EvalOp_ConstU32: + case RDI_EvalOp_ConstU64: + { + nval.u64 = imm; + }break; + + case RDI_EvalOp_Abs: + { + if (imm == RDI_EvalTypeGroup_F32){ + nval.f32 = svals[0].f32; + if (svals[0].f32 < 0){ + nval.f32 = -svals[0].f32; + } + } + else if (imm == RDI_EvalTypeGroup_F64){ + nval.f64 = svals[0].f64; + if (svals[0].f64 < 0){ + nval.f64 = -svals[0].f64; + } + } + else{ + nval.s64 = svals[0].s64; + if (svals[0].s64 < 0){ + nval.s64 = -svals[0].s64; + } + } + }break; + + case RDI_EvalOp_Neg: + { + if (imm == RDI_EvalTypeGroup_F32){ + nval.f32 = -svals[0].f32; + } + else if (imm == RDI_EvalTypeGroup_F64){ + nval.f64 = -svals[0].f64; + } + else{ + nval.u64 = (~svals[0].u64) + 1; + } + }break; + + case RDI_EvalOp_Add: + { + if (imm == RDI_EvalTypeGroup_F32){ + nval.f32 = svals[0].f32 + svals[1].f32; + } + else if (imm == RDI_EvalTypeGroup_F64){ + nval.f64 = svals[0].f64 + svals[1].f64; + } + else{ + nval.u64 = svals[0].u64 + svals[1].u64; + } + }break; + + case RDI_EvalOp_Sub: + { + if (imm == RDI_EvalTypeGroup_F32){ + nval.f32 = svals[0].f32 - svals[1].f32; + } + else if (imm == RDI_EvalTypeGroup_F64){ + nval.f64 = svals[0].f64 - svals[1].f64; + } + else{ + nval.u64 = svals[0].u64 - svals[1].u64; + } + }break; + + case RDI_EvalOp_Mul: + { + if (imm == RDI_EvalTypeGroup_F32){ + nval.f32 = svals[0].f32*svals[1].f32; + } + else if (imm == RDI_EvalTypeGroup_F64){ + nval.f64 = svals[0].f64*svals[1].f64; + } + else{ + nval.u64 = svals[0].u64*svals[1].u64; + } + }break; + + case RDI_EvalOp_Div: + { + if (imm == RDI_EvalTypeGroup_F32){ + if (svals[1].f32 != 0.f){ + nval.f32 = svals[0].f32/svals[1].f32; + } + else + { + result.code = EVAL_ResultCode_DivideByZero; + goto done; + } + } + else if (imm == RDI_EvalTypeGroup_F64){ + if (svals[1].f64 != 0.){ + nval.f64 = svals[0].f64/svals[1].f64; + } + else + { + result.code = EVAL_ResultCode_DivideByZero; + goto done; + } + } + else if (imm == RDI_EvalTypeGroup_U || + imm == RDI_EvalTypeGroup_S){ + if (svals[1].u64 != 0){ + nval.u64 = svals[0].u64/svals[1].u64; + } + else + { + result.code = EVAL_ResultCode_DivideByZero; + goto done; + } + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_Mod: + { + if (imm == RDI_EvalTypeGroup_U || + imm == RDI_EvalTypeGroup_S){ + if (svals[1].u64 != 0){ + nval.u64 = svals[0].u64%svals[1].u64; + } + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_LShift: + { + if (imm == RDI_EvalTypeGroup_U || + imm == RDI_EvalTypeGroup_S){ + nval.u64 = svals[0].u64 << svals[1].u64; + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_RShift: + { + if (imm == RDI_EvalTypeGroup_U){ + nval.u64 = svals[0].u64 >> svals[1].u64; + } + else if (imm == RDI_EvalTypeGroup_S){ + nval.u64 = svals[0].s64 >> svals[1].u64; + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_BitAnd: + { + if (imm == RDI_EvalTypeGroup_U || + imm == RDI_EvalTypeGroup_S){ + nval.u64 = svals[0].u64&svals[1].u64; + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_BitOr: + { + if (imm == RDI_EvalTypeGroup_U || + imm == RDI_EvalTypeGroup_S){ + nval.u64 = svals[0].u64|svals[1].u64; + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_BitXor: + { + if (imm == RDI_EvalTypeGroup_U || + imm == RDI_EvalTypeGroup_S){ + nval.u64 = svals[0].u64^svals[1].u64; + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_BitNot: + { + if (imm == RDI_EvalTypeGroup_U || + imm == RDI_EvalTypeGroup_S){ + nval.u64 = ~svals[0].u64; + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_LogAnd: + { + if (imm == RDI_EvalTypeGroup_U || + imm == RDI_EvalTypeGroup_S){ + nval.u64 = (svals[0].u64 && svals[1].u64); + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_LogOr: + { + if (imm == RDI_EvalTypeGroup_U || + imm == RDI_EvalTypeGroup_S){ + nval.u64 = (svals[0].u64 || svals[1].u64); + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_LogNot: + { + if (imm == RDI_EvalTypeGroup_U || + imm == RDI_EvalTypeGroup_S){ + nval.u64 = (!svals[0].u64); + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_EqEq: + { + nval.u64 = (svals[0].u64 == svals[1].u64); + }break; + + case RDI_EvalOp_NtEq: + { + nval.u64 = (svals[0].u64 != svals[1].u64); + }break; + + case RDI_EvalOp_LsEq: + { + if (imm == RDI_EvalTypeGroup_F32){ + nval.u64 = (svals[0].f32 <= svals[1].f32); + } + else if (imm == RDI_EvalTypeGroup_F64){ + nval.u64 = (svals[0].f64 <= svals[1].f64); + } + else if (imm == RDI_EvalTypeGroup_U){ + nval.u64 = (svals[0].u64 <= svals[1].u64); + } + else if (imm == RDI_EvalTypeGroup_S){ + nval.u64 = (svals[0].s64 <= svals[1].s64); + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_GrEq: + { + if (imm == RDI_EvalTypeGroup_F32){ + nval.u64 = (svals[0].f32 >= svals[1].f32); + } + else if (imm == RDI_EvalTypeGroup_F64){ + nval.u64 = (svals[0].f64 >= svals[1].f64); + } + else if (imm == RDI_EvalTypeGroup_U){ + nval.u64 = (svals[0].u64 >= svals[1].u64); + } + else if (imm == RDI_EvalTypeGroup_S){ + nval.u64 = (svals[0].s64 >= svals[1].s64); + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_Less: + { + if (imm == RDI_EvalTypeGroup_F32){ + nval.u64 = (svals[0].f32 < svals[1].f32); + } + else if (imm == RDI_EvalTypeGroup_F64){ + nval.u64 = (svals[0].f64 < svals[1].f64); + } + else if (imm == RDI_EvalTypeGroup_U){ + nval.u64 = (svals[0].u64 < svals[1].u64); + } + else if (imm == RDI_EvalTypeGroup_S){ + nval.u64 = (svals[0].s64 < svals[1].s64); + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_Grtr: + { + if (imm == RDI_EvalTypeGroup_F32){ + nval.u64 = (svals[0].f32 > svals[1].f32); + } + else if (imm == RDI_EvalTypeGroup_F64){ + nval.u64 = (svals[0].f64 > svals[1].f64); + } + else if (imm == RDI_EvalTypeGroup_U){ + nval.u64 = (svals[0].u64 > svals[1].u64); + } + else if (imm == RDI_EvalTypeGroup_S){ + nval.u64 = (svals[0].s64 > svals[1].s64); + } + else{ + result.code = EVAL_ResultCode_BadOpTypes; + goto done; + } + }break; + + case RDI_EvalOp_Trunc: + { + if (0 < imm){ + U64 mask = 0; + if (imm < 64){ + mask = max_U64 >> (64 - imm); + } + nval.u64 = svals[0].u64&mask; + } + }break; + + case RDI_EvalOp_TruncSigned: + { + if (0 < imm){ + U64 mask = 0; + if (imm < 64){ + mask = max_U64 >> (64 - imm); + } + U64 high = 0; + if (svals[0].u64 & (1 << (imm - 1))){ + high = ~mask; + } + nval.u64 = high|(svals[0].u64&mask); + } + }break; + + case RDI_EvalOp_Convert: + { + U32 in = imm&0xFF; + U32 out = (imm >> 8)&0xFF; + if (in != out){ + switch (in + out*RDI_EvalTypeGroup_COUNT){ + case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_U*RDI_EvalTypeGroup_COUNT: + { + nval.u64 = (U64)svals[0].f32; + }break; + case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_U*RDI_EvalTypeGroup_COUNT: + { + nval.u64 = (U64)svals[0].f64; + }break; + + case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_S*RDI_EvalTypeGroup_COUNT: + { + nval.s64 = (S64)svals[0].f32; + }break; + case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_S*RDI_EvalTypeGroup_COUNT: + { + nval.s64 = (S64)svals[0].f64; + }break; + + case RDI_EvalTypeGroup_U + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: + { + nval.f32 = (F32)svals[0].u64; + }break; + case RDI_EvalTypeGroup_S + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: + { + nval.f32 = (F32)svals[0].s64; + }break; + case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT: + { + nval.f32 = (F32)svals[0].f64; + }break; + + case RDI_EvalTypeGroup_U + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: + { + nval.f64 = (F64)svals[0].u64; + }break; + case RDI_EvalTypeGroup_S + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: + { + nval.f64 = (F64)svals[0].s64; + }break; + case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT: + { + nval.f64 = (F64)svals[0].f32; + }break; + } + } + }break; + + case RDI_EvalOp_Pick: + { + if (stack_count > imm){ + nval = stack[stack_count - imm - 1]; + } + else{ + result.code = EVAL_ResultCode_BadOp; + goto done; + } + }break; + + case RDI_EvalOp_Pop: + { + // do nothing - the pop is handled by the control bits + }break; + + case RDI_EvalOp_Insert: + { + if (stack_count > imm){ + if (imm > 0){ + EVAL_Slot tval = stack[stack_count - 1]; + EVAL_Slot *dst = stack + stack_count - 1 - imm; + EVAL_Slot *shift = dst + 1; + MemoryCopy(shift, dst, imm*sizeof(EVAL_Slot)); + *dst = tval; + } + } + else{ + result.code = EVAL_ResultCode_BadOp; + goto done; + } + }break; + } + + // push + { + U64 push_count = RDI_PUSHN_FROM_CTRLBITS(ctrlbits); + if (push_count == 1){ + if (stack_count < stack_cap){ + stack[stack_count] = nval; + stack_count += 1; + } + else{ + result.code = EVAL_ResultCode_InsufficientStackSpace; + goto done; + } + } + } + + } + done:; + + if (stack_count == 1){ + result.value = stack[0]; + } + else if(result.code == EVAL_ResultCode_Good){ + result.code = EVAL_ResultCode_MalformedBytecode; + } + + scratch_end(scratch); + ProfEnd(); + return(result); } \ No newline at end of file diff --git a/src/eval/eval_machine.h b/src/eval/eval_machine.h index 12b77b2f..71340092 100644 --- a/src/eval/eval_machine.h +++ b/src/eval/eval_machine.h @@ -1,48 +1,48 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef EVAL2_MACHINE_H -#define EVAL2_MACHINE_H - -//////////////////////////////// -//~ allen: Eval Machine Types - -typedef B32 EVAL_MemoryRead(void *u, void *out, U64 addr, U64 size); - -typedef struct EVAL_Machine EVAL_Machine; -struct EVAL_Machine -{ - void *u; - Architecture arch; - EVAL_MemoryRead *memory_read; - void *reg_data; - U64 reg_size; - U64 *module_base; - U64 *frame_base; - U64 *tls_base; -}; - -typedef union EVAL_Slot EVAL_Slot; -union EVAL_Slot -{ - U64 u256[4]; - U64 u128[2]; - U64 u64; - S64 s64; - F64 f64; - F32 f32; -}; - -typedef struct EVAL_Result EVAL_Result; -struct EVAL_Result -{ - EVAL_Slot value; - EVAL_ResultCode code; -}; - -//////////////////////////////// -//~ allen: Eval Machine Functions - -internal EVAL_Result eval_interpret(EVAL_Machine *machine, String8 bytecode); - -#endif //EVAL2_MACHINE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef EVAL2_MACHINE_H +#define EVAL2_MACHINE_H + +//////////////////////////////// +//~ allen: Eval Machine Types + +typedef B32 EVAL_MemoryRead(void *u, void *out, U64 addr, U64 size); + +typedef struct EVAL_Machine EVAL_Machine; +struct EVAL_Machine +{ + void *u; + Architecture arch; + EVAL_MemoryRead *memory_read; + void *reg_data; + U64 reg_size; + U64 *module_base; + U64 *frame_base; + U64 *tls_base; +}; + +typedef union EVAL_Slot EVAL_Slot; +union EVAL_Slot +{ + U64 u256[4]; + U64 u128[2]; + U64 u64; + S64 s64; + F64 f64; + F32 f32; +}; + +typedef struct EVAL_Result EVAL_Result; +struct EVAL_Result +{ + EVAL_Slot value; + EVAL_ResultCode code; +}; + +//////////////////////////////// +//~ allen: Eval Machine Functions + +internal EVAL_Result eval_interpret(EVAL_Machine *machine, String8 bytecode); + +#endif //EVAL2_MACHINE_H diff --git a/src/eval/eval_parser.c b/src/eval/eval_parser.c index 81f70d33..11cf14c1 100644 --- a/src/eval/eval_parser.c +++ b/src/eval/eval_parser.c @@ -1,1444 +1,1444 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Tables - -global read_only String8 eval_g_multichar_symbol_strings[] = -{ - str8_lit_comp("<<"), - str8_lit_comp(">>"), - str8_lit_comp("->"), - str8_lit_comp("<="), - str8_lit_comp(">="), - str8_lit_comp("=="), - str8_lit_comp("!="), - str8_lit_comp("&&"), - str8_lit_comp("||"), -}; - -global read_only struct {EVAL_ExprKind kind; String8 string; S64 precedence;} eval_g_unary_prefix_op_table[] = -{ - // { EVAL_ExprKind_???, str8_lit_comp("+"), 2 }, - { EVAL_ExprKind_Neg, str8_lit_comp("-"), 2 }, - { EVAL_ExprKind_LogNot, str8_lit_comp("!"), 2 }, - { EVAL_ExprKind_Deref, str8_lit_comp("*"), 2 }, - { EVAL_ExprKind_Address,str8_lit_comp("&"), 2 }, - { EVAL_ExprKind_Sizeof, str8_lit_comp("sizeof"), 2 }, - // { EVAL_ExprKind_Alignof, str8_lit_comp("_Alignof"), 2 }, -}; - -global read_only struct {EVAL_ExprKind kind; String8 string; S64 precedence;} eval_g_binary_op_table[] = -{ - { EVAL_ExprKind_Mul, str8_lit_comp("*"), 3 }, - { EVAL_ExprKind_Div, str8_lit_comp("/"), 3 }, - { EVAL_ExprKind_Mod, str8_lit_comp("%"), 3 }, - { EVAL_ExprKind_Add, str8_lit_comp("+"), 4 }, - { EVAL_ExprKind_Sub, str8_lit_comp("-"), 4 }, - { EVAL_ExprKind_LShift, str8_lit_comp("<<"), 5 }, - { EVAL_ExprKind_RShift, str8_lit_comp(">>"), 5 }, - { EVAL_ExprKind_Less, str8_lit_comp("<"), 6 }, - { EVAL_ExprKind_LsEq, str8_lit_comp("<="), 6 }, - { EVAL_ExprKind_Grtr, str8_lit_comp(">"), 6 }, - { EVAL_ExprKind_GrEq, str8_lit_comp(">="), 6 }, - { EVAL_ExprKind_EqEq, str8_lit_comp("=="), 7 }, - { EVAL_ExprKind_NtEq, str8_lit_comp("!="), 7 }, - { EVAL_ExprKind_BitAnd, str8_lit_comp("&"), 8 }, - { EVAL_ExprKind_BitXor, str8_lit_comp("^"), 9 }, - { EVAL_ExprKind_BitOr, str8_lit_comp("|"), 10 }, - { EVAL_ExprKind_LogAnd, str8_lit_comp("&&"), 11 }, - { EVAL_ExprKind_LogOr, str8_lit_comp("||"), 12 }, - { EVAL_ExprKind_Define, str8_lit_comp("="), 13 }, -}; - -global read_only S64 eval_g_max_precedence = 15; - -//////////////////////////////// -//~ rjf: Map Building Fast Paths - -internal EVAL_String2NumMap * -eval_push_locals_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff) -{ - Temp scratch = scratch_begin(&arena, 1); - - //- rjf: gather scopes to walk - typedef struct Task Task; - struct Task - { - Task *next; - RDI_Scope *scope; - }; - Task *first_task = 0; - Task *last_task = 0; - - //- rjf: voff -> tightest scope - RDI_Scope *tightest_scope = 0; - { - U64 scope_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_ScopeVMap, voff); - RDI_Scope *scope = rdi_element_from_name_idx(rdi, Scopes, scope_idx); - Task *task = push_array(scratch.arena, Task, 1); - task->scope = scope; - SLLQueuePush(first_task, last_task, task); - tightest_scope = scope; - } - - //- rjf: voff-1 -> scope - if(voff > 0) - { - U64 scope_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_ScopeVMap, voff-1); - RDI_Scope *scope = rdi_element_from_name_idx(rdi, Scopes, scope_idx); - if(scope != tightest_scope) - { - Task *task = push_array(scratch.arena, Task, 1); - task->scope = scope; - SLLQueuePush(first_task, last_task, task); - } - } - - //- rjf: tightest scope -> walk up the tree & build tasks for each parent scope - if(tightest_scope != 0) - { - RDI_Scope *nil_scope = rdi_element_from_name_idx(rdi, Scopes, 0); - for(RDI_Scope *scope = rdi_element_from_name_idx(rdi, Scopes, tightest_scope->parent_scope_idx); - scope != 0 && scope != nil_scope; - scope = rdi_element_from_name_idx(rdi, Scopes, scope->parent_scope_idx)) - { - Task *task = push_array(scratch.arena, Task, 1); - task->scope = scope; - SLLQueuePush(first_task, last_task, task); - } - } - - //- rjf: build blank map - EVAL_String2NumMap *map = push_array(arena, EVAL_String2NumMap, 1); - *map = eval_string2num_map_make(arena, 1024); - - //- rjf: accumulate locals for all tasks - for(Task *task = first_task; task != 0; task = task->next) - { - RDI_Scope *scope = task->scope; - if(scope != 0) - { - U32 local_opl_idx = scope->local_first + scope->local_count; - for(U32 local_idx = scope->local_first; local_idx < local_opl_idx; local_idx += 1) - { - RDI_Local *local_var = rdi_element_from_name_idx(rdi, Locals, local_idx); - U64 local_name_size = 0; - U8 *local_name_str = rdi_string_from_idx(rdi, local_var->name_string_idx, &local_name_size); - String8 name = push_str8_copy(arena, str8(local_name_str, local_name_size)); - eval_string2num_map_insert(arena, map, name, (U64)local_idx+1); - } - } - } - - scratch_end(scratch); - return map; -} - -internal EVAL_String2NumMap * -eval_push_member_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff) -{ - //- rjf: voff -> tightest scope - U64 scope_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_ScopeVMap, voff); - RDI_Scope *tightest_scope = rdi_element_from_name_idx(rdi, Scopes, scope_idx); - - //- rjf: tightest scope -> procedure - U32 proc_idx = tightest_scope->proc_idx; - RDI_Procedure *procedure = rdi_element_from_name_idx(rdi, Procedures, proc_idx); - - //- rjf: procedure -> udt - U32 udt_idx = procedure->container_idx; - RDI_UDT *udt = rdi_element_from_name_idx(rdi, UDTs, udt_idx); - - //- rjf: build blank map - EVAL_String2NumMap *map = push_array(arena, EVAL_String2NumMap, 1); - *map = eval_string2num_map_make(arena, 64); - - //- rjf: udt -> fill member map - if(!(udt->flags & RDI_UDTFlag_EnumMembers)) - { - U64 data_member_num = 1; - for(U32 member_idx = udt->member_first; - member_idx < udt->member_first+udt->member_count; - member_idx += 1) - { - RDI_Member *m = rdi_element_from_name_idx(rdi, Members, member_idx); - if(m->kind == RDI_MemberKind_DataField) - { - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, m->name_string_idx, &name.size); - eval_string2num_map_insert(arena, map, name, data_member_num); - data_member_num += 1; - } - } - } - - return map; -} - -//////////////////////////////// -//~ rjf: Tokenization Functions - -internal EVAL_Token -eval_token_zero(void) -{ - EVAL_Token token = zero_struct; - return token; -} - -internal void -eval_token_chunk_list_push(Arena *arena, EVAL_TokenChunkList *list, U64 chunk_size, EVAL_Token *token) -{ - EVAL_TokenChunkNode *node = list->last; - if(node == 0 || node->count >= node->cap) - { - node = push_array(arena, EVAL_TokenChunkNode, 1); - SLLQueuePush(list->first, list->last, node); - node->cap = chunk_size; - node->v = push_array_no_zero(arena, EVAL_Token, node->cap); - list->node_count += 1; - } - MemoryCopyStruct(&node->v[node->count], token); - node->count += 1; - list->total_count += 1; -} - -internal EVAL_TokenArray -eval_token_array_from_chunk_list(Arena *arena, EVAL_TokenChunkList *list) -{ - EVAL_TokenArray array = {0}; - array.count = list->total_count; - array.v = push_array_no_zero(arena, EVAL_Token, array.count); - U64 idx = 0; - for(EVAL_TokenChunkNode *node = list->first; node != 0; node = node->next) - { - MemoryCopy(array.v+idx, node->v, sizeof(EVAL_Token)*node->count); - } - return array; -} - -internal EVAL_TokenArray -eval_token_array_from_text(Arena *arena, String8 text) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - - //- rjf: lex loop - EVAL_TokenChunkList tokens = {0}; - U64 active_token_start_idx = 0; - EVAL_TokenKind active_token_kind = EVAL_TokenKind_Null; - B32 active_token_kind_started_with_tick = 0; - B32 escaped = 0; - for(U64 idx = 0, advance = 0; idx <= text.size; idx += advance) - { - U8 byte = (idx+0 < text.size) ? text.str[idx+0] : 0; - U8 byte_next = (idx+1 < text.size) ? text.str[idx+1] : 0; - U8 byte_next2= (idx+2 < text.size) ? text.str[idx+2] : 0; - advance = 1; - B32 token_formed = 0; - U64 token_end_idx_pad = 0; - switch(active_token_kind) - { - //- rjf: no active token -> seek token starter - default: - { - if(char_is_alpha(byte) || byte == '_' || byte == '`' || byte == '$') - { - active_token_kind = EVAL_TokenKind_Identifier; - active_token_start_idx = idx; - active_token_kind_started_with_tick = (byte == '`'); - } - else if(char_is_digit(byte, 10) || (byte == '.' && char_is_digit(byte_next, 10))) - { - active_token_kind = EVAL_TokenKind_Numeric; - active_token_start_idx = idx; - } - else if(byte == '"') - { - active_token_kind = EVAL_TokenKind_StringLiteral; - active_token_start_idx = idx; - } - else if(byte == '\'') - { - active_token_kind = EVAL_TokenKind_CharLiteral; - active_token_start_idx = idx; - } - else if(byte == '~' || byte == '!' || byte == '%' || byte == '^' || - byte == '&' || byte == '*' || byte == '(' || byte == ')' || - byte == '-' || byte == '=' || byte == '+' || byte == '[' || - byte == ']' || byte == '{' || byte == '}' || byte == ':' || - byte == ';' || byte == ',' || byte == '.' || byte == '<' || - byte == '>' || byte == '/' || byte == '?' || byte == '|') - { - active_token_kind = EVAL_TokenKind_Symbol; - active_token_start_idx = idx; - } - }break; - - //- rjf: active tokens -> seek enders - case EVAL_TokenKind_Identifier: - { - if(byte == ':' && byte_next == ':' && (char_is_alpha(byte_next2) || byte_next2 == '_' || byte_next2 == '<')) - { - // NOTE(rjf): encountering C++-style namespaces - skip over scope resolution symbol - // & keep going. - advance = 2; - } - else if((byte == '\'' || byte == '`') && active_token_kind_started_with_tick) - { - // NOTE(rjf): encountering ` -> ' or ` -> ` style identifier escapes - active_token_kind_started_with_tick = 0; - advance = 1; - } - else if(byte == '<') - { - // NOTE(rjf): encountering C++-style templates - try to find ender. if no ender found, - // assume this is an operator & just consume the identifier part. - S64 nest = 1; - for(U64 idx2 = idx+1; idx2 <= text.size; idx2 += 1) - { - if(idx2 < text.size && text.str[idx2] == '<') - { - nest += 1; - } - else if(idx2 < text.size && text.str[idx2] == '>') - { - nest -= 1; - if(nest == 0) - { - advance = (idx2+1-idx); - break; - } - } - else if(idx2 == text.size && nest != 0) - { - token_formed = 1; - advance = 0; - break; - } - } - } - else if(!char_is_alpha(byte) && !char_is_digit(byte, 10) && byte != '_' && !active_token_kind_started_with_tick && byte != '@' && byte != '$') - { - advance = 0; - token_formed = 1; - } - }break; - case EVAL_TokenKind_Numeric: - { - if(!char_is_alpha(byte) && !char_is_digit(byte, 10) && byte != '.') - { - advance = 0; - token_formed = 1; - } - }break; - case EVAL_TokenKind_StringLiteral: - { - if(escaped == 0 && byte == '\\') - { - escaped = 1; - } - else if(escaped) - { - escaped = 0; - } - else if(escaped == 0 && byte == '"') - { - advance = 1; - token_formed = 1; - token_end_idx_pad = 1; - } - }break; - case EVAL_TokenKind_CharLiteral: - { - if(escaped == 0 && byte == '\\') - { - escaped = 1; - } - else if(escaped) - { - escaped = 0; - } - else if(escaped == 0 && byte == '\'') - { - advance = 1; - token_formed = 1; - token_end_idx_pad = 1; - } - }break; - case EVAL_TokenKind_Symbol: - { - if(byte != '~' && byte != '!' && byte != '%' && byte != '^' && - byte != '&' && byte != '*' && byte != '(' && byte != ')' && - byte != '-' && byte != '=' && byte != '+' && byte != '[' && - byte != ']' && byte != '{' && byte != '}' && byte != ':' && - byte != ';' && byte != ',' && byte != '.' && byte != '<' && - byte != '>' && byte != '/' && byte != '?' && byte != '|') - { - advance = 0; - token_formed = 1; - } - }break; - } - - //- rjf: token formed -> push new formed token(s) - if(token_formed) - { - // rjf: non-symbols *or* symbols of only 1-length can be immediately - // pushed as a token - if(active_token_kind != EVAL_TokenKind_Symbol || idx==active_token_start_idx+1) - { - EVAL_Token token = {active_token_kind, r1u64(active_token_start_idx, idx+token_end_idx_pad)}; - eval_token_chunk_list_push(scratch.arena, &tokens, 256, &token); - } - - // rjf: symbolic strings matching `--` mean the remainder of the string - // is reserved for external usage. the rest of the stream should not - // be tokenized. - else if(idx == active_token_start_idx+2 && text.str[active_token_start_idx] == '-' && text.str[active_token_start_idx+1] == '-') - { - break; - } - - // rjf: if we got a symbol string of N>1 characters, then we need to - // apply the maximum-munch rule, and produce M<=N tokens, where each - // formed token is the maximum size possible, given the legal - // >1-length symbol strings. - else - { - U64 advance2 = 0; - for(U64 idx2 = active_token_start_idx; idx2 < idx; idx2 += advance2) - { - advance2 = 1; - for(U64 multichar_symbol_idx = 0; - multichar_symbol_idx < ArrayCount(eval_g_multichar_symbol_strings); - multichar_symbol_idx += 1) - { - String8 multichar_symbol_string = eval_g_multichar_symbol_strings[multichar_symbol_idx]; - String8 part_of_token = str8_substr(text, r1u64(idx2, idx2+multichar_symbol_string.size)); - if(str8_match(part_of_token, multichar_symbol_string, 0)) - { - advance2 = multichar_symbol_string.size; - break; - } - } - EVAL_Token token = {active_token_kind, r1u64(idx2, idx2+advance2)}; - eval_token_chunk_list_push(scratch.arena, &tokens, 256, &token); - } - } - - // rjf: reset for subsequent tokens. - active_token_kind = EVAL_TokenKind_Null; - } - } - - //- rjf: chunk list -> array & return - EVAL_TokenArray array = eval_token_array_from_chunk_list(arena, &tokens); - scratch_end(scratch); - ProfEnd(); - return array; -} - -internal EVAL_TokenArray -eval_token_array_make_first_opl(EVAL_Token *first, EVAL_Token *opl) -{ - EVAL_TokenArray array = {first, (U64)(opl-first)}; - return array; -} - -//////////////////////////////// -//~ rjf: Parser Functions - -internal TG_Key -eval_leaf_type_from_name(RDI_Parsed *rdi, String8 name) -{ - TG_Key key = zero_struct; - B32 found = 0; - { - RDI_NameMap *name_map = rdi_element_from_name_idx(rdi, NameMaps, RDI_NameMapKind_Types); - RDI_ParsedNameMap parsed_name_map = {0}; - rdi_parsed_from_name_map(rdi, name_map, &parsed_name_map); - RDI_NameMapNode *node = rdi_name_map_lookup(rdi, &parsed_name_map, name.str, name.size); - if(node != 0) - { - U32 match_count = 0; - U32 *matches = rdi_matches_from_map_node(rdi, node, &match_count); - if(match_count != 0) - { - RDI_TypeNode *type_node = rdi_element_from_name_idx(rdi, TypeNodes, matches[0]); - found = type_node->kind != RDI_TypeKind_NULL; - key = tg_key_ext(tg_kind_from_rdi_type_kind(type_node->kind), (U64)matches[0]); - } - } - } - if(!found) - { -#define Case(str) (str8_match(name, str8_lit(str), 0)) - if(Case("u8") || Case("uint8") || Case("uint8_t") || Case("uchar8") || Case("U8")) - { - key = tg_key_basic(TG_Kind_U8); - } - else if(Case("u16") || Case("uint16") || Case("uint16_t") || Case("uchar16") || Case("U16")) - { - key = tg_key_basic(TG_Kind_U16); - } - else if(Case("u32") || Case("uint32") || Case("uint32_t") || Case("uchar32") || Case("U32") || Case("uint")) - { - key = tg_key_basic(TG_Kind_U32); - } - else if(Case("u64") || Case("uint64") || Case("uint64_t") || Case("U64")) - { - key = tg_key_basic(TG_Kind_U64); - } - else if(Case("s8") || Case("b8") || Case("B8") || Case("i8") || Case("int8") || Case("int8_t") || Case("char8") || Case("S8")) - { - key = tg_key_basic(TG_Kind_S8); - } - else if(Case("s16") ||Case("b16") || Case("B16") || Case("i16") || Case("int16") || Case("int16_t") || Case("char16") || Case("S16")) - { - key = tg_key_basic(TG_Kind_S16); - } - else if(Case("s32") || Case("b32") || Case("B32") || Case("i32") || Case("int32") || Case("int32_t") || Case("char32") || Case("S32") || Case("int")) - { - key = tg_key_basic(TG_Kind_S32); - } - else if(Case("s64") || Case("b64") || Case("B64") || Case("i64") || Case("int64") || Case("int64_t") || Case("S64")) - { - key = tg_key_basic(TG_Kind_S64); - } - else if(Case("void")) - { - key = tg_key_basic(TG_Kind_Void); - } - else if(Case("bool")) - { - key = tg_key_basic(TG_Kind_Bool); - } - else if(Case("float") || Case("f32") || Case("F32") || Case("r32") || Case("R32")) - { - key = tg_key_basic(TG_Kind_F32); - } - else if(Case("double") || Case("f64") || Case("F64") || Case("r64") || Case("R64")) - { - key = tg_key_basic(TG_Kind_F64); - } -#undef Case - } - return key; -} - -internal EVAL_ParseResult -eval_parse_type_from_text_tokens(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens) -{ - EVAL_ParseResult parse = eval_parse_result_nil; - EVAL_Token *token_it = tokens->v; - - //- rjf: parse unsigned marker - B32 unsigned_marker = 0; - { - EVAL_Token token = eval_token_at_it(token_it, tokens); - if(token.kind == EVAL_TokenKind_Identifier) - { - String8 token_string = str8_substr(text, token.range); - if(str8_match(token_string, str8_lit("unsigned"), 0)) - { - token_it += 1; - unsigned_marker = 1; - } - } - } - - //- rjf: parse base type - { - EVAL_Token token = eval_token_at_it(token_it, tokens); - if(token.kind == EVAL_TokenKind_Identifier) - { - String8 token_string = str8_substr(text, token.range); - TG_Key type_key = eval_leaf_type_from_name(ctx->rdi, token_string); - if(!tg_key_match(tg_key_zero(), type_key)) - { - token_it += 1; - - // rjf: apply unsigned marker to base type - if(unsigned_marker) switch(tg_kind_from_key(type_key)) - { - default:{}break; - case TG_Kind_Char8: {type_key = tg_key_basic(TG_Kind_UChar8);}break; - case TG_Kind_Char16:{type_key = tg_key_basic(TG_Kind_UChar16);}break; - case TG_Kind_Char32:{type_key = tg_key_basic(TG_Kind_UChar32);}break; - case TG_Kind_S8: {type_key = tg_key_basic(TG_Kind_U8);}break; - case TG_Kind_S16: {type_key = tg_key_basic(TG_Kind_U16);}break; - case TG_Kind_S32: {type_key = tg_key_basic(TG_Kind_U32);}break; - case TG_Kind_S64: {type_key = tg_key_basic(TG_Kind_U64);}break; - case TG_Kind_S128:{type_key = tg_key_basic(TG_Kind_U128);}break; - case TG_Kind_S256:{type_key = tg_key_basic(TG_Kind_U256);}break; - case TG_Kind_S512:{type_key = tg_key_basic(TG_Kind_U512);}break; - } - - // rjf: construct leaf type - parse.expr = eval_expr_leaf_type(arena, token_string.str, type_key); - } - } - } - - //- rjf: parse extensions - if(parse.expr != &eval_expr_nil) - { - for(;;) - { - EVAL_Token token = eval_token_at_it(token_it, tokens); - if(token.kind != EVAL_TokenKind_Symbol) - { - break; - } - String8 token_string = str8_substr(text, token.range); - if(str8_match(token_string, str8_lit("*"), 0)) - { - token_it += 1; - parse.expr = eval_expr(arena, EVAL_ExprKind_Ptr, token_string.str, parse.expr, &eval_expr_nil, &eval_expr_nil); - } - else - { - break; - } - } - } - - //- rjf: fill parse & end - parse.last_token = token_it; - return parse; -} - -internal EVAL_ParseResult -eval_parse_expr_from_text_tokens__prec(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens, S64 max_precedence) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - EVAL_Token *it = tokens->v; - EVAL_Token *it_opl = tokens->v + tokens->count; - EVAL_ParseResult result = eval_parse_result_nil; - - //- rjf: parse prefix unaries - typedef struct PrefixUnaryNode PrefixUnaryNode; - struct PrefixUnaryNode - { - PrefixUnaryNode *next; - EVAL_ExprKind kind; - EVAL_Expr *cast_expr; - void *location; - }; - PrefixUnaryNode *first_prefix_unary = 0; - PrefixUnaryNode *last_prefix_unary = 0; - { - for(;it < it_opl;) - { - EVAL_Token *start_it = it; - EVAL_Token token = eval_token_at_it(it, tokens); - String8 token_string = str8_substr(text, token.range); - S64 prefix_unary_precedence = 0; - EVAL_ExprKind prefix_unary_kind = 0; - EVAL_Expr *cast_expr = &eval_expr_nil; - void *location = 0; - - // rjf: try op table - for(U64 idx = 0; idx < ArrayCount(eval_g_unary_prefix_op_table); idx += 1) - { - if(str8_match(token_string, eval_g_unary_prefix_op_table[idx].string, 0)) - { - prefix_unary_precedence = eval_g_unary_prefix_op_table[idx].precedence; - prefix_unary_kind = eval_g_unary_prefix_op_table[idx].kind; - break; - } - } - - // rjf: consume valid op - if(prefix_unary_precedence != 0) - { - location = token_string.str; - it += 1; - } - - // rjf: try casting expression - if(prefix_unary_precedence == 0 && str8_match(token_string, str8_lit("("), 0)) - { - EVAL_Token some_type_identifier_maybe = eval_token_at_it(it+1, tokens); - String8 some_type_identifier_maybe_string = str8_substr(text, some_type_identifier_maybe.range); - if(some_type_identifier_maybe.kind == EVAL_TokenKind_Identifier) - { - TG_Key type_key = eval_leaf_type_from_name(ctx->rdi, some_type_identifier_maybe_string); - if(!tg_key_match(type_key, tg_key_zero()) || str8_match(some_type_identifier_maybe_string, str8_lit("unsigned"), 0)) - { - // rjf: move past open paren - it += 1; - - // rjf: parse type expr - EVAL_TokenArray type_parse_tokens = eval_token_array_make_first_opl(it, it_opl); - EVAL_ParseResult type_parse = eval_parse_type_from_text_tokens(arena, ctx, text, &type_parse_tokens); - EVAL_Expr *type = type_parse.expr; - eval_error_list_concat_in_place(&result.errors, &type_parse.errors); - it = type_parse.last_token; - location = token_string.str; - - // rjf: expect ) - EVAL_Token close_paren_maybe = eval_token_at_it(it, tokens); - String8 close_paren_maybe_string = str8_substr(text, close_paren_maybe.range); - if(close_paren_maybe.kind != EVAL_TokenKind_Symbol || !str8_match(close_paren_maybe_string, str8_lit(")"), 0)) - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Missing )."); - } - - // rjf: consume ) - else - { - it += 1; - } - - // rjf: fill - prefix_unary_precedence = 2; - prefix_unary_kind = EVAL_ExprKind_Cast; - cast_expr = type; - } - } - } - - // rjf: break if we got no operators - if(prefix_unary_precedence == 0) - { - break; - } - - // rjf: break if the token node iterator has not changed - if(it == start_it) - { - break; - } - - // rjf: push prefix unary if we got one - { - PrefixUnaryNode *op_n = push_array(scratch.arena, PrefixUnaryNode, 1); - op_n->kind = prefix_unary_kind; - op_n->cast_expr = cast_expr; - op_n->location = location; - SLLQueuePushFront(first_prefix_unary, last_prefix_unary, op_n); - } - } - } - - //- rjf: parse atom - EVAL_Expr *atom = &eval_expr_nil; - String8 atom_implicit_member_name = {0}; - if(it < it_opl) - { - EVAL_Token token = eval_token_at_it(it, tokens); - String8 token_string = str8_substr(text, token.range); - - //- rjf: descent to nested expression - if(token.kind == EVAL_TokenKind_Symbol && str8_match(token_string, str8_lit("("), 0)) - { - // rjf: skip ( - it += 1; - - // rjf: parse () contents - EVAL_TokenArray nested_parse_tokens = eval_token_array_make_first_opl(it, it_opl); - EVAL_ParseResult nested_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &nested_parse_tokens, eval_g_max_precedence); - eval_error_list_concat_in_place(&result.errors, &nested_parse.errors); - atom = nested_parse.expr; - it = nested_parse.last_token; - - // rjf: expect ) - EVAL_Token close_paren_maybe = eval_token_at_it(it, tokens); - String8 close_paren_maybe_string = str8_substr(text, close_paren_maybe.range); - if(close_paren_maybe.kind != EVAL_TokenKind_Symbol || !str8_match(close_paren_maybe_string, str8_lit(")"), 0)) - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Missing )."); - } - - // rjf: consume ) - else - { - it += 1; - } - } - - //- rjf: descent to assembly-style dereference sub-expression - else if(token.kind == EVAL_TokenKind_Symbol && str8_match(token_string, str8_lit("["), 0)) - { - // rjf: skip [ - it += 1; - - // rjf: parse [] contents - EVAL_TokenArray nested_parse_tokens = eval_token_array_make_first_opl(it, it_opl); - EVAL_ParseResult nested_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &nested_parse_tokens, eval_g_max_precedence); - eval_error_list_concat_in_place(&result.errors, &nested_parse.errors); - atom = nested_parse.expr; - it = nested_parse.last_token; - - // rjf: build cast-to-U64*, and dereference operators - atom = eval_expr(arena, EVAL_ExprKind_Cast, token_string.str, eval_expr_leaf_type(arena, token_string.str, tg_cons_type_make(ctx->type_graph, TG_Kind_Ptr, tg_key_basic(TG_Kind_U64), 0)), atom, &eval_expr_nil); - atom = eval_expr(arena, EVAL_ExprKind_Deref, token_string.str, atom, &eval_expr_nil, &eval_expr_nil); - - // rjf: expect ] - EVAL_Token close_paren_maybe = eval_token_at_it(it, tokens); - String8 close_paren_maybe_string = str8_substr(text, close_paren_maybe.range); - if(close_paren_maybe.kind != EVAL_TokenKind_Symbol || !str8_match(close_paren_maybe_string, str8_lit("]"), 0)) - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Missing ]."); - } - - // rjf: consume ) - else - { - it += 1; - } - } - - //- rjf: leaf (identifier, literal) - else if(token.kind != EVAL_TokenKind_Symbol) - { - switch(token.kind) - { - //- rjf: identifier => name resolution - default: - case EVAL_TokenKind_Identifier: - { - B32 mapped_identifier = 0; - B32 identifier_type_is_possibly_dynamically_overridden = 0; - B32 identifier_looks_like_type_expr = 0; - RDI_LocationKind loc_kind = RDI_LocationKind_NULL; - RDI_LocationReg loc_reg = {0}; - RDI_LocationRegPlusU16 loc_reg_u16 = {0}; - String8 loc_bytecode = {0}; - REGS_RegCode reg_code = 0; - REGS_AliasCode alias_code = 0; - TG_Key type_key = zero_struct; - String8 local_lookup_string = token_string; - - //- rjf: form namespaceified fallback versions of this lookup string - String8List namespaceified_token_strings = {0}; - { - U64 scope_idx = rdi_vmap_idx_from_section_kind_voff(ctx->rdi, RDI_SectionKind_ScopeVMap, ctx->ip_voff); - RDI_Scope *scope = rdi_element_from_name_idx(ctx->rdi, Scopes, scope_idx); - U64 proc_idx = scope->proc_idx; - RDI_Procedure *procedure = rdi_element_from_name_idx(ctx->rdi, Procedures, proc_idx); - U64 name_size = 0; - U8 *name_ptr = rdi_string_from_idx(ctx->rdi, procedure->name_string_idx, &name_size); - String8 containing_procedure_name = str8(name_ptr, name_size); - U64 last_past_scope_resolution_pos = 0; - for(;;) - { - U64 past_next_dbl_colon_pos = str8_find_needle(containing_procedure_name, last_past_scope_resolution_pos, str8_lit("::"), 0)+2; - U64 past_next_dot_pos = str8_find_needle(containing_procedure_name, last_past_scope_resolution_pos, str8_lit("."), 0)+1; - U64 past_next_scope_resolution_pos = Min(past_next_dbl_colon_pos, past_next_dot_pos); - if(past_next_scope_resolution_pos >= containing_procedure_name.size) - { - break; - } - String8 new_namespace_prefix_possibility = str8_prefix(containing_procedure_name, past_next_scope_resolution_pos); - String8 namespaceified_token_string = push_str8f(scratch.arena, "%S%S", new_namespace_prefix_possibility, token_string); - str8_list_push_front(scratch.arena, &namespaceified_token_strings, namespaceified_token_string); - last_past_scope_resolution_pos = past_next_scope_resolution_pos; - } - } - - //- rjf: try members - if(mapped_identifier == 0) - { - U64 data_member_num = eval_num_from_string(ctx->member_map, token_string); - if(data_member_num != 0) - { - atom_implicit_member_name = token_string; - local_lookup_string = str8_lit("this"); - } - } - - //- rjf: try locals - if(mapped_identifier == 0) - { - U64 local_num = eval_num_from_string(ctx->locals_map, local_lookup_string); - if(local_num != 0) - { - mapped_identifier = 1; - identifier_type_is_possibly_dynamically_overridden = 1; - RDI_Local *local_var = rdi_element_from_name_idx(ctx->rdi, Locals, local_num-1); - RDI_TypeNode *type_node = rdi_element_from_name_idx(ctx->rdi, TypeNodes, local_var->type_idx); - type_key = tg_key_ext(tg_kind_from_rdi_type_kind(type_node->kind), (U64)local_var->type_idx); - - // rjf: grab location info - for(U32 loc_block_idx = local_var->location_first; - loc_block_idx < local_var->location_opl; - loc_block_idx += 1) - { - RDI_LocationBlock *block = rdi_element_from_name_idx(ctx->rdi, LocationBlocks, loc_block_idx); - if(block->scope_off_first <= ctx->ip_voff && ctx->ip_voff < block->scope_off_opl) - { - U64 all_location_data_size = 0; - U8 *all_location_data = rdi_table_from_name(ctx->rdi, LocationData, &all_location_data_size); - loc_kind = *((RDI_LocationKind *)(all_location_data + block->location_data_off)); - switch(loc_kind) - { - default:{mapped_identifier = 0;}break; - case RDI_LocationKind_AddrBytecodeStream: - case RDI_LocationKind_ValBytecodeStream: - { - U8 *bytecode_base = all_location_data + block->location_data_off + sizeof(RDI_LocationKind); - U64 bytecode_size = 0; - for(U64 idx = 0; idx < all_location_data_size; idx += 1) - { - U8 op = bytecode_base[idx]; - if(op == 0) - { - break; - } - U8 ctrlbits = rdi_eval_op_ctrlbits_table[op]; - U32 p_size = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); - bytecode_size += 1+p_size; - } - loc_bytecode = str8(bytecode_base, bytecode_size); - }break; - case RDI_LocationKind_AddrRegPlusU16: - case RDI_LocationKind_AddrAddrRegPlusU16: - { - MemoryCopy(&loc_reg_u16, (all_location_data + block->location_data_off), sizeof(loc_reg_u16)); - }break; - case RDI_LocationKind_ValReg: - { - MemoryCopy(&loc_reg, (all_location_data + block->location_data_off), sizeof(loc_reg)); - }break; - } - } - } - } - } - - //- rjf: try registers - if(mapped_identifier == 0) - { - U64 reg_num = eval_num_from_string(ctx->regs_map, token_string); - if(reg_num != 0) - { - reg_code = reg_num; - mapped_identifier = 1; - type_key = tg_key_reg(ctx->arch, reg_code); - } - } - - //- rjf: try register aliases - if(mapped_identifier == 0) - { - U64 alias_num = eval_num_from_string(ctx->reg_alias_map, token_string); - if(alias_num != 0) - { - alias_code = (REGS_AliasCode)alias_num; - type_key = tg_key_reg_alias(ctx->arch, alias_code); - mapped_identifier = 1; - } - } - - //- rjf: try global variables - if(mapped_identifier == 0) - { - RDI_NameMap *name_map = rdi_element_from_name_idx(ctx->rdi, NameMaps, RDI_NameMapKind_GlobalVariables); - RDI_ParsedNameMap parsed_name_map = {0}; - rdi_parsed_from_name_map(ctx->rdi, name_map, &parsed_name_map); - RDI_NameMapNode *node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, token_string.str, token_string.size); - U32 matches_count = 0; - U32 *matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); - for(String8Node *n = namespaceified_token_strings.first; - n != 0 && matches_count == 0; - n = n->next) - { - node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, n->string.str, n->string.size); - matches_count = 0; - matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); - } - if(matches_count != 0) - { - // NOTE(rjf): apparently, PDBs can be produced such that they - // also keep stale *GLOBAL VARIABLE SYMBOLS* around too. I - // don't know of a magic hash table fixup path in PDBs, so - // in this case, I'm going to prefer the latest-added global. - U32 match_idx = matches[matches_count-1]; - RDI_GlobalVariable *global_var = rdi_element_from_name_idx(ctx->rdi, GlobalVariables, match_idx); - EVAL_OpList oplist = {0}; - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_ModuleOff, global_var->voff); - loc_kind = RDI_LocationKind_AddrBytecodeStream; - loc_bytecode = eval_bytecode_from_oplist(arena, &oplist); - U32 type_idx = global_var->type_idx; - RDI_TypeNode *type_node = rdi_element_from_name_idx(ctx->rdi, TypeNodes, type_idx); - type_key = tg_key_ext(tg_kind_from_rdi_type_kind(type_node->kind), (U64)type_idx); - mapped_identifier = 1; - } - } - - //- rjf: try thread variables - if(mapped_identifier == 0) - { - RDI_NameMap *name_map = rdi_element_from_name_idx(ctx->rdi, NameMaps, RDI_NameMapKind_ThreadVariables); - RDI_ParsedNameMap parsed_name_map = {0}; - rdi_parsed_from_name_map(ctx->rdi, name_map, &parsed_name_map); - RDI_NameMapNode *node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, token_string.str, token_string.size); - U32 matches_count = 0; - U32 *matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); - for(String8Node *n = namespaceified_token_strings.first; - n != 0 && matches_count == 0; - n = n->next) - { - node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, n->string.str, n->string.size); - matches_count = 0; - matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); - } - if(matches_count != 0) - { - U32 match_idx = matches[0]; - RDI_ThreadVariable *thread_var = rdi_element_from_name_idx(ctx->rdi, ThreadVariables, match_idx); - EVAL_OpList oplist = {0}; - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_TLSOff, thread_var->tls_off); - loc_kind = RDI_LocationKind_AddrBytecodeStream; - loc_bytecode = eval_bytecode_from_oplist(arena, &oplist); - U32 type_idx = thread_var->type_idx; - RDI_TypeNode *type_node = rdi_element_from_name_idx(ctx->rdi, TypeNodes, type_idx); - type_key = tg_key_ext(tg_kind_from_rdi_type_kind(type_node->kind), (U64)type_idx); - mapped_identifier = 1; - } - } - - //- rjf: try procedures - if(mapped_identifier == 0) - { - RDI_NameMap *name_map = rdi_element_from_name_idx(ctx->rdi, NameMaps, RDI_NameMapKind_Procedures); - RDI_ParsedNameMap parsed_name_map = {0}; - rdi_parsed_from_name_map(ctx->rdi, name_map, &parsed_name_map); - RDI_NameMapNode *node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, token_string.str, token_string.size); - U32 matches_count = 0; - U32 *matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); - for(String8Node *n = namespaceified_token_strings.first; - n != 0 && matches_count == 0; - n = n->next) - { - node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, n->string.str, n->string.size); - matches_count = 0; - matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); - } - if(matches_count != 0) - { - U32 match_idx = matches[0]; - RDI_Procedure *procedure = rdi_element_from_name_idx(ctx->rdi, Procedures, match_idx); - RDI_Scope *scope = rdi_element_from_name_idx(ctx->rdi, Scopes, procedure->root_scope_idx); - U64 voff = *rdi_element_from_name_idx(ctx->rdi, ScopeVOffData, scope->voff_range_first); - EVAL_OpList oplist = {0}; - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_ModuleOff, voff); - loc_kind = RDI_LocationKind_ValBytecodeStream; - loc_bytecode = eval_bytecode_from_oplist(arena, &oplist); - U32 type_idx = procedure->type_idx; - RDI_TypeNode *type_node = rdi_element_from_name_idx(ctx->rdi, TypeNodes, type_idx); - type_key = tg_key_ext(tg_kind_from_rdi_type_kind(type_node->kind), (U64)type_idx); - mapped_identifier = 1; - } - } - - //- rjf: try types - if(mapped_identifier == 0) - { - type_key = eval_leaf_type_from_name(ctx->rdi, token_string); - if(!tg_key_match(tg_key_zero(), type_key)) - { - mapped_identifier = 1; - identifier_looks_like_type_expr = 1; - } - } - - //- rjf: attach on map - if(mapped_identifier != 0) - { - it += 1; - - // rjf: build atom - switch(loc_kind) - { - default: - { - if(identifier_looks_like_type_expr) - { - EVAL_TokenArray type_parse_tokens = eval_token_array_make_first_opl(it-1, it_opl); - EVAL_ParseResult type_parse = eval_parse_type_from_text_tokens(arena, ctx, text, &type_parse_tokens); - EVAL_Expr *type = type_parse.expr; - eval_error_list_concat_in_place(&result.errors, &type_parse.errors); - it = type_parse.last_token; - atom = type; - } - else if(reg_code != 0) - { - REGS_Rng reg_rng = regs_reg_code_rng_table_from_architecture(ctx->arch)[reg_code]; - EVAL_OpList oplist = {0}; - eval_oplist_push_uconst(arena, &oplist, reg_rng.byte_off); - atom = eval_expr_leaf_op_list(arena, token_string.str, type_key, &oplist, EVAL_EvalMode_Reg); - } - else if(alias_code != 0) - { - REGS_Slice alias_slice = regs_alias_code_slice_table_from_architecture(ctx->arch)[alias_code]; - REGS_Rng alias_reg_rng = regs_reg_code_rng_table_from_architecture(ctx->arch)[alias_slice.code]; - EVAL_OpList oplist = {0}; - eval_oplist_push_uconst(arena, &oplist, alias_reg_rng.byte_off + alias_slice.byte_off); - atom = eval_expr_leaf_op_list(arena, token_string.str, type_key, &oplist, EVAL_EvalMode_Reg); - } - else - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MissingInfo, token_string.str, "Missing location information for \"%S\".", token_string); - } - }break; - case RDI_LocationKind_AddrBytecodeStream: - { - atom = eval_expr_leaf_bytecode(arena, token_string.str, type_key, loc_bytecode, EVAL_EvalMode_Addr); - }break; - case RDI_LocationKind_ValBytecodeStream: - { - atom = eval_expr_leaf_bytecode(arena, token_string.str, type_key, loc_bytecode, EVAL_EvalMode_Value); - }break; - case RDI_LocationKind_AddrRegPlusU16: - { - EVAL_OpList oplist = {0}; - U64 byte_size = bit_size_from_arch(ctx->arch)/8; - U64 regread_param = RDI_EncodeRegReadParam(loc_reg_u16.reg_code, byte_size, 0); - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_RegRead, regread_param); - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_ConstU16, loc_reg_u16.offset); - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_Add, 0); - atom = eval_expr_leaf_op_list(arena, token_string.str, type_key, &oplist, EVAL_EvalMode_Addr); - }break; - case RDI_LocationKind_AddrAddrRegPlusU16: - { - EVAL_OpList oplist = {0}; - U64 byte_size = bit_size_from_arch(ctx->arch)/8; - U64 regread_param = RDI_EncodeRegReadParam(loc_reg_u16.reg_code, byte_size, 0); - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_RegRead, regread_param); - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_ConstU16, loc_reg_u16.offset); - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_Add, 0); - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_MemRead, bit_size_from_arch(ctx->arch)/8); - atom = eval_expr_leaf_op_list(arena, token_string.str, type_key, &oplist, EVAL_EvalMode_Addr); - }break; - case RDI_LocationKind_ValReg: - { - REGS_RegCode regs_reg_code = regs_reg_code_from_arch_rdi_code(ctx->arch, loc_reg.reg_code); - REGS_Rng reg_rng = regs_reg_code_rng_table_from_architecture(ctx->arch)[regs_reg_code]; - EVAL_OpList oplist = {0}; - U64 byte_size = (U64)reg_rng.byte_size; - U64 byte_pos = 0; - U64 regread_param = RDI_EncodeRegReadParam(loc_reg.reg_code, byte_size, byte_pos); - eval_oplist_push_op(arena, &oplist, RDI_EvalOp_RegRead, regread_param); - atom = eval_expr_leaf_op_list(arena, token_string.str, type_key, &oplist, EVAL_EvalMode_Value); - }break; - } - - // rjf: implicit local lookup -> attach member access node - if(atom_implicit_member_name.size != 0) - { - EVAL_Expr *member_expr = eval_expr_leaf_member(arena, atom_implicit_member_name.str, atom_implicit_member_name); - atom = eval_expr(arena, EVAL_ExprKind_MemberAccess, atom_implicit_member_name.str, atom, member_expr, &eval_expr_nil); - } - } - - //- rjf: map failure -> attach as leaf identifier, to be resolved later - if(!mapped_identifier) - { - atom = eval_expr_leaf_ident(arena, token_string.str, token_string); - it += 1; - } - }break; - - //- rjf: numeric => directly extract value - case EVAL_TokenKind_Numeric: - { - U64 dot_pos = str8_find_needle(token_string, 0, str8_lit("."), 0); - it += 1; - - // rjf: no . => integral - if(dot_pos == token_string.size) - { - U64 val = 0; - try_u64_from_str8_c_rules(token_string, &val); - atom = eval_expr_u64(arena, token_string.str, val); - break; - } - - // rjf: presence of . => double or float - if(dot_pos < token_string.size) - { - F64 val = f64_from_str8(token_string); - U64 f_pos = str8_find_needle(token_string, 0, str8_lit("f"), StringMatchFlag_CaseInsensitive); - - // rjf: presence of f after . => f32 - if(f_pos < token_string.size) - { - atom = eval_expr_f32(arena, token_string.str, (F32)val); - } - - // rjf: no f => f64 - else - { - atom = eval_expr_f64(arena, token_string.str, val); - } - } - }break; - - //- rjf: char => extract char value - case EVAL_TokenKind_CharLiteral: - { - it += 1; - if(token_string.size > 1 && token_string.str[0] == '\'' && token_string.str[1] != '\'') - { - U8 char_val = token_string.str[1]; - atom = eval_expr_u64(arena, token_string.str, char_val); - } - else - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Malformed character literal."); - } - }break; - - // rjf: string => invalid - case EVAL_TokenKind_StringLiteral: - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "String literals are not supported."); - it += 1; - }break; - - } - } - } - - //- rjf: upgrade atom w/ postfix unaries - if(atom != &eval_expr_nil) for(;it < it_opl;) - { - EVAL_Token token = eval_token_at_it(it, tokens); - String8 token_string = str8_substr(text, token.range); - B32 is_postfix_unary = 0; - - // rjf: dot/arrow operator - if(token.kind == EVAL_TokenKind_Symbol && - (str8_match(token_string, str8_lit("."), 0) || - str8_match(token_string, str8_lit("->"), 0))) - { - is_postfix_unary = 1; - - // rjf: advance past operator - it += 1; - - // rjf: expect member name - String8 member_name = {0}; - B32 good_member_name = 0; - { - EVAL_Token member_name_maybe = eval_token_at_it(it, tokens); - String8 member_name_maybe_string = str8_substr(text, member_name_maybe.range); - if(member_name_maybe.kind != EVAL_TokenKind_Identifier) - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Expected member name after %S.", token_string); - } - else - { - member_name = member_name_maybe_string; - good_member_name = 1; - } - } - - // rjf: produce lookup member expr - if(good_member_name) - { - EVAL_Expr *member_expr = eval_expr_leaf_member(arena, member_name.str, member_name); - atom = eval_expr(arena, EVAL_ExprKind_MemberAccess, token_string.str, atom, member_expr, &eval_expr_nil); - } - - // rjf: increment past good member names - if(good_member_name) - { - it += 1; - } - } - - // rjf: array index - if(token.kind == EVAL_TokenKind_Symbol && - str8_match(token_string, str8_lit("["), 0)) - { - is_postfix_unary = 1; - - // rjf: advance past [ - it += 1; - - // rjf: parse indexing expression - EVAL_TokenArray idx_expr_parse_tokens = eval_token_array_make_first_opl(it, it_opl); - EVAL_ParseResult idx_expr_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &idx_expr_parse_tokens, eval_g_max_precedence); - eval_error_list_concat_in_place(&result.errors, &idx_expr_parse.errors); - it = idx_expr_parse.last_token; - - // rjf: valid indexing expression => produce index expr - if(idx_expr_parse.expr != &eval_expr_nil) - { - atom = eval_expr(arena, EVAL_ExprKind_ArrayIndex, token_string.str, atom, idx_expr_parse.expr, &eval_expr_nil); - } - - // rjf: expect ] - { - EVAL_Token close_brace_maybe = eval_token_at_it(it, tokens); - String8 close_brace_maybe_string = str8_substr(text, close_brace_maybe.range); - if(close_brace_maybe.kind != EVAL_TokenKind_Symbol || !str8_match(close_brace_maybe_string, str8_lit("]"), 0)) - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Unclosed [."); - } - else - { - it += 1; - } - } - } - - // rjf: quit if this doesn't look like any patterns of postfix unary we know - if(!is_postfix_unary) - { - break; - } - } - - //- rjf: upgrade atom w/ previously parsed prefix unaries - if(atom == &eval_expr_nil && first_prefix_unary != 0 && first_prefix_unary->cast_expr != 0) - { - atom = first_prefix_unary->cast_expr; - for(PrefixUnaryNode *prefix_unary = first_prefix_unary->next; - prefix_unary != 0; - prefix_unary = prefix_unary->next) - { - atom = eval_expr(arena, prefix_unary->kind, prefix_unary->location, - prefix_unary->cast_expr != &eval_expr_nil ? prefix_unary->cast_expr : atom, - prefix_unary->cast_expr != &eval_expr_nil ? atom : &eval_expr_nil, - &eval_expr_nil); - } - } - else if(atom == 0 && first_prefix_unary != 0) - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, last_prefix_unary->location, "Missing expression."); - } - else - { - for(PrefixUnaryNode *prefix_unary = first_prefix_unary; prefix_unary != 0; prefix_unary = prefix_unary->next) - { - atom = eval_expr(arena, prefix_unary->kind, prefix_unary->location, - prefix_unary->cast_expr != &eval_expr_nil ? prefix_unary->cast_expr : atom, - prefix_unary->cast_expr != &eval_expr_nil ? atom : &eval_expr_nil, - &eval_expr_nil); - } - } - - //- rjf: parse complex operators - if(atom != &eval_expr_nil) for(;it < it_opl;) - { - EVAL_Token *start_it = it; - EVAL_Token token = eval_token_at_it(it, tokens); - String8 token_string = str8_substr(text, token.range); - - //- rjf: parse binaries - { - // rjf: first try to find a matching binary operator - S64 binary_precedence = 0; - EVAL_ExprKind binary_kind = 0; - for(U64 idx = 0; idx < ArrayCount(eval_g_binary_op_table); idx += 1) - { - if(str8_match(token_string, eval_g_binary_op_table[idx].string, 0)) - { - binary_precedence = eval_g_binary_op_table[idx].precedence; - binary_kind = eval_g_binary_op_table[idx].kind; - break; - } - } - - // rjf: if we got a valid binary precedence, and it's not to be handled by - // a caller, then we need to parse the right-hand-side with a tighter - // precedence - if(binary_precedence != 0 && binary_precedence <= max_precedence) - { - EVAL_TokenArray rhs_expr_parse_tokens = eval_token_array_make_first_opl(it+1, it_opl); - EVAL_ParseResult rhs_expr_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &rhs_expr_parse_tokens, binary_precedence-1); - eval_error_list_concat_in_place(&result.errors, &rhs_expr_parse.errors); - EVAL_Expr *rhs = rhs_expr_parse.expr; - it = rhs_expr_parse.last_token; - if(rhs == &eval_expr_nil) - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Missing right-hand-side of %S.", token_string); - } - else - { - atom = eval_expr(arena, binary_kind, token_string.str, atom, rhs, &eval_expr_nil); - } - } - } - - //- rjf: parse ternaries - { - if(token.kind == EVAL_TokenKind_Symbol && str8_match(token_string, str8_lit("?"), 0) && 13 <= max_precedence) - { - // rjf: parse middle expression - EVAL_TokenArray middle_expr_tokens = eval_token_array_make_first_opl(it, it_opl); - EVAL_ParseResult middle_expr_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &middle_expr_tokens, eval_g_max_precedence); - it = middle_expr_parse.last_token; - EVAL_Expr *middle_expr = middle_expr_parse.expr; - eval_error_list_concat_in_place(&result.errors, &middle_expr_parse.errors); - if(middle_expr_parse.expr == &eval_expr_nil) - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Expected expression after ?."); - } - - // rjf: expect : - B32 got_colon = 0; - EVAL_Token colon_token = zero_struct; - String8 colon_token_string = {0}; - { - EVAL_Token colon_token_maybe = eval_token_at_it(it, tokens); - String8 colon_token_maybe_string = str8_substr(text, colon_token_maybe.range); - if(colon_token_maybe.kind != EVAL_TokenKind_Symbol || !str8_match(colon_token_maybe_string, str8_lit(":"), 0)) - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Expected : after ?."); - } - else - { - got_colon = 1; - colon_token = colon_token_maybe; - colon_token_string = colon_token_maybe_string; - it += 1; - } - } - - // rjf: parse rhs - EVAL_TokenArray rhs_expr_parse_tokens = eval_token_array_make_first_opl(it, it_opl); - EVAL_ParseResult rhs_expr_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &rhs_expr_parse_tokens, eval_g_max_precedence); - if(got_colon) - { - it = rhs_expr_parse.last_token; - eval_error_list_concat_in_place(&result.errors, &rhs_expr_parse.errors); - if(rhs_expr_parse.expr == &eval_expr_nil) - { - eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, colon_token_string.str, "Expected expression after :."); - } - } - - // rjf: build ternary - atom = eval_expr(arena, EVAL_ExprKind_Ternary, token_string.str, atom, middle_expr_parse.expr, rhs_expr_parse.expr); - } - } - - // rjf: if we parsed nothing successfully, we're done - if(it == start_it) - { - break; - } - } - - //- rjf: fill result & return - result.last_token = it; - result.expr = atom; - scratch_end(scratch); - ProfEnd(); - return result; -} - -internal EVAL_ParseResult -eval_parse_expr_from_text_tokens(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens) -{ - EVAL_ParseResult result = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, tokens, eval_g_max_precedence); - return result; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Tables + +global read_only String8 eval_g_multichar_symbol_strings[] = +{ + str8_lit_comp("<<"), + str8_lit_comp(">>"), + str8_lit_comp("->"), + str8_lit_comp("<="), + str8_lit_comp(">="), + str8_lit_comp("=="), + str8_lit_comp("!="), + str8_lit_comp("&&"), + str8_lit_comp("||"), +}; + +global read_only struct {EVAL_ExprKind kind; String8 string; S64 precedence;} eval_g_unary_prefix_op_table[] = +{ + // { EVAL_ExprKind_???, str8_lit_comp("+"), 2 }, + { EVAL_ExprKind_Neg, str8_lit_comp("-"), 2 }, + { EVAL_ExprKind_LogNot, str8_lit_comp("!"), 2 }, + { EVAL_ExprKind_Deref, str8_lit_comp("*"), 2 }, + { EVAL_ExprKind_Address,str8_lit_comp("&"), 2 }, + { EVAL_ExprKind_Sizeof, str8_lit_comp("sizeof"), 2 }, + // { EVAL_ExprKind_Alignof, str8_lit_comp("_Alignof"), 2 }, +}; + +global read_only struct {EVAL_ExprKind kind; String8 string; S64 precedence;} eval_g_binary_op_table[] = +{ + { EVAL_ExprKind_Mul, str8_lit_comp("*"), 3 }, + { EVAL_ExprKind_Div, str8_lit_comp("/"), 3 }, + { EVAL_ExprKind_Mod, str8_lit_comp("%"), 3 }, + { EVAL_ExprKind_Add, str8_lit_comp("+"), 4 }, + { EVAL_ExprKind_Sub, str8_lit_comp("-"), 4 }, + { EVAL_ExprKind_LShift, str8_lit_comp("<<"), 5 }, + { EVAL_ExprKind_RShift, str8_lit_comp(">>"), 5 }, + { EVAL_ExprKind_Less, str8_lit_comp("<"), 6 }, + { EVAL_ExprKind_LsEq, str8_lit_comp("<="), 6 }, + { EVAL_ExprKind_Grtr, str8_lit_comp(">"), 6 }, + { EVAL_ExprKind_GrEq, str8_lit_comp(">="), 6 }, + { EVAL_ExprKind_EqEq, str8_lit_comp("=="), 7 }, + { EVAL_ExprKind_NtEq, str8_lit_comp("!="), 7 }, + { EVAL_ExprKind_BitAnd, str8_lit_comp("&"), 8 }, + { EVAL_ExprKind_BitXor, str8_lit_comp("^"), 9 }, + { EVAL_ExprKind_BitOr, str8_lit_comp("|"), 10 }, + { EVAL_ExprKind_LogAnd, str8_lit_comp("&&"), 11 }, + { EVAL_ExprKind_LogOr, str8_lit_comp("||"), 12 }, + { EVAL_ExprKind_Define, str8_lit_comp("="), 13 }, +}; + +global read_only S64 eval_g_max_precedence = 15; + +//////////////////////////////// +//~ rjf: Map Building Fast Paths + +internal EVAL_String2NumMap * +eval_push_locals_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff) +{ + Temp scratch = scratch_begin(&arena, 1); + + //- rjf: gather scopes to walk + typedef struct Task Task; + struct Task + { + Task *next; + RDI_Scope *scope; + }; + Task *first_task = 0; + Task *last_task = 0; + + //- rjf: voff -> tightest scope + RDI_Scope *tightest_scope = 0; + { + U64 scope_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_ScopeVMap, voff); + RDI_Scope *scope = rdi_element_from_name_idx(rdi, Scopes, scope_idx); + Task *task = push_array(scratch.arena, Task, 1); + task->scope = scope; + SLLQueuePush(first_task, last_task, task); + tightest_scope = scope; + } + + //- rjf: voff-1 -> scope + if(voff > 0) + { + U64 scope_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_ScopeVMap, voff-1); + RDI_Scope *scope = rdi_element_from_name_idx(rdi, Scopes, scope_idx); + if(scope != tightest_scope) + { + Task *task = push_array(scratch.arena, Task, 1); + task->scope = scope; + SLLQueuePush(first_task, last_task, task); + } + } + + //- rjf: tightest scope -> walk up the tree & build tasks for each parent scope + if(tightest_scope != 0) + { + RDI_Scope *nil_scope = rdi_element_from_name_idx(rdi, Scopes, 0); + for(RDI_Scope *scope = rdi_element_from_name_idx(rdi, Scopes, tightest_scope->parent_scope_idx); + scope != 0 && scope != nil_scope; + scope = rdi_element_from_name_idx(rdi, Scopes, scope->parent_scope_idx)) + { + Task *task = push_array(scratch.arena, Task, 1); + task->scope = scope; + SLLQueuePush(first_task, last_task, task); + } + } + + //- rjf: build blank map + EVAL_String2NumMap *map = push_array(arena, EVAL_String2NumMap, 1); + *map = eval_string2num_map_make(arena, 1024); + + //- rjf: accumulate locals for all tasks + for(Task *task = first_task; task != 0; task = task->next) + { + RDI_Scope *scope = task->scope; + if(scope != 0) + { + U32 local_opl_idx = scope->local_first + scope->local_count; + for(U32 local_idx = scope->local_first; local_idx < local_opl_idx; local_idx += 1) + { + RDI_Local *local_var = rdi_element_from_name_idx(rdi, Locals, local_idx); + U64 local_name_size = 0; + U8 *local_name_str = rdi_string_from_idx(rdi, local_var->name_string_idx, &local_name_size); + String8 name = push_str8_copy(arena, str8(local_name_str, local_name_size)); + eval_string2num_map_insert(arena, map, name, (U64)local_idx+1); + } + } + } + + scratch_end(scratch); + return map; +} + +internal EVAL_String2NumMap * +eval_push_member_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff) +{ + //- rjf: voff -> tightest scope + U64 scope_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_ScopeVMap, voff); + RDI_Scope *tightest_scope = rdi_element_from_name_idx(rdi, Scopes, scope_idx); + + //- rjf: tightest scope -> procedure + U32 proc_idx = tightest_scope->proc_idx; + RDI_Procedure *procedure = rdi_element_from_name_idx(rdi, Procedures, proc_idx); + + //- rjf: procedure -> udt + U32 udt_idx = procedure->container_idx; + RDI_UDT *udt = rdi_element_from_name_idx(rdi, UDTs, udt_idx); + + //- rjf: build blank map + EVAL_String2NumMap *map = push_array(arena, EVAL_String2NumMap, 1); + *map = eval_string2num_map_make(arena, 64); + + //- rjf: udt -> fill member map + if(!(udt->flags & RDI_UDTFlag_EnumMembers)) + { + U64 data_member_num = 1; + for(U32 member_idx = udt->member_first; + member_idx < udt->member_first+udt->member_count; + member_idx += 1) + { + RDI_Member *m = rdi_element_from_name_idx(rdi, Members, member_idx); + if(m->kind == RDI_MemberKind_DataField) + { + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, m->name_string_idx, &name.size); + eval_string2num_map_insert(arena, map, name, data_member_num); + data_member_num += 1; + } + } + } + + return map; +} + +//////////////////////////////// +//~ rjf: Tokenization Functions + +internal EVAL_Token +eval_token_zero(void) +{ + EVAL_Token token = zero_struct; + return token; +} + +internal void +eval_token_chunk_list_push(Arena *arena, EVAL_TokenChunkList *list, U64 chunk_size, EVAL_Token *token) +{ + EVAL_TokenChunkNode *node = list->last; + if(node == 0 || node->count >= node->cap) + { + node = push_array(arena, EVAL_TokenChunkNode, 1); + SLLQueuePush(list->first, list->last, node); + node->cap = chunk_size; + node->v = push_array_no_zero(arena, EVAL_Token, node->cap); + list->node_count += 1; + } + MemoryCopyStruct(&node->v[node->count], token); + node->count += 1; + list->total_count += 1; +} + +internal EVAL_TokenArray +eval_token_array_from_chunk_list(Arena *arena, EVAL_TokenChunkList *list) +{ + EVAL_TokenArray array = {0}; + array.count = list->total_count; + array.v = push_array_no_zero(arena, EVAL_Token, array.count); + U64 idx = 0; + for(EVAL_TokenChunkNode *node = list->first; node != 0; node = node->next) + { + MemoryCopy(array.v+idx, node->v, sizeof(EVAL_Token)*node->count); + } + return array; +} + +internal EVAL_TokenArray +eval_token_array_from_text(Arena *arena, String8 text) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(&arena, 1); + + //- rjf: lex loop + EVAL_TokenChunkList tokens = {0}; + U64 active_token_start_idx = 0; + EVAL_TokenKind active_token_kind = EVAL_TokenKind_Null; + B32 active_token_kind_started_with_tick = 0; + B32 escaped = 0; + for(U64 idx = 0, advance = 0; idx <= text.size; idx += advance) + { + U8 byte = (idx+0 < text.size) ? text.str[idx+0] : 0; + U8 byte_next = (idx+1 < text.size) ? text.str[idx+1] : 0; + U8 byte_next2= (idx+2 < text.size) ? text.str[idx+2] : 0; + advance = 1; + B32 token_formed = 0; + U64 token_end_idx_pad = 0; + switch(active_token_kind) + { + //- rjf: no active token -> seek token starter + default: + { + if(char_is_alpha(byte) || byte == '_' || byte == '`' || byte == '$') + { + active_token_kind = EVAL_TokenKind_Identifier; + active_token_start_idx = idx; + active_token_kind_started_with_tick = (byte == '`'); + } + else if(char_is_digit(byte, 10) || (byte == '.' && char_is_digit(byte_next, 10))) + { + active_token_kind = EVAL_TokenKind_Numeric; + active_token_start_idx = idx; + } + else if(byte == '"') + { + active_token_kind = EVAL_TokenKind_StringLiteral; + active_token_start_idx = idx; + } + else if(byte == '\'') + { + active_token_kind = EVAL_TokenKind_CharLiteral; + active_token_start_idx = idx; + } + else if(byte == '~' || byte == '!' || byte == '%' || byte == '^' || + byte == '&' || byte == '*' || byte == '(' || byte == ')' || + byte == '-' || byte == '=' || byte == '+' || byte == '[' || + byte == ']' || byte == '{' || byte == '}' || byte == ':' || + byte == ';' || byte == ',' || byte == '.' || byte == '<' || + byte == '>' || byte == '/' || byte == '?' || byte == '|') + { + active_token_kind = EVAL_TokenKind_Symbol; + active_token_start_idx = idx; + } + }break; + + //- rjf: active tokens -> seek enders + case EVAL_TokenKind_Identifier: + { + if(byte == ':' && byte_next == ':' && (char_is_alpha(byte_next2) || byte_next2 == '_' || byte_next2 == '<')) + { + // NOTE(rjf): encountering C++-style namespaces - skip over scope resolution symbol + // & keep going. + advance = 2; + } + else if((byte == '\'' || byte == '`') && active_token_kind_started_with_tick) + { + // NOTE(rjf): encountering ` -> ' or ` -> ` style identifier escapes + active_token_kind_started_with_tick = 0; + advance = 1; + } + else if(byte == '<') + { + // NOTE(rjf): encountering C++-style templates - try to find ender. if no ender found, + // assume this is an operator & just consume the identifier part. + S64 nest = 1; + for(U64 idx2 = idx+1; idx2 <= text.size; idx2 += 1) + { + if(idx2 < text.size && text.str[idx2] == '<') + { + nest += 1; + } + else if(idx2 < text.size && text.str[idx2] == '>') + { + nest -= 1; + if(nest == 0) + { + advance = (idx2+1-idx); + break; + } + } + else if(idx2 == text.size && nest != 0) + { + token_formed = 1; + advance = 0; + break; + } + } + } + else if(!char_is_alpha(byte) && !char_is_digit(byte, 10) && byte != '_' && !active_token_kind_started_with_tick && byte != '@' && byte != '$') + { + advance = 0; + token_formed = 1; + } + }break; + case EVAL_TokenKind_Numeric: + { + if(!char_is_alpha(byte) && !char_is_digit(byte, 10) && byte != '.') + { + advance = 0; + token_formed = 1; + } + }break; + case EVAL_TokenKind_StringLiteral: + { + if(escaped == 0 && byte == '\\') + { + escaped = 1; + } + else if(escaped) + { + escaped = 0; + } + else if(escaped == 0 && byte == '"') + { + advance = 1; + token_formed = 1; + token_end_idx_pad = 1; + } + }break; + case EVAL_TokenKind_CharLiteral: + { + if(escaped == 0 && byte == '\\') + { + escaped = 1; + } + else if(escaped) + { + escaped = 0; + } + else if(escaped == 0 && byte == '\'') + { + advance = 1; + token_formed = 1; + token_end_idx_pad = 1; + } + }break; + case EVAL_TokenKind_Symbol: + { + if(byte != '~' && byte != '!' && byte != '%' && byte != '^' && + byte != '&' && byte != '*' && byte != '(' && byte != ')' && + byte != '-' && byte != '=' && byte != '+' && byte != '[' && + byte != ']' && byte != '{' && byte != '}' && byte != ':' && + byte != ';' && byte != ',' && byte != '.' && byte != '<' && + byte != '>' && byte != '/' && byte != '?' && byte != '|') + { + advance = 0; + token_formed = 1; + } + }break; + } + + //- rjf: token formed -> push new formed token(s) + if(token_formed) + { + // rjf: non-symbols *or* symbols of only 1-length can be immediately + // pushed as a token + if(active_token_kind != EVAL_TokenKind_Symbol || idx==active_token_start_idx+1) + { + EVAL_Token token = {active_token_kind, r1u64(active_token_start_idx, idx+token_end_idx_pad)}; + eval_token_chunk_list_push(scratch.arena, &tokens, 256, &token); + } + + // rjf: symbolic strings matching `--` mean the remainder of the string + // is reserved for external usage. the rest of the stream should not + // be tokenized. + else if(idx == active_token_start_idx+2 && text.str[active_token_start_idx] == '-' && text.str[active_token_start_idx+1] == '-') + { + break; + } + + // rjf: if we got a symbol string of N>1 characters, then we need to + // apply the maximum-munch rule, and produce M<=N tokens, where each + // formed token is the maximum size possible, given the legal + // >1-length symbol strings. + else + { + U64 advance2 = 0; + for(U64 idx2 = active_token_start_idx; idx2 < idx; idx2 += advance2) + { + advance2 = 1; + for(U64 multichar_symbol_idx = 0; + multichar_symbol_idx < ArrayCount(eval_g_multichar_symbol_strings); + multichar_symbol_idx += 1) + { + String8 multichar_symbol_string = eval_g_multichar_symbol_strings[multichar_symbol_idx]; + String8 part_of_token = str8_substr(text, r1u64(idx2, idx2+multichar_symbol_string.size)); + if(str8_match(part_of_token, multichar_symbol_string, 0)) + { + advance2 = multichar_symbol_string.size; + break; + } + } + EVAL_Token token = {active_token_kind, r1u64(idx2, idx2+advance2)}; + eval_token_chunk_list_push(scratch.arena, &tokens, 256, &token); + } + } + + // rjf: reset for subsequent tokens. + active_token_kind = EVAL_TokenKind_Null; + } + } + + //- rjf: chunk list -> array & return + EVAL_TokenArray array = eval_token_array_from_chunk_list(arena, &tokens); + scratch_end(scratch); + ProfEnd(); + return array; +} + +internal EVAL_TokenArray +eval_token_array_make_first_opl(EVAL_Token *first, EVAL_Token *opl) +{ + EVAL_TokenArray array = {first, (U64)(opl-first)}; + return array; +} + +//////////////////////////////// +//~ rjf: Parser Functions + +internal TG_Key +eval_leaf_type_from_name(RDI_Parsed *rdi, String8 name) +{ + TG_Key key = zero_struct; + B32 found = 0; + { + RDI_NameMap *name_map = rdi_element_from_name_idx(rdi, NameMaps, RDI_NameMapKind_Types); + RDI_ParsedNameMap parsed_name_map = {0}; + rdi_parsed_from_name_map(rdi, name_map, &parsed_name_map); + RDI_NameMapNode *node = rdi_name_map_lookup(rdi, &parsed_name_map, name.str, name.size); + if(node != 0) + { + U32 match_count = 0; + U32 *matches = rdi_matches_from_map_node(rdi, node, &match_count); + if(match_count != 0) + { + RDI_TypeNode *type_node = rdi_element_from_name_idx(rdi, TypeNodes, matches[0]); + found = type_node->kind != RDI_TypeKind_NULL; + key = tg_key_ext(tg_kind_from_rdi_type_kind(type_node->kind), (U64)matches[0]); + } + } + } + if(!found) + { +#define Case(str) (str8_match(name, str8_lit(str), 0)) + if(Case("u8") || Case("uint8") || Case("uint8_t") || Case("uchar8") || Case("U8")) + { + key = tg_key_basic(TG_Kind_U8); + } + else if(Case("u16") || Case("uint16") || Case("uint16_t") || Case("uchar16") || Case("U16")) + { + key = tg_key_basic(TG_Kind_U16); + } + else if(Case("u32") || Case("uint32") || Case("uint32_t") || Case("uchar32") || Case("U32") || Case("uint")) + { + key = tg_key_basic(TG_Kind_U32); + } + else if(Case("u64") || Case("uint64") || Case("uint64_t") || Case("U64")) + { + key = tg_key_basic(TG_Kind_U64); + } + else if(Case("s8") || Case("b8") || Case("B8") || Case("i8") || Case("int8") || Case("int8_t") || Case("char8") || Case("S8")) + { + key = tg_key_basic(TG_Kind_S8); + } + else if(Case("s16") ||Case("b16") || Case("B16") || Case("i16") || Case("int16") || Case("int16_t") || Case("char16") || Case("S16")) + { + key = tg_key_basic(TG_Kind_S16); + } + else if(Case("s32") || Case("b32") || Case("B32") || Case("i32") || Case("int32") || Case("int32_t") || Case("char32") || Case("S32") || Case("int")) + { + key = tg_key_basic(TG_Kind_S32); + } + else if(Case("s64") || Case("b64") || Case("B64") || Case("i64") || Case("int64") || Case("int64_t") || Case("S64")) + { + key = tg_key_basic(TG_Kind_S64); + } + else if(Case("void")) + { + key = tg_key_basic(TG_Kind_Void); + } + else if(Case("bool")) + { + key = tg_key_basic(TG_Kind_Bool); + } + else if(Case("float") || Case("f32") || Case("F32") || Case("r32") || Case("R32")) + { + key = tg_key_basic(TG_Kind_F32); + } + else if(Case("double") || Case("f64") || Case("F64") || Case("r64") || Case("R64")) + { + key = tg_key_basic(TG_Kind_F64); + } +#undef Case + } + return key; +} + +internal EVAL_ParseResult +eval_parse_type_from_text_tokens(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens) +{ + EVAL_ParseResult parse = eval_parse_result_nil; + EVAL_Token *token_it = tokens->v; + + //- rjf: parse unsigned marker + B32 unsigned_marker = 0; + { + EVAL_Token token = eval_token_at_it(token_it, tokens); + if(token.kind == EVAL_TokenKind_Identifier) + { + String8 token_string = str8_substr(text, token.range); + if(str8_match(token_string, str8_lit("unsigned"), 0)) + { + token_it += 1; + unsigned_marker = 1; + } + } + } + + //- rjf: parse base type + { + EVAL_Token token = eval_token_at_it(token_it, tokens); + if(token.kind == EVAL_TokenKind_Identifier) + { + String8 token_string = str8_substr(text, token.range); + TG_Key type_key = eval_leaf_type_from_name(ctx->rdi, token_string); + if(!tg_key_match(tg_key_zero(), type_key)) + { + token_it += 1; + + // rjf: apply unsigned marker to base type + if(unsigned_marker) switch(tg_kind_from_key(type_key)) + { + default:{}break; + case TG_Kind_Char8: {type_key = tg_key_basic(TG_Kind_UChar8);}break; + case TG_Kind_Char16:{type_key = tg_key_basic(TG_Kind_UChar16);}break; + case TG_Kind_Char32:{type_key = tg_key_basic(TG_Kind_UChar32);}break; + case TG_Kind_S8: {type_key = tg_key_basic(TG_Kind_U8);}break; + case TG_Kind_S16: {type_key = tg_key_basic(TG_Kind_U16);}break; + case TG_Kind_S32: {type_key = tg_key_basic(TG_Kind_U32);}break; + case TG_Kind_S64: {type_key = tg_key_basic(TG_Kind_U64);}break; + case TG_Kind_S128:{type_key = tg_key_basic(TG_Kind_U128);}break; + case TG_Kind_S256:{type_key = tg_key_basic(TG_Kind_U256);}break; + case TG_Kind_S512:{type_key = tg_key_basic(TG_Kind_U512);}break; + } + + // rjf: construct leaf type + parse.expr = eval_expr_leaf_type(arena, token_string.str, type_key); + } + } + } + + //- rjf: parse extensions + if(parse.expr != &eval_expr_nil) + { + for(;;) + { + EVAL_Token token = eval_token_at_it(token_it, tokens); + if(token.kind != EVAL_TokenKind_Symbol) + { + break; + } + String8 token_string = str8_substr(text, token.range); + if(str8_match(token_string, str8_lit("*"), 0)) + { + token_it += 1; + parse.expr = eval_expr(arena, EVAL_ExprKind_Ptr, token_string.str, parse.expr, &eval_expr_nil, &eval_expr_nil); + } + else + { + break; + } + } + } + + //- rjf: fill parse & end + parse.last_token = token_it; + return parse; +} + +internal EVAL_ParseResult +eval_parse_expr_from_text_tokens__prec(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens, S64 max_precedence) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(&arena, 1); + EVAL_Token *it = tokens->v; + EVAL_Token *it_opl = tokens->v + tokens->count; + EVAL_ParseResult result = eval_parse_result_nil; + + //- rjf: parse prefix unaries + typedef struct PrefixUnaryNode PrefixUnaryNode; + struct PrefixUnaryNode + { + PrefixUnaryNode *next; + EVAL_ExprKind kind; + EVAL_Expr *cast_expr; + void *location; + }; + PrefixUnaryNode *first_prefix_unary = 0; + PrefixUnaryNode *last_prefix_unary = 0; + { + for(;it < it_opl;) + { + EVAL_Token *start_it = it; + EVAL_Token token = eval_token_at_it(it, tokens); + String8 token_string = str8_substr(text, token.range); + S64 prefix_unary_precedence = 0; + EVAL_ExprKind prefix_unary_kind = 0; + EVAL_Expr *cast_expr = &eval_expr_nil; + void *location = 0; + + // rjf: try op table + for(U64 idx = 0; idx < ArrayCount(eval_g_unary_prefix_op_table); idx += 1) + { + if(str8_match(token_string, eval_g_unary_prefix_op_table[idx].string, 0)) + { + prefix_unary_precedence = eval_g_unary_prefix_op_table[idx].precedence; + prefix_unary_kind = eval_g_unary_prefix_op_table[idx].kind; + break; + } + } + + // rjf: consume valid op + if(prefix_unary_precedence != 0) + { + location = token_string.str; + it += 1; + } + + // rjf: try casting expression + if(prefix_unary_precedence == 0 && str8_match(token_string, str8_lit("("), 0)) + { + EVAL_Token some_type_identifier_maybe = eval_token_at_it(it+1, tokens); + String8 some_type_identifier_maybe_string = str8_substr(text, some_type_identifier_maybe.range); + if(some_type_identifier_maybe.kind == EVAL_TokenKind_Identifier) + { + TG_Key type_key = eval_leaf_type_from_name(ctx->rdi, some_type_identifier_maybe_string); + if(!tg_key_match(type_key, tg_key_zero()) || str8_match(some_type_identifier_maybe_string, str8_lit("unsigned"), 0)) + { + // rjf: move past open paren + it += 1; + + // rjf: parse type expr + EVAL_TokenArray type_parse_tokens = eval_token_array_make_first_opl(it, it_opl); + EVAL_ParseResult type_parse = eval_parse_type_from_text_tokens(arena, ctx, text, &type_parse_tokens); + EVAL_Expr *type = type_parse.expr; + eval_error_list_concat_in_place(&result.errors, &type_parse.errors); + it = type_parse.last_token; + location = token_string.str; + + // rjf: expect ) + EVAL_Token close_paren_maybe = eval_token_at_it(it, tokens); + String8 close_paren_maybe_string = str8_substr(text, close_paren_maybe.range); + if(close_paren_maybe.kind != EVAL_TokenKind_Symbol || !str8_match(close_paren_maybe_string, str8_lit(")"), 0)) + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Missing )."); + } + + // rjf: consume ) + else + { + it += 1; + } + + // rjf: fill + prefix_unary_precedence = 2; + prefix_unary_kind = EVAL_ExprKind_Cast; + cast_expr = type; + } + } + } + + // rjf: break if we got no operators + if(prefix_unary_precedence == 0) + { + break; + } + + // rjf: break if the token node iterator has not changed + if(it == start_it) + { + break; + } + + // rjf: push prefix unary if we got one + { + PrefixUnaryNode *op_n = push_array(scratch.arena, PrefixUnaryNode, 1); + op_n->kind = prefix_unary_kind; + op_n->cast_expr = cast_expr; + op_n->location = location; + SLLQueuePushFront(first_prefix_unary, last_prefix_unary, op_n); + } + } + } + + //- rjf: parse atom + EVAL_Expr *atom = &eval_expr_nil; + String8 atom_implicit_member_name = {0}; + if(it < it_opl) + { + EVAL_Token token = eval_token_at_it(it, tokens); + String8 token_string = str8_substr(text, token.range); + + //- rjf: descent to nested expression + if(token.kind == EVAL_TokenKind_Symbol && str8_match(token_string, str8_lit("("), 0)) + { + // rjf: skip ( + it += 1; + + // rjf: parse () contents + EVAL_TokenArray nested_parse_tokens = eval_token_array_make_first_opl(it, it_opl); + EVAL_ParseResult nested_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &nested_parse_tokens, eval_g_max_precedence); + eval_error_list_concat_in_place(&result.errors, &nested_parse.errors); + atom = nested_parse.expr; + it = nested_parse.last_token; + + // rjf: expect ) + EVAL_Token close_paren_maybe = eval_token_at_it(it, tokens); + String8 close_paren_maybe_string = str8_substr(text, close_paren_maybe.range); + if(close_paren_maybe.kind != EVAL_TokenKind_Symbol || !str8_match(close_paren_maybe_string, str8_lit(")"), 0)) + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Missing )."); + } + + // rjf: consume ) + else + { + it += 1; + } + } + + //- rjf: descent to assembly-style dereference sub-expression + else if(token.kind == EVAL_TokenKind_Symbol && str8_match(token_string, str8_lit("["), 0)) + { + // rjf: skip [ + it += 1; + + // rjf: parse [] contents + EVAL_TokenArray nested_parse_tokens = eval_token_array_make_first_opl(it, it_opl); + EVAL_ParseResult nested_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &nested_parse_tokens, eval_g_max_precedence); + eval_error_list_concat_in_place(&result.errors, &nested_parse.errors); + atom = nested_parse.expr; + it = nested_parse.last_token; + + // rjf: build cast-to-U64*, and dereference operators + atom = eval_expr(arena, EVAL_ExprKind_Cast, token_string.str, eval_expr_leaf_type(arena, token_string.str, tg_cons_type_make(ctx->type_graph, TG_Kind_Ptr, tg_key_basic(TG_Kind_U64), 0)), atom, &eval_expr_nil); + atom = eval_expr(arena, EVAL_ExprKind_Deref, token_string.str, atom, &eval_expr_nil, &eval_expr_nil); + + // rjf: expect ] + EVAL_Token close_paren_maybe = eval_token_at_it(it, tokens); + String8 close_paren_maybe_string = str8_substr(text, close_paren_maybe.range); + if(close_paren_maybe.kind != EVAL_TokenKind_Symbol || !str8_match(close_paren_maybe_string, str8_lit("]"), 0)) + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Missing ]."); + } + + // rjf: consume ) + else + { + it += 1; + } + } + + //- rjf: leaf (identifier, literal) + else if(token.kind != EVAL_TokenKind_Symbol) + { + switch(token.kind) + { + //- rjf: identifier => name resolution + default: + case EVAL_TokenKind_Identifier: + { + B32 mapped_identifier = 0; + B32 identifier_type_is_possibly_dynamically_overridden = 0; + B32 identifier_looks_like_type_expr = 0; + RDI_LocationKind loc_kind = RDI_LocationKind_NULL; + RDI_LocationReg loc_reg = {0}; + RDI_LocationRegPlusU16 loc_reg_u16 = {0}; + String8 loc_bytecode = {0}; + REGS_RegCode reg_code = 0; + REGS_AliasCode alias_code = 0; + TG_Key type_key = zero_struct; + String8 local_lookup_string = token_string; + + //- rjf: form namespaceified fallback versions of this lookup string + String8List namespaceified_token_strings = {0}; + { + U64 scope_idx = rdi_vmap_idx_from_section_kind_voff(ctx->rdi, RDI_SectionKind_ScopeVMap, ctx->ip_voff); + RDI_Scope *scope = rdi_element_from_name_idx(ctx->rdi, Scopes, scope_idx); + U64 proc_idx = scope->proc_idx; + RDI_Procedure *procedure = rdi_element_from_name_idx(ctx->rdi, Procedures, proc_idx); + U64 name_size = 0; + U8 *name_ptr = rdi_string_from_idx(ctx->rdi, procedure->name_string_idx, &name_size); + String8 containing_procedure_name = str8(name_ptr, name_size); + U64 last_past_scope_resolution_pos = 0; + for(;;) + { + U64 past_next_dbl_colon_pos = str8_find_needle(containing_procedure_name, last_past_scope_resolution_pos, str8_lit("::"), 0)+2; + U64 past_next_dot_pos = str8_find_needle(containing_procedure_name, last_past_scope_resolution_pos, str8_lit("."), 0)+1; + U64 past_next_scope_resolution_pos = Min(past_next_dbl_colon_pos, past_next_dot_pos); + if(past_next_scope_resolution_pos >= containing_procedure_name.size) + { + break; + } + String8 new_namespace_prefix_possibility = str8_prefix(containing_procedure_name, past_next_scope_resolution_pos); + String8 namespaceified_token_string = push_str8f(scratch.arena, "%S%S", new_namespace_prefix_possibility, token_string); + str8_list_push_front(scratch.arena, &namespaceified_token_strings, namespaceified_token_string); + last_past_scope_resolution_pos = past_next_scope_resolution_pos; + } + } + + //- rjf: try members + if(mapped_identifier == 0) + { + U64 data_member_num = eval_num_from_string(ctx->member_map, token_string); + if(data_member_num != 0) + { + atom_implicit_member_name = token_string; + local_lookup_string = str8_lit("this"); + } + } + + //- rjf: try locals + if(mapped_identifier == 0) + { + U64 local_num = eval_num_from_string(ctx->locals_map, local_lookup_string); + if(local_num != 0) + { + mapped_identifier = 1; + identifier_type_is_possibly_dynamically_overridden = 1; + RDI_Local *local_var = rdi_element_from_name_idx(ctx->rdi, Locals, local_num-1); + RDI_TypeNode *type_node = rdi_element_from_name_idx(ctx->rdi, TypeNodes, local_var->type_idx); + type_key = tg_key_ext(tg_kind_from_rdi_type_kind(type_node->kind), (U64)local_var->type_idx); + + // rjf: grab location info + for(U32 loc_block_idx = local_var->location_first; + loc_block_idx < local_var->location_opl; + loc_block_idx += 1) + { + RDI_LocationBlock *block = rdi_element_from_name_idx(ctx->rdi, LocationBlocks, loc_block_idx); + if(block->scope_off_first <= ctx->ip_voff && ctx->ip_voff < block->scope_off_opl) + { + U64 all_location_data_size = 0; + U8 *all_location_data = rdi_table_from_name(ctx->rdi, LocationData, &all_location_data_size); + loc_kind = *((RDI_LocationKind *)(all_location_data + block->location_data_off)); + switch(loc_kind) + { + default:{mapped_identifier = 0;}break; + case RDI_LocationKind_AddrBytecodeStream: + case RDI_LocationKind_ValBytecodeStream: + { + U8 *bytecode_base = all_location_data + block->location_data_off + sizeof(RDI_LocationKind); + U64 bytecode_size = 0; + for(U64 idx = 0; idx < all_location_data_size; idx += 1) + { + U8 op = bytecode_base[idx]; + if(op == 0) + { + break; + } + U8 ctrlbits = rdi_eval_op_ctrlbits_table[op]; + U32 p_size = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); + bytecode_size += 1+p_size; + } + loc_bytecode = str8(bytecode_base, bytecode_size); + }break; + case RDI_LocationKind_AddrRegPlusU16: + case RDI_LocationKind_AddrAddrRegPlusU16: + { + MemoryCopy(&loc_reg_u16, (all_location_data + block->location_data_off), sizeof(loc_reg_u16)); + }break; + case RDI_LocationKind_ValReg: + { + MemoryCopy(&loc_reg, (all_location_data + block->location_data_off), sizeof(loc_reg)); + }break; + } + } + } + } + } + + //- rjf: try registers + if(mapped_identifier == 0) + { + U64 reg_num = eval_num_from_string(ctx->regs_map, token_string); + if(reg_num != 0) + { + reg_code = reg_num; + mapped_identifier = 1; + type_key = tg_key_reg(ctx->arch, reg_code); + } + } + + //- rjf: try register aliases + if(mapped_identifier == 0) + { + U64 alias_num = eval_num_from_string(ctx->reg_alias_map, token_string); + if(alias_num != 0) + { + alias_code = (REGS_AliasCode)alias_num; + type_key = tg_key_reg_alias(ctx->arch, alias_code); + mapped_identifier = 1; + } + } + + //- rjf: try global variables + if(mapped_identifier == 0) + { + RDI_NameMap *name_map = rdi_element_from_name_idx(ctx->rdi, NameMaps, RDI_NameMapKind_GlobalVariables); + RDI_ParsedNameMap parsed_name_map = {0}; + rdi_parsed_from_name_map(ctx->rdi, name_map, &parsed_name_map); + RDI_NameMapNode *node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, token_string.str, token_string.size); + U32 matches_count = 0; + U32 *matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); + for(String8Node *n = namespaceified_token_strings.first; + n != 0 && matches_count == 0; + n = n->next) + { + node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, n->string.str, n->string.size); + matches_count = 0; + matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); + } + if(matches_count != 0) + { + // NOTE(rjf): apparently, PDBs can be produced such that they + // also keep stale *GLOBAL VARIABLE SYMBOLS* around too. I + // don't know of a magic hash table fixup path in PDBs, so + // in this case, I'm going to prefer the latest-added global. + U32 match_idx = matches[matches_count-1]; + RDI_GlobalVariable *global_var = rdi_element_from_name_idx(ctx->rdi, GlobalVariables, match_idx); + EVAL_OpList oplist = {0}; + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_ModuleOff, global_var->voff); + loc_kind = RDI_LocationKind_AddrBytecodeStream; + loc_bytecode = eval_bytecode_from_oplist(arena, &oplist); + U32 type_idx = global_var->type_idx; + RDI_TypeNode *type_node = rdi_element_from_name_idx(ctx->rdi, TypeNodes, type_idx); + type_key = tg_key_ext(tg_kind_from_rdi_type_kind(type_node->kind), (U64)type_idx); + mapped_identifier = 1; + } + } + + //- rjf: try thread variables + if(mapped_identifier == 0) + { + RDI_NameMap *name_map = rdi_element_from_name_idx(ctx->rdi, NameMaps, RDI_NameMapKind_ThreadVariables); + RDI_ParsedNameMap parsed_name_map = {0}; + rdi_parsed_from_name_map(ctx->rdi, name_map, &parsed_name_map); + RDI_NameMapNode *node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, token_string.str, token_string.size); + U32 matches_count = 0; + U32 *matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); + for(String8Node *n = namespaceified_token_strings.first; + n != 0 && matches_count == 0; + n = n->next) + { + node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, n->string.str, n->string.size); + matches_count = 0; + matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); + } + if(matches_count != 0) + { + U32 match_idx = matches[0]; + RDI_ThreadVariable *thread_var = rdi_element_from_name_idx(ctx->rdi, ThreadVariables, match_idx); + EVAL_OpList oplist = {0}; + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_TLSOff, thread_var->tls_off); + loc_kind = RDI_LocationKind_AddrBytecodeStream; + loc_bytecode = eval_bytecode_from_oplist(arena, &oplist); + U32 type_idx = thread_var->type_idx; + RDI_TypeNode *type_node = rdi_element_from_name_idx(ctx->rdi, TypeNodes, type_idx); + type_key = tg_key_ext(tg_kind_from_rdi_type_kind(type_node->kind), (U64)type_idx); + mapped_identifier = 1; + } + } + + //- rjf: try procedures + if(mapped_identifier == 0) + { + RDI_NameMap *name_map = rdi_element_from_name_idx(ctx->rdi, NameMaps, RDI_NameMapKind_Procedures); + RDI_ParsedNameMap parsed_name_map = {0}; + rdi_parsed_from_name_map(ctx->rdi, name_map, &parsed_name_map); + RDI_NameMapNode *node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, token_string.str, token_string.size); + U32 matches_count = 0; + U32 *matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); + for(String8Node *n = namespaceified_token_strings.first; + n != 0 && matches_count == 0; + n = n->next) + { + node = rdi_name_map_lookup(ctx->rdi, &parsed_name_map, n->string.str, n->string.size); + matches_count = 0; + matches = rdi_matches_from_map_node(ctx->rdi, node, &matches_count); + } + if(matches_count != 0) + { + U32 match_idx = matches[0]; + RDI_Procedure *procedure = rdi_element_from_name_idx(ctx->rdi, Procedures, match_idx); + RDI_Scope *scope = rdi_element_from_name_idx(ctx->rdi, Scopes, procedure->root_scope_idx); + U64 voff = *rdi_element_from_name_idx(ctx->rdi, ScopeVOffData, scope->voff_range_first); + EVAL_OpList oplist = {0}; + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_ModuleOff, voff); + loc_kind = RDI_LocationKind_ValBytecodeStream; + loc_bytecode = eval_bytecode_from_oplist(arena, &oplist); + U32 type_idx = procedure->type_idx; + RDI_TypeNode *type_node = rdi_element_from_name_idx(ctx->rdi, TypeNodes, type_idx); + type_key = tg_key_ext(tg_kind_from_rdi_type_kind(type_node->kind), (U64)type_idx); + mapped_identifier = 1; + } + } + + //- rjf: try types + if(mapped_identifier == 0) + { + type_key = eval_leaf_type_from_name(ctx->rdi, token_string); + if(!tg_key_match(tg_key_zero(), type_key)) + { + mapped_identifier = 1; + identifier_looks_like_type_expr = 1; + } + } + + //- rjf: attach on map + if(mapped_identifier != 0) + { + it += 1; + + // rjf: build atom + switch(loc_kind) + { + default: + { + if(identifier_looks_like_type_expr) + { + EVAL_TokenArray type_parse_tokens = eval_token_array_make_first_opl(it-1, it_opl); + EVAL_ParseResult type_parse = eval_parse_type_from_text_tokens(arena, ctx, text, &type_parse_tokens); + EVAL_Expr *type = type_parse.expr; + eval_error_list_concat_in_place(&result.errors, &type_parse.errors); + it = type_parse.last_token; + atom = type; + } + else if(reg_code != 0) + { + REGS_Rng reg_rng = regs_reg_code_rng_table_from_architecture(ctx->arch)[reg_code]; + EVAL_OpList oplist = {0}; + eval_oplist_push_uconst(arena, &oplist, reg_rng.byte_off); + atom = eval_expr_leaf_op_list(arena, token_string.str, type_key, &oplist, EVAL_EvalMode_Reg); + } + else if(alias_code != 0) + { + REGS_Slice alias_slice = regs_alias_code_slice_table_from_architecture(ctx->arch)[alias_code]; + REGS_Rng alias_reg_rng = regs_reg_code_rng_table_from_architecture(ctx->arch)[alias_slice.code]; + EVAL_OpList oplist = {0}; + eval_oplist_push_uconst(arena, &oplist, alias_reg_rng.byte_off + alias_slice.byte_off); + atom = eval_expr_leaf_op_list(arena, token_string.str, type_key, &oplist, EVAL_EvalMode_Reg); + } + else + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MissingInfo, token_string.str, "Missing location information for \"%S\".", token_string); + } + }break; + case RDI_LocationKind_AddrBytecodeStream: + { + atom = eval_expr_leaf_bytecode(arena, token_string.str, type_key, loc_bytecode, EVAL_EvalMode_Addr); + }break; + case RDI_LocationKind_ValBytecodeStream: + { + atom = eval_expr_leaf_bytecode(arena, token_string.str, type_key, loc_bytecode, EVAL_EvalMode_Value); + }break; + case RDI_LocationKind_AddrRegPlusU16: + { + EVAL_OpList oplist = {0}; + U64 byte_size = bit_size_from_arch(ctx->arch)/8; + U64 regread_param = RDI_EncodeRegReadParam(loc_reg_u16.reg_code, byte_size, 0); + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_RegRead, regread_param); + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_ConstU16, loc_reg_u16.offset); + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_Add, 0); + atom = eval_expr_leaf_op_list(arena, token_string.str, type_key, &oplist, EVAL_EvalMode_Addr); + }break; + case RDI_LocationKind_AddrAddrRegPlusU16: + { + EVAL_OpList oplist = {0}; + U64 byte_size = bit_size_from_arch(ctx->arch)/8; + U64 regread_param = RDI_EncodeRegReadParam(loc_reg_u16.reg_code, byte_size, 0); + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_RegRead, regread_param); + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_ConstU16, loc_reg_u16.offset); + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_Add, 0); + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_MemRead, bit_size_from_arch(ctx->arch)/8); + atom = eval_expr_leaf_op_list(arena, token_string.str, type_key, &oplist, EVAL_EvalMode_Addr); + }break; + case RDI_LocationKind_ValReg: + { + REGS_RegCode regs_reg_code = regs_reg_code_from_arch_rdi_code(ctx->arch, loc_reg.reg_code); + REGS_Rng reg_rng = regs_reg_code_rng_table_from_architecture(ctx->arch)[regs_reg_code]; + EVAL_OpList oplist = {0}; + U64 byte_size = (U64)reg_rng.byte_size; + U64 byte_pos = 0; + U64 regread_param = RDI_EncodeRegReadParam(loc_reg.reg_code, byte_size, byte_pos); + eval_oplist_push_op(arena, &oplist, RDI_EvalOp_RegRead, regread_param); + atom = eval_expr_leaf_op_list(arena, token_string.str, type_key, &oplist, EVAL_EvalMode_Value); + }break; + } + + // rjf: implicit local lookup -> attach member access node + if(atom_implicit_member_name.size != 0) + { + EVAL_Expr *member_expr = eval_expr_leaf_member(arena, atom_implicit_member_name.str, atom_implicit_member_name); + atom = eval_expr(arena, EVAL_ExprKind_MemberAccess, atom_implicit_member_name.str, atom, member_expr, &eval_expr_nil); + } + } + + //- rjf: map failure -> attach as leaf identifier, to be resolved later + if(!mapped_identifier) + { + atom = eval_expr_leaf_ident(arena, token_string.str, token_string); + it += 1; + } + }break; + + //- rjf: numeric => directly extract value + case EVAL_TokenKind_Numeric: + { + U64 dot_pos = str8_find_needle(token_string, 0, str8_lit("."), 0); + it += 1; + + // rjf: no . => integral + if(dot_pos == token_string.size) + { + U64 val = 0; + try_u64_from_str8_c_rules(token_string, &val); + atom = eval_expr_u64(arena, token_string.str, val); + break; + } + + // rjf: presence of . => double or float + if(dot_pos < token_string.size) + { + F64 val = f64_from_str8(token_string); + U64 f_pos = str8_find_needle(token_string, 0, str8_lit("f"), StringMatchFlag_CaseInsensitive); + + // rjf: presence of f after . => f32 + if(f_pos < token_string.size) + { + atom = eval_expr_f32(arena, token_string.str, (F32)val); + } + + // rjf: no f => f64 + else + { + atom = eval_expr_f64(arena, token_string.str, val); + } + } + }break; + + //- rjf: char => extract char value + case EVAL_TokenKind_CharLiteral: + { + it += 1; + if(token_string.size > 1 && token_string.str[0] == '\'' && token_string.str[1] != '\'') + { + U8 char_val = token_string.str[1]; + atom = eval_expr_u64(arena, token_string.str, char_val); + } + else + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Malformed character literal."); + } + }break; + + // rjf: string => invalid + case EVAL_TokenKind_StringLiteral: + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "String literals are not supported."); + it += 1; + }break; + + } + } + } + + //- rjf: upgrade atom w/ postfix unaries + if(atom != &eval_expr_nil) for(;it < it_opl;) + { + EVAL_Token token = eval_token_at_it(it, tokens); + String8 token_string = str8_substr(text, token.range); + B32 is_postfix_unary = 0; + + // rjf: dot/arrow operator + if(token.kind == EVAL_TokenKind_Symbol && + (str8_match(token_string, str8_lit("."), 0) || + str8_match(token_string, str8_lit("->"), 0))) + { + is_postfix_unary = 1; + + // rjf: advance past operator + it += 1; + + // rjf: expect member name + String8 member_name = {0}; + B32 good_member_name = 0; + { + EVAL_Token member_name_maybe = eval_token_at_it(it, tokens); + String8 member_name_maybe_string = str8_substr(text, member_name_maybe.range); + if(member_name_maybe.kind != EVAL_TokenKind_Identifier) + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Expected member name after %S.", token_string); + } + else + { + member_name = member_name_maybe_string; + good_member_name = 1; + } + } + + // rjf: produce lookup member expr + if(good_member_name) + { + EVAL_Expr *member_expr = eval_expr_leaf_member(arena, member_name.str, member_name); + atom = eval_expr(arena, EVAL_ExprKind_MemberAccess, token_string.str, atom, member_expr, &eval_expr_nil); + } + + // rjf: increment past good member names + if(good_member_name) + { + it += 1; + } + } + + // rjf: array index + if(token.kind == EVAL_TokenKind_Symbol && + str8_match(token_string, str8_lit("["), 0)) + { + is_postfix_unary = 1; + + // rjf: advance past [ + it += 1; + + // rjf: parse indexing expression + EVAL_TokenArray idx_expr_parse_tokens = eval_token_array_make_first_opl(it, it_opl); + EVAL_ParseResult idx_expr_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &idx_expr_parse_tokens, eval_g_max_precedence); + eval_error_list_concat_in_place(&result.errors, &idx_expr_parse.errors); + it = idx_expr_parse.last_token; + + // rjf: valid indexing expression => produce index expr + if(idx_expr_parse.expr != &eval_expr_nil) + { + atom = eval_expr(arena, EVAL_ExprKind_ArrayIndex, token_string.str, atom, idx_expr_parse.expr, &eval_expr_nil); + } + + // rjf: expect ] + { + EVAL_Token close_brace_maybe = eval_token_at_it(it, tokens); + String8 close_brace_maybe_string = str8_substr(text, close_brace_maybe.range); + if(close_brace_maybe.kind != EVAL_TokenKind_Symbol || !str8_match(close_brace_maybe_string, str8_lit("]"), 0)) + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Unclosed [."); + } + else + { + it += 1; + } + } + } + + // rjf: quit if this doesn't look like any patterns of postfix unary we know + if(!is_postfix_unary) + { + break; + } + } + + //- rjf: upgrade atom w/ previously parsed prefix unaries + if(atom == &eval_expr_nil && first_prefix_unary != 0 && first_prefix_unary->cast_expr != 0) + { + atom = first_prefix_unary->cast_expr; + for(PrefixUnaryNode *prefix_unary = first_prefix_unary->next; + prefix_unary != 0; + prefix_unary = prefix_unary->next) + { + atom = eval_expr(arena, prefix_unary->kind, prefix_unary->location, + prefix_unary->cast_expr != &eval_expr_nil ? prefix_unary->cast_expr : atom, + prefix_unary->cast_expr != &eval_expr_nil ? atom : &eval_expr_nil, + &eval_expr_nil); + } + } + else if(atom == 0 && first_prefix_unary != 0) + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, last_prefix_unary->location, "Missing expression."); + } + else + { + for(PrefixUnaryNode *prefix_unary = first_prefix_unary; prefix_unary != 0; prefix_unary = prefix_unary->next) + { + atom = eval_expr(arena, prefix_unary->kind, prefix_unary->location, + prefix_unary->cast_expr != &eval_expr_nil ? prefix_unary->cast_expr : atom, + prefix_unary->cast_expr != &eval_expr_nil ? atom : &eval_expr_nil, + &eval_expr_nil); + } + } + + //- rjf: parse complex operators + if(atom != &eval_expr_nil) for(;it < it_opl;) + { + EVAL_Token *start_it = it; + EVAL_Token token = eval_token_at_it(it, tokens); + String8 token_string = str8_substr(text, token.range); + + //- rjf: parse binaries + { + // rjf: first try to find a matching binary operator + S64 binary_precedence = 0; + EVAL_ExprKind binary_kind = 0; + for(U64 idx = 0; idx < ArrayCount(eval_g_binary_op_table); idx += 1) + { + if(str8_match(token_string, eval_g_binary_op_table[idx].string, 0)) + { + binary_precedence = eval_g_binary_op_table[idx].precedence; + binary_kind = eval_g_binary_op_table[idx].kind; + break; + } + } + + // rjf: if we got a valid binary precedence, and it's not to be handled by + // a caller, then we need to parse the right-hand-side with a tighter + // precedence + if(binary_precedence != 0 && binary_precedence <= max_precedence) + { + EVAL_TokenArray rhs_expr_parse_tokens = eval_token_array_make_first_opl(it+1, it_opl); + EVAL_ParseResult rhs_expr_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &rhs_expr_parse_tokens, binary_precedence-1); + eval_error_list_concat_in_place(&result.errors, &rhs_expr_parse.errors); + EVAL_Expr *rhs = rhs_expr_parse.expr; + it = rhs_expr_parse.last_token; + if(rhs == &eval_expr_nil) + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Missing right-hand-side of %S.", token_string); + } + else + { + atom = eval_expr(arena, binary_kind, token_string.str, atom, rhs, &eval_expr_nil); + } + } + } + + //- rjf: parse ternaries + { + if(token.kind == EVAL_TokenKind_Symbol && str8_match(token_string, str8_lit("?"), 0) && 13 <= max_precedence) + { + // rjf: parse middle expression + EVAL_TokenArray middle_expr_tokens = eval_token_array_make_first_opl(it, it_opl); + EVAL_ParseResult middle_expr_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &middle_expr_tokens, eval_g_max_precedence); + it = middle_expr_parse.last_token; + EVAL_Expr *middle_expr = middle_expr_parse.expr; + eval_error_list_concat_in_place(&result.errors, &middle_expr_parse.errors); + if(middle_expr_parse.expr == &eval_expr_nil) + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Expected expression after ?."); + } + + // rjf: expect : + B32 got_colon = 0; + EVAL_Token colon_token = zero_struct; + String8 colon_token_string = {0}; + { + EVAL_Token colon_token_maybe = eval_token_at_it(it, tokens); + String8 colon_token_maybe_string = str8_substr(text, colon_token_maybe.range); + if(colon_token_maybe.kind != EVAL_TokenKind_Symbol || !str8_match(colon_token_maybe_string, str8_lit(":"), 0)) + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, token_string.str, "Expected : after ?."); + } + else + { + got_colon = 1; + colon_token = colon_token_maybe; + colon_token_string = colon_token_maybe_string; + it += 1; + } + } + + // rjf: parse rhs + EVAL_TokenArray rhs_expr_parse_tokens = eval_token_array_make_first_opl(it, it_opl); + EVAL_ParseResult rhs_expr_parse = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, &rhs_expr_parse_tokens, eval_g_max_precedence); + if(got_colon) + { + it = rhs_expr_parse.last_token; + eval_error_list_concat_in_place(&result.errors, &rhs_expr_parse.errors); + if(rhs_expr_parse.expr == &eval_expr_nil) + { + eval_errorf(arena, &result.errors, EVAL_ErrorKind_MalformedInput, colon_token_string.str, "Expected expression after :."); + } + } + + // rjf: build ternary + atom = eval_expr(arena, EVAL_ExprKind_Ternary, token_string.str, atom, middle_expr_parse.expr, rhs_expr_parse.expr); + } + } + + // rjf: if we parsed nothing successfully, we're done + if(it == start_it) + { + break; + } + } + + //- rjf: fill result & return + result.last_token = it; + result.expr = atom; + scratch_end(scratch); + ProfEnd(); + return result; +} + +internal EVAL_ParseResult +eval_parse_expr_from_text_tokens(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens) +{ + EVAL_ParseResult result = eval_parse_expr_from_text_tokens__prec(arena, ctx, text, tokens, eval_g_max_precedence); + return result; +} diff --git a/src/eval/eval_parser.h b/src/eval/eval_parser.h index d58ab206..f2ed9167 100644 --- a/src/eval/eval_parser.h +++ b/src/eval/eval_parser.h @@ -1,109 +1,109 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef EVAL_PARSER_H -#define EVAL_PARSER_H - -//////////////////////////////// -//~ rjf: Token Types - -typedef enum EVAL_TokenKind -{ - EVAL_TokenKind_Null, - EVAL_TokenKind_Identifier, - EVAL_TokenKind_Numeric, - EVAL_TokenKind_StringLiteral, - EVAL_TokenKind_CharLiteral, - EVAL_TokenKind_Symbol, - EVAL_TokenKind_COUNT -} -EVAL_TokenKind; - -typedef struct EVAL_Token EVAL_Token; -struct EVAL_Token -{ - EVAL_TokenKind kind; - Rng1U64 range; -}; - -typedef struct EVAL_TokenChunkNode EVAL_TokenChunkNode; -struct EVAL_TokenChunkNode -{ - EVAL_TokenChunkNode *next; - EVAL_Token *v; - U64 count; - U64 cap; -}; - -typedef struct EVAL_TokenChunkList EVAL_TokenChunkList; -struct EVAL_TokenChunkList -{ - EVAL_TokenChunkNode *first; - EVAL_TokenChunkNode *last; - U64 node_count; - U64 total_count; -}; - -typedef struct EVAL_TokenArray EVAL_TokenArray; -struct EVAL_TokenArray -{ - EVAL_Token *v; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Parser Types - -typedef struct EVAL_ParseResult EVAL_ParseResult; -struct EVAL_ParseResult -{ - EVAL_Token *last_token; - EVAL_Expr *expr; - EVAL_ErrorList errors; -}; - -typedef struct EVAL_ParseCtx EVAL_ParseCtx; -struct EVAL_ParseCtx -{ - Architecture arch; - U64 ip_voff; - RDI_Parsed *rdi; - TG_Graph *type_graph; - EVAL_String2NumMap *regs_map; - EVAL_String2NumMap *reg_alias_map; - EVAL_String2NumMap *locals_map; - EVAL_String2NumMap *member_map; -}; - -//////////////////////////////// -//~ rjf: Globals - -read_only global EVAL_String2NumMap eval_string2num_map_nil = {0}; -read_only global EVAL_String2ExprMap eval_string2expr_map_nil = {0}; -global read_only EVAL_ParseResult eval_parse_result_nil = {0, &eval_expr_nil}; - -//////////////////////////////// -//~ rjf: Debug-Info-Driven Map Building Fast Paths - -internal EVAL_String2NumMap *eval_push_locals_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff); -internal EVAL_String2NumMap *eval_push_member_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff); - -//////////////////////////////// -//~ rjf: Tokenization Functions - -#define eval_token_at_it(it, arr) (((it) < (arr)->v+(arr)->count) ? (*(it)) : eval_token_zero()) -internal EVAL_Token eval_token_zero(void); -internal void eval_token_chunk_list_push(Arena *arena, EVAL_TokenChunkList *list, U64 chunk_size, EVAL_Token *token); -internal EVAL_TokenArray eval_token_array_from_chunk_list(Arena *arena, EVAL_TokenChunkList *list); -internal EVAL_TokenArray eval_token_array_from_text(Arena *arena, String8 text); -internal EVAL_TokenArray eval_token_array_make_first_opl(EVAL_Token *first, EVAL_Token *opl); - -//////////////////////////////// -//~ rjf: Parser Functions - -internal TG_Key eval_leaf_type_from_name(RDI_Parsed *rdi, String8 name); -internal EVAL_ParseResult eval_parse_type_from_text_tokens(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens); -internal EVAL_ParseResult eval_parse_expr_from_text_tokens__prec(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens, S64 max_precedence); -internal EVAL_ParseResult eval_parse_expr_from_text_tokens(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens); - -#endif // EVAL_PARSER_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef EVAL_PARSER_H +#define EVAL_PARSER_H + +//////////////////////////////// +//~ rjf: Token Types + +typedef enum EVAL_TokenKind +{ + EVAL_TokenKind_Null, + EVAL_TokenKind_Identifier, + EVAL_TokenKind_Numeric, + EVAL_TokenKind_StringLiteral, + EVAL_TokenKind_CharLiteral, + EVAL_TokenKind_Symbol, + EVAL_TokenKind_COUNT +} +EVAL_TokenKind; + +typedef struct EVAL_Token EVAL_Token; +struct EVAL_Token +{ + EVAL_TokenKind kind; + Rng1U64 range; +}; + +typedef struct EVAL_TokenChunkNode EVAL_TokenChunkNode; +struct EVAL_TokenChunkNode +{ + EVAL_TokenChunkNode *next; + EVAL_Token *v; + U64 count; + U64 cap; +}; + +typedef struct EVAL_TokenChunkList EVAL_TokenChunkList; +struct EVAL_TokenChunkList +{ + EVAL_TokenChunkNode *first; + EVAL_TokenChunkNode *last; + U64 node_count; + U64 total_count; +}; + +typedef struct EVAL_TokenArray EVAL_TokenArray; +struct EVAL_TokenArray +{ + EVAL_Token *v; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Parser Types + +typedef struct EVAL_ParseResult EVAL_ParseResult; +struct EVAL_ParseResult +{ + EVAL_Token *last_token; + EVAL_Expr *expr; + EVAL_ErrorList errors; +}; + +typedef struct EVAL_ParseCtx EVAL_ParseCtx; +struct EVAL_ParseCtx +{ + Architecture arch; + U64 ip_voff; + RDI_Parsed *rdi; + TG_Graph *type_graph; + EVAL_String2NumMap *regs_map; + EVAL_String2NumMap *reg_alias_map; + EVAL_String2NumMap *locals_map; + EVAL_String2NumMap *member_map; +}; + +//////////////////////////////// +//~ rjf: Globals + +read_only global EVAL_String2NumMap eval_string2num_map_nil = {0}; +read_only global EVAL_String2ExprMap eval_string2expr_map_nil = {0}; +global read_only EVAL_ParseResult eval_parse_result_nil = {0, &eval_expr_nil}; + +//////////////////////////////// +//~ rjf: Debug-Info-Driven Map Building Fast Paths + +internal EVAL_String2NumMap *eval_push_locals_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff); +internal EVAL_String2NumMap *eval_push_member_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff); + +//////////////////////////////// +//~ rjf: Tokenization Functions + +#define eval_token_at_it(it, arr) (((it) < (arr)->v+(arr)->count) ? (*(it)) : eval_token_zero()) +internal EVAL_Token eval_token_zero(void); +internal void eval_token_chunk_list_push(Arena *arena, EVAL_TokenChunkList *list, U64 chunk_size, EVAL_Token *token); +internal EVAL_TokenArray eval_token_array_from_chunk_list(Arena *arena, EVAL_TokenChunkList *list); +internal EVAL_TokenArray eval_token_array_from_text(Arena *arena, String8 text); +internal EVAL_TokenArray eval_token_array_make_first_opl(EVAL_Token *first, EVAL_Token *opl); + +//////////////////////////////// +//~ rjf: Parser Functions + +internal TG_Key eval_leaf_type_from_name(RDI_Parsed *rdi, String8 name); +internal EVAL_ParseResult eval_parse_type_from_text_tokens(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens); +internal EVAL_ParseResult eval_parse_expr_from_text_tokens__prec(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens, S64 max_precedence); +internal EVAL_ParseResult eval_parse_expr_from_text_tokens(Arena *arena, EVAL_ParseCtx *ctx, String8 text, EVAL_TokenArray *tokens); + +#endif // EVAL_PARSER_H diff --git a/src/file_stream/file_stream.h b/src/file_stream/file_stream.h index fda08134..59762876 100644 --- a/src/file_stream/file_stream.h +++ b/src/file_stream/file_stream.h @@ -1,100 +1,100 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef FILE_STREAM_H -#define FILE_STREAM_H - -//////////////////////////////// -//~ rjf: Per-Path Info Cache Types - -typedef struct FS_Node FS_Node; -struct FS_Node -{ - FS_Node *next; - String8 path; - U64 timestamp; - B32 is_working; -}; - -typedef struct FS_Slot FS_Slot; -struct FS_Slot -{ - FS_Node *first; - FS_Node *last; -}; - -typedef struct FS_Stripe FS_Stripe; -struct FS_Stripe -{ - Arena *arena; - OS_Handle cv; - OS_Handle rw_mutex; -}; - -//////////////////////////////// -//~ rjf: Shared State Bundle - -typedef struct FS_Shared FS_Shared; -struct FS_Shared -{ - Arena *arena; - U64 change_gen; - - // rjf: path info cache - U64 slots_count; - U64 stripes_count; - FS_Slot *slots; - FS_Stripe *stripes; - - // rjf: user -> streamer ring buffer - U64 u2s_ring_size; - U8 *u2s_ring_base; - U64 u2s_ring_write_pos; - U64 u2s_ring_read_pos; - OS_Handle u2s_ring_cv; - OS_Handle u2s_ring_mutex; - - // rjf: streamer threads - U64 streamer_count; - OS_Handle *streamers; - - // rjf: change detector threads - OS_Handle detector_thread; -}; - -//////////////////////////////// -//~ rjf: Globals - -global FS_Shared *fs_shared = 0; - -//////////////////////////////// -//~ rjf: Top-Level API - -internal void fs_init(void); - -//////////////////////////////// -//~ rjf: Change Generation - -internal U64 fs_change_gen(void); - -//////////////////////////////// -//~ rjf: Cache Interaction - -internal U128 fs_hash_from_path(String8 path, U64 endt_us); -internal U128 fs_key_from_path(String8 path); -internal U64 fs_timestamp_from_path(String8 path); - -//////////////////////////////// -//~ rjf: Streamer Threads - -internal B32 fs_u2s_enqueue_path(String8 path, U64 endt_us); -internal String8 fs_u2s_dequeue_path(Arena *arena); - -internal void fs_streamer_thread__entry_point(void *p); - -//////////////////////////////// -//~ rjf: Change Detector Thread - -internal void fs_detector_thread__entry_point(void *p); - -#endif // FILE_STREAM_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef FILE_STREAM_H +#define FILE_STREAM_H + +//////////////////////////////// +//~ rjf: Per-Path Info Cache Types + +typedef struct FS_Node FS_Node; +struct FS_Node +{ + FS_Node *next; + String8 path; + U64 timestamp; + B32 is_working; +}; + +typedef struct FS_Slot FS_Slot; +struct FS_Slot +{ + FS_Node *first; + FS_Node *last; +}; + +typedef struct FS_Stripe FS_Stripe; +struct FS_Stripe +{ + Arena *arena; + OS_Handle cv; + OS_Handle rw_mutex; +}; + +//////////////////////////////// +//~ rjf: Shared State Bundle + +typedef struct FS_Shared FS_Shared; +struct FS_Shared +{ + Arena *arena; + U64 change_gen; + + // rjf: path info cache + U64 slots_count; + U64 stripes_count; + FS_Slot *slots; + FS_Stripe *stripes; + + // rjf: user -> streamer ring buffer + U64 u2s_ring_size; + U8 *u2s_ring_base; + U64 u2s_ring_write_pos; + U64 u2s_ring_read_pos; + OS_Handle u2s_ring_cv; + OS_Handle u2s_ring_mutex; + + // rjf: streamer threads + U64 streamer_count; + OS_Handle *streamers; + + // rjf: change detector threads + OS_Handle detector_thread; +}; + +//////////////////////////////// +//~ rjf: Globals + +global FS_Shared *fs_shared = 0; + +//////////////////////////////// +//~ rjf: Top-Level API + +internal void fs_init(void); + +//////////////////////////////// +//~ rjf: Change Generation + +internal U64 fs_change_gen(void); + +//////////////////////////////// +//~ rjf: Cache Interaction + +internal U128 fs_hash_from_path(String8 path, U64 endt_us); +internal U128 fs_key_from_path(String8 path); +internal U64 fs_timestamp_from_path(String8 path); + +//////////////////////////////// +//~ rjf: Streamer Threads + +internal B32 fs_u2s_enqueue_path(String8 path, U64 endt_us); +internal String8 fs_u2s_dequeue_path(Arena *arena); + +internal void fs_streamer_thread__entry_point(void *p); + +//////////////////////////////// +//~ rjf: Change Detector Thread + +internal void fs_detector_thread__entry_point(void *p); + +#endif // FILE_STREAM_H diff --git a/src/font_cache/font_cache.c b/src/font_cache/font_cache.c index 06809d86..54d543ee 100644 --- a/src/font_cache/font_cache.c +++ b/src/font_cache/font_cache.c @@ -1,1063 +1,1063 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Basic Functions - -#if !defined(BLAKE2_H) -#define HAVE_SSE2 -#include "third_party/blake2/blake2.h" -#include "third_party/blake2/blake2b.c" -#endif - -internal F_Hash -f_hash_from_string(String8 string) -{ - F_Hash result = {0}; - blake2b((U8 *)&result.u64[0], sizeof(result), string.str, string.size, 0, 0); - return result; -} - -internal U64 -f_little_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 Vec2S32 -f_vertex_from_corner(Corner corner) -{ - Vec2S32 result = {0}; - switch(corner) - { - default: break; - case Corner_00:{result = v2s32(0, 0);}break; - case Corner_01:{result = v2s32(0, 1);}break; - case Corner_10:{result = v2s32(1, 0);}break; - case Corner_11:{result = v2s32(1, 1);}break; - } - return result; -} - -//////////////////////////////// -//~ rjf: Font Tags - -internal F_Tag -f_tag_zero(void) -{ - F_Tag result = {0}; - return result; -} - -internal B32 -f_tag_match(F_Tag a, F_Tag b) -{ - return a.u64[0] == b.u64[0] && a.u64[1] == b.u64[1]; -} - -internal FP_Handle -f_handle_from_tag(F_Tag tag) -{ - ProfBeginFunction(); - U64 slot_idx = tag.u64[1] % f_state->font_hash_table_size; - F_FontHashNode *existing_node = 0; - { - for(F_FontHashNode *n = f_state->font_hash_table[slot_idx].first; n != 0 ; n = n->hash_next) - { - if(MemoryMatchStruct(&tag, &n->tag)) - { - existing_node = n; - break; - } - } - } - FP_Handle result = {0}; - if(existing_node != 0) - { - result = existing_node->handle; - } - ProfEnd(); - return result; -} - -internal FP_Metrics -f_fp_metrics_from_tag(F_Tag tag) -{ - ProfBeginFunction(); - U64 slot_idx = tag.u64[1] % f_state->font_hash_table_size; - F_FontHashNode *existing_node = 0; - { - for(F_FontHashNode *n = f_state->font_hash_table[slot_idx].first; n != 0 ; n = n->hash_next) - { - if(MemoryMatchStruct(&tag, &n->tag)) - { - existing_node = n; - break; - } - } - } - FP_Metrics result = {0}; - if(existing_node != 0) - { - result = existing_node->metrics; - } - ProfEnd(); - return result; -} - -internal F_Tag -f_tag_from_path(String8 path) -{ - ProfBeginFunction(); - - //- rjf: produce tag from hash of path - F_Tag result = {0}; - { - F_Hash hash = f_hash_from_string(path); - MemoryCopy(&result, &hash, sizeof(result)); - result.u64[1] |= bit64; - } - - //- rjf: tag -> slot index - U64 slot_idx = result.u64[1] % f_state->font_hash_table_size; - - //- rjf: slot * tag -> existing node - F_FontHashNode *existing_node = 0; - { - for(F_FontHashNode *n = f_state->font_hash_table[slot_idx].first; n != 0 ; n = n->hash_next) - { - if(MemoryMatchStruct(&result, &n->tag)) - { - existing_node = n; - break; - } - } - } - - //- rjf: allocate & push new node if we don't have an existing one - F_FontHashNode *new_node = 0; - if(existing_node == 0) - { - F_FontHashSlot *slot = &f_state->font_hash_table[slot_idx]; - new_node = push_array(f_state->arena, F_FontHashNode, 1); - new_node->tag = result; - new_node->handle = fp_font_open(path); - new_node->metrics = fp_metrics_from_font(new_node->handle); - new_node->path = push_str8_copy(f_state->arena, path); - SLLQueuePush_N(slot->first, slot->last, new_node, hash_next); - } - - //- rjf: return - ProfEnd(); - return result; -} - -internal F_Tag -f_tag_from_static_data_string(String8 *data_ptr) -{ - ProfBeginFunction(); - - //- rjf: produce tag hash of ptr - F_Tag result = {0}; - { - F_Hash hash = f_hash_from_string(str8((U8 *)&data_ptr, sizeof(String8 *))); - MemoryCopy(&result, &hash, sizeof(result)); - result.u64[1] &= ~bit64; - } - - //- rjf: tag -> slot index - U64 slot_idx = result.u64[1] % f_state->font_hash_table_size; - - //- rjf: slot * tag -> existing node - F_FontHashNode *existing_node = 0; - { - for(F_FontHashNode *n = f_state->font_hash_table[slot_idx].first; n != 0 ; n = n->hash_next) - { - if(MemoryMatchStruct(&result, &n->tag)) - { - existing_node = n; - break; - } - } - } - - //- rjf: allocate & push new node if we don't have an existing one - F_FontHashNode *new_node = 0; - if(existing_node == 0) - { - F_FontHashSlot *slot = &f_state->font_hash_table[slot_idx]; - new_node = push_array(f_state->arena, F_FontHashNode, 1); - new_node->tag = result; - new_node->handle = fp_font_open_from_static_data_string(data_ptr); - new_node->metrics = fp_metrics_from_font(new_node->handle); - new_node->path = str8_lit(""); - SLLQueuePush_N(slot->first, slot->last, new_node, hash_next); - } - - //- rjf: return - ProfEnd(); - return result; -} - -internal String8 -f_path_from_tag(F_Tag tag) -{ - //- rjf: tag -> slot index - U64 slot_idx = tag.u64[1] % f_state->font_hash_table_size; - - //- rjf: slot * tag -> existing node - F_FontHashNode *existing_node = 0; - { - for(F_FontHashNode *n = f_state->font_hash_table[slot_idx].first; n != 0 ; n = n->hash_next) - { - if(MemoryMatchStruct(&tag, &n->tag)) - { - existing_node = n; - break; - } - } - } - - //- rjf: existing node -> path - String8 result = {0}; - if(existing_node != 0) - { - result = existing_node->path; - } - - return result; -} - -//////////////////////////////// -//~ rjf: Atlas - -internal Rng2S16 -f_atlas_region_alloc(Arena *arena, F_Atlas *atlas, Vec2S16 needed_size) -{ - ProfBeginFunction(); - - //- rjf: find node with best-fit size - Vec2S16 region_p0 = {0}; - Vec2S16 region_sz = {0}; - Corner node_corner = Corner_Invalid; - F_AtlasRegionNode *node = 0; - { - Vec2S16 n_supported_size = atlas->root_dim; - for(F_AtlasRegionNode *n = atlas->root, *next = 0; n != 0; n = next, next = 0) - { - // rjf: we've traversed to a taken node. - if(n->flags & F_AtlasRegionNodeFlag_Taken) - { - break; - } - - // rjf: calculate if this node can be allocated (all children are non-allocated) - B32 n_can_be_allocated = (n->num_allocated_descendants == 0); - - // rjf: fill size - if(n_can_be_allocated) - { - region_sz = n_supported_size; - } - - // rjf: calculate size of this node's children - Vec2S16 child_size = v2s16(n_supported_size.x/2, n_supported_size.y/2); - - // rjf: find best next child - F_AtlasRegionNode *best_child = 0; - if(child_size.x >= needed_size.x && child_size.y >= needed_size.y) - { - for(Corner corner = (Corner)0; corner < Corner_COUNT; corner = (Corner)(corner+1)) - { - if(n->children[corner] == 0) - { - n->children[corner] = push_array(arena, F_AtlasRegionNode, 1); - n->children[corner]->parent = n; - n->children[corner]->max_free_size[Corner_00] = - n->children[corner]->max_free_size[Corner_01] = - n->children[corner]->max_free_size[Corner_10] = - n->children[corner]->max_free_size[Corner_11] = v2s16(child_size.x/2, child_size.y/2); - } - if(n->max_free_size[corner].x >= needed_size.x && - n->max_free_size[corner].y >= needed_size.y) - { - best_child = n->children[corner]; - node_corner = corner; - Vec2S32 side_vertex = f_vertex_from_corner(corner); - region_p0.x += side_vertex.x*child_size.x; - region_p0.y += side_vertex.y*child_size.y; - break; - } - } - } - - // rjf: resolve node to this node if it can be allocated and children - // don't fit, or keep going to the next best child - if(n_can_be_allocated && best_child == 0) - { - node = n; - } - else - { - next = best_child; - n_supported_size = child_size; - } - } - } - - //- rjf: we're taking the subtree rooted by `node`. mark up all parents - if(node != 0 && node_corner != Corner_Invalid) - { - node->flags |= F_AtlasRegionNodeFlag_Taken; - if(node->parent != 0) - { - MemoryZeroStruct(&node->parent->max_free_size[node_corner]); - } - for(F_AtlasRegionNode *p = node->parent; p != 0; p = p->parent) - { - p->num_allocated_descendants += 1; - F_AtlasRegionNode *parent = p->parent; - if(parent != 0) - { - Corner p_corner = (p == parent->children[Corner_00] ? Corner_00 : - p == parent->children[Corner_01] ? Corner_01 : - p == parent->children[Corner_10] ? Corner_10 : - p == parent->children[Corner_11] ? Corner_11 : - Corner_Invalid); - if(p_corner == Corner_Invalid) - { - InvalidPath; - } - parent->max_free_size[p_corner].x = Max(Max(p->max_free_size[Corner_00].x, - p->max_free_size[Corner_01].x), - Max(p->max_free_size[Corner_10].x, - p->max_free_size[Corner_11].x)); - parent->max_free_size[p_corner].y = Max(Max(p->max_free_size[Corner_00].y, - p->max_free_size[Corner_01].y), - Max(p->max_free_size[Corner_10].y, - p->max_free_size[Corner_11].y)); - } - } - } - - //- rjf: fill rectangular region & return - Rng2S16 result = {0}; - result.p0 = region_p0; - result.p1 = add_2s16(region_p0, region_sz); - ProfEnd(); - return result; -} - -internal void -f_atlas_region_release(F_Atlas *atlas, Rng2S16 region) -{ - ProfBeginFunction(); - - //- rjf: extract region size - Vec2S16 region_size = v2s16(region.x1 - region.x0, region.y1 - region.y0); - - //- rjf: map region to associated node - Vec2S16 calc_region_size = {0}; - F_AtlasRegionNode *node = 0; - Corner node_corner = Corner_Invalid; - { - Vec2S16 n_p0 = v2s16(0, 0); - Vec2S16 n_sz = atlas->root_dim; - for(F_AtlasRegionNode *n = atlas->root, *next = 0; n != 0; n = next) - { - // rjf: is the region within this node's boundaries? (either this node, or a descendant) - if(n_p0.x <= region.p0.x && region.p0.x < n_p0.x+n_sz.x && - n_p0.y <= region.p0.y && region.p0.y < n_p0.y+n_sz.y) - { - // rjf: check the region against this node - if(region.p0.x == n_p0.x && region.p0.y == n_p0.y && - region_size.x == n_sz.x && region_size.y == n_sz.y) - { - node = n; - calc_region_size = n_sz; - break; - } - // rjf: check the region against children & iterate - else - { - Vec2S16 r_midpoint = v2s16(region.p0.x + region_size.x/2, - region.p0.y + region_size.y/2); - Vec2S16 n_midpoint = v2s16(n_p0.x + n_sz.x/2, - n_p0.y + n_sz.y/2); - Corner next_corner = Corner_Invalid; - if(r_midpoint.x <= n_midpoint.x && r_midpoint.y <= n_midpoint.y) - { - next_corner = Corner_00; - } - else if(r_midpoint.x <= n_midpoint.x && n_midpoint.y <= r_midpoint.y) - { - next_corner = Corner_01; - } - else if(n_midpoint.x <= r_midpoint.x && r_midpoint.y <= n_midpoint.y) - { - next_corner = Corner_10; - } - else if(n_midpoint.x <= r_midpoint.x && n_midpoint.y <= r_midpoint.y) - { - next_corner = Corner_11; - } - next = n->children[next_corner]; - node_corner = next_corner; - n_sz.x /= 2; - n_sz.y /= 2; - Vec2S32 side_vertex = f_vertex_from_corner(node_corner); - n_p0.x += side_vertex.x*n_sz.x; - n_p0.y += side_vertex.y*n_sz.y; - } - } - else - { - break; - } - } - } - - //- rjf: free node - if(node != 0 && node_corner != Corner_Invalid) - { - node->flags &= ~F_AtlasRegionNodeFlag_Taken; - if(node->parent != 0) - { - node->parent->max_free_size[node_corner] = calc_region_size; - } - for(F_AtlasRegionNode *p = node->parent; p != 0; p = p->parent) - { - p->num_allocated_descendants -= 1; - F_AtlasRegionNode *parent = p->parent; - if(parent != 0) - { - Corner p_corner = (p == parent->children[Corner_00] ? Corner_00 : - p == parent->children[Corner_01] ? Corner_01 : - p == parent->children[Corner_10] ? Corner_10 : - p == parent->children[Corner_11] ? Corner_11 : - Corner_Invalid); - if(p_corner == Corner_Invalid) - { - InvalidPath; - } - parent->max_free_size[p_corner].x = Max(Max(p->max_free_size[Corner_00].x, - p->max_free_size[Corner_01].x), - Max(p->max_free_size[Corner_10].x, - p->max_free_size[Corner_11].x)); - parent->max_free_size[p_corner].y = Max(Max(p->max_free_size[Corner_00].y, - p->max_free_size[Corner_01].y), - Max(p->max_free_size[Corner_10].y, - p->max_free_size[Corner_11].y)); - } - } - } - ProfEnd(); -} - -//////////////////////////////// -//~ rjf: Piece Type Functions - -internal F_Piece * -f_piece_chunk_list_push_new(Arena *arena, F_PieceChunkList *list, U64 cap) -{ - F_PieceChunkNode *node = list->last; - if(node == 0 || node->count >= node->cap) - { - node = push_array(arena, F_PieceChunkNode, 1); - node->v = push_array_no_zero(arena, F_Piece, cap); - node->cap = cap; - SLLQueuePush(list->first, list->last, node); - list->node_count += 1; - } - F_Piece *result = node->v + node->count; - node->count += 1; - list->total_piece_count += 1; - return result; -} - -internal void -f_piece_chunk_list_push(Arena *arena, F_PieceChunkList *list, U64 cap, F_Piece *piece) -{ - F_Piece *new_piece = f_piece_chunk_list_push_new(arena, list, cap); - MemoryCopyStruct(new_piece, piece); -} - -internal F_PieceArray -f_piece_array_from_chunk_list(Arena *arena, F_PieceChunkList *list) -{ - F_PieceArray array = {0}; - array.count = list->total_piece_count; - array.v = push_array_no_zero(arena, F_Piece, array.count); - U64 write_idx = 0; - for(F_PieceChunkNode *node = list->first; node != 0; node = node->next) - { - MemoryCopy(array.v + write_idx, node->v, node->count * sizeof(F_Piece)); - write_idx += node->count; - } - return array; -} - -internal F_PieceArray -f_piece_array_copy(Arena *arena, F_PieceArray *src) -{ - F_PieceArray dst = {0}; - dst.count = src->count; - dst.v = push_array_no_zero(arena, F_Piece, dst.count); - MemoryCopy(dst.v, src->v, sizeof(F_Piece)*dst.count); - return dst; -} - -//////////////////////////////// -//~ rjf: Rasterization Cache - -internal F_Hash2StyleRasterCacheNode * -f_hash2style_from_tag_size_flags(F_Tag tag, F32 size, F_RasterFlags flags) -{ - //- rjf: tag * size -> style hash - U64 style_hash = {0}; - { - F64 size_f64 = size; - U64 buffer[] = - { - tag.u64[0], - tag.u64[1], - *(U64 *)(&size_f64), - (U64)flags, - }; - style_hash = f_little_hash_from_string(str8((U8 *)buffer, sizeof(buffer))); - } - - //- rjf: style hash -> style node - F_Hash2StyleRasterCacheNode *hash2style_node = 0; - { - ProfBegin("style hash -> style node"); - U64 slot_idx = style_hash%f_state->hash2style_slots_count; - F_Hash2StyleRasterCacheSlot *slot = &f_state->hash2style_slots[slot_idx]; - for(F_Hash2StyleRasterCacheNode *n = slot->first; - n != 0; - n = n->hash_next) - { - if(n->style_hash == style_hash) - { - hash2style_node = n; - break; - } - } - if(Unlikely(hash2style_node == 0)) - { - F_Metrics metrics = f_metrics_from_tag_size(tag, size); - hash2style_node = push_array(f_state->arena, F_Hash2StyleRasterCacheNode, 1); - DLLPushBack_NP(slot->first, slot->last, hash2style_node, hash_next, hash_prev); - hash2style_node->style_hash = style_hash; - hash2style_node->ascent = metrics.ascent; - hash2style_node->descent= metrics.descent; - hash2style_node->utf8_class1_direct_map = push_array_no_zero(f_state->arena, F_RasterCacheInfo, 256); - hash2style_node->hash2info_slots_count = 1024; - hash2style_node->hash2info_slots = push_array(f_state->arena, F_Hash2InfoRasterCacheSlot, hash2style_node->hash2info_slots_count); - } - ProfEnd(); - } - - return hash2style_node; -} - -internal F_Run -f_push_run_from_string(Arena *arena, F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, F_RasterFlags flags, String8 string) -{ - ProfBeginFunction(); - - //- rjf: map tag/size to style node - F_Hash2StyleRasterCacheNode *hash2style_node = f_hash2style_from_tag_size_flags(tag, size, flags); - - //- rjf: decode string & produce run pieces - B32 first = 1; - F_PieceChunkList piece_chunks = {0}; - Vec2F32 dim = {0}; - B32 font_handle_mapped_on_miss = 0; - FP_Handle font_handle = {0}; - U64 piece_substring_start_idx = 0; - for(U64 idx = 0; idx < string.size; first = 0) - { - //- rjf: decode next codepoint & get piece substring, or continuation rule - String8 piece_substring; - B32 need_another_codepoint = 0; - switch(utf8_class[string.str[idx]>>3]) - { - case 1: - { - piece_substring.str = &string.str[idx]; - piece_substring.size = 1; - idx += 1; - }break; - default: - { - UnicodeDecode decode = utf8_decode(string.str+idx, string.size-idx); - idx += decode.inc; - if(decode.inc == 0) { break; } - piece_substring.str = string.str + piece_substring_start_idx; - piece_substring.size = decode.inc; - // NOTE(rjf): assuming 1 codepoint per piece for now. - }break; - } - - //- rjf: need another codepoint? -> continue - if(need_another_codepoint) - { - continue; - } - - //- rjf: do not need another codepoint? -> bump piece start idx - { - piece_substring_start_idx = idx; - } - - //- rjf: determine if this piece is a tab - if so, use space info to draw - B32 is_tab = (piece_substring.size == 1 && piece_substring.str[0] == '\t'); - if(is_tab) - { - piece_substring = str8_lit(" "); - } - - //- rjf: piece substring -> raster cache info - F_RasterCacheInfo *info = 0; - U64 piece_hash = 0; - { - // rjf: fast path for utf8 class 1 -> direct map - if(piece_substring.size == 1 && hash2style_node->utf8_class1_direct_map_mask[piece_substring.str[0]/64] & (1ull<<(piece_substring.str[0]%64))) - { - info = &hash2style_node->utf8_class1_direct_map[piece_substring.str[0]]; - } - - // rjf: more general, slower path for other glyphs - if(piece_substring.size > 1) - { - piece_hash = f_little_hash_from_string(piece_substring); - U64 slot_idx = piece_hash%hash2style_node->hash2info_slots_count; - F_Hash2InfoRasterCacheSlot *slot = &hash2style_node->hash2info_slots[slot_idx]; - for(F_Hash2InfoRasterCacheNode *node = slot->first; node != 0; node = node->hash_next) - { - if(node->hash == piece_hash) - { - info = &node->info; - break; - } - } - } - } - - //- rjf: no info found -> miss... fill this hash in the cache - if(info == 0) - { - ProfBegin("no info found -> miss... fill this hash in the cache"); - Temp scratch = scratch_begin(&arena, 1); - - // rjf: grab font handle for this tag if we don't have one already - if(font_handle_mapped_on_miss == 0) - { - font_handle_mapped_on_miss = 1; - - // rjf: tag -> font slot index - U64 font_slot_idx = tag.u64[1] % f_state->font_hash_table_size; - - // rjf: tag * slot -> existing node - F_FontHashNode *existing_node = 0; - { - for(F_FontHashNode *n = f_state->font_hash_table[font_slot_idx].first; n != 0 ; n = n->hash_next) - { - if(MemoryMatchStruct(&n->tag, &tag)) - { - existing_node = n; - break; - } - } - } - - // rjf: existing node -> font handle - if(existing_node != 0) - { - font_handle = existing_node->handle; - } - } - - // rjf: call into font provider to rasterize this substring - FP_RasterResult raster = {0}; - if(size > 0) - { - FP_RasterFlags fp_flags = 0; - if(flags & F_RasterFlag_Smooth) { fp_flags |= FP_RasterFlag_Smooth; } - if(flags & F_RasterFlag_Hinted) { fp_flags |= FP_RasterFlag_Hinted; } - raster = fp_raster(scratch.arena, font_handle, floor_f32(size), flags, piece_substring); - } - - // rjf: allocate portion of an atlas to upload the rasterization - S16 chosen_atlas_num = 0; - F_Atlas *chosen_atlas = 0; - Rng2S16 chosen_atlas_region = {0}; - if(raster.atlas_dim.x != 0 && raster.atlas_dim.y != 0) - { - U64 num_atlases = 0; - for(F_Atlas *atlas = f_state->first_atlas;; atlas = atlas->next, num_atlases += 1) - { - // rjf: create atlas if needed - if(atlas == 0 && num_atlases < 64) - { - atlas = push_array(f_state->arena, F_Atlas, 1); - DLLPushBack(f_state->first_atlas, f_state->last_atlas, atlas); - atlas->root_dim = v2s16(1024, 1024); - atlas->root = push_array(f_state->arena, F_AtlasRegionNode, 1); - atlas->root->max_free_size[Corner_00] = - atlas->root->max_free_size[Corner_01] = - atlas->root->max_free_size[Corner_10] = - atlas->root->max_free_size[Corner_11] = v2s16(atlas->root_dim.x/2, atlas->root_dim.y/2); - atlas->texture = r_tex2d_alloc(R_ResourceKind_Dynamic, v2s32((S32)atlas->root_dim.x, (S32)atlas->root_dim.y), R_Tex2DFormat_RGBA8, 0); - } - - // rjf: allocate from atlas - if(atlas != 0) - { - Vec2S16 needed_dimensions = v2s16(raster.atlas_dim.x + 2, raster.atlas_dim.y + 2); - chosen_atlas_region = f_atlas_region_alloc(f_state->arena, atlas, needed_dimensions); - if(chosen_atlas_region.x1 != chosen_atlas_region.x0) - { - chosen_atlas = atlas; - chosen_atlas_num = (S32)num_atlases; - break; - } - } - else - { - break; - } - } - } - - // rjf: upload rasterization to allocated region of atlas texture memory - if(chosen_atlas != 0) - { - Rng2S32 subregion = - { - chosen_atlas_region.x0, - chosen_atlas_region.y0, - chosen_atlas_region.x0 + raster.atlas_dim.x, - chosen_atlas_region.y0 + raster.atlas_dim.y - }; - r_fill_tex2d_region(chosen_atlas->texture, subregion, raster.atlas); - } - - // rjf: allocate & fill & push node - { - if(piece_substring.size == 1) - { - info = &hash2style_node->utf8_class1_direct_map[piece_substring.str[0]]; - hash2style_node->utf8_class1_direct_map_mask[piece_substring.str[0]/64] |= (1ull<<(piece_substring.str[0]%64)); - } - else - { - U64 slot_idx = piece_hash%hash2style_node->hash2info_slots_count; - F_Hash2InfoRasterCacheSlot *slot = &hash2style_node->hash2info_slots[slot_idx]; - F_Hash2InfoRasterCacheNode *node = push_array_no_zero(f_state->arena, F_Hash2InfoRasterCacheNode, 1); - DLLPushBack_NP(slot->first, slot->last, node, hash_next, hash_prev); - node->hash = piece_hash; - info = &node->info; - } - if(info != 0) - { - info->subrect = chosen_atlas_region; - info->atlas_num = chosen_atlas_num; - info->raster_dim = raster.atlas_dim; - info->advance = raster.advance; - } - } - - scratch_end(scratch); - ProfEnd(); - } - - //- rjf: push piece for this raster portion - if(info != 0) - { - // rjf: find atlas - F_Atlas *atlas = 0; - { - if(info->subrect.x1 != 0 && info->subrect.y1 != 0) - { - S32 num = 0; - for(F_Atlas *a = f_state->first_atlas; a != 0; a = a->next, num += 1) - { - if(info->atlas_num == num) - { - atlas = a; - break; - } - } - } - } - - // rjf: on tabs -> expand advance - F32 advance = info->advance; - if(is_tab) - { - advance = floor_f32(tab_size_px) - mod_f32(floor_f32(base_align_px), floor_f32(tab_size_px)); - } - - // rjf: push piece - { - F_Piece *piece = f_piece_chunk_list_push_new(arena, &piece_chunks, string.size); - { - piece->texture = atlas ? atlas->texture : r_handle_zero(); - piece->subrect = r2s16p(info->subrect.x0, - info->subrect.y0, - info->subrect.x0 + info->raster_dim.x, - info->subrect.y0 + info->raster_dim.y); - piece->advance = advance; - piece->decode_size = piece_substring.size; - piece->offset = v2s16(0, -(hash2style_node->ascent + hash2style_node->descent)); - } - base_align_px += advance; - dim.x += piece->advance; - dim.y = Max(dim.y, info->raster_dim.y); - } - } - } - - //- rjf: tighten & return - F_Run run = {0}; - { - if(piece_chunks.node_count == 1) - { - run.pieces.v = piece_chunks.first->v; - run.pieces.count = piece_chunks.first->count; - } - else - { - run.pieces = f_piece_array_from_chunk_list(arena, &piece_chunks); - } - run.dim = dim; - run.ascent = hash2style_node->ascent; - run.descent = hash2style_node->descent; - } - - ProfEnd(); - return run; -} - -internal String8List -f_wrapped_string_lines_from_font_size_string_max(Arena *arena, F_Tag font, F32 size, F32 base_align_px, F32 tab_size_px, String8 string, F32 max) -{ - String8List list = {0}; - { - Temp scratch = scratch_begin(&arena, 1); - F_Run run = f_push_run_from_string(scratch.arena, font, size, base_align_px, tab_size_px, 0, string); - F32 off_px = 0; - U64 off_bytes = 0; - U64 line_start_off_bytes = 0; - U64 line_end_off_bytes = 0; - B32 seeking_word_end = 0; - F32 word_start_off_px = 0; - F_Piece *last_word_start_piece = 0; - U64 last_word_start_off_bytes = 0; - F_Piece *pieces_first = run.pieces.v; - F_Piece *pieces_opl = run.pieces.v + run.pieces.count; - for(F_Piece *piece = pieces_first, *next = 0; piece != 0 && piece <= pieces_opl; piece = next) - { - if(piece != 0) {next = piece+1;} - - // rjf: gather info - U8 byte = off_bytes < string.size ? string.str[off_bytes] : 0; - F32 advance = (piece != 0) ? piece->advance : 0; - U64 decode_size = (piece != 0) ? piece->decode_size : 0; - - // rjf: find start/end of words - B32 is_first_byte_of_word = 0; - B32 is_first_space_after_word = 0; - if(!seeking_word_end && !char_is_space(byte)) - { - seeking_word_end = 1; - is_first_byte_of_word = 1; - last_word_start_off_bytes = off_bytes; - last_word_start_piece = piece; - word_start_off_px = off_px; - } - else if(seeking_word_end && char_is_space(byte)) - { - seeking_word_end = 0; - is_first_space_after_word = 1; - } - else if(seeking_word_end && byte == 0) - { - is_first_space_after_word = 1; - } - - // rjf: determine properties of this advance - B32 is_illegal = (off_px >= max); - B32 is_next_illegal = (off_px + advance >= max); - B32 is_end = (byte == 0); - - // rjf: legal word end -> extend line - if(is_first_space_after_word && !is_illegal) - { - line_end_off_bytes = off_bytes; - } - - // rjf: illegal mid-word split -> wrap mid-word - if(is_next_illegal && word_start_off_px == 0) - { - String8 line = str8(string.str + line_start_off_bytes, off_bytes - line_start_off_bytes); - line = str8_skip_chop_whitespace(line); - if(line.size != 0) - { - str8_list_push(arena, &list, line); - } - off_px = advance; - line_start_off_bytes = off_bytes; - line_end_off_bytes = off_bytes; - word_start_off_px = 0; - last_word_start_piece = piece; - last_word_start_off_bytes = off_bytes; - off_bytes += decode_size; - } - - // rjf: illegal word end -> wrap line - else if(is_first_space_after_word && (is_illegal || is_end)) - { - String8 line = str8(string.str + line_start_off_bytes, line_end_off_bytes - line_start_off_bytes); - line = str8_skip_chop_whitespace(line); - if(line.size != 0) - { - str8_list_push(arena, &list, line); - } - line_start_off_bytes = line_end_off_bytes; - if(is_illegal) - { - off_px = 0; - word_start_off_px = 0; - off_bytes = last_word_start_off_bytes; - next = last_word_start_piece; - } - } - - // rjf: advance offsets otherwise - else - { - off_px += advance; - off_bytes += decode_size; - } - - // rjf: 0 piece and 0 next -> done - if(piece == 0 && next == 0) - { - break; - } - } - scratch_end(scratch); - } - return list; -} - -internal Vec2F32 -f_dim_from_tag_size_string(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8 string) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - Vec2F32 result = {0}; - F_Run run = f_push_run_from_string(scratch.arena, tag, size, base_align_px, tab_size_px, 0, string); - result = run.dim; - scratch_end(scratch); - ProfEnd(); - return result; -} - -internal Vec2F32 -f_dim_from_tag_size_string_list(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8List list) -{ - ProfBeginFunction(); - Vec2F32 sum = {0}; - for(String8Node *n = list.first; n != 0; n = n->next) - { - Vec2F32 str_dim = f_dim_from_tag_size_string(tag, size, base_align_px, tab_size_px, n->string); - sum.x += str_dim.x; - sum.y = Max(sum.y, str_dim.y); - } - ProfEnd(); - return sum; -} - -internal F32 -f_column_size_from_tag_size(F_Tag tag, F32 size) -{ - F32 result = f_dim_from_tag_size_string(tag, size, 0, 0, str8_lit("H")).x; - return result; -} - -internal U64 -f_char_pos_from_tag_size_string_p(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8 string, F32 p) -{ - Temp scratch = scratch_begin(0, 0); - U64 best_offset_bytes = 0; - F32 best_offset_px = inf32(); - U64 offset_bytes = 0; - F32 offset_px = 0.f; - F_Run run = f_push_run_from_string(scratch.arena, tag, size, base_align_px, tab_size_px, 0, string); - for(U64 idx = 0; idx <= run.pieces.count; idx += 1) - { - F32 this_piece_offset_px = abs_f32(offset_px - p); - if(this_piece_offset_px < best_offset_px) - { - best_offset_bytes = offset_bytes; - best_offset_px = this_piece_offset_px; - } - if(idx < run.pieces.count) - { - F_Piece *piece = &run.pieces.v[idx]; - offset_px += piece->advance; - offset_bytes += piece->decode_size; - } - } - scratch_end(scratch); - return best_offset_bytes; -} - -//////////////////////////////// -//~ rjf: Metrics - -internal F_Metrics -f_metrics_from_tag_size(F_Tag tag, F32 size) -{ - ProfBeginFunction(); - FP_Metrics metrics = f_fp_metrics_from_tag(tag); - F_Metrics result = {0}; - { - result.ascent = floor_f32(size) * metrics.ascent / metrics.design_units_per_em; - result.descent = floor_f32(size) * metrics.descent / metrics.design_units_per_em; - result.line_gap = floor_f32(size) * metrics.line_gap / metrics.design_units_per_em; - result.capital_height = floor_f32(size) * metrics.capital_height / metrics.design_units_per_em; - } - ProfEnd(); - return result; -} - -internal F32 -f_line_height_from_metrics(F_Metrics *metrics) -{ - return metrics->ascent + metrics->descent + metrics->line_gap; -} - -//////////////////////////////// -//~ rjf: Main Calls - -internal void -f_init(void) -{ - Arena *arena = arena_alloc(); - f_state = push_array(arena, F_State, 1); - f_state->arena = arena; - f_state->font_hash_table_size = 64; - f_state->font_hash_table = push_array(arena, F_FontHashSlot, f_state->font_hash_table_size); - f_state->hash2style_slots_count = 1024; - f_state->hash2style_slots = push_array(arena, F_Hash2StyleRasterCacheSlot, f_state->hash2style_slots_count); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Basic Functions + +#if !defined(BLAKE2_H) +#define HAVE_SSE2 +#include "third_party/blake2/blake2.h" +#include "third_party/blake2/blake2b.c" +#endif + +internal F_Hash +f_hash_from_string(String8 string) +{ + F_Hash result = {0}; + blake2b((U8 *)&result.u64[0], sizeof(result), string.str, string.size, 0, 0); + return result; +} + +internal U64 +f_little_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 Vec2S32 +f_vertex_from_corner(Corner corner) +{ + Vec2S32 result = {0}; + switch(corner) + { + default: break; + case Corner_00:{result = v2s32(0, 0);}break; + case Corner_01:{result = v2s32(0, 1);}break; + case Corner_10:{result = v2s32(1, 0);}break; + case Corner_11:{result = v2s32(1, 1);}break; + } + return result; +} + +//////////////////////////////// +//~ rjf: Font Tags + +internal F_Tag +f_tag_zero(void) +{ + F_Tag result = {0}; + return result; +} + +internal B32 +f_tag_match(F_Tag a, F_Tag b) +{ + return a.u64[0] == b.u64[0] && a.u64[1] == b.u64[1]; +} + +internal FP_Handle +f_handle_from_tag(F_Tag tag) +{ + ProfBeginFunction(); + U64 slot_idx = tag.u64[1] % f_state->font_hash_table_size; + F_FontHashNode *existing_node = 0; + { + for(F_FontHashNode *n = f_state->font_hash_table[slot_idx].first; n != 0 ; n = n->hash_next) + { + if(MemoryMatchStruct(&tag, &n->tag)) + { + existing_node = n; + break; + } + } + } + FP_Handle result = {0}; + if(existing_node != 0) + { + result = existing_node->handle; + } + ProfEnd(); + return result; +} + +internal FP_Metrics +f_fp_metrics_from_tag(F_Tag tag) +{ + ProfBeginFunction(); + U64 slot_idx = tag.u64[1] % f_state->font_hash_table_size; + F_FontHashNode *existing_node = 0; + { + for(F_FontHashNode *n = f_state->font_hash_table[slot_idx].first; n != 0 ; n = n->hash_next) + { + if(MemoryMatchStruct(&tag, &n->tag)) + { + existing_node = n; + break; + } + } + } + FP_Metrics result = {0}; + if(existing_node != 0) + { + result = existing_node->metrics; + } + ProfEnd(); + return result; +} + +internal F_Tag +f_tag_from_path(String8 path) +{ + ProfBeginFunction(); + + //- rjf: produce tag from hash of path + F_Tag result = {0}; + { + F_Hash hash = f_hash_from_string(path); + MemoryCopy(&result, &hash, sizeof(result)); + result.u64[1] |= bit64; + } + + //- rjf: tag -> slot index + U64 slot_idx = result.u64[1] % f_state->font_hash_table_size; + + //- rjf: slot * tag -> existing node + F_FontHashNode *existing_node = 0; + { + for(F_FontHashNode *n = f_state->font_hash_table[slot_idx].first; n != 0 ; n = n->hash_next) + { + if(MemoryMatchStruct(&result, &n->tag)) + { + existing_node = n; + break; + } + } + } + + //- rjf: allocate & push new node if we don't have an existing one + F_FontHashNode *new_node = 0; + if(existing_node == 0) + { + F_FontHashSlot *slot = &f_state->font_hash_table[slot_idx]; + new_node = push_array(f_state->arena, F_FontHashNode, 1); + new_node->tag = result; + new_node->handle = fp_font_open(path); + new_node->metrics = fp_metrics_from_font(new_node->handle); + new_node->path = push_str8_copy(f_state->arena, path); + SLLQueuePush_N(slot->first, slot->last, new_node, hash_next); + } + + //- rjf: return + ProfEnd(); + return result; +} + +internal F_Tag +f_tag_from_static_data_string(String8 *data_ptr) +{ + ProfBeginFunction(); + + //- rjf: produce tag hash of ptr + F_Tag result = {0}; + { + F_Hash hash = f_hash_from_string(str8((U8 *)&data_ptr, sizeof(String8 *))); + MemoryCopy(&result, &hash, sizeof(result)); + result.u64[1] &= ~bit64; + } + + //- rjf: tag -> slot index + U64 slot_idx = result.u64[1] % f_state->font_hash_table_size; + + //- rjf: slot * tag -> existing node + F_FontHashNode *existing_node = 0; + { + for(F_FontHashNode *n = f_state->font_hash_table[slot_idx].first; n != 0 ; n = n->hash_next) + { + if(MemoryMatchStruct(&result, &n->tag)) + { + existing_node = n; + break; + } + } + } + + //- rjf: allocate & push new node if we don't have an existing one + F_FontHashNode *new_node = 0; + if(existing_node == 0) + { + F_FontHashSlot *slot = &f_state->font_hash_table[slot_idx]; + new_node = push_array(f_state->arena, F_FontHashNode, 1); + new_node->tag = result; + new_node->handle = fp_font_open_from_static_data_string(data_ptr); + new_node->metrics = fp_metrics_from_font(new_node->handle); + new_node->path = str8_lit(""); + SLLQueuePush_N(slot->first, slot->last, new_node, hash_next); + } + + //- rjf: return + ProfEnd(); + return result; +} + +internal String8 +f_path_from_tag(F_Tag tag) +{ + //- rjf: tag -> slot index + U64 slot_idx = tag.u64[1] % f_state->font_hash_table_size; + + //- rjf: slot * tag -> existing node + F_FontHashNode *existing_node = 0; + { + for(F_FontHashNode *n = f_state->font_hash_table[slot_idx].first; n != 0 ; n = n->hash_next) + { + if(MemoryMatchStruct(&tag, &n->tag)) + { + existing_node = n; + break; + } + } + } + + //- rjf: existing node -> path + String8 result = {0}; + if(existing_node != 0) + { + result = existing_node->path; + } + + return result; +} + +//////////////////////////////// +//~ rjf: Atlas + +internal Rng2S16 +f_atlas_region_alloc(Arena *arena, F_Atlas *atlas, Vec2S16 needed_size) +{ + ProfBeginFunction(); + + //- rjf: find node with best-fit size + Vec2S16 region_p0 = {0}; + Vec2S16 region_sz = {0}; + Corner node_corner = Corner_Invalid; + F_AtlasRegionNode *node = 0; + { + Vec2S16 n_supported_size = atlas->root_dim; + for(F_AtlasRegionNode *n = atlas->root, *next = 0; n != 0; n = next, next = 0) + { + // rjf: we've traversed to a taken node. + if(n->flags & F_AtlasRegionNodeFlag_Taken) + { + break; + } + + // rjf: calculate if this node can be allocated (all children are non-allocated) + B32 n_can_be_allocated = (n->num_allocated_descendants == 0); + + // rjf: fill size + if(n_can_be_allocated) + { + region_sz = n_supported_size; + } + + // rjf: calculate size of this node's children + Vec2S16 child_size = v2s16(n_supported_size.x/2, n_supported_size.y/2); + + // rjf: find best next child + F_AtlasRegionNode *best_child = 0; + if(child_size.x >= needed_size.x && child_size.y >= needed_size.y) + { + for(Corner corner = (Corner)0; corner < Corner_COUNT; corner = (Corner)(corner+1)) + { + if(n->children[corner] == 0) + { + n->children[corner] = push_array(arena, F_AtlasRegionNode, 1); + n->children[corner]->parent = n; + n->children[corner]->max_free_size[Corner_00] = + n->children[corner]->max_free_size[Corner_01] = + n->children[corner]->max_free_size[Corner_10] = + n->children[corner]->max_free_size[Corner_11] = v2s16(child_size.x/2, child_size.y/2); + } + if(n->max_free_size[corner].x >= needed_size.x && + n->max_free_size[corner].y >= needed_size.y) + { + best_child = n->children[corner]; + node_corner = corner; + Vec2S32 side_vertex = f_vertex_from_corner(corner); + region_p0.x += side_vertex.x*child_size.x; + region_p0.y += side_vertex.y*child_size.y; + break; + } + } + } + + // rjf: resolve node to this node if it can be allocated and children + // don't fit, or keep going to the next best child + if(n_can_be_allocated && best_child == 0) + { + node = n; + } + else + { + next = best_child; + n_supported_size = child_size; + } + } + } + + //- rjf: we're taking the subtree rooted by `node`. mark up all parents + if(node != 0 && node_corner != Corner_Invalid) + { + node->flags |= F_AtlasRegionNodeFlag_Taken; + if(node->parent != 0) + { + MemoryZeroStruct(&node->parent->max_free_size[node_corner]); + } + for(F_AtlasRegionNode *p = node->parent; p != 0; p = p->parent) + { + p->num_allocated_descendants += 1; + F_AtlasRegionNode *parent = p->parent; + if(parent != 0) + { + Corner p_corner = (p == parent->children[Corner_00] ? Corner_00 : + p == parent->children[Corner_01] ? Corner_01 : + p == parent->children[Corner_10] ? Corner_10 : + p == parent->children[Corner_11] ? Corner_11 : + Corner_Invalid); + if(p_corner == Corner_Invalid) + { + InvalidPath; + } + parent->max_free_size[p_corner].x = Max(Max(p->max_free_size[Corner_00].x, + p->max_free_size[Corner_01].x), + Max(p->max_free_size[Corner_10].x, + p->max_free_size[Corner_11].x)); + parent->max_free_size[p_corner].y = Max(Max(p->max_free_size[Corner_00].y, + p->max_free_size[Corner_01].y), + Max(p->max_free_size[Corner_10].y, + p->max_free_size[Corner_11].y)); + } + } + } + + //- rjf: fill rectangular region & return + Rng2S16 result = {0}; + result.p0 = region_p0; + result.p1 = add_2s16(region_p0, region_sz); + ProfEnd(); + return result; +} + +internal void +f_atlas_region_release(F_Atlas *atlas, Rng2S16 region) +{ + ProfBeginFunction(); + + //- rjf: extract region size + Vec2S16 region_size = v2s16(region.x1 - region.x0, region.y1 - region.y0); + + //- rjf: map region to associated node + Vec2S16 calc_region_size = {0}; + F_AtlasRegionNode *node = 0; + Corner node_corner = Corner_Invalid; + { + Vec2S16 n_p0 = v2s16(0, 0); + Vec2S16 n_sz = atlas->root_dim; + for(F_AtlasRegionNode *n = atlas->root, *next = 0; n != 0; n = next) + { + // rjf: is the region within this node's boundaries? (either this node, or a descendant) + if(n_p0.x <= region.p0.x && region.p0.x < n_p0.x+n_sz.x && + n_p0.y <= region.p0.y && region.p0.y < n_p0.y+n_sz.y) + { + // rjf: check the region against this node + if(region.p0.x == n_p0.x && region.p0.y == n_p0.y && + region_size.x == n_sz.x && region_size.y == n_sz.y) + { + node = n; + calc_region_size = n_sz; + break; + } + // rjf: check the region against children & iterate + else + { + Vec2S16 r_midpoint = v2s16(region.p0.x + region_size.x/2, + region.p0.y + region_size.y/2); + Vec2S16 n_midpoint = v2s16(n_p0.x + n_sz.x/2, + n_p0.y + n_sz.y/2); + Corner next_corner = Corner_Invalid; + if(r_midpoint.x <= n_midpoint.x && r_midpoint.y <= n_midpoint.y) + { + next_corner = Corner_00; + } + else if(r_midpoint.x <= n_midpoint.x && n_midpoint.y <= r_midpoint.y) + { + next_corner = Corner_01; + } + else if(n_midpoint.x <= r_midpoint.x && r_midpoint.y <= n_midpoint.y) + { + next_corner = Corner_10; + } + else if(n_midpoint.x <= r_midpoint.x && n_midpoint.y <= r_midpoint.y) + { + next_corner = Corner_11; + } + next = n->children[next_corner]; + node_corner = next_corner; + n_sz.x /= 2; + n_sz.y /= 2; + Vec2S32 side_vertex = f_vertex_from_corner(node_corner); + n_p0.x += side_vertex.x*n_sz.x; + n_p0.y += side_vertex.y*n_sz.y; + } + } + else + { + break; + } + } + } + + //- rjf: free node + if(node != 0 && node_corner != Corner_Invalid) + { + node->flags &= ~F_AtlasRegionNodeFlag_Taken; + if(node->parent != 0) + { + node->parent->max_free_size[node_corner] = calc_region_size; + } + for(F_AtlasRegionNode *p = node->parent; p != 0; p = p->parent) + { + p->num_allocated_descendants -= 1; + F_AtlasRegionNode *parent = p->parent; + if(parent != 0) + { + Corner p_corner = (p == parent->children[Corner_00] ? Corner_00 : + p == parent->children[Corner_01] ? Corner_01 : + p == parent->children[Corner_10] ? Corner_10 : + p == parent->children[Corner_11] ? Corner_11 : + Corner_Invalid); + if(p_corner == Corner_Invalid) + { + InvalidPath; + } + parent->max_free_size[p_corner].x = Max(Max(p->max_free_size[Corner_00].x, + p->max_free_size[Corner_01].x), + Max(p->max_free_size[Corner_10].x, + p->max_free_size[Corner_11].x)); + parent->max_free_size[p_corner].y = Max(Max(p->max_free_size[Corner_00].y, + p->max_free_size[Corner_01].y), + Max(p->max_free_size[Corner_10].y, + p->max_free_size[Corner_11].y)); + } + } + } + ProfEnd(); +} + +//////////////////////////////// +//~ rjf: Piece Type Functions + +internal F_Piece * +f_piece_chunk_list_push_new(Arena *arena, F_PieceChunkList *list, U64 cap) +{ + F_PieceChunkNode *node = list->last; + if(node == 0 || node->count >= node->cap) + { + node = push_array(arena, F_PieceChunkNode, 1); + node->v = push_array_no_zero(arena, F_Piece, cap); + node->cap = cap; + SLLQueuePush(list->first, list->last, node); + list->node_count += 1; + } + F_Piece *result = node->v + node->count; + node->count += 1; + list->total_piece_count += 1; + return result; +} + +internal void +f_piece_chunk_list_push(Arena *arena, F_PieceChunkList *list, U64 cap, F_Piece *piece) +{ + F_Piece *new_piece = f_piece_chunk_list_push_new(arena, list, cap); + MemoryCopyStruct(new_piece, piece); +} + +internal F_PieceArray +f_piece_array_from_chunk_list(Arena *arena, F_PieceChunkList *list) +{ + F_PieceArray array = {0}; + array.count = list->total_piece_count; + array.v = push_array_no_zero(arena, F_Piece, array.count); + U64 write_idx = 0; + for(F_PieceChunkNode *node = list->first; node != 0; node = node->next) + { + MemoryCopy(array.v + write_idx, node->v, node->count * sizeof(F_Piece)); + write_idx += node->count; + } + return array; +} + +internal F_PieceArray +f_piece_array_copy(Arena *arena, F_PieceArray *src) +{ + F_PieceArray dst = {0}; + dst.count = src->count; + dst.v = push_array_no_zero(arena, F_Piece, dst.count); + MemoryCopy(dst.v, src->v, sizeof(F_Piece)*dst.count); + return dst; +} + +//////////////////////////////// +//~ rjf: Rasterization Cache + +internal F_Hash2StyleRasterCacheNode * +f_hash2style_from_tag_size_flags(F_Tag tag, F32 size, F_RasterFlags flags) +{ + //- rjf: tag * size -> style hash + U64 style_hash = {0}; + { + F64 size_f64 = size; + U64 buffer[] = + { + tag.u64[0], + tag.u64[1], + *(U64 *)(&size_f64), + (U64)flags, + }; + style_hash = f_little_hash_from_string(str8((U8 *)buffer, sizeof(buffer))); + } + + //- rjf: style hash -> style node + F_Hash2StyleRasterCacheNode *hash2style_node = 0; + { + ProfBegin("style hash -> style node"); + U64 slot_idx = style_hash%f_state->hash2style_slots_count; + F_Hash2StyleRasterCacheSlot *slot = &f_state->hash2style_slots[slot_idx]; + for(F_Hash2StyleRasterCacheNode *n = slot->first; + n != 0; + n = n->hash_next) + { + if(n->style_hash == style_hash) + { + hash2style_node = n; + break; + } + } + if(Unlikely(hash2style_node == 0)) + { + F_Metrics metrics = f_metrics_from_tag_size(tag, size); + hash2style_node = push_array(f_state->arena, F_Hash2StyleRasterCacheNode, 1); + DLLPushBack_NP(slot->first, slot->last, hash2style_node, hash_next, hash_prev); + hash2style_node->style_hash = style_hash; + hash2style_node->ascent = metrics.ascent; + hash2style_node->descent= metrics.descent; + hash2style_node->utf8_class1_direct_map = push_array_no_zero(f_state->arena, F_RasterCacheInfo, 256); + hash2style_node->hash2info_slots_count = 1024; + hash2style_node->hash2info_slots = push_array(f_state->arena, F_Hash2InfoRasterCacheSlot, hash2style_node->hash2info_slots_count); + } + ProfEnd(); + } + + return hash2style_node; +} + +internal F_Run +f_push_run_from_string(Arena *arena, F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, F_RasterFlags flags, String8 string) +{ + ProfBeginFunction(); + + //- rjf: map tag/size to style node + F_Hash2StyleRasterCacheNode *hash2style_node = f_hash2style_from_tag_size_flags(tag, size, flags); + + //- rjf: decode string & produce run pieces + B32 first = 1; + F_PieceChunkList piece_chunks = {0}; + Vec2F32 dim = {0}; + B32 font_handle_mapped_on_miss = 0; + FP_Handle font_handle = {0}; + U64 piece_substring_start_idx = 0; + for(U64 idx = 0; idx < string.size; first = 0) + { + //- rjf: decode next codepoint & get piece substring, or continuation rule + String8 piece_substring; + B32 need_another_codepoint = 0; + switch(utf8_class[string.str[idx]>>3]) + { + case 1: + { + piece_substring.str = &string.str[idx]; + piece_substring.size = 1; + idx += 1; + }break; + default: + { + UnicodeDecode decode = utf8_decode(string.str+idx, string.size-idx); + idx += decode.inc; + if(decode.inc == 0) { break; } + piece_substring.str = string.str + piece_substring_start_idx; + piece_substring.size = decode.inc; + // NOTE(rjf): assuming 1 codepoint per piece for now. + }break; + } + + //- rjf: need another codepoint? -> continue + if(need_another_codepoint) + { + continue; + } + + //- rjf: do not need another codepoint? -> bump piece start idx + { + piece_substring_start_idx = idx; + } + + //- rjf: determine if this piece is a tab - if so, use space info to draw + B32 is_tab = (piece_substring.size == 1 && piece_substring.str[0] == '\t'); + if(is_tab) + { + piece_substring = str8_lit(" "); + } + + //- rjf: piece substring -> raster cache info + F_RasterCacheInfo *info = 0; + U64 piece_hash = 0; + { + // rjf: fast path for utf8 class 1 -> direct map + if(piece_substring.size == 1 && hash2style_node->utf8_class1_direct_map_mask[piece_substring.str[0]/64] & (1ull<<(piece_substring.str[0]%64))) + { + info = &hash2style_node->utf8_class1_direct_map[piece_substring.str[0]]; + } + + // rjf: more general, slower path for other glyphs + if(piece_substring.size > 1) + { + piece_hash = f_little_hash_from_string(piece_substring); + U64 slot_idx = piece_hash%hash2style_node->hash2info_slots_count; + F_Hash2InfoRasterCacheSlot *slot = &hash2style_node->hash2info_slots[slot_idx]; + for(F_Hash2InfoRasterCacheNode *node = slot->first; node != 0; node = node->hash_next) + { + if(node->hash == piece_hash) + { + info = &node->info; + break; + } + } + } + } + + //- rjf: no info found -> miss... fill this hash in the cache + if(info == 0) + { + ProfBegin("no info found -> miss... fill this hash in the cache"); + Temp scratch = scratch_begin(&arena, 1); + + // rjf: grab font handle for this tag if we don't have one already + if(font_handle_mapped_on_miss == 0) + { + font_handle_mapped_on_miss = 1; + + // rjf: tag -> font slot index + U64 font_slot_idx = tag.u64[1] % f_state->font_hash_table_size; + + // rjf: tag * slot -> existing node + F_FontHashNode *existing_node = 0; + { + for(F_FontHashNode *n = f_state->font_hash_table[font_slot_idx].first; n != 0 ; n = n->hash_next) + { + if(MemoryMatchStruct(&n->tag, &tag)) + { + existing_node = n; + break; + } + } + } + + // rjf: existing node -> font handle + if(existing_node != 0) + { + font_handle = existing_node->handle; + } + } + + // rjf: call into font provider to rasterize this substring + FP_RasterResult raster = {0}; + if(size > 0) + { + FP_RasterFlags fp_flags = 0; + if(flags & F_RasterFlag_Smooth) { fp_flags |= FP_RasterFlag_Smooth; } + if(flags & F_RasterFlag_Hinted) { fp_flags |= FP_RasterFlag_Hinted; } + raster = fp_raster(scratch.arena, font_handle, floor_f32(size), flags, piece_substring); + } + + // rjf: allocate portion of an atlas to upload the rasterization + S16 chosen_atlas_num = 0; + F_Atlas *chosen_atlas = 0; + Rng2S16 chosen_atlas_region = {0}; + if(raster.atlas_dim.x != 0 && raster.atlas_dim.y != 0) + { + U64 num_atlases = 0; + for(F_Atlas *atlas = f_state->first_atlas;; atlas = atlas->next, num_atlases += 1) + { + // rjf: create atlas if needed + if(atlas == 0 && num_atlases < 64) + { + atlas = push_array(f_state->arena, F_Atlas, 1); + DLLPushBack(f_state->first_atlas, f_state->last_atlas, atlas); + atlas->root_dim = v2s16(1024, 1024); + atlas->root = push_array(f_state->arena, F_AtlasRegionNode, 1); + atlas->root->max_free_size[Corner_00] = + atlas->root->max_free_size[Corner_01] = + atlas->root->max_free_size[Corner_10] = + atlas->root->max_free_size[Corner_11] = v2s16(atlas->root_dim.x/2, atlas->root_dim.y/2); + atlas->texture = r_tex2d_alloc(R_ResourceKind_Dynamic, v2s32((S32)atlas->root_dim.x, (S32)atlas->root_dim.y), R_Tex2DFormat_RGBA8, 0); + } + + // rjf: allocate from atlas + if(atlas != 0) + { + Vec2S16 needed_dimensions = v2s16(raster.atlas_dim.x + 2, raster.atlas_dim.y + 2); + chosen_atlas_region = f_atlas_region_alloc(f_state->arena, atlas, needed_dimensions); + if(chosen_atlas_region.x1 != chosen_atlas_region.x0) + { + chosen_atlas = atlas; + chosen_atlas_num = (S32)num_atlases; + break; + } + } + else + { + break; + } + } + } + + // rjf: upload rasterization to allocated region of atlas texture memory + if(chosen_atlas != 0) + { + Rng2S32 subregion = + { + chosen_atlas_region.x0, + chosen_atlas_region.y0, + chosen_atlas_region.x0 + raster.atlas_dim.x, + chosen_atlas_region.y0 + raster.atlas_dim.y + }; + r_fill_tex2d_region(chosen_atlas->texture, subregion, raster.atlas); + } + + // rjf: allocate & fill & push node + { + if(piece_substring.size == 1) + { + info = &hash2style_node->utf8_class1_direct_map[piece_substring.str[0]]; + hash2style_node->utf8_class1_direct_map_mask[piece_substring.str[0]/64] |= (1ull<<(piece_substring.str[0]%64)); + } + else + { + U64 slot_idx = piece_hash%hash2style_node->hash2info_slots_count; + F_Hash2InfoRasterCacheSlot *slot = &hash2style_node->hash2info_slots[slot_idx]; + F_Hash2InfoRasterCacheNode *node = push_array_no_zero(f_state->arena, F_Hash2InfoRasterCacheNode, 1); + DLLPushBack_NP(slot->first, slot->last, node, hash_next, hash_prev); + node->hash = piece_hash; + info = &node->info; + } + if(info != 0) + { + info->subrect = chosen_atlas_region; + info->atlas_num = chosen_atlas_num; + info->raster_dim = raster.atlas_dim; + info->advance = raster.advance; + } + } + + scratch_end(scratch); + ProfEnd(); + } + + //- rjf: push piece for this raster portion + if(info != 0) + { + // rjf: find atlas + F_Atlas *atlas = 0; + { + if(info->subrect.x1 != 0 && info->subrect.y1 != 0) + { + S32 num = 0; + for(F_Atlas *a = f_state->first_atlas; a != 0; a = a->next, num += 1) + { + if(info->atlas_num == num) + { + atlas = a; + break; + } + } + } + } + + // rjf: on tabs -> expand advance + F32 advance = info->advance; + if(is_tab) + { + advance = floor_f32(tab_size_px) - mod_f32(floor_f32(base_align_px), floor_f32(tab_size_px)); + } + + // rjf: push piece + { + F_Piece *piece = f_piece_chunk_list_push_new(arena, &piece_chunks, string.size); + { + piece->texture = atlas ? atlas->texture : r_handle_zero(); + piece->subrect = r2s16p(info->subrect.x0, + info->subrect.y0, + info->subrect.x0 + info->raster_dim.x, + info->subrect.y0 + info->raster_dim.y); + piece->advance = advance; + piece->decode_size = piece_substring.size; + piece->offset = v2s16(0, -(hash2style_node->ascent + hash2style_node->descent)); + } + base_align_px += advance; + dim.x += piece->advance; + dim.y = Max(dim.y, info->raster_dim.y); + } + } + } + + //- rjf: tighten & return + F_Run run = {0}; + { + if(piece_chunks.node_count == 1) + { + run.pieces.v = piece_chunks.first->v; + run.pieces.count = piece_chunks.first->count; + } + else + { + run.pieces = f_piece_array_from_chunk_list(arena, &piece_chunks); + } + run.dim = dim; + run.ascent = hash2style_node->ascent; + run.descent = hash2style_node->descent; + } + + ProfEnd(); + return run; +} + +internal String8List +f_wrapped_string_lines_from_font_size_string_max(Arena *arena, F_Tag font, F32 size, F32 base_align_px, F32 tab_size_px, String8 string, F32 max) +{ + String8List list = {0}; + { + Temp scratch = scratch_begin(&arena, 1); + F_Run run = f_push_run_from_string(scratch.arena, font, size, base_align_px, tab_size_px, 0, string); + F32 off_px = 0; + U64 off_bytes = 0; + U64 line_start_off_bytes = 0; + U64 line_end_off_bytes = 0; + B32 seeking_word_end = 0; + F32 word_start_off_px = 0; + F_Piece *last_word_start_piece = 0; + U64 last_word_start_off_bytes = 0; + F_Piece *pieces_first = run.pieces.v; + F_Piece *pieces_opl = run.pieces.v + run.pieces.count; + for(F_Piece *piece = pieces_first, *next = 0; piece != 0 && piece <= pieces_opl; piece = next) + { + if(piece != 0) {next = piece+1;} + + // rjf: gather info + U8 byte = off_bytes < string.size ? string.str[off_bytes] : 0; + F32 advance = (piece != 0) ? piece->advance : 0; + U64 decode_size = (piece != 0) ? piece->decode_size : 0; + + // rjf: find start/end of words + B32 is_first_byte_of_word = 0; + B32 is_first_space_after_word = 0; + if(!seeking_word_end && !char_is_space(byte)) + { + seeking_word_end = 1; + is_first_byte_of_word = 1; + last_word_start_off_bytes = off_bytes; + last_word_start_piece = piece; + word_start_off_px = off_px; + } + else if(seeking_word_end && char_is_space(byte)) + { + seeking_word_end = 0; + is_first_space_after_word = 1; + } + else if(seeking_word_end && byte == 0) + { + is_first_space_after_word = 1; + } + + // rjf: determine properties of this advance + B32 is_illegal = (off_px >= max); + B32 is_next_illegal = (off_px + advance >= max); + B32 is_end = (byte == 0); + + // rjf: legal word end -> extend line + if(is_first_space_after_word && !is_illegal) + { + line_end_off_bytes = off_bytes; + } + + // rjf: illegal mid-word split -> wrap mid-word + if(is_next_illegal && word_start_off_px == 0) + { + String8 line = str8(string.str + line_start_off_bytes, off_bytes - line_start_off_bytes); + line = str8_skip_chop_whitespace(line); + if(line.size != 0) + { + str8_list_push(arena, &list, line); + } + off_px = advance; + line_start_off_bytes = off_bytes; + line_end_off_bytes = off_bytes; + word_start_off_px = 0; + last_word_start_piece = piece; + last_word_start_off_bytes = off_bytes; + off_bytes += decode_size; + } + + // rjf: illegal word end -> wrap line + else if(is_first_space_after_word && (is_illegal || is_end)) + { + String8 line = str8(string.str + line_start_off_bytes, line_end_off_bytes - line_start_off_bytes); + line = str8_skip_chop_whitespace(line); + if(line.size != 0) + { + str8_list_push(arena, &list, line); + } + line_start_off_bytes = line_end_off_bytes; + if(is_illegal) + { + off_px = 0; + word_start_off_px = 0; + off_bytes = last_word_start_off_bytes; + next = last_word_start_piece; + } + } + + // rjf: advance offsets otherwise + else + { + off_px += advance; + off_bytes += decode_size; + } + + // rjf: 0 piece and 0 next -> done + if(piece == 0 && next == 0) + { + break; + } + } + scratch_end(scratch); + } + return list; +} + +internal Vec2F32 +f_dim_from_tag_size_string(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8 string) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0, 0); + Vec2F32 result = {0}; + F_Run run = f_push_run_from_string(scratch.arena, tag, size, base_align_px, tab_size_px, 0, string); + result = run.dim; + scratch_end(scratch); + ProfEnd(); + return result; +} + +internal Vec2F32 +f_dim_from_tag_size_string_list(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8List list) +{ + ProfBeginFunction(); + Vec2F32 sum = {0}; + for(String8Node *n = list.first; n != 0; n = n->next) + { + Vec2F32 str_dim = f_dim_from_tag_size_string(tag, size, base_align_px, tab_size_px, n->string); + sum.x += str_dim.x; + sum.y = Max(sum.y, str_dim.y); + } + ProfEnd(); + return sum; +} + +internal F32 +f_column_size_from_tag_size(F_Tag tag, F32 size) +{ + F32 result = f_dim_from_tag_size_string(tag, size, 0, 0, str8_lit("H")).x; + return result; +} + +internal U64 +f_char_pos_from_tag_size_string_p(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8 string, F32 p) +{ + Temp scratch = scratch_begin(0, 0); + U64 best_offset_bytes = 0; + F32 best_offset_px = inf32(); + U64 offset_bytes = 0; + F32 offset_px = 0.f; + F_Run run = f_push_run_from_string(scratch.arena, tag, size, base_align_px, tab_size_px, 0, string); + for(U64 idx = 0; idx <= run.pieces.count; idx += 1) + { + F32 this_piece_offset_px = abs_f32(offset_px - p); + if(this_piece_offset_px < best_offset_px) + { + best_offset_bytes = offset_bytes; + best_offset_px = this_piece_offset_px; + } + if(idx < run.pieces.count) + { + F_Piece *piece = &run.pieces.v[idx]; + offset_px += piece->advance; + offset_bytes += piece->decode_size; + } + } + scratch_end(scratch); + return best_offset_bytes; +} + +//////////////////////////////// +//~ rjf: Metrics + +internal F_Metrics +f_metrics_from_tag_size(F_Tag tag, F32 size) +{ + ProfBeginFunction(); + FP_Metrics metrics = f_fp_metrics_from_tag(tag); + F_Metrics result = {0}; + { + result.ascent = floor_f32(size) * metrics.ascent / metrics.design_units_per_em; + result.descent = floor_f32(size) * metrics.descent / metrics.design_units_per_em; + result.line_gap = floor_f32(size) * metrics.line_gap / metrics.design_units_per_em; + result.capital_height = floor_f32(size) * metrics.capital_height / metrics.design_units_per_em; + } + ProfEnd(); + return result; +} + +internal F32 +f_line_height_from_metrics(F_Metrics *metrics) +{ + return metrics->ascent + metrics->descent + metrics->line_gap; +} + +//////////////////////////////// +//~ rjf: Main Calls + +internal void +f_init(void) +{ + Arena *arena = arena_alloc(); + f_state = push_array(arena, F_State, 1); + f_state->arena = arena; + f_state->font_hash_table_size = 64; + f_state->font_hash_table = push_array(arena, F_FontHashSlot, f_state->font_hash_table_size); + f_state->hash2style_slots_count = 1024; + f_state->hash2style_slots = push_array(arena, F_Hash2StyleRasterCacheSlot, f_state->hash2style_slots_count); +} diff --git a/src/font_cache/font_cache.h b/src/font_cache/font_cache.h index 79abcc36..663c5da1 100644 --- a/src/font_cache/font_cache.h +++ b/src/font_cache/font_cache.h @@ -1,270 +1,270 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef FONT_CACHE_H -#define FONT_CACHE_H - -//////////////////////////////// -//~ rjf: Rasterization Flags - -typedef U32 F_RasterFlags; -enum -{ - F_RasterFlag_Smooth = (1<<0), - F_RasterFlag_Hinted = (1<<1), -}; - -//////////////////////////////// -//~ rjf: Handles & Tags - -typedef struct F_Hash F_Hash; -struct F_Hash -{ - U64 u64[2]; -}; - -typedef struct F_Tag F_Tag; -struct F_Tag -{ - U64 u64[2]; -}; - -//////////////////////////////// -//~ rjf: Draw Package Types (For Cache Queries) - -typedef struct F_Piece F_Piece; -struct F_Piece -{ - R_Handle texture; - Rng2S16 subrect; - Vec2S16 offset; - F32 advance; - U16 decode_size; -}; - -typedef struct F_PieceChunkNode F_PieceChunkNode; -struct F_PieceChunkNode -{ - F_PieceChunkNode *next; - F_Piece *v; - U64 count; - U64 cap; -}; - -typedef struct F_PieceChunkList F_PieceChunkList; -struct F_PieceChunkList -{ - F_PieceChunkNode *first; - F_PieceChunkNode *last; - U64 node_count; - U64 total_piece_count; -}; - -typedef struct F_PieceArray F_PieceArray; -struct F_PieceArray -{ - F_Piece *v; - U64 count; -}; - -typedef struct F_Run F_Run; -struct F_Run -{ - F_PieceArray pieces; - Vec2F32 dim; - F32 ascent; - F32 descent; -}; - -//////////////////////////////// -//~ rjf: Font Path -> Handle * Metrics * Path Cache Types - -typedef struct F_FontHashNode F_FontHashNode; -struct F_FontHashNode -{ - F_FontHashNode *hash_next; - F_Tag tag; - FP_Handle handle; - FP_Metrics metrics; - String8 path; -}; - -typedef struct F_FontHashSlot F_FontHashSlot; -struct F_FontHashSlot -{ - F_FontHashNode *first; - F_FontHashNode *last; -}; - -//////////////////////////////// -//~ rjf: Rasterization Cache Types - -typedef struct F_RasterCacheInfo F_RasterCacheInfo; -struct F_RasterCacheInfo -{ - Rng2S16 subrect; - Vec2S16 raster_dim; - S16 atlas_num; - F32 advance; -}; - -typedef struct F_Hash2InfoRasterCacheNode F_Hash2InfoRasterCacheNode; -struct F_Hash2InfoRasterCacheNode -{ - F_Hash2InfoRasterCacheNode *hash_next; - F_Hash2InfoRasterCacheNode *hash_prev; - U64 hash; - F_RasterCacheInfo info; -}; - -typedef struct F_Hash2InfoRasterCacheSlot F_Hash2InfoRasterCacheSlot; -struct F_Hash2InfoRasterCacheSlot -{ - F_Hash2InfoRasterCacheNode *first; - F_Hash2InfoRasterCacheNode *last; -}; - -typedef struct F_Hash2StyleRasterCacheNode F_Hash2StyleRasterCacheNode; -struct F_Hash2StyleRasterCacheNode -{ - F_Hash2StyleRasterCacheNode *hash_next; - F_Hash2StyleRasterCacheNode *hash_prev; - U64 style_hash; - F32 ascent; - F32 descent; - F32 column_width; - F_RasterCacheInfo *utf8_class1_direct_map; - U64 utf8_class1_direct_map_mask[4]; - U64 hash2info_slots_count; - F_Hash2InfoRasterCacheSlot *hash2info_slots; -}; - -typedef struct F_Hash2StyleRasterCacheSlot F_Hash2StyleRasterCacheSlot; -struct F_Hash2StyleRasterCacheSlot -{ - F_Hash2StyleRasterCacheNode *first; - F_Hash2StyleRasterCacheNode *last; -}; - -//////////////////////////////// -//~ rjf: Atlas Types - -typedef U32 F_AtlasRegionNodeFlags; -enum -{ - F_AtlasRegionNodeFlag_Taken = (1<<0), -}; - -typedef struct F_AtlasRegionNode F_AtlasRegionNode; -struct F_AtlasRegionNode -{ - F_AtlasRegionNode *parent; - F_AtlasRegionNode *children[Corner_COUNT]; - Vec2S16 max_free_size[Corner_COUNT]; - F_AtlasRegionNodeFlags flags; - U64 num_allocated_descendants; -}; - -typedef struct F_Atlas F_Atlas; -struct F_Atlas -{ - F_Atlas *next; - F_Atlas *prev; - R_Handle texture; - Vec2S16 root_dim; - F_AtlasRegionNode *root; -}; - -//////////////////////////////// -//~ rjf: Metrics - -typedef struct F_Metrics F_Metrics; -struct F_Metrics -{ - F32 ascent; - F32 descent; - F32 line_gap; - F32 capital_height; -}; - -//////////////////////////////// -//~ rjf: Main State Type - -typedef struct F_State F_State; -struct F_State -{ - Arena *arena; - - // rjf: font table - U64 font_hash_table_size; - F_FontHashSlot *font_hash_table; - - // rjf: hash -> raster cache table - U64 hash2style_slots_count; - F_Hash2StyleRasterCacheSlot *hash2style_slots; - - // rjf: atlas list - F_Atlas *first_atlas; - F_Atlas *last_atlas; -}; - -//////////////////////////////// -//~ rjf: Globals - -global F_State *f_state = 0; - -//////////////////////////////// -//~ rjf: Basic Functions - -internal F_Hash f_hash_from_string(String8 string); -internal U64 f_little_hash_from_string(String8 string); -internal Vec2S32 f_vertex_from_corner(Corner corner); - -//////////////////////////////// -//~ rjf: Font Tags - -internal F_Tag f_tag_zero(void); -internal B32 f_tag_match(F_Tag a, F_Tag b); -internal FP_Handle f_handle_from_tag(F_Tag tag); -internal FP_Metrics f_fp_metrics_from_tag(F_Tag tag); -internal F_Tag f_tag_from_path(String8 path); -internal F_Tag f_tag_from_static_data_string(String8 *data_ptr); -internal String8 f_path_from_tag(F_Tag tag); - -//////////////////////////////// -//~ rjf: Atlas - -internal Rng2S16 f_atlas_region_alloc(Arena *arena, F_Atlas *atlas, Vec2S16 needed_size); -internal void f_atlas_region_release(F_Atlas *atlas, Rng2S16 region); - -//////////////////////////////// -//~ rjf: Piece Type Functions - -internal F_Piece *f_piece_chunk_list_push_new(Arena *arena, F_PieceChunkList *list, U64 cap); -internal void f_piece_chunk_list_push(Arena *arena, F_PieceChunkList *list, U64 cap, F_Piece *piece); -internal F_PieceArray f_piece_array_from_chunk_list(Arena *arena, F_PieceChunkList *list); -internal F_PieceArray f_piece_array_copy(Arena *arena, F_PieceArray *src); - -//////////////////////////////// -//~ rjf: Rasterization Cache - -internal F_Hash2StyleRasterCacheNode *f_hash2style_from_tag_size_flags(F_Tag tag, F32 size, F_RasterFlags flags); -internal F_Run f_push_run_from_string(Arena *arena, F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, F_RasterFlags flags, String8 string); -internal String8List f_wrapped_string_lines_from_font_size_string_max(Arena *arena, F_Tag font, F32 size, F32 base_align_px, F32 tab_size_px, String8 string, F32 max); -internal Vec2F32 f_dim_from_tag_size_string(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8 string); -internal Vec2F32 f_dim_from_tag_size_string_list(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8List list); -internal F32 f_column_size_from_tag_size(F_Tag tag, F32 size); -internal U64 f_char_pos_from_tag_size_string_p(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8 string, F32 p); - -//////////////////////////////// -//~ rjf: Metrics - -internal F_Metrics f_metrics_from_tag_size(F_Tag tag, F32 size); -internal F32 f_line_height_from_metrics(F_Metrics *metrics); - -//////////////////////////////// -//~ rjf: Main Calls - -internal void f_init(void); - -#endif // FONT_CACHE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef FONT_CACHE_H +#define FONT_CACHE_H + +//////////////////////////////// +//~ rjf: Rasterization Flags + +typedef U32 F_RasterFlags; +enum +{ + F_RasterFlag_Smooth = (1<<0), + F_RasterFlag_Hinted = (1<<1), +}; + +//////////////////////////////// +//~ rjf: Handles & Tags + +typedef struct F_Hash F_Hash; +struct F_Hash +{ + U64 u64[2]; +}; + +typedef struct F_Tag F_Tag; +struct F_Tag +{ + U64 u64[2]; +}; + +//////////////////////////////// +//~ rjf: Draw Package Types (For Cache Queries) + +typedef struct F_Piece F_Piece; +struct F_Piece +{ + R_Handle texture; + Rng2S16 subrect; + Vec2S16 offset; + F32 advance; + U16 decode_size; +}; + +typedef struct F_PieceChunkNode F_PieceChunkNode; +struct F_PieceChunkNode +{ + F_PieceChunkNode *next; + F_Piece *v; + U64 count; + U64 cap; +}; + +typedef struct F_PieceChunkList F_PieceChunkList; +struct F_PieceChunkList +{ + F_PieceChunkNode *first; + F_PieceChunkNode *last; + U64 node_count; + U64 total_piece_count; +}; + +typedef struct F_PieceArray F_PieceArray; +struct F_PieceArray +{ + F_Piece *v; + U64 count; +}; + +typedef struct F_Run F_Run; +struct F_Run +{ + F_PieceArray pieces; + Vec2F32 dim; + F32 ascent; + F32 descent; +}; + +//////////////////////////////// +//~ rjf: Font Path -> Handle * Metrics * Path Cache Types + +typedef struct F_FontHashNode F_FontHashNode; +struct F_FontHashNode +{ + F_FontHashNode *hash_next; + F_Tag tag; + FP_Handle handle; + FP_Metrics metrics; + String8 path; +}; + +typedef struct F_FontHashSlot F_FontHashSlot; +struct F_FontHashSlot +{ + F_FontHashNode *first; + F_FontHashNode *last; +}; + +//////////////////////////////// +//~ rjf: Rasterization Cache Types + +typedef struct F_RasterCacheInfo F_RasterCacheInfo; +struct F_RasterCacheInfo +{ + Rng2S16 subrect; + Vec2S16 raster_dim; + S16 atlas_num; + F32 advance; +}; + +typedef struct F_Hash2InfoRasterCacheNode F_Hash2InfoRasterCacheNode; +struct F_Hash2InfoRasterCacheNode +{ + F_Hash2InfoRasterCacheNode *hash_next; + F_Hash2InfoRasterCacheNode *hash_prev; + U64 hash; + F_RasterCacheInfo info; +}; + +typedef struct F_Hash2InfoRasterCacheSlot F_Hash2InfoRasterCacheSlot; +struct F_Hash2InfoRasterCacheSlot +{ + F_Hash2InfoRasterCacheNode *first; + F_Hash2InfoRasterCacheNode *last; +}; + +typedef struct F_Hash2StyleRasterCacheNode F_Hash2StyleRasterCacheNode; +struct F_Hash2StyleRasterCacheNode +{ + F_Hash2StyleRasterCacheNode *hash_next; + F_Hash2StyleRasterCacheNode *hash_prev; + U64 style_hash; + F32 ascent; + F32 descent; + F32 column_width; + F_RasterCacheInfo *utf8_class1_direct_map; + U64 utf8_class1_direct_map_mask[4]; + U64 hash2info_slots_count; + F_Hash2InfoRasterCacheSlot *hash2info_slots; +}; + +typedef struct F_Hash2StyleRasterCacheSlot F_Hash2StyleRasterCacheSlot; +struct F_Hash2StyleRasterCacheSlot +{ + F_Hash2StyleRasterCacheNode *first; + F_Hash2StyleRasterCacheNode *last; +}; + +//////////////////////////////// +//~ rjf: Atlas Types + +typedef U32 F_AtlasRegionNodeFlags; +enum +{ + F_AtlasRegionNodeFlag_Taken = (1<<0), +}; + +typedef struct F_AtlasRegionNode F_AtlasRegionNode; +struct F_AtlasRegionNode +{ + F_AtlasRegionNode *parent; + F_AtlasRegionNode *children[Corner_COUNT]; + Vec2S16 max_free_size[Corner_COUNT]; + F_AtlasRegionNodeFlags flags; + U64 num_allocated_descendants; +}; + +typedef struct F_Atlas F_Atlas; +struct F_Atlas +{ + F_Atlas *next; + F_Atlas *prev; + R_Handle texture; + Vec2S16 root_dim; + F_AtlasRegionNode *root; +}; + +//////////////////////////////// +//~ rjf: Metrics + +typedef struct F_Metrics F_Metrics; +struct F_Metrics +{ + F32 ascent; + F32 descent; + F32 line_gap; + F32 capital_height; +}; + +//////////////////////////////// +//~ rjf: Main State Type + +typedef struct F_State F_State; +struct F_State +{ + Arena *arena; + + // rjf: font table + U64 font_hash_table_size; + F_FontHashSlot *font_hash_table; + + // rjf: hash -> raster cache table + U64 hash2style_slots_count; + F_Hash2StyleRasterCacheSlot *hash2style_slots; + + // rjf: atlas list + F_Atlas *first_atlas; + F_Atlas *last_atlas; +}; + +//////////////////////////////// +//~ rjf: Globals + +global F_State *f_state = 0; + +//////////////////////////////// +//~ rjf: Basic Functions + +internal F_Hash f_hash_from_string(String8 string); +internal U64 f_little_hash_from_string(String8 string); +internal Vec2S32 f_vertex_from_corner(Corner corner); + +//////////////////////////////// +//~ rjf: Font Tags + +internal F_Tag f_tag_zero(void); +internal B32 f_tag_match(F_Tag a, F_Tag b); +internal FP_Handle f_handle_from_tag(F_Tag tag); +internal FP_Metrics f_fp_metrics_from_tag(F_Tag tag); +internal F_Tag f_tag_from_path(String8 path); +internal F_Tag f_tag_from_static_data_string(String8 *data_ptr); +internal String8 f_path_from_tag(F_Tag tag); + +//////////////////////////////// +//~ rjf: Atlas + +internal Rng2S16 f_atlas_region_alloc(Arena *arena, F_Atlas *atlas, Vec2S16 needed_size); +internal void f_atlas_region_release(F_Atlas *atlas, Rng2S16 region); + +//////////////////////////////// +//~ rjf: Piece Type Functions + +internal F_Piece *f_piece_chunk_list_push_new(Arena *arena, F_PieceChunkList *list, U64 cap); +internal void f_piece_chunk_list_push(Arena *arena, F_PieceChunkList *list, U64 cap, F_Piece *piece); +internal F_PieceArray f_piece_array_from_chunk_list(Arena *arena, F_PieceChunkList *list); +internal F_PieceArray f_piece_array_copy(Arena *arena, F_PieceArray *src); + +//////////////////////////////// +//~ rjf: Rasterization Cache + +internal F_Hash2StyleRasterCacheNode *f_hash2style_from_tag_size_flags(F_Tag tag, F32 size, F_RasterFlags flags); +internal F_Run f_push_run_from_string(Arena *arena, F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, F_RasterFlags flags, String8 string); +internal String8List f_wrapped_string_lines_from_font_size_string_max(Arena *arena, F_Tag font, F32 size, F32 base_align_px, F32 tab_size_px, String8 string, F32 max); +internal Vec2F32 f_dim_from_tag_size_string(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8 string); +internal Vec2F32 f_dim_from_tag_size_string_list(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8List list); +internal F32 f_column_size_from_tag_size(F_Tag tag, F32 size); +internal U64 f_char_pos_from_tag_size_string_p(F_Tag tag, F32 size, F32 base_align_px, F32 tab_size_px, String8 string, F32 p); + +//////////////////////////////// +//~ rjf: Metrics + +internal F_Metrics f_metrics_from_tag_size(F_Tag tag, F32 size); +internal F32 f_line_height_from_metrics(F_Metrics *metrics); + +//////////////////////////////// +//~ rjf: Main Calls + +internal void f_init(void); + +#endif // FONT_CACHE_H diff --git a/src/font_provider/dwrite/font_provider_dwrite.c b/src/font_provider/dwrite/font_provider_dwrite.c index fe378ab1..89ae9f44 100644 --- a/src/font_provider/dwrite/font_provider_dwrite.c +++ b/src/font_provider/dwrite/font_provider_dwrite.c @@ -1,567 +1,567 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Globals - -global FP_DWrite_State *fp_dwrite_state = 0; -global FP_DWrite_FontFileLoaderVTable fp_dwrite_static_data_font_file_loader__vtable = -{ - fp_dwrite_iunknown_noop__query_interface, - fp_dwrite_iunknown_noop__add_ref, - fp_dwrite_iunknown_noop__release, - fp_dwrite_static_font_file_loader__stream_from_key, -}; -global FP_DWrite_FontFileLoader fp_dwrite_static_data_font_file_loader = {&fp_dwrite_static_data_font_file_loader__vtable}; -global FP_DWrite_FontFileStreamVTable fp_dwrite_static_data_font_file_stream__vtable = -{ - fp_dwrite_iunknown_noop__query_interface, - fp_dwrite_iunknown_noop__add_ref, - fp_dwrite_iunknown_noop__release, - fp_dwrite_static_font_file_stream__read_file_fragment, - fp_dwrite_static_font_file_stream__release_file_fragment, - fp_dwrite_static_font_file_stream__get_file_size, - fp_dwrite_static_font_file_stream__get_last_write_time, -}; - -//////////////////////////////// -//~ rjf: Helpers - -//- rjf: handle conversion functions - -internal FP_DWrite_Font -fp_dwrite_font_from_handle(FP_Handle handle) -{ - FP_DWrite_Font result = {0}; - result.file = (IDWriteFontFile *)handle.u64[0]; - result.face = (IDWriteFontFace *)handle.u64[1]; - return result; -} - -internal FP_Handle -fp_dwrite_handle_from_font(FP_DWrite_Font font) -{ - FP_Handle result = {0}; - result.u64[0] = (U64)font.file; - result.u64[1] = (U64)font.face; - return result; -} - -//- rjf: file stream allocator - -internal FP_DWrite_FontFileStreamNode * -fp_dwrite_font_file_stream_node_alloc(String8 *data_ptr) -{ - FP_DWrite_FontFileStreamNode *node = 0; - for(FP_DWrite_FontFileStreamNode *n = fp_dwrite_state->first_stream_node; n != 0; n = n->next) - { - if(n->stream.data == data_ptr) - { - node = n; - break; - } - } - if(node == 0) - { - node = fp_dwrite_state->free_stream_node; - if(node != 0) - { - SLLStackPop(fp_dwrite_state->free_stream_node); - } - else - { - node = push_array_no_zero(fp_dwrite_state->arena, FP_DWrite_FontFileStreamNode, 1); - } - MemoryZeroStruct(node); - node->stream.lpVtbl = &fp_dwrite_static_data_font_file_stream__vtable; - node->stream.data = data_ptr; - DLLPushBack(fp_dwrite_state->first_stream_node, fp_dwrite_state->last_stream_node, node); - } - return node; -} - -internal void -fp_dwrite_font_file_stream_node_release(FP_DWrite_FontFileStreamNode *node) -{ - DLLPushBack(fp_dwrite_state->first_stream_node, fp_dwrite_state->last_stream_node, node); - SLLStackPush(fp_dwrite_state->free_stream_node, node); -} - -//- rjf: iunknown no-op helpers - -internal HRESULT -fp_dwrite_iunknown_noop__query_interface(void *obj, REFIID riid, void *ptr_to_object) -{ - return E_NOINTERFACE; -} - -internal ULONG -fp_dwrite_iunknown_noop__add_ref(void *obj) -{ - ULONG result = 1; - return result; -} - -internal ULONG -fp_dwrite_iunknown_noop__release(void *obj) -{ - ULONG result = 1; - return result; -} - -//- rjf: font file loader interface function implementations - -internal HRESULT -fp_dwrite_static_font_file_loader__stream_from_key(FP_DWrite_FontFileLoader *obj, void const *font_file_ref_key, UINT32 font_file_ref_key_size, IDWriteFontFileStream **stream_out) -{ - HRESULT result = S_OK; - String8 *key = *(String8 **)font_file_ref_key; - FP_DWrite_FontFileStreamNode *node = fp_dwrite_font_file_stream_node_alloc(key); - *stream_out = (IDWriteFontFileStream *)&node->stream; - return result; -} - -//- rjf: font file stream interface function implementations - -internal HRESULT -fp_dwrite_static_font_file_stream__read_file_fragment(FP_DWrite_FontFileStream *obj, void const **fragment_start, UINT64 file_offset, UINT64 fragment_size, void **fragment_context) -{ - HRESULT result = S_OK; - *fragment_start = obj->data->str + file_offset; - *fragment_context = 0; - return result; -} - -internal HRESULT -fp_dwrite_static_font_file_stream__release_file_fragment(FP_DWrite_FontFileStream *obj, void *fragment_context) -{ - HRESULT result = S_OK; - return result; -} - -internal HRESULT -fp_dwrite_static_font_file_stream__get_file_size(FP_DWrite_FontFileStream *obj, UINT64 *size_out) -{ - HRESULT result = S_OK; - *size_out = obj->data->size; - return result; -} - -internal HRESULT -fp_dwrite_static_font_file_stream__get_last_write_time(FP_DWrite_FontFileStream *obj, UINT64 *time_out) -{ - HRESULT result = S_OK; - *time_out = 0; - return result; -} - -//////////////////////////////// -//~ rjf: Backend Implementations - -fp_hook void -fp_init(void) -{ - ProfBeginFunction(); - HRESULT error = 0; - - //- rjf: initialize main state - { - Arena *arena = arena_alloc(); - fp_dwrite_state = push_array(arena, FP_DWrite_State, 1); - fp_dwrite_state->arena = arena; - } - - //- rjf: make dwrite factory - error = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, &IID_IDWriteFactory2, (void **)&fp_dwrite_state->factory); - if(error == S_OK) - { - fp_dwrite_state->dwrite2_is_supported = 1; - } - else - { - error = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, &IID_IDWriteFactory, (void **)&fp_dwrite_state->factory); - } - - //- rjf: register static data font "loader" interface - error = IDWriteFactory_RegisterFontFileLoader(fp_dwrite_state->factory, (IDWriteFontFileLoader *)&fp_dwrite_static_data_font_file_loader); - - //- rjf: make base rendering params - error = IDWriteFactory_CreateRenderingParams(fp_dwrite_state->factory, &fp_dwrite_state->base_rendering_params); - - //- rjf: make sharp-hinted rendering params - { - FLOAT gamma = IDWriteRenderingParams_GetGamma(fp_dwrite_state->base_rendering_params); - FLOAT enhanced_contrast = IDWriteRenderingParams_GetEnhancedContrast(fp_dwrite_state->base_rendering_params); - if(fp_dwrite_state->dwrite2_is_supported) - { - error = IDWriteFactory2_CreateCustomRenderingParams2((IDWriteFactory2 *)fp_dwrite_state->factory, - gamma, - enhanced_contrast, - enhanced_contrast, - 0.f, - DWRITE_PIXEL_GEOMETRY_FLAT, - DWRITE_RENDERING_MODE_GDI_NATURAL, - DWRITE_GRID_FIT_MODE_ENABLED, - (IDWriteRenderingParams2 **)&fp_dwrite_state->rendering_params_sharp_hinted); - } - else - { - error = IDWriteFactory_CreateCustomRenderingParams(fp_dwrite_state->factory, - gamma, - enhanced_contrast, - 0.f, - DWRITE_PIXEL_GEOMETRY_FLAT, - DWRITE_RENDERING_MODE_GDI_NATURAL, - &fp_dwrite_state->rendering_params_sharp_hinted); - } - } - - //- rjf: make sharp-unhinted rendering params - { - FLOAT gamma = IDWriteRenderingParams_GetGamma(fp_dwrite_state->base_rendering_params); - FLOAT enhanced_contrast = IDWriteRenderingParams_GetEnhancedContrast(fp_dwrite_state->base_rendering_params); - if(fp_dwrite_state->dwrite2_is_supported) - { - error = IDWriteFactory2_CreateCustomRenderingParams2((IDWriteFactory2 *)fp_dwrite_state->factory, - gamma, - enhanced_contrast, - enhanced_contrast, - 0.f, - DWRITE_PIXEL_GEOMETRY_FLAT, - DWRITE_RENDERING_MODE_GDI_NATURAL, - DWRITE_GRID_FIT_MODE_DISABLED, - (IDWriteRenderingParams2 **)&fp_dwrite_state->rendering_params_sharp_unhinted); - } - else - { - error = IDWriteFactory_CreateCustomRenderingParams(fp_dwrite_state->factory, - gamma, - enhanced_contrast, - 0.f, - DWRITE_PIXEL_GEOMETRY_FLAT, - DWRITE_RENDERING_MODE_GDI_NATURAL, - &fp_dwrite_state->rendering_params_sharp_unhinted); - } - } - - //- rjf: make smooth-hinted rendering params - { - FLOAT gamma = IDWriteRenderingParams_GetGamma(fp_dwrite_state->base_rendering_params); - FLOAT enhanced_contrast = IDWriteRenderingParams_GetEnhancedContrast(fp_dwrite_state->base_rendering_params); - if(fp_dwrite_state->dwrite2_is_supported) - { - error = IDWriteFactory2_CreateCustomRenderingParams2((IDWriteFactory2 *)fp_dwrite_state->factory, - gamma, - enhanced_contrast, - enhanced_contrast, - 0.f, - DWRITE_PIXEL_GEOMETRY_FLAT, - DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC, - DWRITE_GRID_FIT_MODE_ENABLED, - (IDWriteRenderingParams2 **)&fp_dwrite_state->rendering_params_smooth_hinted); - } - else - { - error = IDWriteFactory_CreateCustomRenderingParams(fp_dwrite_state->factory, - gamma, - enhanced_contrast, - 0.f, - DWRITE_PIXEL_GEOMETRY_FLAT, - DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC, - &fp_dwrite_state->rendering_params_smooth_hinted); - } - } - - //- rjf: make smooth rendering params - { - FLOAT gamma = 1.f; - FLOAT enhanced_contrast = 0.f; - if(fp_dwrite_state->dwrite2_is_supported) - { - error = IDWriteFactory2_CreateCustomRenderingParams2((IDWriteFactory2 *)fp_dwrite_state->factory, - gamma, - enhanced_contrast, - enhanced_contrast, - 0.f, - DWRITE_PIXEL_GEOMETRY_FLAT, - DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, - DWRITE_GRID_FIT_MODE_DISABLED, - (IDWriteRenderingParams2 **)&fp_dwrite_state->rendering_params_smooth_unhinted); - } - else - { - error = IDWriteFactory_CreateCustomRenderingParams(fp_dwrite_state->factory, - gamma, - enhanced_contrast, - 0.f, - DWRITE_PIXEL_GEOMETRY_FLAT, - DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC, - &fp_dwrite_state->rendering_params_smooth_unhinted); - } - } - - //- rjf: make dwrite gdi interop - error = IDWriteFactory_GetGdiInterop(fp_dwrite_state->factory, &fp_dwrite_state->gdi_interop); - - //- rjf: build render target for rasterization - fp_dwrite_state->bitmap_render_target_dim = v2s32(2048, 256); - error = IDWriteGdiInterop_CreateBitmapRenderTarget(fp_dwrite_state->gdi_interop, 0, fp_dwrite_state->bitmap_render_target_dim.x, fp_dwrite_state->bitmap_render_target_dim.y, &fp_dwrite_state->bitmap_render_target); - IDWriteBitmapRenderTarget_SetPixelsPerDip(fp_dwrite_state->bitmap_render_target, 1.0); - ProfEnd(); -} - -fp_hook FP_Handle -fp_font_open(String8 path) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - String16 path16 = str16_from_8(scratch.arena, path); - FP_DWrite_Font font = {0}; - HRESULT error = 0; - - //- rjf: open font file reference - error = IDWriteFactory_CreateFontFileReference(fp_dwrite_state->factory, (WCHAR *)path16.str, 0, &font.file); - - //- rjf: open font face - error = IDWriteFactory_CreateFontFace(fp_dwrite_state->factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &font.file, 0, DWRITE_FONT_SIMULATIONS_NONE, &font.face); - - //- rjf: handlify & return - FP_Handle handle = fp_dwrite_handle_from_font(font); - scratch_end(scratch); - ProfEnd(); - return handle; -} - -fp_hook FP_Handle -fp_font_open_from_static_data_string(String8 *data_ptr) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(0, 0); - FP_DWrite_Font font = {0}; - HRESULT error = 0; - - //- rjf: open font file reference - error = IDWriteFactory_CreateCustomFontFileReference(fp_dwrite_state->factory, &data_ptr, sizeof(String8 *), (IDWriteFontFileLoader *)&fp_dwrite_static_data_font_file_loader, &font.file); - - //- rjf: open font face - error = IDWriteFactory_CreateFontFace(fp_dwrite_state->factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &font.file, 0, DWRITE_FONT_SIMULATIONS_NONE, &font.face); - - //- rjf: handlify & return - FP_Handle handle = fp_dwrite_handle_from_font(font); - scratch_end(scratch); - ProfEnd(); - return handle; -} - -fp_hook void -fp_font_close(FP_Handle handle) -{ - ProfBeginFunction(); - FP_DWrite_Font font = fp_dwrite_font_from_handle(handle); - if(font.face != 0) - { - IDWriteFontFace_Release(font.face); - } - if(font.file != 0) - { - IDWriteFontFile_Release(font.file); - } - ProfEnd(); -} - -fp_hook FP_Metrics -fp_metrics_from_font(FP_Handle handle) -{ - ProfBeginFunction(); - FP_DWrite_Font font = fp_dwrite_font_from_handle(handle); - DWRITE_FONT_METRICS metrics = {0}; - if(font.face != 0) - { - IDWriteFontFace_GetMetrics(font.face, &metrics); - } - FP_Metrics result = {0}; - { - result.design_units_per_em = (F32)metrics.designUnitsPerEm; - result.ascent = (F32)metrics.ascent; - result.descent = (F32)metrics.descent; - result.line_gap = (F32)metrics.lineGap; - result.capital_height = (F32)metrics.capHeight; - } - ProfEnd(); - return result; -} - -fp_hook NO_ASAN FP_RasterResult -fp_raster(Arena *arena, FP_Handle font_handle, F32 size, FP_RasterFlags flags, String8 string) -{ - ProfBeginFunction(); - Temp scratch = scratch_begin(&arena, 1); - HRESULT error = 0; - String32 string32 = str32_from_8(scratch.arena, string); - FP_DWrite_Font font = fp_dwrite_font_from_handle(font_handle); - COLORREF bg_color = RGB(0, 0, 0); - COLORREF fg_color = RGB(255, 255, 255); - - //- rjf: get font metrics - DWRITE_FONT_METRICS font_metrics = {0}; - if(font.face != 0) - { - IDWriteFontFace_GetMetrics(font.face, &font_metrics); - } - F32 design_units_per_em = (F32)font_metrics.designUnitsPerEm; - - //- rjf: get glyph indices - U16 *glyph_indices = push_array_no_zero(scratch.arena, U16, string32.size); - if(font.face != 0) - { - error = IDWriteFontFace_GetGlyphIndices(font.face, string32.str, string32.size, glyph_indices); - } - - //- rjf: get metrics info - U64 glyphs_count = string32.size; - DWRITE_GLYPH_METRICS *glyphs_metrics = push_array_no_zero(scratch.arena, DWRITE_GLYPH_METRICS, glyphs_count); - if(font.face != 0) - { - error = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(font.face, (96.f/72.f)*size, 1.f, 0, 1, glyph_indices, glyphs_count, glyphs_metrics, 0); - } - - //- rjf: derive info from metrics - F32 advance = 0; - Vec2S16 atlas_dim = {0}; - F32 left_side_bearing = 0; - F32 right_side_bearing = 0; - if(font.face != 0) - { - atlas_dim.y = (S16)((96.f/72.f) * size * (font_metrics.ascent + font_metrics.descent) / design_units_per_em); - for(U64 idx = 0; idx < glyphs_count; idx += 1) - { - DWRITE_GLYPH_METRICS *glyph_metrics = glyphs_metrics + idx; - F32 glyph_advance_width = (96.f/72.f) * size * glyph_metrics->advanceWidth / design_units_per_em; - F32 glyph_advance_height = (96.f/72.f) * size * glyph_metrics->advanceHeight / design_units_per_em; - advance += glyph_advance_width; - atlas_dim.x = Max(atlas_dim.x, (S16)(advance+1)); - if(idx == 0) - { - left_side_bearing = (96.f/72.f) * size * glyph_metrics->leftSideBearing / design_units_per_em; - } - if(idx+1 == glyphs_count) - { - right_side_bearing = (96.f/72.f) * size * glyph_metrics->rightSideBearing / design_units_per_em; - } - } - atlas_dim.x -= right_side_bearing; - atlas_dim.x += 7; - atlas_dim.x -= atlas_dim.x%8; - } - - //- rjf: make dwrite bitmap for rendering - IDWriteBitmapRenderTarget *render_target = 0; - if(font.face != 0) - { - error = IDWriteGdiInterop_CreateBitmapRenderTarget(fp_dwrite_state->gdi_interop, 0, atlas_dim.x, atlas_dim.y, &render_target); - IDWriteBitmapRenderTarget_SetPixelsPerDip(render_target, 1.f); - } - - //- rjf: get bitmap & clear - HDC dc = 0; - if(font.face != 0) - { - dc = IDWriteBitmapRenderTarget_GetMemoryDC(render_target); - HGDIOBJ original = SelectObject(dc, GetStockObject(DC_PEN)); - SetDCPenColor(dc, bg_color); - SelectObject(dc, GetStockObject(DC_BRUSH)); - SetDCBrushColor(dc, bg_color); - Rectangle(dc, 0, 0, atlas_dim.x, atlas_dim.y); - SelectObject(dc, original); - } - - //- rjf: draw glyph run - Vec2F32 draw_p = {0, (F32)atlas_dim.y}; - if(font.face != 0) - { - F32 descent = (96.f/72.f)*size * font_metrics.descent / design_units_per_em; - draw_p.y -= descent; - } - DWRITE_GLYPH_RUN glyph_run = {0}; - if(font.face != 0) - { - glyph_run.fontFace = font.face; - glyph_run.fontEmSize = size * 96.f/72.f; - glyph_run.glyphCount = string32.size; - glyph_run.glyphIndices = glyph_indices; - } - RECT bounding_box = {0}; - if(font.face != 0) - { - IDWriteRenderingParams *rendering_params = fp_dwrite_state->rendering_params_sharp_hinted; - switch(flags) - { - default:{}break; - case 0:{rendering_params = fp_dwrite_state->rendering_params_sharp_unhinted;}break; - case FP_RasterFlag_Hinted:{rendering_params = fp_dwrite_state->rendering_params_sharp_hinted;}break; - case FP_RasterFlag_Smooth:{rendering_params = fp_dwrite_state->rendering_params_smooth_unhinted;}break; - case FP_RasterFlag_Smooth|FP_RasterFlag_Hinted:{rendering_params = fp_dwrite_state->rendering_params_smooth_hinted;}break; - } - error = IDWriteBitmapRenderTarget_DrawGlyphRun(render_target, draw_p.x, draw_p.y, - DWRITE_MEASURING_MODE_NATURAL, - &glyph_run, - rendering_params, - fg_color, - &bounding_box); - } - - //- rjf: get bitmap - DIBSECTION dib = {0}; - if(font.face != 0) - { - HBITMAP bitmap = (HBITMAP)GetCurrentObject(dc, OBJ_BITMAP); - GetObject(bitmap, sizeof(dib), &dib); - } - - //- rjf: fill & return - FP_RasterResult result = {0}; - if(font.face != 0) - { - // rjf: fill basics - result.atlas_dim = atlas_dim; - result.atlas = push_array_no_zero(arena, U8, atlas_dim.x*atlas_dim.y*4); - result.advance = floor_f32(advance); - - // rjf: fill atlas - { - U8 *in_data = (U8 *)dib.dsBm.bmBits; - U64 in_pitch = (U64)dib.dsBm.bmWidthBytes; - U8 *out_data = (U8 *)result.atlas; - U64 out_pitch = atlas_dim.x * 4; - U64 color_sum = 0; - U8 *in_line = (U8 *)in_data; - U8 *out_line = out_data; - for(U64 y = 0; y < atlas_dim.y; y += 1) - { - U8 *in_pixel = in_line; - U8 *out_pixel = out_line; - for(U64 x = 0; x < atlas_dim.x; x += 1) - { - U8 in_pixel_byte = in_pixel[0]; - out_pixel[0] = 255; - out_pixel[1] = 255; - out_pixel[2] = 255; - out_pixel[3] = in_pixel_byte; - color_sum += in_pixel_byte; - in_pixel += 4; - out_pixel += 4; - } - in_line += in_pitch; - out_line += out_pitch; - } - if(color_sum == 0) - { - result.atlas_dim = v2s16(0, 0); - } - } - IDWriteBitmapRenderTarget_Release(render_target); - } - scratch_end(scratch); - ProfEnd(); - return result; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Globals + +global FP_DWrite_State *fp_dwrite_state = 0; +global FP_DWrite_FontFileLoaderVTable fp_dwrite_static_data_font_file_loader__vtable = +{ + fp_dwrite_iunknown_noop__query_interface, + fp_dwrite_iunknown_noop__add_ref, + fp_dwrite_iunknown_noop__release, + fp_dwrite_static_font_file_loader__stream_from_key, +}; +global FP_DWrite_FontFileLoader fp_dwrite_static_data_font_file_loader = {&fp_dwrite_static_data_font_file_loader__vtable}; +global FP_DWrite_FontFileStreamVTable fp_dwrite_static_data_font_file_stream__vtable = +{ + fp_dwrite_iunknown_noop__query_interface, + fp_dwrite_iunknown_noop__add_ref, + fp_dwrite_iunknown_noop__release, + fp_dwrite_static_font_file_stream__read_file_fragment, + fp_dwrite_static_font_file_stream__release_file_fragment, + fp_dwrite_static_font_file_stream__get_file_size, + fp_dwrite_static_font_file_stream__get_last_write_time, +}; + +//////////////////////////////// +//~ rjf: Helpers + +//- rjf: handle conversion functions + +internal FP_DWrite_Font +fp_dwrite_font_from_handle(FP_Handle handle) +{ + FP_DWrite_Font result = {0}; + result.file = (IDWriteFontFile *)handle.u64[0]; + result.face = (IDWriteFontFace *)handle.u64[1]; + return result; +} + +internal FP_Handle +fp_dwrite_handle_from_font(FP_DWrite_Font font) +{ + FP_Handle result = {0}; + result.u64[0] = (U64)font.file; + result.u64[1] = (U64)font.face; + return result; +} + +//- rjf: file stream allocator + +internal FP_DWrite_FontFileStreamNode * +fp_dwrite_font_file_stream_node_alloc(String8 *data_ptr) +{ + FP_DWrite_FontFileStreamNode *node = 0; + for(FP_DWrite_FontFileStreamNode *n = fp_dwrite_state->first_stream_node; n != 0; n = n->next) + { + if(n->stream.data == data_ptr) + { + node = n; + break; + } + } + if(node == 0) + { + node = fp_dwrite_state->free_stream_node; + if(node != 0) + { + SLLStackPop(fp_dwrite_state->free_stream_node); + } + else + { + node = push_array_no_zero(fp_dwrite_state->arena, FP_DWrite_FontFileStreamNode, 1); + } + MemoryZeroStruct(node); + node->stream.lpVtbl = &fp_dwrite_static_data_font_file_stream__vtable; + node->stream.data = data_ptr; + DLLPushBack(fp_dwrite_state->first_stream_node, fp_dwrite_state->last_stream_node, node); + } + return node; +} + +internal void +fp_dwrite_font_file_stream_node_release(FP_DWrite_FontFileStreamNode *node) +{ + DLLPushBack(fp_dwrite_state->first_stream_node, fp_dwrite_state->last_stream_node, node); + SLLStackPush(fp_dwrite_state->free_stream_node, node); +} + +//- rjf: iunknown no-op helpers + +internal HRESULT +fp_dwrite_iunknown_noop__query_interface(void *obj, REFIID riid, void *ptr_to_object) +{ + return E_NOINTERFACE; +} + +internal ULONG +fp_dwrite_iunknown_noop__add_ref(void *obj) +{ + ULONG result = 1; + return result; +} + +internal ULONG +fp_dwrite_iunknown_noop__release(void *obj) +{ + ULONG result = 1; + return result; +} + +//- rjf: font file loader interface function implementations + +internal HRESULT +fp_dwrite_static_font_file_loader__stream_from_key(FP_DWrite_FontFileLoader *obj, void const *font_file_ref_key, UINT32 font_file_ref_key_size, IDWriteFontFileStream **stream_out) +{ + HRESULT result = S_OK; + String8 *key = *(String8 **)font_file_ref_key; + FP_DWrite_FontFileStreamNode *node = fp_dwrite_font_file_stream_node_alloc(key); + *stream_out = (IDWriteFontFileStream *)&node->stream; + return result; +} + +//- rjf: font file stream interface function implementations + +internal HRESULT +fp_dwrite_static_font_file_stream__read_file_fragment(FP_DWrite_FontFileStream *obj, void const **fragment_start, UINT64 file_offset, UINT64 fragment_size, void **fragment_context) +{ + HRESULT result = S_OK; + *fragment_start = obj->data->str + file_offset; + *fragment_context = 0; + return result; +} + +internal HRESULT +fp_dwrite_static_font_file_stream__release_file_fragment(FP_DWrite_FontFileStream *obj, void *fragment_context) +{ + HRESULT result = S_OK; + return result; +} + +internal HRESULT +fp_dwrite_static_font_file_stream__get_file_size(FP_DWrite_FontFileStream *obj, UINT64 *size_out) +{ + HRESULT result = S_OK; + *size_out = obj->data->size; + return result; +} + +internal HRESULT +fp_dwrite_static_font_file_stream__get_last_write_time(FP_DWrite_FontFileStream *obj, UINT64 *time_out) +{ + HRESULT result = S_OK; + *time_out = 0; + return result; +} + +//////////////////////////////// +//~ rjf: Backend Implementations + +fp_hook void +fp_init(void) +{ + ProfBeginFunction(); + HRESULT error = 0; + + //- rjf: initialize main state + { + Arena *arena = arena_alloc(); + fp_dwrite_state = push_array(arena, FP_DWrite_State, 1); + fp_dwrite_state->arena = arena; + } + + //- rjf: make dwrite factory + error = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, &IID_IDWriteFactory2, (void **)&fp_dwrite_state->factory); + if(error == S_OK) + { + fp_dwrite_state->dwrite2_is_supported = 1; + } + else + { + error = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, &IID_IDWriteFactory, (void **)&fp_dwrite_state->factory); + } + + //- rjf: register static data font "loader" interface + error = IDWriteFactory_RegisterFontFileLoader(fp_dwrite_state->factory, (IDWriteFontFileLoader *)&fp_dwrite_static_data_font_file_loader); + + //- rjf: make base rendering params + error = IDWriteFactory_CreateRenderingParams(fp_dwrite_state->factory, &fp_dwrite_state->base_rendering_params); + + //- rjf: make sharp-hinted rendering params + { + FLOAT gamma = IDWriteRenderingParams_GetGamma(fp_dwrite_state->base_rendering_params); + FLOAT enhanced_contrast = IDWriteRenderingParams_GetEnhancedContrast(fp_dwrite_state->base_rendering_params); + if(fp_dwrite_state->dwrite2_is_supported) + { + error = IDWriteFactory2_CreateCustomRenderingParams2((IDWriteFactory2 *)fp_dwrite_state->factory, + gamma, + enhanced_contrast, + enhanced_contrast, + 0.f, + DWRITE_PIXEL_GEOMETRY_FLAT, + DWRITE_RENDERING_MODE_GDI_NATURAL, + DWRITE_GRID_FIT_MODE_ENABLED, + (IDWriteRenderingParams2 **)&fp_dwrite_state->rendering_params_sharp_hinted); + } + else + { + error = IDWriteFactory_CreateCustomRenderingParams(fp_dwrite_state->factory, + gamma, + enhanced_contrast, + 0.f, + DWRITE_PIXEL_GEOMETRY_FLAT, + DWRITE_RENDERING_MODE_GDI_NATURAL, + &fp_dwrite_state->rendering_params_sharp_hinted); + } + } + + //- rjf: make sharp-unhinted rendering params + { + FLOAT gamma = IDWriteRenderingParams_GetGamma(fp_dwrite_state->base_rendering_params); + FLOAT enhanced_contrast = IDWriteRenderingParams_GetEnhancedContrast(fp_dwrite_state->base_rendering_params); + if(fp_dwrite_state->dwrite2_is_supported) + { + error = IDWriteFactory2_CreateCustomRenderingParams2((IDWriteFactory2 *)fp_dwrite_state->factory, + gamma, + enhanced_contrast, + enhanced_contrast, + 0.f, + DWRITE_PIXEL_GEOMETRY_FLAT, + DWRITE_RENDERING_MODE_GDI_NATURAL, + DWRITE_GRID_FIT_MODE_DISABLED, + (IDWriteRenderingParams2 **)&fp_dwrite_state->rendering_params_sharp_unhinted); + } + else + { + error = IDWriteFactory_CreateCustomRenderingParams(fp_dwrite_state->factory, + gamma, + enhanced_contrast, + 0.f, + DWRITE_PIXEL_GEOMETRY_FLAT, + DWRITE_RENDERING_MODE_GDI_NATURAL, + &fp_dwrite_state->rendering_params_sharp_unhinted); + } + } + + //- rjf: make smooth-hinted rendering params + { + FLOAT gamma = IDWriteRenderingParams_GetGamma(fp_dwrite_state->base_rendering_params); + FLOAT enhanced_contrast = IDWriteRenderingParams_GetEnhancedContrast(fp_dwrite_state->base_rendering_params); + if(fp_dwrite_state->dwrite2_is_supported) + { + error = IDWriteFactory2_CreateCustomRenderingParams2((IDWriteFactory2 *)fp_dwrite_state->factory, + gamma, + enhanced_contrast, + enhanced_contrast, + 0.f, + DWRITE_PIXEL_GEOMETRY_FLAT, + DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC, + DWRITE_GRID_FIT_MODE_ENABLED, + (IDWriteRenderingParams2 **)&fp_dwrite_state->rendering_params_smooth_hinted); + } + else + { + error = IDWriteFactory_CreateCustomRenderingParams(fp_dwrite_state->factory, + gamma, + enhanced_contrast, + 0.f, + DWRITE_PIXEL_GEOMETRY_FLAT, + DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC, + &fp_dwrite_state->rendering_params_smooth_hinted); + } + } + + //- rjf: make smooth rendering params + { + FLOAT gamma = 1.f; + FLOAT enhanced_contrast = 0.f; + if(fp_dwrite_state->dwrite2_is_supported) + { + error = IDWriteFactory2_CreateCustomRenderingParams2((IDWriteFactory2 *)fp_dwrite_state->factory, + gamma, + enhanced_contrast, + enhanced_contrast, + 0.f, + DWRITE_PIXEL_GEOMETRY_FLAT, + DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, + DWRITE_GRID_FIT_MODE_DISABLED, + (IDWriteRenderingParams2 **)&fp_dwrite_state->rendering_params_smooth_unhinted); + } + else + { + error = IDWriteFactory_CreateCustomRenderingParams(fp_dwrite_state->factory, + gamma, + enhanced_contrast, + 0.f, + DWRITE_PIXEL_GEOMETRY_FLAT, + DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC, + &fp_dwrite_state->rendering_params_smooth_unhinted); + } + } + + //- rjf: make dwrite gdi interop + error = IDWriteFactory_GetGdiInterop(fp_dwrite_state->factory, &fp_dwrite_state->gdi_interop); + + //- rjf: build render target for rasterization + fp_dwrite_state->bitmap_render_target_dim = v2s32(2048, 256); + error = IDWriteGdiInterop_CreateBitmapRenderTarget(fp_dwrite_state->gdi_interop, 0, fp_dwrite_state->bitmap_render_target_dim.x, fp_dwrite_state->bitmap_render_target_dim.y, &fp_dwrite_state->bitmap_render_target); + IDWriteBitmapRenderTarget_SetPixelsPerDip(fp_dwrite_state->bitmap_render_target, 1.0); + ProfEnd(); +} + +fp_hook FP_Handle +fp_font_open(String8 path) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0, 0); + String16 path16 = str16_from_8(scratch.arena, path); + FP_DWrite_Font font = {0}; + HRESULT error = 0; + + //- rjf: open font file reference + error = IDWriteFactory_CreateFontFileReference(fp_dwrite_state->factory, (WCHAR *)path16.str, 0, &font.file); + + //- rjf: open font face + error = IDWriteFactory_CreateFontFace(fp_dwrite_state->factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &font.file, 0, DWRITE_FONT_SIMULATIONS_NONE, &font.face); + + //- rjf: handlify & return + FP_Handle handle = fp_dwrite_handle_from_font(font); + scratch_end(scratch); + ProfEnd(); + return handle; +} + +fp_hook FP_Handle +fp_font_open_from_static_data_string(String8 *data_ptr) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0, 0); + FP_DWrite_Font font = {0}; + HRESULT error = 0; + + //- rjf: open font file reference + error = IDWriteFactory_CreateCustomFontFileReference(fp_dwrite_state->factory, &data_ptr, sizeof(String8 *), (IDWriteFontFileLoader *)&fp_dwrite_static_data_font_file_loader, &font.file); + + //- rjf: open font face + error = IDWriteFactory_CreateFontFace(fp_dwrite_state->factory, DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &font.file, 0, DWRITE_FONT_SIMULATIONS_NONE, &font.face); + + //- rjf: handlify & return + FP_Handle handle = fp_dwrite_handle_from_font(font); + scratch_end(scratch); + ProfEnd(); + return handle; +} + +fp_hook void +fp_font_close(FP_Handle handle) +{ + ProfBeginFunction(); + FP_DWrite_Font font = fp_dwrite_font_from_handle(handle); + if(font.face != 0) + { + IDWriteFontFace_Release(font.face); + } + if(font.file != 0) + { + IDWriteFontFile_Release(font.file); + } + ProfEnd(); +} + +fp_hook FP_Metrics +fp_metrics_from_font(FP_Handle handle) +{ + ProfBeginFunction(); + FP_DWrite_Font font = fp_dwrite_font_from_handle(handle); + DWRITE_FONT_METRICS metrics = {0}; + if(font.face != 0) + { + IDWriteFontFace_GetMetrics(font.face, &metrics); + } + FP_Metrics result = {0}; + { + result.design_units_per_em = (F32)metrics.designUnitsPerEm; + result.ascent = (F32)metrics.ascent; + result.descent = (F32)metrics.descent; + result.line_gap = (F32)metrics.lineGap; + result.capital_height = (F32)metrics.capHeight; + } + ProfEnd(); + return result; +} + +fp_hook NO_ASAN FP_RasterResult +fp_raster(Arena *arena, FP_Handle font_handle, F32 size, FP_RasterFlags flags, String8 string) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(&arena, 1); + HRESULT error = 0; + String32 string32 = str32_from_8(scratch.arena, string); + FP_DWrite_Font font = fp_dwrite_font_from_handle(font_handle); + COLORREF bg_color = RGB(0, 0, 0); + COLORREF fg_color = RGB(255, 255, 255); + + //- rjf: get font metrics + DWRITE_FONT_METRICS font_metrics = {0}; + if(font.face != 0) + { + IDWriteFontFace_GetMetrics(font.face, &font_metrics); + } + F32 design_units_per_em = (F32)font_metrics.designUnitsPerEm; + + //- rjf: get glyph indices + U16 *glyph_indices = push_array_no_zero(scratch.arena, U16, string32.size); + if(font.face != 0) + { + error = IDWriteFontFace_GetGlyphIndices(font.face, string32.str, string32.size, glyph_indices); + } + + //- rjf: get metrics info + U64 glyphs_count = string32.size; + DWRITE_GLYPH_METRICS *glyphs_metrics = push_array_no_zero(scratch.arena, DWRITE_GLYPH_METRICS, glyphs_count); + if(font.face != 0) + { + error = IDWriteFontFace_GetGdiCompatibleGlyphMetrics(font.face, (96.f/72.f)*size, 1.f, 0, 1, glyph_indices, glyphs_count, glyphs_metrics, 0); + } + + //- rjf: derive info from metrics + F32 advance = 0; + Vec2S16 atlas_dim = {0}; + F32 left_side_bearing = 0; + F32 right_side_bearing = 0; + if(font.face != 0) + { + atlas_dim.y = (S16)((96.f/72.f) * size * (font_metrics.ascent + font_metrics.descent) / design_units_per_em); + for(U64 idx = 0; idx < glyphs_count; idx += 1) + { + DWRITE_GLYPH_METRICS *glyph_metrics = glyphs_metrics + idx; + F32 glyph_advance_width = (96.f/72.f) * size * glyph_metrics->advanceWidth / design_units_per_em; + F32 glyph_advance_height = (96.f/72.f) * size * glyph_metrics->advanceHeight / design_units_per_em; + advance += glyph_advance_width; + atlas_dim.x = Max(atlas_dim.x, (S16)(advance+1)); + if(idx == 0) + { + left_side_bearing = (96.f/72.f) * size * glyph_metrics->leftSideBearing / design_units_per_em; + } + if(idx+1 == glyphs_count) + { + right_side_bearing = (96.f/72.f) * size * glyph_metrics->rightSideBearing / design_units_per_em; + } + } + atlas_dim.x -= right_side_bearing; + atlas_dim.x += 7; + atlas_dim.x -= atlas_dim.x%8; + } + + //- rjf: make dwrite bitmap for rendering + IDWriteBitmapRenderTarget *render_target = 0; + if(font.face != 0) + { + error = IDWriteGdiInterop_CreateBitmapRenderTarget(fp_dwrite_state->gdi_interop, 0, atlas_dim.x, atlas_dim.y, &render_target); + IDWriteBitmapRenderTarget_SetPixelsPerDip(render_target, 1.f); + } + + //- rjf: get bitmap & clear + HDC dc = 0; + if(font.face != 0) + { + dc = IDWriteBitmapRenderTarget_GetMemoryDC(render_target); + HGDIOBJ original = SelectObject(dc, GetStockObject(DC_PEN)); + SetDCPenColor(dc, bg_color); + SelectObject(dc, GetStockObject(DC_BRUSH)); + SetDCBrushColor(dc, bg_color); + Rectangle(dc, 0, 0, atlas_dim.x, atlas_dim.y); + SelectObject(dc, original); + } + + //- rjf: draw glyph run + Vec2F32 draw_p = {0, (F32)atlas_dim.y}; + if(font.face != 0) + { + F32 descent = (96.f/72.f)*size * font_metrics.descent / design_units_per_em; + draw_p.y -= descent; + } + DWRITE_GLYPH_RUN glyph_run = {0}; + if(font.face != 0) + { + glyph_run.fontFace = font.face; + glyph_run.fontEmSize = size * 96.f/72.f; + glyph_run.glyphCount = string32.size; + glyph_run.glyphIndices = glyph_indices; + } + RECT bounding_box = {0}; + if(font.face != 0) + { + IDWriteRenderingParams *rendering_params = fp_dwrite_state->rendering_params_sharp_hinted; + switch(flags) + { + default:{}break; + case 0:{rendering_params = fp_dwrite_state->rendering_params_sharp_unhinted;}break; + case FP_RasterFlag_Hinted:{rendering_params = fp_dwrite_state->rendering_params_sharp_hinted;}break; + case FP_RasterFlag_Smooth:{rendering_params = fp_dwrite_state->rendering_params_smooth_unhinted;}break; + case FP_RasterFlag_Smooth|FP_RasterFlag_Hinted:{rendering_params = fp_dwrite_state->rendering_params_smooth_hinted;}break; + } + error = IDWriteBitmapRenderTarget_DrawGlyphRun(render_target, draw_p.x, draw_p.y, + DWRITE_MEASURING_MODE_NATURAL, + &glyph_run, + rendering_params, + fg_color, + &bounding_box); + } + + //- rjf: get bitmap + DIBSECTION dib = {0}; + if(font.face != 0) + { + HBITMAP bitmap = (HBITMAP)GetCurrentObject(dc, OBJ_BITMAP); + GetObject(bitmap, sizeof(dib), &dib); + } + + //- rjf: fill & return + FP_RasterResult result = {0}; + if(font.face != 0) + { + // rjf: fill basics + result.atlas_dim = atlas_dim; + result.atlas = push_array_no_zero(arena, U8, atlas_dim.x*atlas_dim.y*4); + result.advance = floor_f32(advance); + + // rjf: fill atlas + { + U8 *in_data = (U8 *)dib.dsBm.bmBits; + U64 in_pitch = (U64)dib.dsBm.bmWidthBytes; + U8 *out_data = (U8 *)result.atlas; + U64 out_pitch = atlas_dim.x * 4; + U64 color_sum = 0; + U8 *in_line = (U8 *)in_data; + U8 *out_line = out_data; + for(U64 y = 0; y < atlas_dim.y; y += 1) + { + U8 *in_pixel = in_line; + U8 *out_pixel = out_line; + for(U64 x = 0; x < atlas_dim.x; x += 1) + { + U8 in_pixel_byte = in_pixel[0]; + out_pixel[0] = 255; + out_pixel[1] = 255; + out_pixel[2] = 255; + out_pixel[3] = in_pixel_byte; + color_sum += in_pixel_byte; + in_pixel += 4; + out_pixel += 4; + } + in_line += in_pitch; + out_line += out_pitch; + } + if(color_sum == 0) + { + result.atlas_dim = v2s16(0, 0); + } + } + IDWriteBitmapRenderTarget_Release(render_target); + } + scratch_end(scratch); + ProfEnd(); + return result; +} diff --git a/src/font_provider/font_provider.c b/src/font_provider/font_provider.c index 8a500e38..4b5298a6 100644 --- a/src/font_provider/font_provider.c +++ b/src/font_provider/font_provider.c @@ -1,18 +1,18 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Basic Type Functions - -internal FP_Handle -fp_handle_zero(void) -{ - FP_Handle result = {0}; - return result; -} - -internal B32 -fp_handle_match(FP_Handle a, FP_Handle b) -{ - return (a.u64[0] == b.u64[0] && a.u64[1] == b.u64[1]); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Basic Type Functions + +internal FP_Handle +fp_handle_zero(void) +{ + FP_Handle result = {0}; + return result; +} + +internal B32 +fp_handle_match(FP_Handle a, FP_Handle b) +{ + return (a.u64[0] == b.u64[0] && a.u64[1] == b.u64[1]); +} diff --git a/src/font_provider/font_provider.h b/src/font_provider/font_provider.h index 6d6c3f5b..f836aa19 100644 --- a/src/font_provider/font_provider.h +++ b/src/font_provider/font_provider.h @@ -1,59 +1,59 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef FONT_PROVIDER_H -#define FONT_PROVIDER_H - -#define fp_hook C_LINKAGE - -//////////////////////////////// -//~ rjf: Types - -typedef U32 FP_RasterFlags; -enum -{ - FP_RasterFlag_Smooth = (1<<0), - FP_RasterFlag_Hinted = (1<<1), -}; - -typedef struct FP_Handle FP_Handle; -struct FP_Handle -{ - U64 u64[2]; -}; - -typedef struct FP_Metrics FP_Metrics; -struct FP_Metrics -{ - F32 design_units_per_em; - F32 ascent; - F32 descent; - F32 line_gap; - F32 capital_height; -}; - -typedef struct FP_RasterResult FP_RasterResult; -struct FP_RasterResult -{ - Vec2S16 atlas_dim; - void *atlas; - F32 advance; -}; - -//////////////////////////////// -//~ rjf: Basic Type Functions - -internal FP_Handle fp_handle_zero(void); -internal B32 fp_handle_match(FP_Handle a, FP_Handle b); - -//////////////////////////////// -//~ rjf: Backend Hooks - -fp_hook void fp_init(void); -fp_hook FP_Handle fp_font_open(String8 path); -fp_hook FP_Handle fp_font_open_from_static_data_string(String8 *data_ptr); -fp_hook void fp_font_close(FP_Handle handle); -fp_hook FP_Metrics fp_metrics_from_font(FP_Handle font); -fp_hook NO_ASAN FP_RasterResult fp_raster(Arena *arena, FP_Handle font, F32 size, FP_RasterFlags flags, String8 string); - -#endif // FONT_PROVIDER_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef FONT_PROVIDER_H +#define FONT_PROVIDER_H + +#define fp_hook C_LINKAGE + +//////////////////////////////// +//~ rjf: Types + +typedef U32 FP_RasterFlags; +enum +{ + FP_RasterFlag_Smooth = (1<<0), + FP_RasterFlag_Hinted = (1<<1), +}; + +typedef struct FP_Handle FP_Handle; +struct FP_Handle +{ + U64 u64[2]; +}; + +typedef struct FP_Metrics FP_Metrics; +struct FP_Metrics +{ + F32 design_units_per_em; + F32 ascent; + F32 descent; + F32 line_gap; + F32 capital_height; +}; + +typedef struct FP_RasterResult FP_RasterResult; +struct FP_RasterResult +{ + Vec2S16 atlas_dim; + void *atlas; + F32 advance; +}; + +//////////////////////////////// +//~ rjf: Basic Type Functions + +internal FP_Handle fp_handle_zero(void); +internal B32 fp_handle_match(FP_Handle a, FP_Handle b); + +//////////////////////////////// +//~ rjf: Backend Hooks + +fp_hook void fp_init(void); +fp_hook FP_Handle fp_font_open(String8 path); +fp_hook FP_Handle fp_font_open_from_static_data_string(String8 *data_ptr); +fp_hook void fp_font_close(FP_Handle handle); +fp_hook FP_Metrics fp_metrics_from_font(FP_Handle font); +fp_hook NO_ASAN FP_RasterResult fp_raster(Arena *arena, FP_Handle font, F32 size, FP_RasterFlags flags, String8 string); + +#endif // FONT_PROVIDER_H diff --git a/src/font_provider/font_provider_inc.c b/src/font_provider/font_provider_inc.c index f2dc49ae..d5c7665b 100644 --- a/src/font_provider/font_provider_inc.c +++ b/src/font_provider/font_provider_inc.c @@ -1,10 +1,10 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#include "font_provider.c" - -#if FP_BACKEND == FP_BACKEND_DWRITE -# include "dwrite/font_provider_dwrite.c" -#else -# error Font provider backend not specified. -#endif +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "font_provider.c" + +#if FP_BACKEND == FP_BACKEND_DWRITE +# include "dwrite/font_provider_dwrite.c" +#else +# error Font provider backend not specified. +#endif diff --git a/src/fuzzy_search/fuzzy_search.h b/src/fuzzy_search/fuzzy_search.h index 5608c902..931311ed 100644 --- a/src/fuzzy_search/fuzzy_search.h +++ b/src/fuzzy_search/fuzzy_search.h @@ -1,202 +1,202 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef FUZZY_SEARCH_H -#define FUZZY_SEARCH_H - -//////////////////////////////// -//~ rjf: Result Types - -typedef struct FZY_Item FZY_Item; -struct FZY_Item -{ - U64 idx; // indexes into whole space of parameter tables. [rdis[0] element count) [rdis[1] element count) ... [rdis[n] element count) - U64 missed_size; - FuzzyMatchRangeList match_ranges; -}; - -typedef struct FZY_ItemChunk FZY_ItemChunk; -struct FZY_ItemChunk -{ - FZY_ItemChunk *next; - FZY_Item *v; - U64 count; - U64 cap; -}; - -typedef struct FZY_ItemChunkList FZY_ItemChunkList; -struct FZY_ItemChunkList -{ - FZY_ItemChunk *first; - FZY_ItemChunk *last; - U64 chunk_count; - U64 total_count; -}; - -typedef struct FZY_ItemArray FZY_ItemArray; -struct FZY_ItemArray -{ - FZY_Item *v; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Search Parameter Types - -typedef enum FZY_Target -{ - FZY_Target_Procedures, - FZY_Target_GlobalVariables, - FZY_Target_ThreadVariables, - FZY_Target_UDTs, - FZY_Target_COUNT -} -FZY_Target; - -typedef struct FZY_Params FZY_Params; -struct FZY_Params -{ - FZY_Target target; - DI_KeyArray dbgi_keys; -}; - -//////////////////////////////// -//~ rjf: Cache Types - -typedef struct FZY_Bucket FZY_Bucket; -struct FZY_Bucket -{ - Arena *arena; - String8 query; - FZY_Params params; - U64 params_hash; -}; - -typedef struct FZY_Node FZY_Node; -struct FZY_Node -{ - FZY_Node *next; - U128 key; - U64 touch_count; - U64 last_time_submitted_us; - FZY_Bucket buckets[3]; - U64 gen; - U64 submit_gen; - FZY_ItemArray gen_items; -}; - -typedef struct FZY_Slot FZY_Slot; -struct FZY_Slot -{ - FZY_Node *first; - FZY_Node *last; -}; - -typedef struct FZY_Stripe FZY_Stripe; -struct FZY_Stripe -{ - Arena *arena; - OS_Handle rw_mutex; - OS_Handle cv; -}; - -//////////////////////////////// -//~ rjf: Scoped Access Types - -typedef struct FZY_Touch FZY_Touch; -struct FZY_Touch -{ - FZY_Touch *next; - FZY_Node *node; -}; - -typedef struct FZY_Scope FZY_Scope; -struct FZY_Scope -{ - FZY_Scope *next; - FZY_Touch *first_touch; - FZY_Touch *last_touch; -}; - -typedef struct FZY_TCTX FZY_TCTX; -struct FZY_TCTX -{ - Arena *arena; - FZY_Scope *free_scope; - FZY_Touch *free_touch; -}; - -//////////////////////////////// -//~ rjf: Shared State Types - -typedef struct FZY_Thread FZY_Thread; -struct FZY_Thread -{ - OS_Handle thread; - OS_Handle u2f_ring_mutex; - OS_Handle u2f_ring_cv; - U64 u2f_ring_size; - U8 *u2f_ring_base; - U64 u2f_ring_write_pos; - U64 u2f_ring_read_pos; -}; - -typedef struct FZY_Shared FZY_Shared; -struct FZY_Shared -{ - Arena *arena; - - // rjf: search artifact cache table - U64 slots_count; - U64 stripes_count; - FZY_Slot *slots; - FZY_Stripe *stripes; - - // rjf: threads - U64 thread_count; - FZY_Thread *threads; -}; - -//////////////////////////////// -//~ rjf: Globals - -global FZY_Shared *fzy_shared = 0; -thread_static FZY_TCTX *fzy_tctx = 0; - -//////////////////////////////// -//~ rjf: Helpers - -internal U64 fzy_hash_from_string(U64 seed, String8 string); -internal U64 fzy_hash_from_params(FZY_Params *params); -internal U64 fzy_item_num_from_array_element_idx__linear_search(FZY_ItemArray *array, U64 element_idx); -internal String8 fzy_item_string_from_rdi_target_element_idx(RDI_Parsed *rdi, FZY_Target target, U64 element_idx); -internal FZY_Params fzy_params_copy(Arena *arena, FZY_Params *src); - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void fzy_init(void); - -//////////////////////////////// -//~ rjf: Scope Functions - -internal FZY_Scope *fzy_scope_open(void); -internal void fzy_scope_close(FZY_Scope *scope); -internal void fzy_scope_touch_node__stripe_mutex_r_guarded(FZY_Scope *scope, FZY_Node *node); - -//////////////////////////////// -//~ rjf: Cache Lookup Functions - -internal FZY_ItemArray fzy_items_from_key_params_query(FZY_Scope *scope, U128 key, FZY_Params *params, String8 query, U64 endt_us, B32 *stale_out); - -//////////////////////////////// -//~ rjf: Searcher Threads - -internal B32 fzy_u2s_enqueue_req(U128 key, U64 endt_us); -internal void fzy_u2s_dequeue_req(Arena *arena, FZY_Thread *thread, U128 *key_out); - -internal int fzy_qsort_compare_items(FZY_Item *a, FZY_Item *b); - -internal void fzy_search_thread__entry_point(void *p); - -#endif // FUZZY_SEARCH_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef FUZZY_SEARCH_H +#define FUZZY_SEARCH_H + +//////////////////////////////// +//~ rjf: Result Types + +typedef struct FZY_Item FZY_Item; +struct FZY_Item +{ + U64 idx; // indexes into whole space of parameter tables. [rdis[0] element count) [rdis[1] element count) ... [rdis[n] element count) + U64 missed_size; + FuzzyMatchRangeList match_ranges; +}; + +typedef struct FZY_ItemChunk FZY_ItemChunk; +struct FZY_ItemChunk +{ + FZY_ItemChunk *next; + FZY_Item *v; + U64 count; + U64 cap; +}; + +typedef struct FZY_ItemChunkList FZY_ItemChunkList; +struct FZY_ItemChunkList +{ + FZY_ItemChunk *first; + FZY_ItemChunk *last; + U64 chunk_count; + U64 total_count; +}; + +typedef struct FZY_ItemArray FZY_ItemArray; +struct FZY_ItemArray +{ + FZY_Item *v; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Search Parameter Types + +typedef enum FZY_Target +{ + FZY_Target_Procedures, + FZY_Target_GlobalVariables, + FZY_Target_ThreadVariables, + FZY_Target_UDTs, + FZY_Target_COUNT +} +FZY_Target; + +typedef struct FZY_Params FZY_Params; +struct FZY_Params +{ + FZY_Target target; + DI_KeyArray dbgi_keys; +}; + +//////////////////////////////// +//~ rjf: Cache Types + +typedef struct FZY_Bucket FZY_Bucket; +struct FZY_Bucket +{ + Arena *arena; + String8 query; + FZY_Params params; + U64 params_hash; +}; + +typedef struct FZY_Node FZY_Node; +struct FZY_Node +{ + FZY_Node *next; + U128 key; + U64 touch_count; + U64 last_time_submitted_us; + FZY_Bucket buckets[3]; + U64 gen; + U64 submit_gen; + FZY_ItemArray gen_items; +}; + +typedef struct FZY_Slot FZY_Slot; +struct FZY_Slot +{ + FZY_Node *first; + FZY_Node *last; +}; + +typedef struct FZY_Stripe FZY_Stripe; +struct FZY_Stripe +{ + Arena *arena; + OS_Handle rw_mutex; + OS_Handle cv; +}; + +//////////////////////////////// +//~ rjf: Scoped Access Types + +typedef struct FZY_Touch FZY_Touch; +struct FZY_Touch +{ + FZY_Touch *next; + FZY_Node *node; +}; + +typedef struct FZY_Scope FZY_Scope; +struct FZY_Scope +{ + FZY_Scope *next; + FZY_Touch *first_touch; + FZY_Touch *last_touch; +}; + +typedef struct FZY_TCTX FZY_TCTX; +struct FZY_TCTX +{ + Arena *arena; + FZY_Scope *free_scope; + FZY_Touch *free_touch; +}; + +//////////////////////////////// +//~ rjf: Shared State Types + +typedef struct FZY_Thread FZY_Thread; +struct FZY_Thread +{ + OS_Handle thread; + OS_Handle u2f_ring_mutex; + OS_Handle u2f_ring_cv; + U64 u2f_ring_size; + U8 *u2f_ring_base; + U64 u2f_ring_write_pos; + U64 u2f_ring_read_pos; +}; + +typedef struct FZY_Shared FZY_Shared; +struct FZY_Shared +{ + Arena *arena; + + // rjf: search artifact cache table + U64 slots_count; + U64 stripes_count; + FZY_Slot *slots; + FZY_Stripe *stripes; + + // rjf: threads + U64 thread_count; + FZY_Thread *threads; +}; + +//////////////////////////////// +//~ rjf: Globals + +global FZY_Shared *fzy_shared = 0; +thread_static FZY_TCTX *fzy_tctx = 0; + +//////////////////////////////// +//~ rjf: Helpers + +internal U64 fzy_hash_from_string(U64 seed, String8 string); +internal U64 fzy_hash_from_params(FZY_Params *params); +internal U64 fzy_item_num_from_array_element_idx__linear_search(FZY_ItemArray *array, U64 element_idx); +internal String8 fzy_item_string_from_rdi_target_element_idx(RDI_Parsed *rdi, FZY_Target target, U64 element_idx); +internal FZY_Params fzy_params_copy(Arena *arena, FZY_Params *src); + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void fzy_init(void); + +//////////////////////////////// +//~ rjf: Scope Functions + +internal FZY_Scope *fzy_scope_open(void); +internal void fzy_scope_close(FZY_Scope *scope); +internal void fzy_scope_touch_node__stripe_mutex_r_guarded(FZY_Scope *scope, FZY_Node *node); + +//////////////////////////////// +//~ rjf: Cache Lookup Functions + +internal FZY_ItemArray fzy_items_from_key_params_query(FZY_Scope *scope, U128 key, FZY_Params *params, String8 query, U64 endt_us, B32 *stale_out); + +//////////////////////////////// +//~ rjf: Searcher Threads + +internal B32 fzy_u2s_enqueue_req(U128 key, U64 endt_us); +internal void fzy_u2s_dequeue_req(Arena *arena, FZY_Thread *thread, U128 *key_out); + +internal int fzy_qsort_compare_items(FZY_Item *a, FZY_Item *b); + +internal void fzy_search_thread__entry_point(void *p); + +#endif // FUZZY_SEARCH_H diff --git a/src/geo_cache/geo_cache.h b/src/geo_cache/geo_cache.h index 60d882b3..62fc29fb 100644 --- a/src/geo_cache/geo_cache.h +++ b/src/geo_cache/geo_cache.h @@ -1,148 +1,148 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef GEO_CACHE_H -#define GEO_CACHE_H - -//////////////////////////////// -//~ rjf: Cache Types - -typedef struct GEO_Node GEO_Node; -struct GEO_Node -{ - GEO_Node *next; - GEO_Node *prev; - U128 hash; - R_Handle buffer; - B32 is_working; - U64 scope_ref_count; - U64 last_time_touched_us; - U64 last_user_clock_idx_touched; - U64 load_count; -}; - -typedef struct GEO_Slot GEO_Slot; -struct GEO_Slot -{ - GEO_Node *first; - GEO_Node *last; -}; - -typedef struct GEO_Stripe GEO_Stripe; -struct GEO_Stripe -{ - Arena *arena; - OS_Handle rw_mutex; - OS_Handle cv; -}; - -//////////////////////////////// -//~ rjf: Scoped Access - -typedef struct GEO_Touch GEO_Touch; -struct GEO_Touch -{ - GEO_Touch *next; - U128 hash; -}; - -typedef struct GEO_Scope GEO_Scope; -struct GEO_Scope -{ - GEO_Scope *next; - GEO_Touch *top_touch; -}; - -//////////////////////////////// -//~ rjf: Thread Context - -typedef struct GEO_TCTX GEO_TCTX; -struct GEO_TCTX -{ - Arena *arena; - GEO_Scope *free_scope; - GEO_Touch *free_touch; -}; - -//////////////////////////////// -//~ rjf: Shared State - -typedef struct GEO_Shared GEO_Shared; -struct GEO_Shared -{ - Arena *arena; - - // rjf: user clock - U64 user_clock_idx; - - // rjf: cache - U64 slots_count; - U64 stripes_count; - GEO_Slot *slots; - GEO_Stripe *stripes; - GEO_Node **stripes_free_nodes; - - // rjf: user -> xfer thread - U64 u2x_ring_size; - U8 *u2x_ring_base; - U64 u2x_ring_write_pos; - U64 u2x_ring_read_pos; - OS_Handle u2x_ring_cv; - OS_Handle u2x_ring_mutex; - - // rjf: transfer threads - U64 xfer_thread_count; - OS_Handle *xfer_threads; - - // rjf: evictor thread - OS_Handle evictor_thread; -}; - -//////////////////////////////// -//~ rjf: Globals - -thread_static GEO_TCTX *geo_tctx = 0; -global GEO_Shared *geo_shared = 0; - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void geo_init(void); - -//////////////////////////////// -//~ rjf: Thread Context Initialization - -internal void geo_tctx_ensure_inited(void); - -//////////////////////////////// -//~ rjf: User Clock - -internal void geo_user_clock_tick(void); -internal U64 geo_user_clock_idx(void); - -//////////////////////////////// -//~ rjf: Scoped Access - -internal GEO_Scope *geo_scope_open(void); -internal void geo_scope_close(GEO_Scope *scope); -internal void geo_scope_touch_node__stripe_r_guarded(GEO_Scope *scope, GEO_Node *node); - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal R_Handle geo_buffer_from_hash(GEO_Scope *scope, U128 hash); -internal R_Handle geo_buffer_from_key(GEO_Scope *scope, U128 key); - -//////////////////////////////// -//~ rjf: Transfer Threads - -internal B32 geo_u2x_enqueue_req(U128 hash, U64 endt_us); -internal void geo_u2x_dequeue_req(U128 *hash_out); -internal void geo_xfer_thread__entry_point(void *p); - -//////////////////////////////// -//~ rjf: Evictor Threads - -internal void geo_evictor_thread__entry_point(void *p); - -#endif //GEO_CACHE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef GEO_CACHE_H +#define GEO_CACHE_H + +//////////////////////////////// +//~ rjf: Cache Types + +typedef struct GEO_Node GEO_Node; +struct GEO_Node +{ + GEO_Node *next; + GEO_Node *prev; + U128 hash; + R_Handle buffer; + B32 is_working; + U64 scope_ref_count; + U64 last_time_touched_us; + U64 last_user_clock_idx_touched; + U64 load_count; +}; + +typedef struct GEO_Slot GEO_Slot; +struct GEO_Slot +{ + GEO_Node *first; + GEO_Node *last; +}; + +typedef struct GEO_Stripe GEO_Stripe; +struct GEO_Stripe +{ + Arena *arena; + OS_Handle rw_mutex; + OS_Handle cv; +}; + +//////////////////////////////// +//~ rjf: Scoped Access + +typedef struct GEO_Touch GEO_Touch; +struct GEO_Touch +{ + GEO_Touch *next; + U128 hash; +}; + +typedef struct GEO_Scope GEO_Scope; +struct GEO_Scope +{ + GEO_Scope *next; + GEO_Touch *top_touch; +}; + +//////////////////////////////// +//~ rjf: Thread Context + +typedef struct GEO_TCTX GEO_TCTX; +struct GEO_TCTX +{ + Arena *arena; + GEO_Scope *free_scope; + GEO_Touch *free_touch; +}; + +//////////////////////////////// +//~ rjf: Shared State + +typedef struct GEO_Shared GEO_Shared; +struct GEO_Shared +{ + Arena *arena; + + // rjf: user clock + U64 user_clock_idx; + + // rjf: cache + U64 slots_count; + U64 stripes_count; + GEO_Slot *slots; + GEO_Stripe *stripes; + GEO_Node **stripes_free_nodes; + + // rjf: user -> xfer thread + U64 u2x_ring_size; + U8 *u2x_ring_base; + U64 u2x_ring_write_pos; + U64 u2x_ring_read_pos; + OS_Handle u2x_ring_cv; + OS_Handle u2x_ring_mutex; + + // rjf: transfer threads + U64 xfer_thread_count; + OS_Handle *xfer_threads; + + // rjf: evictor thread + OS_Handle evictor_thread; +}; + +//////////////////////////////// +//~ rjf: Globals + +thread_static GEO_TCTX *geo_tctx = 0; +global GEO_Shared *geo_shared = 0; + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void geo_init(void); + +//////////////////////////////// +//~ rjf: Thread Context Initialization + +internal void geo_tctx_ensure_inited(void); + +//////////////////////////////// +//~ rjf: User Clock + +internal void geo_user_clock_tick(void); +internal U64 geo_user_clock_idx(void); + +//////////////////////////////// +//~ rjf: Scoped Access + +internal GEO_Scope *geo_scope_open(void); +internal void geo_scope_close(GEO_Scope *scope); +internal void geo_scope_touch_node__stripe_r_guarded(GEO_Scope *scope, GEO_Node *node); + +//////////////////////////////// +//~ rjf: Cache Lookups + +internal R_Handle geo_buffer_from_hash(GEO_Scope *scope, U128 hash); +internal R_Handle geo_buffer_from_key(GEO_Scope *scope, U128 key); + +//////////////////////////////// +//~ rjf: Transfer Threads + +internal B32 geo_u2x_enqueue_req(U128 hash, U64 endt_us); +internal void geo_u2x_dequeue_req(U128 *hash_out); +internal void geo_xfer_thread__entry_point(void *p); + +//////////////////////////////// +//~ rjf: Evictor Threads + +internal void geo_evictor_thread__entry_point(void *p); + +#endif //GEO_CACHE_H diff --git a/src/hash_store/hash_store.h b/src/hash_store/hash_store.h index d2eae943..4caa1e24 100644 --- a/src/hash_store/hash_store.h +++ b/src/hash_store/hash_store.h @@ -1,150 +1,150 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef HASH_STORE_H -#define HASH_STORE_H - -//////////////////////////////// -//~ rjf: Cache Types - -typedef struct HS_KeyNode HS_KeyNode; -struct HS_KeyNode -{ - HS_KeyNode *next; - U128 key; - U128 hash_history[2]; - U64 hash_history_gen; -}; - -typedef struct HS_KeySlot HS_KeySlot; -struct HS_KeySlot -{ - HS_KeyNode *first; - HS_KeyNode *last; -}; - -typedef struct HS_Node HS_Node; -struct HS_Node -{ - HS_Node *next; - HS_Node *prev; - U128 hash; - Arena *arena; - String8 data; - U64 scope_ref_count; - U64 key_ref_count; -}; - -typedef struct HS_Slot HS_Slot; -struct HS_Slot -{ - HS_Node *first; - HS_Node *last; -}; - -typedef struct HS_Stripe HS_Stripe; -struct HS_Stripe -{ - Arena *arena; - OS_Handle rw_mutex; - OS_Handle cv; -}; - -//////////////////////////////// -//~ rjf: Scoped Access - -typedef struct HS_Touch HS_Touch; -struct HS_Touch -{ - HS_Touch *next; - U128 hash; -}; - -typedef struct HS_Scope HS_Scope; -struct HS_Scope -{ - HS_Scope *next; - HS_Touch *top_touch; -}; - -//////////////////////////////// -//~ rjf: Thread Context - -typedef struct HS_TCTX HS_TCTX; -struct HS_TCTX -{ - Arena *arena; - HS_Scope *free_scope; - HS_Touch *free_touch; -}; - -//////////////////////////////// -//~ rjf: Shared State - -typedef struct HS_Shared HS_Shared; -struct HS_Shared -{ - Arena *arena; - - // rjf: main data cache - U64 slots_count; - U64 stripes_count; - HS_Slot *slots; - HS_Stripe *stripes; - HS_Node **stripes_free_nodes; - - // rjf: key cache - U64 key_slots_count; - U64 key_stripes_count; - HS_KeySlot *key_slots; - HS_Stripe *key_stripes; - - // rjf: evictor thread - OS_Handle evictor_thread; -}; - -//////////////////////////////// -//~ rjf: Globals - -thread_static HS_TCTX *hs_tctx = 0; -global HS_Shared *hs_shared = 0; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal U128 hs_hash_from_data(String8 data); - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void hs_init(void); - -//////////////////////////////// -//~ rjf: Thread Context Initialization - -internal void hs_tctx_ensure_inited(void); - -//////////////////////////////// -//~ rjf: Cache Submission/Derefs - -internal U128 hs_submit_data(U128 key, Arena **data_arena, String8 data); - -//////////////////////////////// -//~ rjf: Scoped Access - -internal HS_Scope *hs_scope_open(void); -internal void hs_scope_close(HS_Scope *scope); -internal void hs_scope_touch_node__stripe_r_guarded(HS_Scope *scope, HS_Node *node); - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal U128 hs_hash_from_key(U128 key, U64 rewind_count); -internal String8 hs_data_from_hash(HS_Scope *scope, U128 hash); - -//////////////////////////////// -//~ rjf: Evictor Thread - -internal void hs_evictor_thread__entry_point(void *p); - -#endif // HASH_STORE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef HASH_STORE_H +#define HASH_STORE_H + +//////////////////////////////// +//~ rjf: Cache Types + +typedef struct HS_KeyNode HS_KeyNode; +struct HS_KeyNode +{ + HS_KeyNode *next; + U128 key; + U128 hash_history[2]; + U64 hash_history_gen; +}; + +typedef struct HS_KeySlot HS_KeySlot; +struct HS_KeySlot +{ + HS_KeyNode *first; + HS_KeyNode *last; +}; + +typedef struct HS_Node HS_Node; +struct HS_Node +{ + HS_Node *next; + HS_Node *prev; + U128 hash; + Arena *arena; + String8 data; + U64 scope_ref_count; + U64 key_ref_count; +}; + +typedef struct HS_Slot HS_Slot; +struct HS_Slot +{ + HS_Node *first; + HS_Node *last; +}; + +typedef struct HS_Stripe HS_Stripe; +struct HS_Stripe +{ + Arena *arena; + OS_Handle rw_mutex; + OS_Handle cv; +}; + +//////////////////////////////// +//~ rjf: Scoped Access + +typedef struct HS_Touch HS_Touch; +struct HS_Touch +{ + HS_Touch *next; + U128 hash; +}; + +typedef struct HS_Scope HS_Scope; +struct HS_Scope +{ + HS_Scope *next; + HS_Touch *top_touch; +}; + +//////////////////////////////// +//~ rjf: Thread Context + +typedef struct HS_TCTX HS_TCTX; +struct HS_TCTX +{ + Arena *arena; + HS_Scope *free_scope; + HS_Touch *free_touch; +}; + +//////////////////////////////// +//~ rjf: Shared State + +typedef struct HS_Shared HS_Shared; +struct HS_Shared +{ + Arena *arena; + + // rjf: main data cache + U64 slots_count; + U64 stripes_count; + HS_Slot *slots; + HS_Stripe *stripes; + HS_Node **stripes_free_nodes; + + // rjf: key cache + U64 key_slots_count; + U64 key_stripes_count; + HS_KeySlot *key_slots; + HS_Stripe *key_stripes; + + // rjf: evictor thread + OS_Handle evictor_thread; +}; + +//////////////////////////////// +//~ rjf: Globals + +thread_static HS_TCTX *hs_tctx = 0; +global HS_Shared *hs_shared = 0; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal U128 hs_hash_from_data(String8 data); + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void hs_init(void); + +//////////////////////////////// +//~ rjf: Thread Context Initialization + +internal void hs_tctx_ensure_inited(void); + +//////////////////////////////// +//~ rjf: Cache Submission/Derefs + +internal U128 hs_submit_data(U128 key, Arena **data_arena, String8 data); + +//////////////////////////////// +//~ rjf: Scoped Access + +internal HS_Scope *hs_scope_open(void); +internal void hs_scope_close(HS_Scope *scope); +internal void hs_scope_touch_node__stripe_r_guarded(HS_Scope *scope, HS_Node *node); + +//////////////////////////////// +//~ rjf: Cache Lookups + +internal U128 hs_hash_from_key(U128 key, U64 rewind_count); +internal String8 hs_data_from_hash(HS_Scope *scope, U128 hash); + +//////////////////////////////// +//~ rjf: Evictor Thread + +internal void hs_evictor_thread__entry_point(void *p); + +#endif // HASH_STORE_H diff --git a/src/ico/ico.c b/src/ico/ico.c index 7ea8904c..8ac6bce5 100644 --- a/src/ico/ico.c +++ b/src/ico/ico.c @@ -1,2 +1,2 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) diff --git a/src/ico/ico.h b/src/ico/ico.h index 18df7b54..8230898d 100644 --- a/src/ico/ico.h +++ b/src/ico/ico.h @@ -1,43 +1,43 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef ICO_H -#define ICO_H - -//////////////////////////////// -//~ rjf: ICO File Format Types - -#pragma pack(push, 1) - -typedef struct ICO_Header ICO_Header; -struct ICO_Header -{ - U16 reserved_padding; // must be 0 - U16 image_type; // if 1 -> ICO, if 2 -> CUR - U16 num_images; -}; - -typedef struct ICO_Entry ICO_Entry; -struct ICO_Entry -{ - U8 image_width_px; - U8 image_height_px; - U8 num_colors; - U8 reserved_padding; // should be 0 - union - { - U16 ico_color_planes; // in ICO - U16 cur_hotspot_x_px; // in CUR - }; - union - { - U16 ico_bits_per_pixel; // in ICO - U16 cur_hotspot_y_px; // in CUR - }; - U32 image_data_size; - U32 image_data_off; -}; - -#pragma pack(pop) - -#endif // ICO_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef ICO_H +#define ICO_H + +//////////////////////////////// +//~ rjf: ICO File Format Types + +#pragma pack(push, 1) + +typedef struct ICO_Header ICO_Header; +struct ICO_Header +{ + U16 reserved_padding; // must be 0 + U16 image_type; // if 1 -> ICO, if 2 -> CUR + U16 num_images; +}; + +typedef struct ICO_Entry ICO_Entry; +struct ICO_Entry +{ + U8 image_width_px; + U8 image_height_px; + U8 num_colors; + U8 reserved_padding; // should be 0 + union + { + U16 ico_color_planes; // in ICO + U16 cur_hotspot_x_px; // in CUR + }; + union + { + U16 ico_bits_per_pixel; // in ICO + U16 cur_hotspot_y_px; // in CUR + }; + U32 image_data_size; + U32 image_data_off; +}; + +#pragma pack(pop) + +#endif // ICO_H diff --git a/src/lib_raddbg_markup/raddbg_markup.h b/src/lib_raddbg_markup/raddbg_markup.h index 66b0fa31..327eb9b5 100644 --- a/src/lib_raddbg_markup/raddbg_markup.h +++ b/src/lib_raddbg_markup/raddbg_markup.h @@ -1,78 +1,78 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RADDBG_MARKUP_H -#define RADDBG_MARKUP_H - -//////////////////////////////// -//~ Usage Macros - -#define raddbg_is_attached(...) raddbg_is_attached__impl() -#define raddbg_thread_name(fmt, ...) raddbg_thread_name__impl((fmt), __VA_ARGS__) -#define raddbg_thread_color_hex(hexcode) raddbg_thread_color__impl((hexcode)) -#define raddbg_thread_color_rgba(r, g, b, a) raddbg_thread_color__impl((unsigned int)(((r)*255) << 24) | (unsigned int)(((g)*255) << 16) | (unsigned int)(((b)*255) << 8) | (unsigned int)((a)*255)) -#define raddbg_break(...) raddbg_break__impl() -#define raddbg_break_if(expr, ...) ((expr) ? raddbg_break__impl() : (void)0) -#define raddbg_watch(fmt, ...) raddbg_watch__impl((fmt), __VA_ARGS__) -#define raddbg_pin(expr, ...) /* NOTE(rjf): inspected by debugger ui - does not change program execution */ -#define raddbg_log(fmt, ...) raddbg_log__impl((fmt), __VA_ARGS__) - -//////////////////////////////// -//~ Win32 Implementations - -#if defined(_WIN32) - -//- types - -typedef unsigned long DWORD; -typedef char const *LPCSTR; - -#pragma pack(push, 8) -typedef struct THREADNAME_INFO THREADNAME_INFO; -struct THREADNAME_INFO -{ - DWORD dwType; - LPCSTR szName; - DWORD dwThreadID; - DWORD dwFlags; -}; -#pragma pack(pop) - -//- implementations - -static inline int -raddbg_is_attached__impl(void) -{ - // TODO(rjf) - return 0; -} - -static inline void -raddbg_thread_name__impl(char *fmt, ...) -{ - // TODO(rjf) -} - -static inline void -raddbg_thread_color__impl(unsigned int hexcode) -{ - // TODO(rjf) -} - -#define raddbg_break__impl() (__debugbreak()) - -static inline void -raddbg_watch__impl(char *fmt, ...) -{ - // TODO(rjf) -} - -static inline void -raddbg_log__impl(char *fmt, ...) -{ - // TODO(rjf) -} - -#endif // defined(_WIN32) - -#endif // RADDBG_MARKUP_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RADDBG_MARKUP_H +#define RADDBG_MARKUP_H + +//////////////////////////////// +//~ Usage Macros + +#define raddbg_is_attached(...) raddbg_is_attached__impl() +#define raddbg_thread_name(fmt, ...) raddbg_thread_name__impl((fmt), __VA_ARGS__) +#define raddbg_thread_color_hex(hexcode) raddbg_thread_color__impl((hexcode)) +#define raddbg_thread_color_rgba(r, g, b, a) raddbg_thread_color__impl((unsigned int)(((r)*255) << 24) | (unsigned int)(((g)*255) << 16) | (unsigned int)(((b)*255) << 8) | (unsigned int)((a)*255)) +#define raddbg_break(...) raddbg_break__impl() +#define raddbg_break_if(expr, ...) ((expr) ? raddbg_break__impl() : (void)0) +#define raddbg_watch(fmt, ...) raddbg_watch__impl((fmt), __VA_ARGS__) +#define raddbg_pin(expr, ...) /* NOTE(rjf): inspected by debugger ui - does not change program execution */ +#define raddbg_log(fmt, ...) raddbg_log__impl((fmt), __VA_ARGS__) + +//////////////////////////////// +//~ Win32 Implementations + +#if defined(_WIN32) + +//- types + +typedef unsigned long DWORD; +typedef char const *LPCSTR; + +#pragma pack(push, 8) +typedef struct THREADNAME_INFO THREADNAME_INFO; +struct THREADNAME_INFO +{ + DWORD dwType; + LPCSTR szName; + DWORD dwThreadID; + DWORD dwFlags; +}; +#pragma pack(pop) + +//- implementations + +static inline int +raddbg_is_attached__impl(void) +{ + // TODO(rjf) + return 0; +} + +static inline void +raddbg_thread_name__impl(char *fmt, ...) +{ + // TODO(rjf) +} + +static inline void +raddbg_thread_color__impl(unsigned int hexcode) +{ + // TODO(rjf) +} + +#define raddbg_break__impl() (__debugbreak()) + +static inline void +raddbg_watch__impl(char *fmt, ...) +{ + // TODO(rjf) +} + +static inline void +raddbg_log__impl(char *fmt, ...) +{ + // TODO(rjf) +} + +#endif // defined(_WIN32) + +#endif // RADDBG_MARKUP_H diff --git a/src/lib_rdi_format/rdi_format_parse.h b/src/lib_rdi_format/rdi_format_parse.h index 66a20cdc..509f9580 100644 --- a/src/lib_rdi_format/rdi_format_parse.h +++ b/src/lib_rdi_format/rdi_format_parse.h @@ -1,224 +1,224 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////////////////////////////////////// -//~ RAD Debug Info, (R)AD(D)BG(I) Format Parsing Library -// -// Defines helper types and functions for extracting data from -// RDI files. - -//////////////////////////////////////////////////////////////// -//~ Usage Samples -// -#if 0 -// Procedure Name -> Line -{ - RDI_Parsed *rdi = ...; - char *name = "mule_main"; - RDI_Procedure *procedure = rdi_procedure_from_name_cstr(rdi, name); // 1. name -> procedure - RDI_U64 procedure_first_voff = rdi_first_voff_from_procedure(rdi, procedure); // 2. procedure -> virtual offset - RDI_Line line = rdi_line_from_voff(rdi, procedure_first_voff); // 3. virtual offset -> line - RDI_SourceFile *file = rdi_source_file_from_line(rdi, &line); // 4. line -> source file - RDI_U64 file_path_size = 0; // 5. source file -> path - RDI_U8 *file_path = rdi_normal_path_from_source_file(rdi, file, &file_path_size); - printf("%s is at %.*s:%u\n", name, (int)file_path_size, file_path, line.line_num); -} - -// Line -> Procedure Name -{ - RDI_Parsed *rdi = ...; - char *path = "c:/devel/raddebugger/src/mule/mule_main.cpp"; - RDI_U32 line_num = 2557; - RDI_SourceFile *file = rdi_source_file_from_normal_path_cstr(rdi, path); // 1. path -> source file - RDI_U64 voff = rdi_first_voff_from_source_file_line_num(rdi, file, line_num); // 2. (source file, line) -> virtual offset - RDI_Procedure *procedure = rdi_procedure_from_voff(rdi, voff); // 3. virtual offset -> procedure - RDI_U64 name_size = 0; // 4. procedure -> name - RDI_U8 *name = rdi_name_from_procedure(rdi, procedure, &name_size); - printf("%s:%u is inside %.*s\n", path, line_num, (int)name_size, name); -} -#endif - -#ifndef RDI_FORMAT_PARSE_H -#define RDI_FORMAT_PARSE_H - -//////////////////////////////////////////////////////////////// -//~ Parsed Information Types - -typedef enum RDI_ParseStatus -{ - RDI_ParseStatus_Good = 0, - RDI_ParseStatus_HeaderDoesNotMatch = 1, - RDI_ParseStatus_UnsupportedVersionNumber = 2, - RDI_ParseStatus_InvalidDataSecionLayout = 3, - RDI_ParseStatus_MissingRequiredSection = 4, -} -RDI_ParseStatus; - -typedef struct RDI_Parsed RDI_Parsed; -struct RDI_Parsed -{ - RDI_U8 *raw_data; - RDI_U64 raw_data_size; - RDI_Section *sections; - RDI_U64 sections_count; -}; - -typedef struct RDI_ParsedLineTable RDI_ParsedLineTable; -struct RDI_ParsedLineTable -{ - // NOTE: Mapping VOFF -> LINE_INFO - // - // * [ voff[i], voff[i + 1] ) forms the voff range - // * for the line info at lines[i] (and cols[i] if i < col_count) - RDI_U64* voffs; // [count + 1] sorted - RDI_Line* lines; // [count] - RDI_Column* cols; // [col_count] - RDI_U64 count; - RDI_U64 col_count; -}; - -typedef struct RDI_ParsedSourceLineMap RDI_ParsedSourceLineMap; -struct RDI_ParsedSourceLineMap -{ - // NOTE: Mapping LINE_NUMBER -> VOFFs - // - // * nums[i] gives a line number - // * that line number has one or more associated voffs - // - // * to find all associated voffs for the line number nums[i] : - // * let k span over the range [ ranges[i], ranges[i + 1] ) - // * voffs[k] gives the associated voffs - RDI_U32* nums; // [count] sorted - RDI_U32* ranges; // [count + 1] - RDI_U64* voffs; // [voff_count] - RDI_U64 count; - RDI_U64 voff_count; -}; - -typedef struct RDI_ParsedNameMap RDI_ParsedNameMap; -struct RDI_ParsedNameMap -{ - RDI_NameMapBucket *buckets; - RDI_NameMapNode *nodes; - RDI_U64 bucket_count; - RDI_U64 node_count; -}; - -//////////////////////////////// -//~ Global Nils - -static union -{ - RDI_TopLevelInfo top_level_info; - RDI_BinarySection binary_section; - RDI_FilePathNode file_path_node; - RDI_SourceFile source_file; - RDI_LineTable line_table; - RDI_SourceLineMap source_line_map; - RDI_Line line; - RDI_Column column; - RDI_Unit unit; - RDI_VMapEntry vmap_entry; - RDI_TypeNode type_node; - RDI_UDT udt; - RDI_Member member; - RDI_EnumMember enum_member; - RDI_GlobalVariable global_variable; - RDI_ThreadVariable thread_variable; - RDI_Procedure procedure; - RDI_Scope scope; - RDI_U64 voff; - RDI_LocationBlock location_block; - RDI_Local local; -} -rdi_nil_element_union = {0}; - -//////////////////////////////// -//~ Top-Level Parsing API - -RDI_PROC RDI_ParseStatus rdi_parse(RDI_U8 *data, RDI_U64 size, RDI_Parsed *out); - -//////////////////////////////// -//~ Base Parsed Info Extraction Helpers - -//- section table/element raw data extraction -RDI_PROC void *rdi_section_raw_data_from_kind(RDI_Parsed *rdi, RDI_SectionKind kind, RDI_SectionEncoding *encoding_out, RDI_U64 *size_out); -RDI_PROC void *rdi_section_raw_table_from_kind(RDI_Parsed *rdi, RDI_SectionKind kind, RDI_U64 *count_out); -RDI_PROC void *rdi_section_raw_element_from_kind_idx(RDI_Parsed *rdi, RDI_SectionKind kind, RDI_U64 idx); -#define rdi_table_from_name(rdi, name, count_out) (RDI_SectionElementType_##name *)rdi_section_raw_table_from_kind((rdi), RDI_SectionKind_##name, (count_out)) -#define rdi_element_from_name_idx(rdi, name, idx) (RDI_SectionElementType_##name *)rdi_section_raw_element_from_kind_idx((rdi), RDI_SectionKind_##name, (idx)) - -//- info about whole parse -RDI_PROC RDI_U64 rdi_decompressed_size_from_parsed(RDI_Parsed *rdi); - -//- strings -RDI_PROC RDI_U8 *rdi_string_from_idx(RDI_Parsed *rdi, RDI_U32 idx, RDI_U64 *len_out); - -//- index runs -RDI_PROC RDI_U32 *rdi_idx_run_from_first_count(RDI_Parsed *rdi, RDI_U32 raw_first, RDI_U32 raw_count, RDI_U32 *n_out); - -//- line info -RDI_PROC void rdi_parsed_from_line_table(RDI_Parsed *rdi, RDI_LineTable *line_table, RDI_ParsedLineTable *out); -RDI_PROC RDI_U64 rdi_line_info_idx_range_from_voff(RDI_ParsedLineTable *line_info, RDI_U64 voff, RDI_U64 *n_out); -RDI_PROC RDI_U64 rdi_line_info_idx_from_voff(RDI_ParsedLineTable *line_info, RDI_U64 voff); -RDI_PROC void rdi_parsed_from_source_line_map(RDI_Parsed *rdi, RDI_SourceLineMap *map, RDI_ParsedSourceLineMap *out); -RDI_PROC RDI_U64 *rdi_line_voffs_from_num(RDI_ParsedSourceLineMap *map, RDI_U32 linenum, RDI_U32 *n_out); - -//- vmap lookups -RDI_PROC RDI_U64 rdi_vmap_idx_from_voff(RDI_VMapEntry *vmap, RDI_U64 vmap_count, RDI_U64 voff); - -//- name maps -RDI_PROC RDI_NameMap *rdi_name_map_from_kind(RDI_Parsed *p, RDI_NameMapKind kind); -RDI_PROC void rdi_name_map_parse(RDI_Parsed* p, RDI_NameMap *mapptr, RDI_ParsedNameMap *out); -RDI_PROC RDI_NameMapNode *rdi_name_map_lookup(RDI_Parsed *p, RDI_ParsedNameMap *map, RDI_U8 *str, RDI_U64 len); -RDI_PROC RDI_U32 *rdi_matches_from_map_node(RDI_Parsed *p, RDI_NameMapNode *node, RDI_U32 *n_out); - -//////////////////////////////// -//~ High-Level Composite Lookup Functions - -//- procedures -RDI_PROC RDI_Procedure *rdi_procedure_from_name(RDI_Parsed *rdi, RDI_U8 *name, RDI_U64 name_size); -RDI_PROC RDI_Procedure *rdi_procedure_from_name_cstr(RDI_Parsed *rdi, char *cstr); -RDI_PROC RDI_U8 *rdi_name_from_procedure(RDI_Parsed *rdi, RDI_Procedure *procedure, RDI_U64 *len_out); -RDI_PROC RDI_Scope *rdi_root_scope_from_procedure(RDI_Parsed *rdi, RDI_Procedure *procedure); -RDI_PROC RDI_U64 rdi_first_voff_from_procedure(RDI_Parsed *rdi, RDI_Procedure *procedure); -RDI_PROC RDI_U64 rdi_opl_voff_from_procedure(RDI_Parsed *rdi, RDI_Procedure *procedure); -RDI_PROC RDI_Procedure *rdi_procedure_from_voff(RDI_Parsed *rdi, RDI_U64 voff); - -//- scopes -RDI_PROC RDI_U64 rdi_first_voff_from_scope(RDI_Parsed *rdi, RDI_Scope *scope); -RDI_PROC RDI_U64 rdi_opl_voff_from_scope(RDI_Parsed *rdi, RDI_Scope *scope); -RDI_PROC RDI_Scope *rdi_scope_from_voff(RDI_Parsed *rdi, RDI_U64 voff); -RDI_PROC RDI_Procedure *rdi_procedure_from_scope(RDI_Parsed *rdi, RDI_Scope *scope); - -//- units -RDI_PROC RDI_Unit *rdi_unit_from_voff(RDI_Parsed *rdi, RDI_U64 voff); -RDI_PROC RDI_LineTable *rdi_line_table_from_unit(RDI_Parsed *rdi, RDI_Unit *unit); - -//- line tables -RDI_PROC RDI_Line rdi_line_from_voff(RDI_Parsed *rdi, RDI_U64 voff); -RDI_PROC RDI_Line rdi_line_from_line_table_voff(RDI_Parsed *rdi, RDI_LineTable *line_table, RDI_U64 voff); -RDI_PROC RDI_SourceFile *rdi_source_file_from_line(RDI_Parsed *rdi, RDI_Line *line); - -//- source files -RDI_PROC RDI_SourceFile *rdi_source_file_from_normal_path(RDI_Parsed *rdi, RDI_U8 *name, RDI_U64 name_size); -RDI_PROC RDI_SourceFile *rdi_source_file_from_normal_path_cstr(RDI_Parsed *rdi, char *cstr); -RDI_PROC RDI_U8 *rdi_normal_path_from_source_file(RDI_Parsed *rdi, RDI_SourceFile *src_file, RDI_U64 *len_out); -RDI_PROC RDI_FilePathNode *rdi_file_path_node_from_source_file(RDI_Parsed *rdi, RDI_SourceFile *src_file); -RDI_PROC RDI_SourceLineMap *rdi_source_line_map_from_source_file(RDI_Parsed *rdi, RDI_SourceFile *src_file); -RDI_PROC RDI_U64 rdi_first_voff_from_source_file_line_num(RDI_Parsed *rdi, RDI_SourceFile *src_file, RDI_U32 line_num); - -//- source line maps -RDI_PROC RDI_U64 rdi_first_voff_from_source_line_map_num(RDI_Parsed *rdi, RDI_SourceLineMap *map, RDI_U32 line_num); - -//- file path nodes -RDI_PROC RDI_FilePathNode *rdi_parent_from_file_path_node(RDI_Parsed *rdi, RDI_FilePathNode *node); -RDI_PROC RDI_U8 *rdi_name_from_file_path_node(RDI_Parsed *rdi, RDI_FilePathNode *node, RDI_U64 *len_out); - -//////////////////////////////// -//~ Parser Helpers - -#define rdi_parse__min(a,b) (((a)<(b))?(a):(b)) -RDI_PROC RDI_U64 rdi_cstring_length(char *cstr); - -#endif // RDI_FORMAT_PARSE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////////////////////////////////////// +//~ RAD Debug Info, (R)AD(D)BG(I) Format Parsing Library +// +// Defines helper types and functions for extracting data from +// RDI files. + +//////////////////////////////////////////////////////////////// +//~ Usage Samples +// +#if 0 +// Procedure Name -> Line +{ + RDI_Parsed *rdi = ...; + char *name = "mule_main"; + RDI_Procedure *procedure = rdi_procedure_from_name_cstr(rdi, name); // 1. name -> procedure + RDI_U64 procedure_first_voff = rdi_first_voff_from_procedure(rdi, procedure); // 2. procedure -> virtual offset + RDI_Line line = rdi_line_from_voff(rdi, procedure_first_voff); // 3. virtual offset -> line + RDI_SourceFile *file = rdi_source_file_from_line(rdi, &line); // 4. line -> source file + RDI_U64 file_path_size = 0; // 5. source file -> path + RDI_U8 *file_path = rdi_normal_path_from_source_file(rdi, file, &file_path_size); + printf("%s is at %.*s:%u\n", name, (int)file_path_size, file_path, line.line_num); +} + +// Line -> Procedure Name +{ + RDI_Parsed *rdi = ...; + char *path = "c:/devel/raddebugger/src/mule/mule_main.cpp"; + RDI_U32 line_num = 2557; + RDI_SourceFile *file = rdi_source_file_from_normal_path_cstr(rdi, path); // 1. path -> source file + RDI_U64 voff = rdi_first_voff_from_source_file_line_num(rdi, file, line_num); // 2. (source file, line) -> virtual offset + RDI_Procedure *procedure = rdi_procedure_from_voff(rdi, voff); // 3. virtual offset -> procedure + RDI_U64 name_size = 0; // 4. procedure -> name + RDI_U8 *name = rdi_name_from_procedure(rdi, procedure, &name_size); + printf("%s:%u is inside %.*s\n", path, line_num, (int)name_size, name); +} +#endif + +#ifndef RDI_FORMAT_PARSE_H +#define RDI_FORMAT_PARSE_H + +//////////////////////////////////////////////////////////////// +//~ Parsed Information Types + +typedef enum RDI_ParseStatus +{ + RDI_ParseStatus_Good = 0, + RDI_ParseStatus_HeaderDoesNotMatch = 1, + RDI_ParseStatus_UnsupportedVersionNumber = 2, + RDI_ParseStatus_InvalidDataSecionLayout = 3, + RDI_ParseStatus_MissingRequiredSection = 4, +} +RDI_ParseStatus; + +typedef struct RDI_Parsed RDI_Parsed; +struct RDI_Parsed +{ + RDI_U8 *raw_data; + RDI_U64 raw_data_size; + RDI_Section *sections; + RDI_U64 sections_count; +}; + +typedef struct RDI_ParsedLineTable RDI_ParsedLineTable; +struct RDI_ParsedLineTable +{ + // NOTE: Mapping VOFF -> LINE_INFO + // + // * [ voff[i], voff[i + 1] ) forms the voff range + // * for the line info at lines[i] (and cols[i] if i < col_count) + RDI_U64* voffs; // [count + 1] sorted + RDI_Line* lines; // [count] + RDI_Column* cols; // [col_count] + RDI_U64 count; + RDI_U64 col_count; +}; + +typedef struct RDI_ParsedSourceLineMap RDI_ParsedSourceLineMap; +struct RDI_ParsedSourceLineMap +{ + // NOTE: Mapping LINE_NUMBER -> VOFFs + // + // * nums[i] gives a line number + // * that line number has one or more associated voffs + // + // * to find all associated voffs for the line number nums[i] : + // * let k span over the range [ ranges[i], ranges[i + 1] ) + // * voffs[k] gives the associated voffs + RDI_U32* nums; // [count] sorted + RDI_U32* ranges; // [count + 1] + RDI_U64* voffs; // [voff_count] + RDI_U64 count; + RDI_U64 voff_count; +}; + +typedef struct RDI_ParsedNameMap RDI_ParsedNameMap; +struct RDI_ParsedNameMap +{ + RDI_NameMapBucket *buckets; + RDI_NameMapNode *nodes; + RDI_U64 bucket_count; + RDI_U64 node_count; +}; + +//////////////////////////////// +//~ Global Nils + +static union +{ + RDI_TopLevelInfo top_level_info; + RDI_BinarySection binary_section; + RDI_FilePathNode file_path_node; + RDI_SourceFile source_file; + RDI_LineTable line_table; + RDI_SourceLineMap source_line_map; + RDI_Line line; + RDI_Column column; + RDI_Unit unit; + RDI_VMapEntry vmap_entry; + RDI_TypeNode type_node; + RDI_UDT udt; + RDI_Member member; + RDI_EnumMember enum_member; + RDI_GlobalVariable global_variable; + RDI_ThreadVariable thread_variable; + RDI_Procedure procedure; + RDI_Scope scope; + RDI_U64 voff; + RDI_LocationBlock location_block; + RDI_Local local; +} +rdi_nil_element_union = {0}; + +//////////////////////////////// +//~ Top-Level Parsing API + +RDI_PROC RDI_ParseStatus rdi_parse(RDI_U8 *data, RDI_U64 size, RDI_Parsed *out); + +//////////////////////////////// +//~ Base Parsed Info Extraction Helpers + +//- section table/element raw data extraction +RDI_PROC void *rdi_section_raw_data_from_kind(RDI_Parsed *rdi, RDI_SectionKind kind, RDI_SectionEncoding *encoding_out, RDI_U64 *size_out); +RDI_PROC void *rdi_section_raw_table_from_kind(RDI_Parsed *rdi, RDI_SectionKind kind, RDI_U64 *count_out); +RDI_PROC void *rdi_section_raw_element_from_kind_idx(RDI_Parsed *rdi, RDI_SectionKind kind, RDI_U64 idx); +#define rdi_table_from_name(rdi, name, count_out) (RDI_SectionElementType_##name *)rdi_section_raw_table_from_kind((rdi), RDI_SectionKind_##name, (count_out)) +#define rdi_element_from_name_idx(rdi, name, idx) (RDI_SectionElementType_##name *)rdi_section_raw_element_from_kind_idx((rdi), RDI_SectionKind_##name, (idx)) + +//- info about whole parse +RDI_PROC RDI_U64 rdi_decompressed_size_from_parsed(RDI_Parsed *rdi); + +//- strings +RDI_PROC RDI_U8 *rdi_string_from_idx(RDI_Parsed *rdi, RDI_U32 idx, RDI_U64 *len_out); + +//- index runs +RDI_PROC RDI_U32 *rdi_idx_run_from_first_count(RDI_Parsed *rdi, RDI_U32 raw_first, RDI_U32 raw_count, RDI_U32 *n_out); + +//- line info +RDI_PROC void rdi_parsed_from_line_table(RDI_Parsed *rdi, RDI_LineTable *line_table, RDI_ParsedLineTable *out); +RDI_PROC RDI_U64 rdi_line_info_idx_range_from_voff(RDI_ParsedLineTable *line_info, RDI_U64 voff, RDI_U64 *n_out); +RDI_PROC RDI_U64 rdi_line_info_idx_from_voff(RDI_ParsedLineTable *line_info, RDI_U64 voff); +RDI_PROC void rdi_parsed_from_source_line_map(RDI_Parsed *rdi, RDI_SourceLineMap *map, RDI_ParsedSourceLineMap *out); +RDI_PROC RDI_U64 *rdi_line_voffs_from_num(RDI_ParsedSourceLineMap *map, RDI_U32 linenum, RDI_U32 *n_out); + +//- vmap lookups +RDI_PROC RDI_U64 rdi_vmap_idx_from_voff(RDI_VMapEntry *vmap, RDI_U64 vmap_count, RDI_U64 voff); + +//- name maps +RDI_PROC RDI_NameMap *rdi_name_map_from_kind(RDI_Parsed *p, RDI_NameMapKind kind); +RDI_PROC void rdi_name_map_parse(RDI_Parsed* p, RDI_NameMap *mapptr, RDI_ParsedNameMap *out); +RDI_PROC RDI_NameMapNode *rdi_name_map_lookup(RDI_Parsed *p, RDI_ParsedNameMap *map, RDI_U8 *str, RDI_U64 len); +RDI_PROC RDI_U32 *rdi_matches_from_map_node(RDI_Parsed *p, RDI_NameMapNode *node, RDI_U32 *n_out); + +//////////////////////////////// +//~ High-Level Composite Lookup Functions + +//- procedures +RDI_PROC RDI_Procedure *rdi_procedure_from_name(RDI_Parsed *rdi, RDI_U8 *name, RDI_U64 name_size); +RDI_PROC RDI_Procedure *rdi_procedure_from_name_cstr(RDI_Parsed *rdi, char *cstr); +RDI_PROC RDI_U8 *rdi_name_from_procedure(RDI_Parsed *rdi, RDI_Procedure *procedure, RDI_U64 *len_out); +RDI_PROC RDI_Scope *rdi_root_scope_from_procedure(RDI_Parsed *rdi, RDI_Procedure *procedure); +RDI_PROC RDI_U64 rdi_first_voff_from_procedure(RDI_Parsed *rdi, RDI_Procedure *procedure); +RDI_PROC RDI_U64 rdi_opl_voff_from_procedure(RDI_Parsed *rdi, RDI_Procedure *procedure); +RDI_PROC RDI_Procedure *rdi_procedure_from_voff(RDI_Parsed *rdi, RDI_U64 voff); + +//- scopes +RDI_PROC RDI_U64 rdi_first_voff_from_scope(RDI_Parsed *rdi, RDI_Scope *scope); +RDI_PROC RDI_U64 rdi_opl_voff_from_scope(RDI_Parsed *rdi, RDI_Scope *scope); +RDI_PROC RDI_Scope *rdi_scope_from_voff(RDI_Parsed *rdi, RDI_U64 voff); +RDI_PROC RDI_Procedure *rdi_procedure_from_scope(RDI_Parsed *rdi, RDI_Scope *scope); + +//- units +RDI_PROC RDI_Unit *rdi_unit_from_voff(RDI_Parsed *rdi, RDI_U64 voff); +RDI_PROC RDI_LineTable *rdi_line_table_from_unit(RDI_Parsed *rdi, RDI_Unit *unit); + +//- line tables +RDI_PROC RDI_Line rdi_line_from_voff(RDI_Parsed *rdi, RDI_U64 voff); +RDI_PROC RDI_Line rdi_line_from_line_table_voff(RDI_Parsed *rdi, RDI_LineTable *line_table, RDI_U64 voff); +RDI_PROC RDI_SourceFile *rdi_source_file_from_line(RDI_Parsed *rdi, RDI_Line *line); + +//- source files +RDI_PROC RDI_SourceFile *rdi_source_file_from_normal_path(RDI_Parsed *rdi, RDI_U8 *name, RDI_U64 name_size); +RDI_PROC RDI_SourceFile *rdi_source_file_from_normal_path_cstr(RDI_Parsed *rdi, char *cstr); +RDI_PROC RDI_U8 *rdi_normal_path_from_source_file(RDI_Parsed *rdi, RDI_SourceFile *src_file, RDI_U64 *len_out); +RDI_PROC RDI_FilePathNode *rdi_file_path_node_from_source_file(RDI_Parsed *rdi, RDI_SourceFile *src_file); +RDI_PROC RDI_SourceLineMap *rdi_source_line_map_from_source_file(RDI_Parsed *rdi, RDI_SourceFile *src_file); +RDI_PROC RDI_U64 rdi_first_voff_from_source_file_line_num(RDI_Parsed *rdi, RDI_SourceFile *src_file, RDI_U32 line_num); + +//- source line maps +RDI_PROC RDI_U64 rdi_first_voff_from_source_line_map_num(RDI_Parsed *rdi, RDI_SourceLineMap *map, RDI_U32 line_num); + +//- file path nodes +RDI_PROC RDI_FilePathNode *rdi_parent_from_file_path_node(RDI_Parsed *rdi, RDI_FilePathNode *node); +RDI_PROC RDI_U8 *rdi_name_from_file_path_node(RDI_Parsed *rdi, RDI_FilePathNode *node, RDI_U64 *len_out); + +//////////////////////////////// +//~ Parser Helpers + +#define rdi_parse__min(a,b) (((a)<(b))?(a):(b)) +RDI_PROC RDI_U64 rdi_cstring_length(char *cstr); + +#endif // RDI_FORMAT_PARSE_H diff --git a/src/mdesk/mdesk.c b/src/mdesk/mdesk.c index f39af878..f3d0dc33 100644 --- a/src/mdesk/mdesk.c +++ b/src/mdesk/mdesk.c @@ -1,1093 +1,1093 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Message Type Functions - -internal void -md_msg_list_push(Arena *arena, MD_MsgList *msgs, MD_Node *node, MD_MsgKind kind, String8 string) -{ - MD_Msg *msg = push_array(arena, MD_Msg, 1); - msg->node = node; - msg->kind = kind; - msg->string = string; - SLLQueuePush(msgs->first, msgs->last, msg); - msgs->count += 1; - msgs->worst_message_kind = Max(kind, msgs->worst_message_kind); -} - -internal void -md_msg_list_pushf(Arena *arena, MD_MsgList *msgs, MD_Node *node, MD_MsgKind kind, char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(arena, fmt, args); - md_msg_list_push(arena, msgs, node, kind, string); - va_end(args); -} - -internal void -md_msg_list_concat_in_place(MD_MsgList *dst, MD_MsgList *to_push) -{ - if(to_push->first != 0) - { - if(dst->last) - { - dst->last->next = to_push->first; - dst->last = to_push->last; - dst->count += to_push->count; - dst->worst_message_kind = Max(dst->worst_message_kind, to_push->worst_message_kind); - } - else - { - MemoryCopyStruct(dst, to_push); - } - } - MemoryZeroStruct(to_push); -} - -//////////////////////////////// -//~ rjf: Token Type Functions - -internal MD_Token -md_token_make(Rng1U64 range, MD_TokenFlags flags) -{ - MD_Token token = {range, flags}; - return token; -} - -internal B32 -md_token_match(MD_Token a, MD_Token b) -{ - return (a.range.min == b.range.min && - a.range.max == b.range.max && - a.flags == b.flags); -} - -internal String8List -md_string_list_from_token_flags(Arena *arena, MD_TokenFlags flags) -{ - String8List strs = {0}; - if(flags & MD_TokenFlag_Identifier ){str8_list_push(arena, &strs, str8_lit("Identifier"));} - if(flags & MD_TokenFlag_Numeric ){str8_list_push(arena, &strs, str8_lit("Numeric"));} - if(flags & MD_TokenFlag_StringLiteral ){str8_list_push(arena, &strs, str8_lit("StringLiteral"));} - if(flags & MD_TokenFlag_Symbol ){str8_list_push(arena, &strs, str8_lit("Symbol"));} - if(flags & MD_TokenFlag_Reserved ){str8_list_push(arena, &strs, str8_lit("Reserved"));} - if(flags & MD_TokenFlag_Comment ){str8_list_push(arena, &strs, str8_lit("Comment"));} - if(flags & MD_TokenFlag_Whitespace ){str8_list_push(arena, &strs, str8_lit("Whitespace"));} - if(flags & MD_TokenFlag_Newline ){str8_list_push(arena, &strs, str8_lit("Newline"));} - if(flags & MD_TokenFlag_BrokenComment ){str8_list_push(arena, &strs, str8_lit("BrokenComment"));} - if(flags & MD_TokenFlag_BrokenStringLiteral ){str8_list_push(arena, &strs, str8_lit("BrokenStringLiteral"));} - if(flags & MD_TokenFlag_BadCharacter ){str8_list_push(arena, &strs, str8_lit("BadCharacter"));} - return strs; -} - -internal void -md_token_chunk_list_push(Arena *arena, MD_TokenChunkList *list, U64 cap, MD_Token token) -{ - MD_TokenChunkNode *node = list->last; - if(node == 0 || node->count >= node->cap) - { - node = push_array(arena, MD_TokenChunkNode, 1); - node->cap = cap; - node->v = push_array_no_zero(arena, MD_Token, cap); - SLLQueuePush(list->first, list->last, node); - list->chunk_count += 1; - } - MemoryCopyStruct(&node->v[node->count], &token); - node->count += 1; - list->total_token_count += 1; -} - -internal MD_TokenArray -md_token_array_from_chunk_list(Arena *arena, MD_TokenChunkList *chunks) -{ - MD_TokenArray result = {0}; - result.count = chunks->total_token_count; - result.v = push_array_no_zero(arena, MD_Token, result.count); - U64 write_idx = 0; - for(MD_TokenChunkNode *n = chunks->first; n != 0; n = n->next) - { - MemoryCopy(result.v+write_idx, n->v, sizeof(MD_Token)*n->count); - write_idx += n->count; - } - return result; -} - -internal String8 -md_content_string_from_token_flags_str8(MD_TokenFlags flags, String8 string) -{ - U64 num_chop = 0; - U64 num_skip = 0; - { - num_skip += 3*!!(flags & MD_TokenFlag_StringTriplet); - num_chop += 3*!!(flags & MD_TokenFlag_StringTriplet); - num_skip += 1*(!(flags & MD_TokenFlag_StringTriplet) && flags & MD_TokenFlag_StringLiteral); - num_chop += 1*(!(flags & MD_TokenFlag_StringTriplet) && flags & MD_TokenFlag_StringLiteral); - } - String8 result = string; - result = str8_chop(result, num_chop); - result = str8_skip(result, num_skip); - return result; -} - -//////////////////////////////// -//~ rjf: Node Type Functions - -//- rjf: flag conversions - -internal MD_NodeFlags -md_node_flags_from_token_flags(MD_TokenFlags flags) -{ - MD_NodeFlags result = 0; - result |= MD_NodeFlag_Identifier*!!(flags&MD_TokenFlag_Identifier); - result |= MD_NodeFlag_Numeric*!!(flags&MD_TokenFlag_Numeric); - result |= MD_NodeFlag_StringLiteral*!!(flags&MD_TokenFlag_StringLiteral); - result |= MD_NodeFlag_Symbol*!!(flags&MD_TokenFlag_Symbol); - result |= MD_NodeFlag_StringSingleQuote *!!(flags&MD_TokenFlag_StringSingleQuote); - result |= MD_NodeFlag_StringDoubleQuote *!!(flags&MD_TokenFlag_StringDoubleQuote); - result |= MD_NodeFlag_StringTick*!!(flags&MD_TokenFlag_StringTick); - result |= MD_NodeFlag_StringTriplet*!!(flags&MD_TokenFlag_StringTriplet); - return result; -} - -//- rjf: nil - -internal B32 -md_node_is_nil(MD_Node *node) -{ - return (node == 0 || node == &md_nil_node || node->kind == MD_NodeKind_Nil); -} - -//- rjf: iteration - -internal MD_NodeRec -md_node_rec_depth_first(MD_Node *node, MD_Node *subtree_root, U64 child_off, U64 sib_off) -{ - MD_NodeRec rec = {0}; - rec.next = &md_nil_node; - if(!md_node_is_nil(MemberFromOffset(MD_Node *, node, child_off))) - { - rec.next = MemberFromOffset(MD_Node *, node, child_off); - rec.push_count = 1; - } - else for(MD_Node *p = node; !md_node_is_nil(p) && p != subtree_root; p = p->parent, rec.pop_count += 1) - { - if(!md_node_is_nil(MemberFromOffset(MD_Node *, p, sib_off))) - { - rec.next = MemberFromOffset(MD_Node *, p, sib_off); - break; - } - } - return rec; -} - -//- rjf: tree building - -internal MD_Node * -md_push_node(Arena *arena, MD_NodeKind kind, MD_NodeFlags flags, String8 string, String8 raw_string, U64 src_offset) -{ - MD_Node *node = push_array(arena, MD_Node, 1); - node->first = node->last = node->parent = node->next = node->prev = node->first_tag = node->last_tag = &md_nil_node; - node->kind = kind; - node->flags = flags; - node->string = string; - node->raw_string = raw_string; - node->src_offset = src_offset; - return node; -} - -internal void -md_node_push_child(MD_Node *parent, MD_Node *node) -{ - node->parent = parent; - DLLPushBack_NPZ(&md_nil_node, parent->first, parent->last, node, next, prev); -} - -internal void -md_node_push_tag(MD_Node *parent, MD_Node *node) -{ - node->parent = parent; - DLLPushBack_NPZ(&md_nil_node, parent->first_tag, parent->last_tag, node, next, prev); -} - -//- rjf: tree introspection - -internal MD_Node * -md_node_from_chain_string(MD_Node *first, MD_Node *opl, String8 string, StringMatchFlags flags) -{ - MD_Node *result = &md_nil_node; - for(MD_Node *n = first; !md_node_is_nil(n) && n != opl; n = n->next) - { - if(str8_match(n->string, string, flags)) - { - result = n; - break; - } - } - return result; -} - -internal MD_Node * -md_node_from_chain_index(MD_Node *first, MD_Node *opl, U64 index) -{ - MD_Node *result = &md_nil_node; - S64 idx = 0; - for(MD_Node *n = first; !md_node_is_nil(n) && n != opl; n = n->next, idx += 1) - { - if(index == idx) - { - result = n; - break; - } - } - return result; -} - -internal MD_Node * -md_node_from_chain_flags(MD_Node *first, MD_Node *opl, MD_NodeFlags flags) -{ - MD_Node *result = &md_nil_node; - for(MD_Node *n = first; !md_node_is_nil(n) && n != opl; n = n->next) - { - if(n->flags & flags) - { - result = n; - break; - } - } - return result; -} - -internal U64 -md_index_from_node(MD_Node *node) -{ - U64 index = 0; - for(MD_Node *n = node->prev; !md_node_is_nil(n); n = n->prev) - { - index += 1; - } - return index; -} - -internal MD_Node * -md_root_from_node(MD_Node *node) -{ - MD_Node *result = node; - for(MD_Node *p = node->parent; (p->kind == MD_NodeKind_Main || p->kind == MD_NodeKind_Tag) && !md_node_is_nil(p); p = p->parent) - { - result = p; - } - return result; -} - -internal MD_Node * -md_child_from_string(MD_Node *node, String8 child_string, StringMatchFlags flags) -{ - return md_node_from_chain_string(node->first, &md_nil_node, child_string, flags); -} - -internal MD_Node * -md_tag_from_string(MD_Node *node, String8 tag_string, StringMatchFlags flags) -{ - return md_node_from_chain_string(node->first_tag, &md_nil_node, tag_string, flags); -} - -internal MD_Node * -md_child_from_index(MD_Node *node, U64 index) -{ - return md_node_from_chain_index(node->first, &md_nil_node, index); -} - -internal MD_Node * -md_tag_from_index(MD_Node *node, U64 index) -{ - return md_node_from_chain_index(node->first_tag, &md_nil_node, index); -} - -internal MD_Node * -md_tag_arg_from_index(MD_Node *node, String8 tag_string, StringMatchFlags flags, U64 index) -{ - MD_Node *tag = md_tag_from_string(node, tag_string, flags); - return md_child_from_index(tag, index); -} - -internal MD_Node * -md_tag_arg_from_string(MD_Node *node, String8 tag_string, StringMatchFlags tag_str_flags, String8 arg_string, StringMatchFlags arg_str_flags) -{ - MD_Node *tag = md_tag_from_string(node, tag_string, tag_str_flags); - MD_Node *arg = md_child_from_string(tag, arg_string, arg_str_flags); - return arg; -} - -internal B32 -md_node_has_child(MD_Node *node, String8 string, StringMatchFlags flags) -{ - return !md_node_is_nil(md_child_from_string(node, string, flags)); -} - -internal B32 -md_node_has_tag(MD_Node *node, String8 string, StringMatchFlags flags) -{ - return !md_node_is_nil(md_tag_from_string(node, string, flags)); -} - -internal U64 -md_child_count_from_node(MD_Node *node) -{ - U64 result = 0; - for(MD_Node *child = node->first; !md_node_is_nil(child); child = child->next) - { - result += 1; - } - return result; -} - -internal U64 -md_tag_count_from_node(MD_Node *node) -{ - U64 result = 0; - for(MD_Node *child = node->first_tag; !md_node_is_nil(child); child = child->next) - { - result += 1; - } - return result; -} - -//- rjf: tree comparison - -internal B32 -md_node_match(MD_Node *a, MD_Node *b, StringMatchFlags flags) -{ - B32 result = 0; - if(a->kind == b->kind && str8_match(a->string, b->string, flags)) - { - result = 1; - if(result) - { - result = result && a->flags == b->flags; - } - if(result && a->kind != MD_NodeKind_Tag) - { - for(MD_Node *a_tag = a->first_tag, *b_tag = b->first_tag; - !md_node_is_nil(a_tag) || !md_node_is_nil(b_tag); - a_tag = a_tag->next, b_tag = b_tag->next) - { - if(md_node_match(a_tag, b_tag, flags)) - { - for(MD_Node *a_tag_arg = a_tag->first, *b_tag_arg = b_tag->first; - !md_node_is_nil(a_tag_arg) || !md_node_is_nil(b_tag_arg); - a_tag_arg = a_tag_arg->next, b_tag_arg = b_tag_arg->next) - { - if(!md_node_deep_match(a_tag_arg, b_tag_arg, flags)) - { - result = 0; - goto end; - } - } - } - else - { - result = 0; - goto end; - } - } - } - } - end:; - return result; -} - -internal B32 -md_node_deep_match(MD_Node *a, MD_Node *b, StringMatchFlags flags) -{ - B32 result = md_node_match(a, b, flags); - if(result) - { - for(MD_Node *a_child = a->first, *b_child = b->first; - !md_node_is_nil(a_child) || !md_node_is_nil(b_child); - a_child = a_child->next, b_child = b_child->next) - { - if(!md_node_deep_match(a_child, b_child, flags)) - { - result = 0; - goto end; - } - } - } - end:; - return result; -} - -//////////////////////////////// -//~ rjf: Text -> Tokens Functions - -internal MD_TokenizeResult -md_tokenize_from_text(Arena *arena, String8 text) -{ - Temp scratch = scratch_begin(&arena, 1); - MD_TokenChunkList tokens = {0}; - MD_MsgList msgs = {0}; - U8 *byte_first = text.str; - U8 *byte_opl = byte_first + text.size; - U8 *byte = byte_first; - - //- rjf: scan string & produce tokens - for(;byte < byte_opl;) - { - MD_TokenFlags token_flags = 0; - U8 *token_start = 0; - U8 *token_opl = 0; - - //- rjf: whitespace - if(token_flags == 0 && (*byte == ' ' || *byte == '\t' || *byte == '\v' || *byte == '\r')) - { - token_flags = MD_TokenFlag_Whitespace; - token_start = byte; - token_opl = byte; - byte += 1; - for(;byte <= byte_opl; byte += 1) - { - token_opl += 1; - if(byte == byte_opl || (*byte != ' ' && *byte != '\t' && *byte != '\v' && *byte != '\r')) - { - break; - } - } - } - - //- rjf: newlines - if(token_flags == 0 && *byte == '\n') - { - token_flags = MD_TokenFlag_Newline; - token_start = byte; - token_opl = byte+1; - byte += 1; - } - - //- rjf: single-line comments - if(token_flags == 0 && (byte+1 < byte_opl && *byte == '/' && byte[1] == '/')) - { - token_flags = MD_TokenFlag_Comment; - token_start = byte; - token_opl = byte+2; - byte += 2; - B32 escaped = 0; - for(;byte <= byte_opl; byte += 1) - { - token_opl += 1; - if(byte == byte_opl) - { - break; - } - if(escaped) - { - escaped = 0; - } - else - { - if(*byte == '\n') - { - break; - } - else if(*byte == '\\') - { - escaped = 1; - } - } - } - } - - //- rjf: multi-line comments - if(token_flags == 0 && (byte+1 < byte_opl && *byte == '/' && byte[1] == '*')) - { - token_flags = MD_TokenFlag_Comment; - token_start = byte; - token_opl = byte+2; - byte += 2; - for(;byte <= byte_opl; byte += 1) - { - token_opl += 1; - if(byte == byte_opl) - { - token_flags |= MD_TokenFlag_BrokenComment; - break; - } - if(byte+1 < byte_opl && byte[0] == '*' && byte[1] == '/') - { - token_opl += 2; - break; - } - } - } - - //- rjf: identifiers - if(token_flags == 0 && (('A' <= *byte && *byte <= 'Z') || - ('a' <= *byte && *byte <= 'z') || - *byte == '_' || - utf8_class[*byte>>3] >= 2 )) - { - token_flags = MD_TokenFlag_Identifier; - token_start = byte; - token_opl = byte; - byte += 1; - for(;byte <= byte_opl; byte += 1) - { - token_opl += 1; - if(byte == byte_opl || - (!('A' <= *byte && *byte <= 'Z') && - !('a' <= *byte && *byte <= 'z') && - !('0' <= *byte && *byte <= '9') && - *byte != '_' && - utf8_class[*byte>>3] < 2)) - { - break; - } - } - } - - //- rjf: numerics - if(token_flags == 0 && (('0' <= *byte && *byte <= '9') || - (*byte == '.' && byte+1 < byte_opl && '0' <= byte[1] && byte[1] <= '9') || - (*byte == '-' && byte+1 < byte_opl && '0' <= byte[1] && byte[1] <= '9') || - *byte == '_')) - { - token_flags = MD_TokenFlag_Numeric; - token_start = byte; - token_opl = byte; - byte += 1; - for(;byte <= byte_opl; byte += 1) - { - token_opl += 1; - if(byte == byte_opl || - (!('A' <= *byte && *byte <= 'Z') && - !('a' <= *byte && *byte <= 'z') && - !('0' <= *byte && *byte <= '9') && - *byte != '_' && - *byte != '.')) - { - break; - } - } - } - - //- rjf: triplet string literals - if(token_flags == 0 && byte+2 < byte_opl && - ((byte[0] == '"' && byte[1] == '"' && byte[2] == '"') || - (byte[0] == '\''&& byte[1] == '\''&& byte[2] == '\'') || - (byte[0] == '`' && byte[1] == '`' && byte[2] == '`'))) - { - U8 literal_style = byte[0]; - token_flags = MD_TokenFlag_StringLiteral|MD_TokenFlag_StringTriplet; - token_flags |= (literal_style == '\'')*MD_TokenFlag_StringSingleQuote; - token_flags |= (literal_style == '"')*MD_TokenFlag_StringDoubleQuote; - token_flags |= (literal_style == '`')*MD_TokenFlag_StringTick; - token_start = byte; - token_opl = byte+3; - byte += 3; - for(;byte <= byte_opl; byte += 1) - { - if(byte == byte_opl) - { - token_flags |= MD_TokenFlag_BrokenStringLiteral; - token_opl = byte; - break; - } - if(byte+2 < byte_opl && (byte[0] == literal_style && byte[1] == literal_style && byte[2] == literal_style)) - { - byte += 3; - token_opl = byte; - break; - } - } - } - - //- rjf: singlet string literals - if(token_flags == 0 && (byte[0] == '"' || byte[0] == '\'' || byte[0] == '`')) - { - U8 literal_style = byte[0]; - token_flags = MD_TokenFlag_StringLiteral; - token_flags |= (literal_style == '\'')*MD_TokenFlag_StringSingleQuote; - token_flags |= (literal_style == '"')*MD_TokenFlag_StringDoubleQuote; - token_flags |= (literal_style == '`')*MD_TokenFlag_StringTick; - token_start = byte; - token_opl = byte+1; - byte += 1; - B32 escaped = 0; - for(;byte <= byte_opl; byte += 1) - { - if(byte == byte_opl || *byte == '\n') - { - token_opl = byte; - token_flags |= MD_TokenFlag_BrokenStringLiteral; - break; - } - if(!escaped && byte[0] == '\\') - { - escaped = 1; - } - else if(!escaped && byte[0] == literal_style) - { - token_opl = byte+1; - byte += 1; - break; - } - else if(escaped) - { - escaped = 0; - } - } - } - - //- rjf: non-reserved symbols - if(token_flags == 0 && (*byte == '~' || *byte == '!' || *byte == '$' || *byte == '%' || *byte == '^' || - *byte == '&' || *byte == '*' || *byte == '-' || *byte == '=' || *byte == '+' || - *byte == '<' || *byte == '.' || *byte == '>' || *byte == '/' || *byte == '?' || - *byte == '|')) - { - token_flags = MD_TokenFlag_Symbol; - token_start = byte; - token_opl = byte; - byte += 1; - for(;byte <= byte_opl; byte += 1) - { - token_opl += 1; - if(byte == byte_opl || - (*byte != '~' && *byte != '!' && *byte != '$' && *byte != '%' && *byte != '^' && - *byte != '&' && *byte != '*' && *byte != '-' && *byte != '=' && *byte != '+' && - *byte != '<' && *byte != '.' && *byte != '>' && *byte != '/' && *byte != '?' && - *byte != '|')) - { - break; - } - } - } - - //- rjf: reserved symbols - if(token_flags == 0 && (*byte == '{' || *byte == '}' || *byte == '(' || *byte == ')' || - *byte == '[' || *byte == ']' || *byte == '#' || *byte == ',' || - *byte == '\\'|| *byte == ':' || *byte == ';' || *byte == '@')) - { - token_flags = MD_TokenFlag_Reserved; - token_start = byte; - token_opl = byte+1; - byte += 1; - } - - //- rjf: bad characters in all other cases - if(token_flags == 0) - { - token_flags = MD_TokenFlag_BadCharacter; - token_start = byte; - token_opl = byte+1; - byte += 1; - } - - //- rjf; push token if formed - if(token_flags != 0 && token_start != 0 && token_opl > token_start) - { - MD_Token token = {{(U64)(token_start - byte_first), (U64)(token_opl - byte_first)}, token_flags}; - md_token_chunk_list_push(scratch.arena, &tokens, 4096, token); - } - - //- rjf: push errors on unterminated comments - if(token_flags & MD_TokenFlag_BrokenComment) - { - MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, str8_lit(""), str8_lit(""), token_start - byte_first); - String8 error_string = str8_lit("Unterminated comment."); - md_msg_list_push(arena, &msgs, error, MD_MsgKind_Error, error_string); - } - - //- rjf: push errors on unterminated strings - if(token_flags & MD_TokenFlag_BrokenStringLiteral) - { - MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, str8_lit(""), str8_lit(""), token_start - byte_first); - String8 error_string = str8_lit("Unterminated string literal."); - md_msg_list_push(arena, &msgs, error, MD_MsgKind_Error, error_string); - } - } - - //- rjf: bake, fill & return - MD_TokenizeResult result = {0}; - { - result.tokens = md_token_array_from_chunk_list(arena, &tokens); - result.msgs = msgs; - } - scratch_end(scratch); - return result; -} - -//////////////////////////////// -//~ rjf: Tokens -> Tree Functions - -internal MD_ParseResult -md_parse_from_text_tokens(Arena *arena, String8 filename, String8 text, MD_TokenArray tokens) -{ - Temp scratch = scratch_begin(&arena, 1); - - //- rjf: set up outputs - MD_MsgList msgs = {0}; - MD_Node *root = md_push_node(arena, MD_NodeKind_File, 0, filename, text, 0); - - //- rjf: set up parse rule stack - typedef enum MD_ParseWorkKind - { - MD_ParseWorkKind_Main, - MD_ParseWorkKind_MainImplicit, - MD_ParseWorkKind_NodeOptionalFollowUp, - MD_ParseWorkKind_NodeChildrenStyleScan, - } - MD_ParseWorkKind; - typedef struct MD_ParseWorkNode MD_ParseWorkNode; - struct MD_ParseWorkNode - { - MD_ParseWorkNode *next; - MD_ParseWorkKind kind; - MD_Node *parent; - MD_Node *first_gathered_tag; - MD_Node *last_gathered_tag; - MD_NodeFlags gathered_node_flags; - S32 counted_newlines; - }; - MD_ParseWorkNode first_work = - { - 0, - MD_ParseWorkKind_Main, - root, - }; - MD_ParseWorkNode broken_work = { 0, MD_ParseWorkKind_Main, root,}; - MD_ParseWorkNode *work_top = &first_work; - MD_ParseWorkNode *work_free = 0; -#define MD_ParseWorkPush(work_kind, work_parent) do\ -{\ -MD_ParseWorkNode *work_node = work_free;\ -if(work_node == 0) {work_node = push_array(scratch.arena, MD_ParseWorkNode, 1);}\ -else { SLLStackPop(work_free); }\ -work_node->kind = (work_kind);\ -work_node->parent = (work_parent);\ -SLLStackPush(work_top, work_node);\ -}while(0) -#define MD_ParseWorkPop() do\ -{\ -SLLStackPop(work_top);\ -if(work_top == 0) {work_top = &broken_work;}\ -}while(0) - - //- rjf: parse - MD_Token *tokens_first = tokens.v; - MD_Token *tokens_opl = tokens_first + tokens.count; - MD_Token *token = tokens_first; - for(;token < tokens_opl;) - { - //- rjf: unpack token - String8 token_string = str8_substr(text, token[0].range); - - //- rjf: whitespace -> always no-op & inc - if(token->flags & MD_TokenFlag_Whitespace) - { - token += 1; - goto end_consume; - } - - //- rjf: comments -> always no-op & inc - if(token->flags & MD_TokenGroup_Comment) - { - token += 1; - goto end_consume; - } - - //- rjf: [node follow up] : following label -> work top parent has children. we need - // to scan for explicit delimiters, else parse an implicitly delimited set of children - if(work_top->kind == MD_ParseWorkKind_NodeOptionalFollowUp && str8_match(token_string, str8_lit(":"), 0)) - { - MD_Node *parent = work_top->parent; - MD_ParseWorkPop(); - MD_ParseWorkPush(MD_ParseWorkKind_NodeChildrenStyleScan, parent); - token += 1; - goto end_consume; - } - - //- rjf: [node follow up] anything but : following label -> node has no children. just - // pop & move on - if(work_top->kind == MD_ParseWorkKind_NodeOptionalFollowUp) - { - MD_ParseWorkPop(); - goto end_consume; - } - - //- rjf: [main] separators -> mark & inc - if(work_top->kind == MD_ParseWorkKind_Main && token->flags & MD_TokenFlag_Reserved && - (str8_match(token_string, str8_lit(","), 0) || - str8_match(token_string, str8_lit(";"), 0))) - { - MD_Node *parent = work_top->parent; - if(!md_node_is_nil(parent->last)) - { - parent->last->flags |= MD_NodeFlag_IsBeforeComma*!!str8_match(token_string, str8_lit(","), 0); - parent->last->flags |= MD_NodeFlag_IsBeforeSemicolon*!!str8_match(token_string, str8_lit(";"), 0); - work_top->gathered_node_flags |= MD_NodeFlag_IsAfterComma*!!str8_match(token_string, str8_lit(","), 0); - work_top->gathered_node_flags |= MD_NodeFlag_IsAfterSemicolon*!!str8_match(token_string, str8_lit(";"), 0); - } - token += 1; - goto end_consume; - } - - //- rjf: [main_implicit] separators -> pop - if(work_top->kind == MD_ParseWorkKind_MainImplicit && token->flags & MD_TokenFlag_Reserved && - (str8_match(token_string, str8_lit(","), 0) || - str8_match(token_string, str8_lit(";"), 0))) - { - MD_ParseWorkPop(); - goto end_consume; - } - - //- rjf: [main, main_implicit] unexpected reserved tokens - if((work_top->kind == MD_ParseWorkKind_Main || work_top->kind == MD_ParseWorkKind_MainImplicit) && - token->flags & MD_TokenFlag_Reserved && - (str8_match(token_string, str8_lit("#"), 0) || - str8_match(token_string, str8_lit("\\"), 0) || - str8_match(token_string, str8_lit(":"), 0))) - { - MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, token_string, token_string, token->range.min); - String8 error_string = push_str8f(arena, "Unexpected reserved symbol \"%S\".", token_string); - md_msg_list_push(arena, &msgs, error, MD_MsgKind_Error, error_string); - token += 1; - goto end_consume; - } - - //- rjf: [main, main_implicit] tag signifier -> create new tag - if((work_top->kind == MD_ParseWorkKind_Main || work_top->kind == MD_ParseWorkKind_MainImplicit) && - token[0].flags & MD_TokenFlag_Reserved && str8_match(token_string, str8_lit("@"), 0)) - { - if(token+1 >= tokens_opl || - !(token[1].flags & MD_TokenGroup_Label)) - { - MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, token_string, token_string, token->range.min); - String8 error_string = str8_lit("Tag label expected after @ symbol."); - md_msg_list_push(arena, &msgs, error, MD_MsgKind_Error, error_string); - token += 1; - goto end_consume; - } - else - { - String8 tag_name_raw = str8_substr(text, token[1].range); - String8 tag_name = md_content_string_from_token_flags_str8(token[1].flags, tag_name_raw); - MD_Node *node = md_push_node(arena, MD_NodeKind_Tag, md_node_flags_from_token_flags(token[1].flags), tag_name, tag_name_raw, token[0].range.min); - DLLPushBack_NPZ(&md_nil_node, work_top->first_gathered_tag, work_top->last_gathered_tag, node, next, prev); - if(token+2 < tokens_opl && token[2].flags & MD_TokenFlag_Reserved && str8_match(str8_substr(text, token[2].range), str8_lit("("), 0)) - { - token += 3; - MD_ParseWorkPush(MD_ParseWorkKind_Main, node); - } - else - { - token += 2; - } - goto end_consume; - } - } - - //- rjf: [main, main_implicit] label -> create new main - if((work_top->kind == MD_ParseWorkKind_Main || work_top->kind == MD_ParseWorkKind_MainImplicit) && - token->flags & MD_TokenGroup_Label) - { - String8 node_string_raw = token_string; - String8 node_string = md_content_string_from_token_flags_str8(token->flags, node_string_raw); - MD_NodeFlags flags = md_node_flags_from_token_flags(token->flags)|work_top->gathered_node_flags; - work_top->gathered_node_flags = 0; - MD_Node *node = md_push_node(arena, MD_NodeKind_Main, flags, node_string, node_string_raw, token[0].range.min); - node->first_tag = work_top->first_gathered_tag; - node->last_tag = work_top->last_gathered_tag; - for(MD_Node *tag = work_top->first_gathered_tag; !md_node_is_nil(tag); tag = tag->next) - { - tag->parent = node; - } - work_top->first_gathered_tag = work_top->last_gathered_tag = &md_nil_node; - md_node_push_child(work_top->parent, node); - MD_ParseWorkPush(MD_ParseWorkKind_NodeOptionalFollowUp, node); - token += 1; - goto end_consume; - } - - //- rjf: [main] {s, [s, and (s -> create new main - if(work_top->kind == MD_ParseWorkKind_Main && token->flags & MD_TokenFlag_Reserved && - (str8_match(token_string, str8_lit("{"), 0) || - str8_match(token_string, str8_lit("["), 0) || - str8_match(token_string, str8_lit("("), 0))) - { - MD_NodeFlags flags = md_node_flags_from_token_flags(token->flags)|work_top->gathered_node_flags; - flags |= MD_NodeFlag_HasBraceLeft*!!str8_match(token_string, str8_lit("{"), 0); - flags |= MD_NodeFlag_HasBracketLeft*!!str8_match(token_string, str8_lit("["), 0); - flags |= MD_NodeFlag_HasParenLeft*!!str8_match(token_string, str8_lit("("), 0); - work_top->gathered_node_flags = 0; - MD_Node *node = md_push_node(arena, MD_NodeKind_Main, flags, str8_lit(""), str8_lit(""), token[0].range.min); - node->first_tag = work_top->first_gathered_tag; - node->last_tag = work_top->last_gathered_tag; - for(MD_Node *tag = work_top->first_gathered_tag; !md_node_is_nil(tag); tag = tag->next) - { - tag->parent = node; - } - work_top->first_gathered_tag = work_top->last_gathered_tag = &md_nil_node; - md_node_push_child(work_top->parent, node); - MD_ParseWorkPush(MD_ParseWorkKind_Main, node); - token += 1; - goto end_consume; - } - - //- rjf: [node children style scan] {s, [s, and (s -> explicitly delimited children - if(work_top->kind == MD_ParseWorkKind_NodeChildrenStyleScan && token->flags & MD_TokenFlag_Reserved && - (str8_match(token_string, str8_lit("{"), 0) || - str8_match(token_string, str8_lit("["), 0) || - str8_match(token_string, str8_lit("("), 0))) - { - MD_Node *parent = work_top->parent; - parent->flags |= MD_NodeFlag_HasBraceLeft*!!str8_match(token_string, str8_lit("{"), 0); - parent->flags |= MD_NodeFlag_HasBracketLeft*!!str8_match(token_string, str8_lit("["), 0); - parent->flags |= MD_NodeFlag_HasParenLeft*!!str8_match(token_string, str8_lit("("), 0); - MD_ParseWorkPop(); - MD_ParseWorkPush(MD_ParseWorkKind_Main, parent); - token += 1; - goto end_consume; - } - - //- rjf: [node children style scan] count newlines - if(work_top->kind == MD_ParseWorkKind_NodeChildrenStyleScan && token->flags & MD_TokenFlag_Newline) - { - work_top->counted_newlines += 1; - token += 1; - goto end_consume; - } - - //- rjf: [main_implicit] newline -> pop - if(work_top->kind == MD_ParseWorkKind_MainImplicit && token->flags & MD_TokenFlag_Newline) - { - MD_ParseWorkPop(); - token += 1; - goto end_consume; - } - - //- rjf: [all but main_implicit] newline -> no-op & inc - if(work_top->kind != MD_ParseWorkKind_MainImplicit && token->flags & MD_TokenFlag_Newline) - { - token += 1; - goto end_consume; - } - - //- rjf: [node children style scan] anything causing implicit set -> <2 newlines, all good, - // >=2 newlines, houston we have a problem - if(work_top->kind == MD_ParseWorkKind_NodeChildrenStyleScan) - { - if(work_top->counted_newlines >= 2) - { - MD_Node *node = work_top->parent; - MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, token_string, token_string, token->range.min); - String8 error_string = push_str8f(arena, "More than two newlines following \"%S\", which has implicitly-delimited children, resulting in an empty list of children.", node->string); - md_msg_list_push(arena, &msgs, error, MD_MsgKind_Warning, error_string); - MD_ParseWorkPop(); - } - else - { - MD_Node *parent = work_top->parent; - MD_ParseWorkPop(); - MD_ParseWorkPush(MD_ParseWorkKind_MainImplicit, parent); - } - goto end_consume; - } - - //- rjf: [main] }s, ]s, and )s -> pop - if(work_top->kind == MD_ParseWorkKind_Main && token->flags & MD_TokenFlag_Reserved && - (str8_match(token_string, str8_lit("}"), 0) || - str8_match(token_string, str8_lit("]"), 0) || - str8_match(token_string, str8_lit(")"), 0))) - { - MD_Node *parent = work_top->parent; - parent->flags |= MD_NodeFlag_HasBraceRight*!!str8_match(token_string, str8_lit("}"), 0); - parent->flags |= MD_NodeFlag_HasBracketRight*!!str8_match(token_string, str8_lit("]"), 0); - parent->flags |= MD_NodeFlag_HasParenRight*!!str8_match(token_string, str8_lit(")"), 0); - MD_ParseWorkPop(); - token += 1; - goto end_consume; - } - - //- rjf: [main implicit] }s, ]s, and )s -> pop without advancing - if(work_top->kind == MD_ParseWorkKind_MainImplicit && token->flags & MD_TokenFlag_Reserved && - (str8_match(token_string, str8_lit("}"), 0) || - str8_match(token_string, str8_lit("]"), 0) || - str8_match(token_string, str8_lit(")"), 0))) - { - MD_ParseWorkPop(); - goto end_consume; - } - - //- rjf: no consumption -> unexpected token! we don't know what to do with this. - { - MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, token_string, token_string, token->range.min); - String8 error_string = push_str8f(arena, "Unexpected \"%S\" token.", token_string); - md_msg_list_push(arena, &msgs, error, MD_MsgKind_Error, error_string); - token += 1; - } - - end_consume:; - } - - //- rjf: fill & return - MD_ParseResult result = {0}; - result.root = root; - result.msgs = msgs; - scratch_end(scratch); - return result; -} - -//////////////////////////////// -//~ rjf: Tree -> Text Functions - -internal String8List -md_debug_string_list_from_tree(Arena *arena, MD_Node *root) -{ - String8List strings = {0}; - { - char *indentation = " "; - S32 depth = 0; - for(MD_Node *node = root, *next = &md_nil_node; !md_node_is_nil(node); node = next) - { - // rjf: get next recursion - MD_NodeRec rec = md_node_rec_depth_first_pre(node, root); - next = rec.next; - - // rjf: extract node info - String8 kind_string = str8_lit("Unknown"); - switch(node->kind) - { - default:{}break; - case MD_NodeKind_File: {kind_string = str8_lit("File"); }break; - case MD_NodeKind_ErrorMarker:{kind_string = str8_lit("ErrorMarker");}break; - case MD_NodeKind_Main: {kind_string = str8_lit("Main"); }break; - case MD_NodeKind_Tag: {kind_string = str8_lit("Tag"); }break; - case MD_NodeKind_List: {kind_string = str8_lit("List"); }break; - case MD_NodeKind_Reference: {kind_string = str8_lit("Reference"); }break; - } - - // rjf: push node line - str8_list_pushf(arena, &strings, "%.*s\"%S\" : %S", depth, indentation, node->string, kind_string); - - // rjf: children -> open brace - if(rec.push_count != 0) - { - str8_list_pushf(arena, &strings, "%.*s{", depth, indentation); - } - - // rjf: descend - depth += rec.push_count; - - // rjf: popping -> close braces - for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1) - { - str8_list_pushf(arena, &strings, "%.*s}", depth-1-pop_idx, indentation); - } - - // rjf: ascend - depth -= rec.pop_count; - } - } - return strings; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Message Type Functions + +internal void +md_msg_list_push(Arena *arena, MD_MsgList *msgs, MD_Node *node, MD_MsgKind kind, String8 string) +{ + MD_Msg *msg = push_array(arena, MD_Msg, 1); + msg->node = node; + msg->kind = kind; + msg->string = string; + SLLQueuePush(msgs->first, msgs->last, msg); + msgs->count += 1; + msgs->worst_message_kind = Max(kind, msgs->worst_message_kind); +} + +internal void +md_msg_list_pushf(Arena *arena, MD_MsgList *msgs, MD_Node *node, MD_MsgKind kind, char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(arena, fmt, args); + md_msg_list_push(arena, msgs, node, kind, string); + va_end(args); +} + +internal void +md_msg_list_concat_in_place(MD_MsgList *dst, MD_MsgList *to_push) +{ + if(to_push->first != 0) + { + if(dst->last) + { + dst->last->next = to_push->first; + dst->last = to_push->last; + dst->count += to_push->count; + dst->worst_message_kind = Max(dst->worst_message_kind, to_push->worst_message_kind); + } + else + { + MemoryCopyStruct(dst, to_push); + } + } + MemoryZeroStruct(to_push); +} + +//////////////////////////////// +//~ rjf: Token Type Functions + +internal MD_Token +md_token_make(Rng1U64 range, MD_TokenFlags flags) +{ + MD_Token token = {range, flags}; + return token; +} + +internal B32 +md_token_match(MD_Token a, MD_Token b) +{ + return (a.range.min == b.range.min && + a.range.max == b.range.max && + a.flags == b.flags); +} + +internal String8List +md_string_list_from_token_flags(Arena *arena, MD_TokenFlags flags) +{ + String8List strs = {0}; + if(flags & MD_TokenFlag_Identifier ){str8_list_push(arena, &strs, str8_lit("Identifier"));} + if(flags & MD_TokenFlag_Numeric ){str8_list_push(arena, &strs, str8_lit("Numeric"));} + if(flags & MD_TokenFlag_StringLiteral ){str8_list_push(arena, &strs, str8_lit("StringLiteral"));} + if(flags & MD_TokenFlag_Symbol ){str8_list_push(arena, &strs, str8_lit("Symbol"));} + if(flags & MD_TokenFlag_Reserved ){str8_list_push(arena, &strs, str8_lit("Reserved"));} + if(flags & MD_TokenFlag_Comment ){str8_list_push(arena, &strs, str8_lit("Comment"));} + if(flags & MD_TokenFlag_Whitespace ){str8_list_push(arena, &strs, str8_lit("Whitespace"));} + if(flags & MD_TokenFlag_Newline ){str8_list_push(arena, &strs, str8_lit("Newline"));} + if(flags & MD_TokenFlag_BrokenComment ){str8_list_push(arena, &strs, str8_lit("BrokenComment"));} + if(flags & MD_TokenFlag_BrokenStringLiteral ){str8_list_push(arena, &strs, str8_lit("BrokenStringLiteral"));} + if(flags & MD_TokenFlag_BadCharacter ){str8_list_push(arena, &strs, str8_lit("BadCharacter"));} + return strs; +} + +internal void +md_token_chunk_list_push(Arena *arena, MD_TokenChunkList *list, U64 cap, MD_Token token) +{ + MD_TokenChunkNode *node = list->last; + if(node == 0 || node->count >= node->cap) + { + node = push_array(arena, MD_TokenChunkNode, 1); + node->cap = cap; + node->v = push_array_no_zero(arena, MD_Token, cap); + SLLQueuePush(list->first, list->last, node); + list->chunk_count += 1; + } + MemoryCopyStruct(&node->v[node->count], &token); + node->count += 1; + list->total_token_count += 1; +} + +internal MD_TokenArray +md_token_array_from_chunk_list(Arena *arena, MD_TokenChunkList *chunks) +{ + MD_TokenArray result = {0}; + result.count = chunks->total_token_count; + result.v = push_array_no_zero(arena, MD_Token, result.count); + U64 write_idx = 0; + for(MD_TokenChunkNode *n = chunks->first; n != 0; n = n->next) + { + MemoryCopy(result.v+write_idx, n->v, sizeof(MD_Token)*n->count); + write_idx += n->count; + } + return result; +} + +internal String8 +md_content_string_from_token_flags_str8(MD_TokenFlags flags, String8 string) +{ + U64 num_chop = 0; + U64 num_skip = 0; + { + num_skip += 3*!!(flags & MD_TokenFlag_StringTriplet); + num_chop += 3*!!(flags & MD_TokenFlag_StringTriplet); + num_skip += 1*(!(flags & MD_TokenFlag_StringTriplet) && flags & MD_TokenFlag_StringLiteral); + num_chop += 1*(!(flags & MD_TokenFlag_StringTriplet) && flags & MD_TokenFlag_StringLiteral); + } + String8 result = string; + result = str8_chop(result, num_chop); + result = str8_skip(result, num_skip); + return result; +} + +//////////////////////////////// +//~ rjf: Node Type Functions + +//- rjf: flag conversions + +internal MD_NodeFlags +md_node_flags_from_token_flags(MD_TokenFlags flags) +{ + MD_NodeFlags result = 0; + result |= MD_NodeFlag_Identifier*!!(flags&MD_TokenFlag_Identifier); + result |= MD_NodeFlag_Numeric*!!(flags&MD_TokenFlag_Numeric); + result |= MD_NodeFlag_StringLiteral*!!(flags&MD_TokenFlag_StringLiteral); + result |= MD_NodeFlag_Symbol*!!(flags&MD_TokenFlag_Symbol); + result |= MD_NodeFlag_StringSingleQuote *!!(flags&MD_TokenFlag_StringSingleQuote); + result |= MD_NodeFlag_StringDoubleQuote *!!(flags&MD_TokenFlag_StringDoubleQuote); + result |= MD_NodeFlag_StringTick*!!(flags&MD_TokenFlag_StringTick); + result |= MD_NodeFlag_StringTriplet*!!(flags&MD_TokenFlag_StringTriplet); + return result; +} + +//- rjf: nil + +internal B32 +md_node_is_nil(MD_Node *node) +{ + return (node == 0 || node == &md_nil_node || node->kind == MD_NodeKind_Nil); +} + +//- rjf: iteration + +internal MD_NodeRec +md_node_rec_depth_first(MD_Node *node, MD_Node *subtree_root, U64 child_off, U64 sib_off) +{ + MD_NodeRec rec = {0}; + rec.next = &md_nil_node; + if(!md_node_is_nil(MemberFromOffset(MD_Node *, node, child_off))) + { + rec.next = MemberFromOffset(MD_Node *, node, child_off); + rec.push_count = 1; + } + else for(MD_Node *p = node; !md_node_is_nil(p) && p != subtree_root; p = p->parent, rec.pop_count += 1) + { + if(!md_node_is_nil(MemberFromOffset(MD_Node *, p, sib_off))) + { + rec.next = MemberFromOffset(MD_Node *, p, sib_off); + break; + } + } + return rec; +} + +//- rjf: tree building + +internal MD_Node * +md_push_node(Arena *arena, MD_NodeKind kind, MD_NodeFlags flags, String8 string, String8 raw_string, U64 src_offset) +{ + MD_Node *node = push_array(arena, MD_Node, 1); + node->first = node->last = node->parent = node->next = node->prev = node->first_tag = node->last_tag = &md_nil_node; + node->kind = kind; + node->flags = flags; + node->string = string; + node->raw_string = raw_string; + node->src_offset = src_offset; + return node; +} + +internal void +md_node_push_child(MD_Node *parent, MD_Node *node) +{ + node->parent = parent; + DLLPushBack_NPZ(&md_nil_node, parent->first, parent->last, node, next, prev); +} + +internal void +md_node_push_tag(MD_Node *parent, MD_Node *node) +{ + node->parent = parent; + DLLPushBack_NPZ(&md_nil_node, parent->first_tag, parent->last_tag, node, next, prev); +} + +//- rjf: tree introspection + +internal MD_Node * +md_node_from_chain_string(MD_Node *first, MD_Node *opl, String8 string, StringMatchFlags flags) +{ + MD_Node *result = &md_nil_node; + for(MD_Node *n = first; !md_node_is_nil(n) && n != opl; n = n->next) + { + if(str8_match(n->string, string, flags)) + { + result = n; + break; + } + } + return result; +} + +internal MD_Node * +md_node_from_chain_index(MD_Node *first, MD_Node *opl, U64 index) +{ + MD_Node *result = &md_nil_node; + S64 idx = 0; + for(MD_Node *n = first; !md_node_is_nil(n) && n != opl; n = n->next, idx += 1) + { + if(index == idx) + { + result = n; + break; + } + } + return result; +} + +internal MD_Node * +md_node_from_chain_flags(MD_Node *first, MD_Node *opl, MD_NodeFlags flags) +{ + MD_Node *result = &md_nil_node; + for(MD_Node *n = first; !md_node_is_nil(n) && n != opl; n = n->next) + { + if(n->flags & flags) + { + result = n; + break; + } + } + return result; +} + +internal U64 +md_index_from_node(MD_Node *node) +{ + U64 index = 0; + for(MD_Node *n = node->prev; !md_node_is_nil(n); n = n->prev) + { + index += 1; + } + return index; +} + +internal MD_Node * +md_root_from_node(MD_Node *node) +{ + MD_Node *result = node; + for(MD_Node *p = node->parent; (p->kind == MD_NodeKind_Main || p->kind == MD_NodeKind_Tag) && !md_node_is_nil(p); p = p->parent) + { + result = p; + } + return result; +} + +internal MD_Node * +md_child_from_string(MD_Node *node, String8 child_string, StringMatchFlags flags) +{ + return md_node_from_chain_string(node->first, &md_nil_node, child_string, flags); +} + +internal MD_Node * +md_tag_from_string(MD_Node *node, String8 tag_string, StringMatchFlags flags) +{ + return md_node_from_chain_string(node->first_tag, &md_nil_node, tag_string, flags); +} + +internal MD_Node * +md_child_from_index(MD_Node *node, U64 index) +{ + return md_node_from_chain_index(node->first, &md_nil_node, index); +} + +internal MD_Node * +md_tag_from_index(MD_Node *node, U64 index) +{ + return md_node_from_chain_index(node->first_tag, &md_nil_node, index); +} + +internal MD_Node * +md_tag_arg_from_index(MD_Node *node, String8 tag_string, StringMatchFlags flags, U64 index) +{ + MD_Node *tag = md_tag_from_string(node, tag_string, flags); + return md_child_from_index(tag, index); +} + +internal MD_Node * +md_tag_arg_from_string(MD_Node *node, String8 tag_string, StringMatchFlags tag_str_flags, String8 arg_string, StringMatchFlags arg_str_flags) +{ + MD_Node *tag = md_tag_from_string(node, tag_string, tag_str_flags); + MD_Node *arg = md_child_from_string(tag, arg_string, arg_str_flags); + return arg; +} + +internal B32 +md_node_has_child(MD_Node *node, String8 string, StringMatchFlags flags) +{ + return !md_node_is_nil(md_child_from_string(node, string, flags)); +} + +internal B32 +md_node_has_tag(MD_Node *node, String8 string, StringMatchFlags flags) +{ + return !md_node_is_nil(md_tag_from_string(node, string, flags)); +} + +internal U64 +md_child_count_from_node(MD_Node *node) +{ + U64 result = 0; + for(MD_Node *child = node->first; !md_node_is_nil(child); child = child->next) + { + result += 1; + } + return result; +} + +internal U64 +md_tag_count_from_node(MD_Node *node) +{ + U64 result = 0; + for(MD_Node *child = node->first_tag; !md_node_is_nil(child); child = child->next) + { + result += 1; + } + return result; +} + +//- rjf: tree comparison + +internal B32 +md_node_match(MD_Node *a, MD_Node *b, StringMatchFlags flags) +{ + B32 result = 0; + if(a->kind == b->kind && str8_match(a->string, b->string, flags)) + { + result = 1; + if(result) + { + result = result && a->flags == b->flags; + } + if(result && a->kind != MD_NodeKind_Tag) + { + for(MD_Node *a_tag = a->first_tag, *b_tag = b->first_tag; + !md_node_is_nil(a_tag) || !md_node_is_nil(b_tag); + a_tag = a_tag->next, b_tag = b_tag->next) + { + if(md_node_match(a_tag, b_tag, flags)) + { + for(MD_Node *a_tag_arg = a_tag->first, *b_tag_arg = b_tag->first; + !md_node_is_nil(a_tag_arg) || !md_node_is_nil(b_tag_arg); + a_tag_arg = a_tag_arg->next, b_tag_arg = b_tag_arg->next) + { + if(!md_node_deep_match(a_tag_arg, b_tag_arg, flags)) + { + result = 0; + goto end; + } + } + } + else + { + result = 0; + goto end; + } + } + } + } + end:; + return result; +} + +internal B32 +md_node_deep_match(MD_Node *a, MD_Node *b, StringMatchFlags flags) +{ + B32 result = md_node_match(a, b, flags); + if(result) + { + for(MD_Node *a_child = a->first, *b_child = b->first; + !md_node_is_nil(a_child) || !md_node_is_nil(b_child); + a_child = a_child->next, b_child = b_child->next) + { + if(!md_node_deep_match(a_child, b_child, flags)) + { + result = 0; + goto end; + } + } + } + end:; + return result; +} + +//////////////////////////////// +//~ rjf: Text -> Tokens Functions + +internal MD_TokenizeResult +md_tokenize_from_text(Arena *arena, String8 text) +{ + Temp scratch = scratch_begin(&arena, 1); + MD_TokenChunkList tokens = {0}; + MD_MsgList msgs = {0}; + U8 *byte_first = text.str; + U8 *byte_opl = byte_first + text.size; + U8 *byte = byte_first; + + //- rjf: scan string & produce tokens + for(;byte < byte_opl;) + { + MD_TokenFlags token_flags = 0; + U8 *token_start = 0; + U8 *token_opl = 0; + + //- rjf: whitespace + if(token_flags == 0 && (*byte == ' ' || *byte == '\t' || *byte == '\v' || *byte == '\r')) + { + token_flags = MD_TokenFlag_Whitespace; + token_start = byte; + token_opl = byte; + byte += 1; + for(;byte <= byte_opl; byte += 1) + { + token_opl += 1; + if(byte == byte_opl || (*byte != ' ' && *byte != '\t' && *byte != '\v' && *byte != '\r')) + { + break; + } + } + } + + //- rjf: newlines + if(token_flags == 0 && *byte == '\n') + { + token_flags = MD_TokenFlag_Newline; + token_start = byte; + token_opl = byte+1; + byte += 1; + } + + //- rjf: single-line comments + if(token_flags == 0 && (byte+1 < byte_opl && *byte == '/' && byte[1] == '/')) + { + token_flags = MD_TokenFlag_Comment; + token_start = byte; + token_opl = byte+2; + byte += 2; + B32 escaped = 0; + for(;byte <= byte_opl; byte += 1) + { + token_opl += 1; + if(byte == byte_opl) + { + break; + } + if(escaped) + { + escaped = 0; + } + else + { + if(*byte == '\n') + { + break; + } + else if(*byte == '\\') + { + escaped = 1; + } + } + } + } + + //- rjf: multi-line comments + if(token_flags == 0 && (byte+1 < byte_opl && *byte == '/' && byte[1] == '*')) + { + token_flags = MD_TokenFlag_Comment; + token_start = byte; + token_opl = byte+2; + byte += 2; + for(;byte <= byte_opl; byte += 1) + { + token_opl += 1; + if(byte == byte_opl) + { + token_flags |= MD_TokenFlag_BrokenComment; + break; + } + if(byte+1 < byte_opl && byte[0] == '*' && byte[1] == '/') + { + token_opl += 2; + break; + } + } + } + + //- rjf: identifiers + if(token_flags == 0 && (('A' <= *byte && *byte <= 'Z') || + ('a' <= *byte && *byte <= 'z') || + *byte == '_' || + utf8_class[*byte>>3] >= 2 )) + { + token_flags = MD_TokenFlag_Identifier; + token_start = byte; + token_opl = byte; + byte += 1; + for(;byte <= byte_opl; byte += 1) + { + token_opl += 1; + if(byte == byte_opl || + (!('A' <= *byte && *byte <= 'Z') && + !('a' <= *byte && *byte <= 'z') && + !('0' <= *byte && *byte <= '9') && + *byte != '_' && + utf8_class[*byte>>3] < 2)) + { + break; + } + } + } + + //- rjf: numerics + if(token_flags == 0 && (('0' <= *byte && *byte <= '9') || + (*byte == '.' && byte+1 < byte_opl && '0' <= byte[1] && byte[1] <= '9') || + (*byte == '-' && byte+1 < byte_opl && '0' <= byte[1] && byte[1] <= '9') || + *byte == '_')) + { + token_flags = MD_TokenFlag_Numeric; + token_start = byte; + token_opl = byte; + byte += 1; + for(;byte <= byte_opl; byte += 1) + { + token_opl += 1; + if(byte == byte_opl || + (!('A' <= *byte && *byte <= 'Z') && + !('a' <= *byte && *byte <= 'z') && + !('0' <= *byte && *byte <= '9') && + *byte != '_' && + *byte != '.')) + { + break; + } + } + } + + //- rjf: triplet string literals + if(token_flags == 0 && byte+2 < byte_opl && + ((byte[0] == '"' && byte[1] == '"' && byte[2] == '"') || + (byte[0] == '\''&& byte[1] == '\''&& byte[2] == '\'') || + (byte[0] == '`' && byte[1] == '`' && byte[2] == '`'))) + { + U8 literal_style = byte[0]; + token_flags = MD_TokenFlag_StringLiteral|MD_TokenFlag_StringTriplet; + token_flags |= (literal_style == '\'')*MD_TokenFlag_StringSingleQuote; + token_flags |= (literal_style == '"')*MD_TokenFlag_StringDoubleQuote; + token_flags |= (literal_style == '`')*MD_TokenFlag_StringTick; + token_start = byte; + token_opl = byte+3; + byte += 3; + for(;byte <= byte_opl; byte += 1) + { + if(byte == byte_opl) + { + token_flags |= MD_TokenFlag_BrokenStringLiteral; + token_opl = byte; + break; + } + if(byte+2 < byte_opl && (byte[0] == literal_style && byte[1] == literal_style && byte[2] == literal_style)) + { + byte += 3; + token_opl = byte; + break; + } + } + } + + //- rjf: singlet string literals + if(token_flags == 0 && (byte[0] == '"' || byte[0] == '\'' || byte[0] == '`')) + { + U8 literal_style = byte[0]; + token_flags = MD_TokenFlag_StringLiteral; + token_flags |= (literal_style == '\'')*MD_TokenFlag_StringSingleQuote; + token_flags |= (literal_style == '"')*MD_TokenFlag_StringDoubleQuote; + token_flags |= (literal_style == '`')*MD_TokenFlag_StringTick; + token_start = byte; + token_opl = byte+1; + byte += 1; + B32 escaped = 0; + for(;byte <= byte_opl; byte += 1) + { + if(byte == byte_opl || *byte == '\n') + { + token_opl = byte; + token_flags |= MD_TokenFlag_BrokenStringLiteral; + break; + } + if(!escaped && byte[0] == '\\') + { + escaped = 1; + } + else if(!escaped && byte[0] == literal_style) + { + token_opl = byte+1; + byte += 1; + break; + } + else if(escaped) + { + escaped = 0; + } + } + } + + //- rjf: non-reserved symbols + if(token_flags == 0 && (*byte == '~' || *byte == '!' || *byte == '$' || *byte == '%' || *byte == '^' || + *byte == '&' || *byte == '*' || *byte == '-' || *byte == '=' || *byte == '+' || + *byte == '<' || *byte == '.' || *byte == '>' || *byte == '/' || *byte == '?' || + *byte == '|')) + { + token_flags = MD_TokenFlag_Symbol; + token_start = byte; + token_opl = byte; + byte += 1; + for(;byte <= byte_opl; byte += 1) + { + token_opl += 1; + if(byte == byte_opl || + (*byte != '~' && *byte != '!' && *byte != '$' && *byte != '%' && *byte != '^' && + *byte != '&' && *byte != '*' && *byte != '-' && *byte != '=' && *byte != '+' && + *byte != '<' && *byte != '.' && *byte != '>' && *byte != '/' && *byte != '?' && + *byte != '|')) + { + break; + } + } + } + + //- rjf: reserved symbols + if(token_flags == 0 && (*byte == '{' || *byte == '}' || *byte == '(' || *byte == ')' || + *byte == '[' || *byte == ']' || *byte == '#' || *byte == ',' || + *byte == '\\'|| *byte == ':' || *byte == ';' || *byte == '@')) + { + token_flags = MD_TokenFlag_Reserved; + token_start = byte; + token_opl = byte+1; + byte += 1; + } + + //- rjf: bad characters in all other cases + if(token_flags == 0) + { + token_flags = MD_TokenFlag_BadCharacter; + token_start = byte; + token_opl = byte+1; + byte += 1; + } + + //- rjf; push token if formed + if(token_flags != 0 && token_start != 0 && token_opl > token_start) + { + MD_Token token = {{(U64)(token_start - byte_first), (U64)(token_opl - byte_first)}, token_flags}; + md_token_chunk_list_push(scratch.arena, &tokens, 4096, token); + } + + //- rjf: push errors on unterminated comments + if(token_flags & MD_TokenFlag_BrokenComment) + { + MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, str8_lit(""), str8_lit(""), token_start - byte_first); + String8 error_string = str8_lit("Unterminated comment."); + md_msg_list_push(arena, &msgs, error, MD_MsgKind_Error, error_string); + } + + //- rjf: push errors on unterminated strings + if(token_flags & MD_TokenFlag_BrokenStringLiteral) + { + MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, str8_lit(""), str8_lit(""), token_start - byte_first); + String8 error_string = str8_lit("Unterminated string literal."); + md_msg_list_push(arena, &msgs, error, MD_MsgKind_Error, error_string); + } + } + + //- rjf: bake, fill & return + MD_TokenizeResult result = {0}; + { + result.tokens = md_token_array_from_chunk_list(arena, &tokens); + result.msgs = msgs; + } + scratch_end(scratch); + return result; +} + +//////////////////////////////// +//~ rjf: Tokens -> Tree Functions + +internal MD_ParseResult +md_parse_from_text_tokens(Arena *arena, String8 filename, String8 text, MD_TokenArray tokens) +{ + Temp scratch = scratch_begin(&arena, 1); + + //- rjf: set up outputs + MD_MsgList msgs = {0}; + MD_Node *root = md_push_node(arena, MD_NodeKind_File, 0, filename, text, 0); + + //- rjf: set up parse rule stack + typedef enum MD_ParseWorkKind + { + MD_ParseWorkKind_Main, + MD_ParseWorkKind_MainImplicit, + MD_ParseWorkKind_NodeOptionalFollowUp, + MD_ParseWorkKind_NodeChildrenStyleScan, + } + MD_ParseWorkKind; + typedef struct MD_ParseWorkNode MD_ParseWorkNode; + struct MD_ParseWorkNode + { + MD_ParseWorkNode *next; + MD_ParseWorkKind kind; + MD_Node *parent; + MD_Node *first_gathered_tag; + MD_Node *last_gathered_tag; + MD_NodeFlags gathered_node_flags; + S32 counted_newlines; + }; + MD_ParseWorkNode first_work = + { + 0, + MD_ParseWorkKind_Main, + root, + }; + MD_ParseWorkNode broken_work = { 0, MD_ParseWorkKind_Main, root,}; + MD_ParseWorkNode *work_top = &first_work; + MD_ParseWorkNode *work_free = 0; +#define MD_ParseWorkPush(work_kind, work_parent) do\ +{\ +MD_ParseWorkNode *work_node = work_free;\ +if(work_node == 0) {work_node = push_array(scratch.arena, MD_ParseWorkNode, 1);}\ +else { SLLStackPop(work_free); }\ +work_node->kind = (work_kind);\ +work_node->parent = (work_parent);\ +SLLStackPush(work_top, work_node);\ +}while(0) +#define MD_ParseWorkPop() do\ +{\ +SLLStackPop(work_top);\ +if(work_top == 0) {work_top = &broken_work;}\ +}while(0) + + //- rjf: parse + MD_Token *tokens_first = tokens.v; + MD_Token *tokens_opl = tokens_first + tokens.count; + MD_Token *token = tokens_first; + for(;token < tokens_opl;) + { + //- rjf: unpack token + String8 token_string = str8_substr(text, token[0].range); + + //- rjf: whitespace -> always no-op & inc + if(token->flags & MD_TokenFlag_Whitespace) + { + token += 1; + goto end_consume; + } + + //- rjf: comments -> always no-op & inc + if(token->flags & MD_TokenGroup_Comment) + { + token += 1; + goto end_consume; + } + + //- rjf: [node follow up] : following label -> work top parent has children. we need + // to scan for explicit delimiters, else parse an implicitly delimited set of children + if(work_top->kind == MD_ParseWorkKind_NodeOptionalFollowUp && str8_match(token_string, str8_lit(":"), 0)) + { + MD_Node *parent = work_top->parent; + MD_ParseWorkPop(); + MD_ParseWorkPush(MD_ParseWorkKind_NodeChildrenStyleScan, parent); + token += 1; + goto end_consume; + } + + //- rjf: [node follow up] anything but : following label -> node has no children. just + // pop & move on + if(work_top->kind == MD_ParseWorkKind_NodeOptionalFollowUp) + { + MD_ParseWorkPop(); + goto end_consume; + } + + //- rjf: [main] separators -> mark & inc + if(work_top->kind == MD_ParseWorkKind_Main && token->flags & MD_TokenFlag_Reserved && + (str8_match(token_string, str8_lit(","), 0) || + str8_match(token_string, str8_lit(";"), 0))) + { + MD_Node *parent = work_top->parent; + if(!md_node_is_nil(parent->last)) + { + parent->last->flags |= MD_NodeFlag_IsBeforeComma*!!str8_match(token_string, str8_lit(","), 0); + parent->last->flags |= MD_NodeFlag_IsBeforeSemicolon*!!str8_match(token_string, str8_lit(";"), 0); + work_top->gathered_node_flags |= MD_NodeFlag_IsAfterComma*!!str8_match(token_string, str8_lit(","), 0); + work_top->gathered_node_flags |= MD_NodeFlag_IsAfterSemicolon*!!str8_match(token_string, str8_lit(";"), 0); + } + token += 1; + goto end_consume; + } + + //- rjf: [main_implicit] separators -> pop + if(work_top->kind == MD_ParseWorkKind_MainImplicit && token->flags & MD_TokenFlag_Reserved && + (str8_match(token_string, str8_lit(","), 0) || + str8_match(token_string, str8_lit(";"), 0))) + { + MD_ParseWorkPop(); + goto end_consume; + } + + //- rjf: [main, main_implicit] unexpected reserved tokens + if((work_top->kind == MD_ParseWorkKind_Main || work_top->kind == MD_ParseWorkKind_MainImplicit) && + token->flags & MD_TokenFlag_Reserved && + (str8_match(token_string, str8_lit("#"), 0) || + str8_match(token_string, str8_lit("\\"), 0) || + str8_match(token_string, str8_lit(":"), 0))) + { + MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, token_string, token_string, token->range.min); + String8 error_string = push_str8f(arena, "Unexpected reserved symbol \"%S\".", token_string); + md_msg_list_push(arena, &msgs, error, MD_MsgKind_Error, error_string); + token += 1; + goto end_consume; + } + + //- rjf: [main, main_implicit] tag signifier -> create new tag + if((work_top->kind == MD_ParseWorkKind_Main || work_top->kind == MD_ParseWorkKind_MainImplicit) && + token[0].flags & MD_TokenFlag_Reserved && str8_match(token_string, str8_lit("@"), 0)) + { + if(token+1 >= tokens_opl || + !(token[1].flags & MD_TokenGroup_Label)) + { + MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, token_string, token_string, token->range.min); + String8 error_string = str8_lit("Tag label expected after @ symbol."); + md_msg_list_push(arena, &msgs, error, MD_MsgKind_Error, error_string); + token += 1; + goto end_consume; + } + else + { + String8 tag_name_raw = str8_substr(text, token[1].range); + String8 tag_name = md_content_string_from_token_flags_str8(token[1].flags, tag_name_raw); + MD_Node *node = md_push_node(arena, MD_NodeKind_Tag, md_node_flags_from_token_flags(token[1].flags), tag_name, tag_name_raw, token[0].range.min); + DLLPushBack_NPZ(&md_nil_node, work_top->first_gathered_tag, work_top->last_gathered_tag, node, next, prev); + if(token+2 < tokens_opl && token[2].flags & MD_TokenFlag_Reserved && str8_match(str8_substr(text, token[2].range), str8_lit("("), 0)) + { + token += 3; + MD_ParseWorkPush(MD_ParseWorkKind_Main, node); + } + else + { + token += 2; + } + goto end_consume; + } + } + + //- rjf: [main, main_implicit] label -> create new main + if((work_top->kind == MD_ParseWorkKind_Main || work_top->kind == MD_ParseWorkKind_MainImplicit) && + token->flags & MD_TokenGroup_Label) + { + String8 node_string_raw = token_string; + String8 node_string = md_content_string_from_token_flags_str8(token->flags, node_string_raw); + MD_NodeFlags flags = md_node_flags_from_token_flags(token->flags)|work_top->gathered_node_flags; + work_top->gathered_node_flags = 0; + MD_Node *node = md_push_node(arena, MD_NodeKind_Main, flags, node_string, node_string_raw, token[0].range.min); + node->first_tag = work_top->first_gathered_tag; + node->last_tag = work_top->last_gathered_tag; + for(MD_Node *tag = work_top->first_gathered_tag; !md_node_is_nil(tag); tag = tag->next) + { + tag->parent = node; + } + work_top->first_gathered_tag = work_top->last_gathered_tag = &md_nil_node; + md_node_push_child(work_top->parent, node); + MD_ParseWorkPush(MD_ParseWorkKind_NodeOptionalFollowUp, node); + token += 1; + goto end_consume; + } + + //- rjf: [main] {s, [s, and (s -> create new main + if(work_top->kind == MD_ParseWorkKind_Main && token->flags & MD_TokenFlag_Reserved && + (str8_match(token_string, str8_lit("{"), 0) || + str8_match(token_string, str8_lit("["), 0) || + str8_match(token_string, str8_lit("("), 0))) + { + MD_NodeFlags flags = md_node_flags_from_token_flags(token->flags)|work_top->gathered_node_flags; + flags |= MD_NodeFlag_HasBraceLeft*!!str8_match(token_string, str8_lit("{"), 0); + flags |= MD_NodeFlag_HasBracketLeft*!!str8_match(token_string, str8_lit("["), 0); + flags |= MD_NodeFlag_HasParenLeft*!!str8_match(token_string, str8_lit("("), 0); + work_top->gathered_node_flags = 0; + MD_Node *node = md_push_node(arena, MD_NodeKind_Main, flags, str8_lit(""), str8_lit(""), token[0].range.min); + node->first_tag = work_top->first_gathered_tag; + node->last_tag = work_top->last_gathered_tag; + for(MD_Node *tag = work_top->first_gathered_tag; !md_node_is_nil(tag); tag = tag->next) + { + tag->parent = node; + } + work_top->first_gathered_tag = work_top->last_gathered_tag = &md_nil_node; + md_node_push_child(work_top->parent, node); + MD_ParseWorkPush(MD_ParseWorkKind_Main, node); + token += 1; + goto end_consume; + } + + //- rjf: [node children style scan] {s, [s, and (s -> explicitly delimited children + if(work_top->kind == MD_ParseWorkKind_NodeChildrenStyleScan && token->flags & MD_TokenFlag_Reserved && + (str8_match(token_string, str8_lit("{"), 0) || + str8_match(token_string, str8_lit("["), 0) || + str8_match(token_string, str8_lit("("), 0))) + { + MD_Node *parent = work_top->parent; + parent->flags |= MD_NodeFlag_HasBraceLeft*!!str8_match(token_string, str8_lit("{"), 0); + parent->flags |= MD_NodeFlag_HasBracketLeft*!!str8_match(token_string, str8_lit("["), 0); + parent->flags |= MD_NodeFlag_HasParenLeft*!!str8_match(token_string, str8_lit("("), 0); + MD_ParseWorkPop(); + MD_ParseWorkPush(MD_ParseWorkKind_Main, parent); + token += 1; + goto end_consume; + } + + //- rjf: [node children style scan] count newlines + if(work_top->kind == MD_ParseWorkKind_NodeChildrenStyleScan && token->flags & MD_TokenFlag_Newline) + { + work_top->counted_newlines += 1; + token += 1; + goto end_consume; + } + + //- rjf: [main_implicit] newline -> pop + if(work_top->kind == MD_ParseWorkKind_MainImplicit && token->flags & MD_TokenFlag_Newline) + { + MD_ParseWorkPop(); + token += 1; + goto end_consume; + } + + //- rjf: [all but main_implicit] newline -> no-op & inc + if(work_top->kind != MD_ParseWorkKind_MainImplicit && token->flags & MD_TokenFlag_Newline) + { + token += 1; + goto end_consume; + } + + //- rjf: [node children style scan] anything causing implicit set -> <2 newlines, all good, + // >=2 newlines, houston we have a problem + if(work_top->kind == MD_ParseWorkKind_NodeChildrenStyleScan) + { + if(work_top->counted_newlines >= 2) + { + MD_Node *node = work_top->parent; + MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, token_string, token_string, token->range.min); + String8 error_string = push_str8f(arena, "More than two newlines following \"%S\", which has implicitly-delimited children, resulting in an empty list of children.", node->string); + md_msg_list_push(arena, &msgs, error, MD_MsgKind_Warning, error_string); + MD_ParseWorkPop(); + } + else + { + MD_Node *parent = work_top->parent; + MD_ParseWorkPop(); + MD_ParseWorkPush(MD_ParseWorkKind_MainImplicit, parent); + } + goto end_consume; + } + + //- rjf: [main] }s, ]s, and )s -> pop + if(work_top->kind == MD_ParseWorkKind_Main && token->flags & MD_TokenFlag_Reserved && + (str8_match(token_string, str8_lit("}"), 0) || + str8_match(token_string, str8_lit("]"), 0) || + str8_match(token_string, str8_lit(")"), 0))) + { + MD_Node *parent = work_top->parent; + parent->flags |= MD_NodeFlag_HasBraceRight*!!str8_match(token_string, str8_lit("}"), 0); + parent->flags |= MD_NodeFlag_HasBracketRight*!!str8_match(token_string, str8_lit("]"), 0); + parent->flags |= MD_NodeFlag_HasParenRight*!!str8_match(token_string, str8_lit(")"), 0); + MD_ParseWorkPop(); + token += 1; + goto end_consume; + } + + //- rjf: [main implicit] }s, ]s, and )s -> pop without advancing + if(work_top->kind == MD_ParseWorkKind_MainImplicit && token->flags & MD_TokenFlag_Reserved && + (str8_match(token_string, str8_lit("}"), 0) || + str8_match(token_string, str8_lit("]"), 0) || + str8_match(token_string, str8_lit(")"), 0))) + { + MD_ParseWorkPop(); + goto end_consume; + } + + //- rjf: no consumption -> unexpected token! we don't know what to do with this. + { + MD_Node *error = md_push_node(arena, MD_NodeKind_ErrorMarker, 0, token_string, token_string, token->range.min); + String8 error_string = push_str8f(arena, "Unexpected \"%S\" token.", token_string); + md_msg_list_push(arena, &msgs, error, MD_MsgKind_Error, error_string); + token += 1; + } + + end_consume:; + } + + //- rjf: fill & return + MD_ParseResult result = {0}; + result.root = root; + result.msgs = msgs; + scratch_end(scratch); + return result; +} + +//////////////////////////////// +//~ rjf: Tree -> Text Functions + +internal String8List +md_debug_string_list_from_tree(Arena *arena, MD_Node *root) +{ + String8List strings = {0}; + { + char *indentation = " "; + S32 depth = 0; + for(MD_Node *node = root, *next = &md_nil_node; !md_node_is_nil(node); node = next) + { + // rjf: get next recursion + MD_NodeRec rec = md_node_rec_depth_first_pre(node, root); + next = rec.next; + + // rjf: extract node info + String8 kind_string = str8_lit("Unknown"); + switch(node->kind) + { + default:{}break; + case MD_NodeKind_File: {kind_string = str8_lit("File"); }break; + case MD_NodeKind_ErrorMarker:{kind_string = str8_lit("ErrorMarker");}break; + case MD_NodeKind_Main: {kind_string = str8_lit("Main"); }break; + case MD_NodeKind_Tag: {kind_string = str8_lit("Tag"); }break; + case MD_NodeKind_List: {kind_string = str8_lit("List"); }break; + case MD_NodeKind_Reference: {kind_string = str8_lit("Reference"); }break; + } + + // rjf: push node line + str8_list_pushf(arena, &strings, "%.*s\"%S\" : %S", depth, indentation, node->string, kind_string); + + // rjf: children -> open brace + if(rec.push_count != 0) + { + str8_list_pushf(arena, &strings, "%.*s{", depth, indentation); + } + + // rjf: descend + depth += rec.push_count; + + // rjf: popping -> close braces + for(S32 pop_idx = 0; pop_idx < rec.pop_count; pop_idx += 1) + { + str8_list_pushf(arena, &strings, "%.*s}", depth-1-pop_idx, indentation); + } + + // rjf: ascend + depth -= rec.pop_count; + } + } + return strings; +} diff --git a/src/mdesk/mdesk.h b/src/mdesk/mdesk.h index 3daad8e4..043fed70 100644 --- a/src/mdesk/mdesk.h +++ b/src/mdesk/mdesk.h @@ -1,302 +1,302 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef MDESK_H -#define MDESK_H - -//////////////////////////////// -//~ rjf: Messages - -typedef enum MD_MsgKind -{ - MD_MsgKind_Null, - MD_MsgKind_Note, - MD_MsgKind_Warning, - MD_MsgKind_Error, - MD_MsgKind_FatalError, -} -MD_MsgKind; - -typedef struct MD_Msg MD_Msg; -struct MD_Msg -{ - MD_Msg *next; - struct MD_Node *node; - MD_MsgKind kind; - String8 string; -}; - -typedef struct MD_MsgList MD_MsgList; -struct MD_MsgList -{ - MD_Msg *first; - MD_Msg *last; - U64 count; - MD_MsgKind worst_message_kind; -}; - -//////////////////////////////// -//~ rjf: Token Types - -typedef U32 MD_TokenFlags; -enum -{ - // rjf: base kind info - MD_TokenFlag_Identifier = (1<<0), - MD_TokenFlag_Numeric = (1<<1), - MD_TokenFlag_StringLiteral = (1<<2), - MD_TokenFlag_Symbol = (1<<3), - MD_TokenFlag_Reserved = (1<<4), - MD_TokenFlag_Comment = (1<<5), - MD_TokenFlag_Whitespace = (1<<6), - MD_TokenFlag_Newline = (1<<7), - - // rjf: decoration info - MD_TokenFlag_StringSingleQuote = (1<<8), - MD_TokenFlag_StringDoubleQuote = (1<<9), - MD_TokenFlag_StringTick = (1<<10), - MD_TokenFlag_StringTriplet = (1<<11), - - // rjf: error info - MD_TokenFlag_BrokenComment = (1<<12), - MD_TokenFlag_BrokenStringLiteral = (1<<13), - MD_TokenFlag_BadCharacter = (1<<14), -}; - -typedef U32 MD_TokenGroups; -enum -{ - MD_TokenGroup_Comment = MD_TokenFlag_Comment, - MD_TokenGroup_Whitespace = (MD_TokenFlag_Whitespace| - MD_TokenFlag_Newline), - MD_TokenGroup_Irregular = (MD_TokenGroup_Comment| - MD_TokenGroup_Whitespace), - MD_TokenGroup_Regular = ~MD_TokenGroup_Irregular, - MD_TokenGroup_Label = (MD_TokenFlag_Identifier| - MD_TokenFlag_Numeric| - MD_TokenFlag_StringLiteral| - MD_TokenFlag_Symbol), - MD_TokenGroup_Error = (MD_TokenFlag_BrokenComment| - MD_TokenFlag_BrokenStringLiteral| - MD_TokenFlag_BadCharacter), -}; - -typedef struct MD_Token MD_Token; -struct MD_Token -{ - Rng1U64 range; - MD_TokenFlags flags; -}; - -typedef struct MD_TokenChunkNode MD_TokenChunkNode; -struct MD_TokenChunkNode -{ - MD_TokenChunkNode *next; - MD_Token *v; - U64 count; - U64 cap; -}; - -typedef struct MD_TokenChunkList MD_TokenChunkList; -struct MD_TokenChunkList -{ - MD_TokenChunkNode *first; - MD_TokenChunkNode *last; - U64 chunk_count; - U64 total_token_count; -}; - -typedef struct MD_TokenArray MD_TokenArray; -struct MD_TokenArray -{ - MD_Token *v; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Node Types - -typedef enum MD_NodeKind -{ - MD_NodeKind_Nil, - MD_NodeKind_File, - MD_NodeKind_ErrorMarker, - MD_NodeKind_Main, - MD_NodeKind_Tag, - MD_NodeKind_List, - MD_NodeKind_Reference, - MD_NodeKind_COUNT -} -MD_NodeKind; - -typedef U64 MD_NodeFlags; -enum -{ - MD_NodeFlag_MaskSetDelimiters = (0x3F<<0), - MD_NodeFlag_HasParenLeft = (1<<0), - MD_NodeFlag_HasParenRight = (1<<1), - MD_NodeFlag_HasBracketLeft = (1<<2), - MD_NodeFlag_HasBracketRight = (1<<3), - MD_NodeFlag_HasBraceLeft = (1<<4), - MD_NodeFlag_HasBraceRight = (1<<5), - - MD_NodeFlag_MaskSeparators = (0xF<<6), - MD_NodeFlag_IsBeforeSemicolon = (1<<6), - MD_NodeFlag_IsAfterSemicolon = (1<<7), - MD_NodeFlag_IsBeforeComma = (1<<8), - MD_NodeFlag_IsAfterComma = (1<<9), - - MD_NodeFlag_MaskStringDelimiters = (0xF<<10), - MD_NodeFlag_StringSingleQuote = (1<<10), - MD_NodeFlag_StringDoubleQuote = (1<<11), - MD_NodeFlag_StringTick = (1<<12), - MD_NodeFlag_StringTriplet = (1<<13), - - MD_NodeFlag_MaskLabelKind = (0xF<<14), - MD_NodeFlag_Numeric = (1<<14), - MD_NodeFlag_Identifier = (1<<15), - MD_NodeFlag_StringLiteral = (1<<16), - MD_NodeFlag_Symbol = (1<<17), -}; -#define MD_NodeFlag_AfterFromBefore(f) ((f) << 1) - -typedef struct MD_Node MD_Node; -struct MD_Node -{ - // rjf: tree links - MD_Node *next; - MD_Node *prev; - MD_Node *parent; - MD_Node *first; - MD_Node *last; - - // rjf: tag links - MD_Node *first_tag; - MD_Node *last_tag; - - // rjf: node info - MD_NodeKind kind; - MD_NodeFlags flags; - String8 string; - String8 raw_string; - - // rjf: source code info - U64 src_offset; -}; - -typedef struct MD_NodeRec MD_NodeRec; -struct MD_NodeRec -{ - MD_Node *next; - S32 push_count; - S32 pop_count; -}; - -//////////////////////////////// -//~ rjf: Text -> Tokens Types - -typedef struct MD_TokenizeResult MD_TokenizeResult; -struct MD_TokenizeResult -{ - MD_TokenArray tokens; - MD_MsgList msgs; -}; - -//////////////////////////////// -//~ rjf: Tokens -> Tree Types - -typedef struct MD_ParseResult MD_ParseResult; -struct MD_ParseResult -{ - MD_Node *root; - MD_MsgList msgs; -}; - -//////////////////////////////// -//~ rjf: Globals - -global read_only MD_Node md_nil_node = -{ - &md_nil_node, - &md_nil_node, - &md_nil_node, - &md_nil_node, - &md_nil_node, - &md_nil_node, - &md_nil_node, -}; - -//////////////////////////////// -//~ rjf: Message Type Functions - -internal void md_msg_list_push(Arena *arena, MD_MsgList *msgs, MD_Node *node, MD_MsgKind kind, String8 string); -internal void md_msg_list_pushf(Arena *arena, MD_MsgList *msgs, MD_Node *node, MD_MsgKind kind, char *fmt, ...); -internal void md_msg_list_concat_in_place(MD_MsgList *dst, MD_MsgList *to_push); - -//////////////////////////////// -//~ rjf: Token Type Functions - -internal MD_Token md_token_make(Rng1U64 range, MD_TokenFlags flags); -internal B32 md_token_match(MD_Token a, MD_Token b); -internal String8List md_string_list_from_token_flags(Arena *arena, MD_TokenFlags flags); -internal void md_token_chunk_list_push(Arena *arena, MD_TokenChunkList *list, U64 cap, MD_Token token); -internal MD_TokenArray md_token_array_from_chunk_list(Arena *arena, MD_TokenChunkList *chunks); -internal String8 md_content_string_from_token_flags_str8(MD_TokenFlags flags, String8 string); - -//////////////////////////////// -//~ rjf: Node Type Functions - -//- rjf: flag conversions -internal MD_NodeFlags md_node_flags_from_token_flags(MD_TokenFlags flags); - -//- rjf: nil -internal B32 md_node_is_nil(MD_Node *node); - -//- rjf: iteration -#define MD_EachNode(it, first) MD_Node *it = first; !md_node_is_nil(it); it = it->next -internal MD_NodeRec md_node_rec_depth_first(MD_Node *node, MD_Node *subtree_root, U64 child_off, U64 sib_off); -#define md_node_rec_depth_first_pre(node, subtree_root) md_node_rec_depth_first((node), (subtree_root), OffsetOf(MD_Node, first), OffsetOf(MD_Node, next)) -#define md_node_rec_depth_first_pre_rev(node, subtree_root) md_node_rec_depth_first((node), (subtree_root), OffsetOf(MD_Node, last), OffsetOf(MD_Node, prev)) - -//- rjf: tree building -internal MD_Node *md_push_node(Arena *arena, MD_NodeKind kind, MD_NodeFlags flags, String8 string, String8 raw_string, U64 src_offset); -internal void md_node_push_child(MD_Node *parent, MD_Node *node); -internal void md_node_push_tag(MD_Node *parent, MD_Node *node); - -//- rjf: tree introspection -internal MD_Node * md_node_from_chain_string(MD_Node *first, MD_Node *opl, String8 string, StringMatchFlags flags); -internal MD_Node * md_node_from_chain_index(MD_Node *first, MD_Node *opl, U64 index); -internal MD_Node * md_node_from_chain_flags(MD_Node *first, MD_Node *opl, MD_NodeFlags flags); -internal U64 md_index_from_node(MD_Node *node); -internal MD_Node * md_root_from_node(MD_Node *node); -internal MD_Node * md_child_from_string(MD_Node *node, String8 child_string, StringMatchFlags flags); -internal MD_Node * md_tag_from_string(MD_Node *node, String8 tag_string, StringMatchFlags flags); -internal MD_Node * md_child_from_index(MD_Node *node, U64 index); -internal MD_Node * md_tag_from_index(MD_Node *node, U64 index); -internal MD_Node * md_tag_arg_from_index(MD_Node *node, String8 tag_string, StringMatchFlags flags, U64 index); -internal MD_Node * md_tag_arg_from_string(MD_Node *node, String8 tag_string, StringMatchFlags tag_str_flags, String8 arg_string, StringMatchFlags arg_str_flags); -internal B32 md_node_has_child(MD_Node *node, String8 string, StringMatchFlags flags); -internal B32 md_node_has_tag(MD_Node *node, String8 string, StringMatchFlags flags); -internal U64 md_child_count_from_node(MD_Node *node); -internal U64 md_tag_count_from_node(MD_Node *node); - -//- rjf: tree comparison -internal B32 md_node_deep_match(MD_Node *a, MD_Node *b, StringMatchFlags flags); -internal B32 md_node_match(MD_Node *a, MD_Node *b, StringMatchFlags flags); - -//////////////////////////////// -//~ rjf: Text -> Tokens Functions - -internal MD_TokenizeResult md_tokenize_from_text(Arena *arena, String8 text); - -//////////////////////////////// -//~ rjf: Tokens -> Tree Functions - -internal MD_ParseResult md_parse_from_text_tokens(Arena *arena, String8 filename, String8 text, MD_TokenArray tokens); - -//////////////////////////////// -//~ rjf: Tree -> Text Functions - -internal String8List md_debug_string_list_from_tree(Arena *arena, MD_Node *root); - -#endif // MDESK_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef MDESK_H +#define MDESK_H + +//////////////////////////////// +//~ rjf: Messages + +typedef enum MD_MsgKind +{ + MD_MsgKind_Null, + MD_MsgKind_Note, + MD_MsgKind_Warning, + MD_MsgKind_Error, + MD_MsgKind_FatalError, +} +MD_MsgKind; + +typedef struct MD_Msg MD_Msg; +struct MD_Msg +{ + MD_Msg *next; + struct MD_Node *node; + MD_MsgKind kind; + String8 string; +}; + +typedef struct MD_MsgList MD_MsgList; +struct MD_MsgList +{ + MD_Msg *first; + MD_Msg *last; + U64 count; + MD_MsgKind worst_message_kind; +}; + +//////////////////////////////// +//~ rjf: Token Types + +typedef U32 MD_TokenFlags; +enum +{ + // rjf: base kind info + MD_TokenFlag_Identifier = (1<<0), + MD_TokenFlag_Numeric = (1<<1), + MD_TokenFlag_StringLiteral = (1<<2), + MD_TokenFlag_Symbol = (1<<3), + MD_TokenFlag_Reserved = (1<<4), + MD_TokenFlag_Comment = (1<<5), + MD_TokenFlag_Whitespace = (1<<6), + MD_TokenFlag_Newline = (1<<7), + + // rjf: decoration info + MD_TokenFlag_StringSingleQuote = (1<<8), + MD_TokenFlag_StringDoubleQuote = (1<<9), + MD_TokenFlag_StringTick = (1<<10), + MD_TokenFlag_StringTriplet = (1<<11), + + // rjf: error info + MD_TokenFlag_BrokenComment = (1<<12), + MD_TokenFlag_BrokenStringLiteral = (1<<13), + MD_TokenFlag_BadCharacter = (1<<14), +}; + +typedef U32 MD_TokenGroups; +enum +{ + MD_TokenGroup_Comment = MD_TokenFlag_Comment, + MD_TokenGroup_Whitespace = (MD_TokenFlag_Whitespace| + MD_TokenFlag_Newline), + MD_TokenGroup_Irregular = (MD_TokenGroup_Comment| + MD_TokenGroup_Whitespace), + MD_TokenGroup_Regular = ~MD_TokenGroup_Irregular, + MD_TokenGroup_Label = (MD_TokenFlag_Identifier| + MD_TokenFlag_Numeric| + MD_TokenFlag_StringLiteral| + MD_TokenFlag_Symbol), + MD_TokenGroup_Error = (MD_TokenFlag_BrokenComment| + MD_TokenFlag_BrokenStringLiteral| + MD_TokenFlag_BadCharacter), +}; + +typedef struct MD_Token MD_Token; +struct MD_Token +{ + Rng1U64 range; + MD_TokenFlags flags; +}; + +typedef struct MD_TokenChunkNode MD_TokenChunkNode; +struct MD_TokenChunkNode +{ + MD_TokenChunkNode *next; + MD_Token *v; + U64 count; + U64 cap; +}; + +typedef struct MD_TokenChunkList MD_TokenChunkList; +struct MD_TokenChunkList +{ + MD_TokenChunkNode *first; + MD_TokenChunkNode *last; + U64 chunk_count; + U64 total_token_count; +}; + +typedef struct MD_TokenArray MD_TokenArray; +struct MD_TokenArray +{ + MD_Token *v; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Node Types + +typedef enum MD_NodeKind +{ + MD_NodeKind_Nil, + MD_NodeKind_File, + MD_NodeKind_ErrorMarker, + MD_NodeKind_Main, + MD_NodeKind_Tag, + MD_NodeKind_List, + MD_NodeKind_Reference, + MD_NodeKind_COUNT +} +MD_NodeKind; + +typedef U64 MD_NodeFlags; +enum +{ + MD_NodeFlag_MaskSetDelimiters = (0x3F<<0), + MD_NodeFlag_HasParenLeft = (1<<0), + MD_NodeFlag_HasParenRight = (1<<1), + MD_NodeFlag_HasBracketLeft = (1<<2), + MD_NodeFlag_HasBracketRight = (1<<3), + MD_NodeFlag_HasBraceLeft = (1<<4), + MD_NodeFlag_HasBraceRight = (1<<5), + + MD_NodeFlag_MaskSeparators = (0xF<<6), + MD_NodeFlag_IsBeforeSemicolon = (1<<6), + MD_NodeFlag_IsAfterSemicolon = (1<<7), + MD_NodeFlag_IsBeforeComma = (1<<8), + MD_NodeFlag_IsAfterComma = (1<<9), + + MD_NodeFlag_MaskStringDelimiters = (0xF<<10), + MD_NodeFlag_StringSingleQuote = (1<<10), + MD_NodeFlag_StringDoubleQuote = (1<<11), + MD_NodeFlag_StringTick = (1<<12), + MD_NodeFlag_StringTriplet = (1<<13), + + MD_NodeFlag_MaskLabelKind = (0xF<<14), + MD_NodeFlag_Numeric = (1<<14), + MD_NodeFlag_Identifier = (1<<15), + MD_NodeFlag_StringLiteral = (1<<16), + MD_NodeFlag_Symbol = (1<<17), +}; +#define MD_NodeFlag_AfterFromBefore(f) ((f) << 1) + +typedef struct MD_Node MD_Node; +struct MD_Node +{ + // rjf: tree links + MD_Node *next; + MD_Node *prev; + MD_Node *parent; + MD_Node *first; + MD_Node *last; + + // rjf: tag links + MD_Node *first_tag; + MD_Node *last_tag; + + // rjf: node info + MD_NodeKind kind; + MD_NodeFlags flags; + String8 string; + String8 raw_string; + + // rjf: source code info + U64 src_offset; +}; + +typedef struct MD_NodeRec MD_NodeRec; +struct MD_NodeRec +{ + MD_Node *next; + S32 push_count; + S32 pop_count; +}; + +//////////////////////////////// +//~ rjf: Text -> Tokens Types + +typedef struct MD_TokenizeResult MD_TokenizeResult; +struct MD_TokenizeResult +{ + MD_TokenArray tokens; + MD_MsgList msgs; +}; + +//////////////////////////////// +//~ rjf: Tokens -> Tree Types + +typedef struct MD_ParseResult MD_ParseResult; +struct MD_ParseResult +{ + MD_Node *root; + MD_MsgList msgs; +}; + +//////////////////////////////// +//~ rjf: Globals + +global read_only MD_Node md_nil_node = +{ + &md_nil_node, + &md_nil_node, + &md_nil_node, + &md_nil_node, + &md_nil_node, + &md_nil_node, + &md_nil_node, +}; + +//////////////////////////////// +//~ rjf: Message Type Functions + +internal void md_msg_list_push(Arena *arena, MD_MsgList *msgs, MD_Node *node, MD_MsgKind kind, String8 string); +internal void md_msg_list_pushf(Arena *arena, MD_MsgList *msgs, MD_Node *node, MD_MsgKind kind, char *fmt, ...); +internal void md_msg_list_concat_in_place(MD_MsgList *dst, MD_MsgList *to_push); + +//////////////////////////////// +//~ rjf: Token Type Functions + +internal MD_Token md_token_make(Rng1U64 range, MD_TokenFlags flags); +internal B32 md_token_match(MD_Token a, MD_Token b); +internal String8List md_string_list_from_token_flags(Arena *arena, MD_TokenFlags flags); +internal void md_token_chunk_list_push(Arena *arena, MD_TokenChunkList *list, U64 cap, MD_Token token); +internal MD_TokenArray md_token_array_from_chunk_list(Arena *arena, MD_TokenChunkList *chunks); +internal String8 md_content_string_from_token_flags_str8(MD_TokenFlags flags, String8 string); + +//////////////////////////////// +//~ rjf: Node Type Functions + +//- rjf: flag conversions +internal MD_NodeFlags md_node_flags_from_token_flags(MD_TokenFlags flags); + +//- rjf: nil +internal B32 md_node_is_nil(MD_Node *node); + +//- rjf: iteration +#define MD_EachNode(it, first) MD_Node *it = first; !md_node_is_nil(it); it = it->next +internal MD_NodeRec md_node_rec_depth_first(MD_Node *node, MD_Node *subtree_root, U64 child_off, U64 sib_off); +#define md_node_rec_depth_first_pre(node, subtree_root) md_node_rec_depth_first((node), (subtree_root), OffsetOf(MD_Node, first), OffsetOf(MD_Node, next)) +#define md_node_rec_depth_first_pre_rev(node, subtree_root) md_node_rec_depth_first((node), (subtree_root), OffsetOf(MD_Node, last), OffsetOf(MD_Node, prev)) + +//- rjf: tree building +internal MD_Node *md_push_node(Arena *arena, MD_NodeKind kind, MD_NodeFlags flags, String8 string, String8 raw_string, U64 src_offset); +internal void md_node_push_child(MD_Node *parent, MD_Node *node); +internal void md_node_push_tag(MD_Node *parent, MD_Node *node); + +//- rjf: tree introspection +internal MD_Node * md_node_from_chain_string(MD_Node *first, MD_Node *opl, String8 string, StringMatchFlags flags); +internal MD_Node * md_node_from_chain_index(MD_Node *first, MD_Node *opl, U64 index); +internal MD_Node * md_node_from_chain_flags(MD_Node *first, MD_Node *opl, MD_NodeFlags flags); +internal U64 md_index_from_node(MD_Node *node); +internal MD_Node * md_root_from_node(MD_Node *node); +internal MD_Node * md_child_from_string(MD_Node *node, String8 child_string, StringMatchFlags flags); +internal MD_Node * md_tag_from_string(MD_Node *node, String8 tag_string, StringMatchFlags flags); +internal MD_Node * md_child_from_index(MD_Node *node, U64 index); +internal MD_Node * md_tag_from_index(MD_Node *node, U64 index); +internal MD_Node * md_tag_arg_from_index(MD_Node *node, String8 tag_string, StringMatchFlags flags, U64 index); +internal MD_Node * md_tag_arg_from_string(MD_Node *node, String8 tag_string, StringMatchFlags tag_str_flags, String8 arg_string, StringMatchFlags arg_str_flags); +internal B32 md_node_has_child(MD_Node *node, String8 string, StringMatchFlags flags); +internal B32 md_node_has_tag(MD_Node *node, String8 string, StringMatchFlags flags); +internal U64 md_child_count_from_node(MD_Node *node); +internal U64 md_tag_count_from_node(MD_Node *node); + +//- rjf: tree comparison +internal B32 md_node_deep_match(MD_Node *a, MD_Node *b, StringMatchFlags flags); +internal B32 md_node_match(MD_Node *a, MD_Node *b, StringMatchFlags flags); + +//////////////////////////////// +//~ rjf: Text -> Tokens Functions + +internal MD_TokenizeResult md_tokenize_from_text(Arena *arena, String8 text); + +//////////////////////////////// +//~ rjf: Tokens -> Tree Functions + +internal MD_ParseResult md_parse_from_text_tokens(Arena *arena, String8 filename, String8 text, MD_TokenArray tokens); + +//////////////////////////////// +//~ rjf: Tree -> Text Functions + +internal String8List md_debug_string_list_from_tree(Arena *arena, MD_Node *root); + +#endif // MDESK_H diff --git a/src/metagen/metagen.c b/src/metagen/metagen.c index 51d9f06c..8cfc6a3d 100644 --- a/src/metagen/metagen.c +++ b/src/metagen/metagen.c @@ -1,1136 +1,1136 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: String Expression Operator Tables - -read_only global String8 mg_str_expr_op_symbol_string_table[MG_StrExprOp_COUNT] = -{ - str8_lit_comp(""), - str8_lit_comp("."), // MG_StrExprOp_Dot - str8_lit_comp("->"), // MG_StrExprOp_ExpandIfTrue - str8_lit_comp(".."), // MG_StrExprOp_Concat - str8_lit_comp("=>"), // MG_StrExprOp_BumpToColumn - str8_lit_comp("+"), // MG_StrExprOp_Add - str8_lit_comp("-"), // MG_StrExprOp_Subtract - str8_lit_comp("*"), // MG_StrExprOp_Multiply - str8_lit_comp("/"), // MG_StrExprOp_Divide - str8_lit_comp("%"), // MG_StrExprOp_Modulo - str8_lit_comp("<<"), // MG_StrExprOp_LeftShift - str8_lit_comp(">>"), // MG_StrExprOp_RightShift - str8_lit_comp("&"), // MG_StrExprOp_BitwiseAnd - str8_lit_comp("|"), // MG_StrExprOp_BitwiseOr - str8_lit_comp("^"), // MG_StrExprOp_BitwiseXor - str8_lit_comp("~"), // MG_StrExprOp_BitwiseNegate - str8_lit_comp("&&"), // MG_StrExprOp_BooleanAnd - str8_lit_comp("||"), // MG_StrExprOp_BooleanOr - str8_lit_comp("!"), // MG_StrExprOp_BooleanNot - str8_lit_comp("=="), // MG_StrExprOp_Equals - str8_lit_comp("!="), // MG_StrExprOp_DoesNotEqual -}; - -read_only global S8 mg_str_expr_op_precedence_table[MG_StrExprOp_COUNT] = -{ - 0, - 20, // MG_StrExprOp_Dot - 1, // MG_StrExprOp_ExpandIfTrue - 2, // MG_StrExprOp_Concat - 12, // MG_StrExprOp_BumpToColumn - 5, // MG_StrExprOp_Add - 5, // MG_StrExprOp_Subtract - 6, // MG_StrExprOp_Multiply - 6, // MG_StrExprOp_Divide - 6, // MG_StrExprOp_Modulo - 7, // MG_StrExprOp_LeftShift - 7, // MG_StrExprOp_RightShift - 8, // MG_StrExprOp_BitwiseAnd - 10, // MG_StrExprOp_BitwiseOr - 9, // MG_StrExprOp_BitwiseXor - 11, // MG_StrExprOp_BitwiseNegate - 3, // MG_StrExprOp_BooleanAnd - 3, // MG_StrExprOp_BooleanOr - 11, // MG_StrExprOp_BooleanNot - 4, // MG_StrExprOp_Equals - 4, // MG_StrExprOp_DoesNotEqual -}; - -read_only global MG_StrExprOpKind mg_str_expr_op_kind_table[MG_StrExprOp_COUNT] = -{ - MG_StrExprOpKind_Null, - MG_StrExprOpKind_Binary, // MG_StrExprOp_Dot - MG_StrExprOpKind_Binary, // MG_StrExprOp_ExpandIfTrue - MG_StrExprOpKind_Binary, // MG_StrExprOp_Concat - MG_StrExprOpKind_Prefix, // MG_StrExprOp_BumpToColumn - MG_StrExprOpKind_Binary, // MG_StrExprOp_Add - MG_StrExprOpKind_Binary, // MG_StrExprOp_Subtract - MG_StrExprOpKind_Binary, // MG_StrExprOp_Multiply - MG_StrExprOpKind_Binary, // MG_StrExprOp_Divide - MG_StrExprOpKind_Binary, // MG_StrExprOp_Modulo - MG_StrExprOpKind_Binary, // MG_StrExprOp_LeftShift - MG_StrExprOpKind_Binary, // MG_StrExprOp_RightShift - MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseAnd - MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseOr - MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseXor - MG_StrExprOpKind_Prefix, // MG_StrExprOp_BitwiseNegate - MG_StrExprOpKind_Binary, // MG_StrExprOp_BooleanAnd - MG_StrExprOpKind_Binary, // MG_StrExprOp_BooleanOr - MG_StrExprOpKind_Prefix, // MG_StrExprOp_BooleanNot - MG_StrExprOpKind_Binary, // MG_StrExprOp_Equals - MG_StrExprOpKind_Binary, // MG_StrExprOp_DoesNotEqual -}; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal U64 -mg_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 TxtPt -mg_txt_pt_from_string_off(String8 string, U64 off) -{ - TxtPt pt = txt_pt(1, 1); - for(U64 idx = 0; idx < string.size && idx < off; idx += 1) - { - if(string.str[idx] == '\n') - { - pt.line += 1; - pt.column = 1; - } - else - { - pt.column += 1; - } - } - return pt; -} - -//////////////////////////////// -//~ rjf: Message Lists - -internal void -mg_msg_list_push(Arena *arena, MG_MsgList *msgs, MG_Msg *msg) -{ - MG_MsgNode *n = push_array(arena, MG_MsgNode, 1); - MemoryCopyStruct(&n->v, msg); - SLLQueuePush(msgs->first, msgs->last, n); - msgs->count += 1; -} - -//////////////////////////////// -//~ rjf: String Escaping - -internal String8 -mg_escaped_from_str8(Arena *arena, String8 string) -{ - // NOTE(rjf): This doesn't handle hex/octal/unicode escape sequences right - // now, just the simple stuff. - Temp scratch = scratch_begin(&arena, 1); - String8List strs = {0}; - U64 start = 0; - for(U64 idx = 0; idx <= string.size; idx += 1) - { - if(idx == string.size || string.str[idx] == '\\' || string.str[idx] == '\r') - { - String8 str = str8_substr(string, r1u64(start, idx)); - if(str.size != 0) - { - str8_list_push(arena, &strs, str); - } - start = idx+1; - } - if(idx < string.size && string.str[idx] == '\\') - { - U8 next_char = string.str[idx+1]; - U8 replace_byte = 0; - switch(next_char) - { - default:{}break; - case 'a': replace_byte = 0x07; break; - case 'b': replace_byte = 0x08; break; - case 'e': replace_byte = 0x1b; break; - case 'f': replace_byte = 0x0c; break; - case 'n': replace_byte = 0x0a; break; - case 'r': replace_byte = 0x0d; break; - case 't': replace_byte = 0x09; break; - case 'v': replace_byte = 0x0b; break; - case '\\':replace_byte = '\\'; break; - case '\'':replace_byte = '\''; break; - case '"': replace_byte = '"'; break; - case '?': replace_byte = '?'; break; - } - String8 replace_string = push_str8_copy(scratch.arena, str8(&replace_byte, 1)); - str8_list_push(scratch.arena, &strs, replace_string); - if(replace_byte == '\\' || replace_byte == '"' || replace_byte == '\'') - { - idx += 1; - start += 1; - } - } - } - String8 result = str8_list_join(arena, &strs, 0); - scratch_end(scratch); - return result; -} - -//////////////////////////////// -//~ rjf: String Wrapping - -internal String8List -mg_wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent) -{ - String8List list = {0}; - Rng1U64 line_range = r1u64(0, 0); - U64 wrapped_indent_level = 0; - static char *spaces = " "; - for (U64 idx = 0; idx <= string.size; idx += 1){ - U8 chr = idx < string.size ? string.str[idx] : 0; - if (chr == '\n'){ - Rng1U64 candidate_line_range = line_range; - candidate_line_range.max = idx; - // NOTE(nick): when wrapping is interrupted with \n we emit a string without including \n - // because later tool_fprint_list inserts separator after each node - // except for last node, so don't strip last \n. - if (idx + 1 == string.size){ - candidate_line_range.max += 1; - } - String8 substr = str8_substr(string, candidate_line_range); - str8_list_push(arena, &list, substr); - line_range = r1u64(idx+1,idx+1); - } - else - if (char_is_space(chr) || chr == 0){ - Rng1U64 candidate_line_range = line_range; - candidate_line_range.max = idx; - String8 substr = str8_substr(string, candidate_line_range); - U64 width_this_line = max_width-wrapped_indent_level; - if (list.node_count == 0){ - width_this_line = first_line_max_width; - } - if (substr.size > width_this_line){ - String8 line = str8_substr(string, line_range); - if (wrapped_indent_level > 0){ - line = push_str8f(arena, "%.*s%S", wrapped_indent_level, spaces, line); - } - str8_list_push(arena, &list, line); - line_range = r1u64(line_range.max+1, candidate_line_range.max); - wrapped_indent_level = ClampTop(64, wrap_indent); - } - else{ - line_range = candidate_line_range; - } - } - } - if (line_range.min < string.size && line_range.max > line_range.min){ - String8 line = str8_substr(string, line_range); - if (wrapped_indent_level > 0){ - line = push_str8f(arena, "%.*s%S", wrapped_indent_level, spaces, line); - } - str8_list_push(arena, &list, line); - } - return list; -} - -//////////////////////////////// -//~ rjf: C-String-Izing - -internal String8 -mg_c_string_literal_from_multiline_string(String8 string) -{ - String8List strings = {0}; - { - str8_list_push(mg_arena, &strings, str8_lit("\"\"\n")); - U64 active_line_start_off = 0; - for(U64 off = 0; off <= string.size; off += 1) - { - B32 is_newline = (off < string.size && (string.str[off] == '\n' || string.str[off] == '\r')); - B32 is_ender = (off >= string.size || is_newline); - if(is_ender) - { - String8 line = str8_substr(string, r1u64(active_line_start_off, off)); - str8_list_push(mg_arena, &strings, str8_lit("\"")); - str8_list_push(mg_arena, &strings, line); - if(is_newline) - { - str8_list_push(mg_arena, &strings, str8_lit("\\n\"\n")); - } - else - { - str8_list_push(mg_arena, &strings, str8_lit("\"\n")); - } - active_line_start_off = off+1; - } - if(is_newline && string.str[off] == '\r') - { - active_line_start_off += 1; - off += 1; - } - } - } - String8 result = str8_list_join(mg_arena, &strings, 0); - return result; -} - -internal String8 -mg_c_array_literal_contents_from_data(String8 data) -{ - Temp scratch = scratch_begin(0, 0); - String8List strings = {0}; - { - for(U64 off = 0; off < data.size;) - { - U64 chunk_size = Min(data.size-off, 64); - U8 *chunk_bytes = data.str+off; - String8 chunk_text_string = {0}; - chunk_text_string.size = chunk_size*5; - chunk_text_string.str = push_array(mg_arena, U8, chunk_text_string.size); - for(U64 byte_idx = 0; byte_idx < chunk_size; byte_idx += 1) - { - String8 byte_str = push_str8f(scratch.arena, "0x%02x,", chunk_bytes[byte_idx]); - MemoryCopy(chunk_text_string.str+byte_idx*5, byte_str.str, byte_str.size); - } - off += chunk_size; - str8_list_push(mg_arena, &strings, chunk_text_string); - str8_list_push(mg_arena, &strings, str8_lit("\n")); - } - } - String8 result = str8_list_join(mg_arena, &strings, 0); - scratch_end(scratch); - return result; -} - -//////////////////////////////// -//~ rjf: Map Functions - -internal MG_Map -mg_push_map(Arena *arena, U64 slot_count) -{ - MG_Map map = {0}; - map.slots_count = slot_count; - map.slots = push_array(arena, MG_MapSlot, map.slots_count); - return map; -} - -internal void * -mg_map_ptr_from_string(MG_Map *map, String8 string) -{ - void *result = 0; - { - U64 hash = mg_hash_from_string(string); - U64 slot_idx = hash%map->slots_count; - MG_MapSlot *slot = &map->slots[slot_idx]; - for(MG_MapNode *n = slot->first; n != 0; n = n->next) - { - if(str8_match(n->key, string, 0)) - { - result = n->val; - break; - } - } - } - return result; -} - -internal void -mg_map_insert_ptr(Arena *arena, MG_Map *map, String8 string, void *val) -{ - U64 hash = mg_hash_from_string(string); - U64 slot_idx = hash%map->slots_count; - MG_MapSlot *slot = &map->slots[slot_idx]; - MG_MapNode *n = push_array(arena, MG_MapNode, 1); - n->key = push_str8_copy(arena, string); - n->val = val; - SLLQueuePush(slot->first, slot->last, n); -} - -//////////////////////////////// -//~ rjf: String Expression Parsing - -internal MG_StrExpr * -mg_push_str_expr(Arena *arena, MG_StrExprOp op, MD_Node *node) -{ - MG_StrExpr *expr = push_array(arena, MG_StrExpr, 1); - MemoryCopyStruct(expr, &mg_str_expr_nil); - expr->op = op; - expr->node = node; - return expr; -} - -internal MG_StrExprParseResult -mg_str_expr_parse_from_first_opl__min_prec(Arena *arena, MD_Node *first, MD_Node *opl, S8 min_prec) -{ - MG_StrExprParseResult parse = {&mg_str_expr_nil}; - { - MD_Node *it = first; - - //- rjf: consume prefix operators - MG_StrExpr *leafmost_op = &mg_str_expr_nil; - for(;it != opl && !md_node_is_nil(it);) - { - MG_StrExprOp found_op = MG_StrExprOp_Null; - for(MG_StrExprOp op = (MG_StrExprOp)(MG_StrExprOp_Null+1); - op < MG_StrExprOp_COUNT; - op = (MG_StrExprOp)(op+1)) - { - if(mg_str_expr_op_kind_table[op] == MG_StrExprOpKind_Prefix && - str8_match(it->string, mg_str_expr_op_symbol_string_table[op], 0) && - mg_str_expr_op_precedence_table[op] >= min_prec) - { - found_op = op; - break; - } - } - if(found_op != MG_StrExprOp_Null) - { - MG_StrExpr *op_expr = mg_push_str_expr(arena, found_op, it); - if(leafmost_op == &mg_str_expr_nil) - { - leafmost_op = op_expr; - } - op_expr->left = parse.root; - parse.root = op_expr; - it = it->next; - } - else - { - break; - } - } - - //- rjf: parse atom - { - MG_StrExpr *atom = &mg_str_expr_nil; - if(it->flags & (MD_NodeFlag_Identifier|MD_NodeFlag_Numeric|MD_NodeFlag_StringLiteral) && - md_node_is_nil(it->first)) - { - atom = mg_push_str_expr(arena, MG_StrExprOp_Null, it); - it = it->next; - } - else if(!md_node_is_nil(it->first)) - { - MG_StrExprParseResult subparse = mg_str_expr_parse_from_first_opl__min_prec(arena, it->first, &md_nil_node, 0); - atom = subparse.root; - md_msg_list_concat_in_place(&parse.msgs, &subparse.msgs); - it = it->next; - } - if(leafmost_op != &mg_str_expr_nil) - { - leafmost_op->left = atom; - } - else - { - parse.root = atom; - } - } - - //- rjf: parse binary operator extensions at this precedence level - for(;it != opl && !md_node_is_nil(it);) - { - // rjf: find binary op kind of `it` - MG_StrExprOp found_op = MG_StrExprOp_Null; - for(MG_StrExprOp op = (MG_StrExprOp)(MG_StrExprOp_Null+1); - op < MG_StrExprOp_COUNT; - op = (MG_StrExprOp)(op+1)) - { - if(mg_str_expr_op_kind_table[op] == MG_StrExprOpKind_Binary && - str8_match(it->string, mg_str_expr_op_symbol_string_table[op], 0) && - mg_str_expr_op_precedence_table[op] >= min_prec) - { - found_op = op; - break; - } - } - - // rjf: good found_op -> build binary expr - if(found_op != MG_StrExprOp_Null) - { - MG_StrExpr *op_expr = mg_push_str_expr(arena, found_op, it); - if(leafmost_op == &mg_str_expr_nil) - { - leafmost_op = op_expr; - } - op_expr->left = parse.root; - parse.root = op_expr; - it = it->next; - } - else - { - break; - } - - // rjf: parse right hand side of binary operator - MG_StrExprParseResult subparse = mg_str_expr_parse_from_first_opl__min_prec(arena, it, opl, mg_str_expr_op_precedence_table[found_op]+1); - parse.root->right = subparse.root; - md_msg_list_concat_in_place(&parse.msgs, &subparse.msgs); - if(subparse.root == &mg_str_expr_nil) - { - md_msg_list_pushf(arena, &parse.msgs, it, MD_MsgKind_Error, "Missing right-hand-side of '%S'.", mg_str_expr_op_symbol_string_table[found_op]); - } - it = subparse.next_node; - } - - // rjf: store next node for more caller-side parsing - parse.next_node = it; - } - return parse; -} - -internal MG_StrExprParseResult -mg_str_expr_parse_from_first_opl(Arena *arena, MD_Node *first, MD_Node *opl) -{ - MG_StrExprParseResult parse = mg_str_expr_parse_from_first_opl__min_prec(arena, first, opl, 0); - return parse; -} - -internal MG_StrExprParseResult -mg_str_expr_parse_from_root(Arena *arena, MD_Node *root) -{ - MG_StrExprParseResult parse = mg_str_expr_parse_from_first_opl__min_prec(arena, root->first, &md_nil_node, 0); - return parse; -} - -//////////////////////////////// -//~ rjf: Table Generation Functions - -internal MG_NodeArray -mg_node_array_make(Arena *arena, U64 count) -{ - MG_NodeArray result = {0}; - result.count = count; - result.v = push_array(arena, MD_Node *, result.count); - for(U64 idx = 0; idx < result.count; idx += 1) - { - result.v[idx] = &md_nil_node; - } - return result; -} - -internal MG_NodeArray -mg_child_array_from_node(Arena *arena, MD_Node *node) -{ - MG_NodeArray children = mg_node_array_make(arena, md_child_count_from_node(node)); - U64 idx = 0; - for(MD_EachNode(child, node->first)) - { - children.v[idx] = child; - idx += 1; - } - return children; -} - -internal MG_NodeGrid -mg_node_grid_make_from_node(Arena *arena, MD_Node *root) -{ - MG_NodeGrid grid = {0}; - - // rjf: determine dimensions - U64 row_count = md_child_count_from_node(root); - U64 column_count = 0; - for(MD_EachNode(row, root->first)) - { - U64 cell_count_this_row = md_child_count_from_node(row); - column_count = Max(column_count, cell_count_this_row); - } - - // rjf: fill grid - grid.x_stride = 1; - grid.y_stride = column_count; - grid.cells = mg_node_array_make(arena, row_count*column_count); - grid.row_parents = mg_node_array_make(arena, row_count); - - // rjf: fill nodes - { - U64 y = 0; - for(MD_EachNode(row, root->first)) - { - U64 x = 0; - grid.row_parents.v[y] = row; - for(MD_EachNode(cell, row->first)) - { - grid.cells.v[x*grid.x_stride + y*grid.y_stride] = cell; - x += 1; - } - y += 1; - } - } - - return grid; -} - -internal MG_NodeArray -mg_row_from_index(MG_NodeGrid grid, U64 index) -{ - MG_NodeArray result = {0}; - if(0 <= index && index < grid.cells.count / grid.x_stride) - { - result.count = grid.y_stride; - result.v = &grid.cells.v[index*grid.y_stride]; - } - return result; -} - -internal MG_NodeArray -mg_column_from_index(Arena *arena, MG_NodeGrid grid, U64 index) -{ - MG_NodeArray result = {0}; - if(0 <= index && index < grid.y_stride) - { - U64 row_count = grid.cells.count / grid.y_stride; - result = mg_node_array_make(arena, row_count); - U64 idx = 0; - for(U64 row_idx = 0; row_idx < row_count; row_idx += 1, idx += 1) - { - result.v[idx] = grid.cells.v[index*grid.x_stride + row_idx*grid.y_stride]; - } - } - return result; -} - -internal MD_Node * -mg_node_from_grid_xy(MG_NodeGrid grid, U64 x, U64 y) -{ - MD_Node *result = &md_nil_node; - U64 idx = x*grid.x_stride + y*grid.y_stride; - if(0 <= idx && idx < grid.cells.count) - { - result = grid.cells.v[idx]; - } - return result; -} - -internal MG_ColumnDescArray -mg_column_desc_array_make(Arena *arena, U64 count, MG_ColumnDesc *descs) -{ - MG_ColumnDescArray result = {0}; - result.count = count; - result.v = push_array(arena, MG_ColumnDesc, result.count); - MemoryCopy(result.v, descs, sizeof(*result.v)*result.count); - return result; -} - -internal MG_ColumnDescArray -mg_column_desc_array_from_tag(Arena *arena, MD_Node *tag) -{ - MG_ColumnDescArray result = {0}; - result.count = md_child_count_from_node(tag); - result.v = push_array(arena, MG_ColumnDesc, result.count); - U64 idx = 0; - for(MD_EachNode(hdr, tag->first)) - { - result.v[idx].name = push_str8_copy(arena, hdr->string); - result.v[idx].kind = MG_ColumnKind_DirectCell; - if(md_node_has_tag(hdr, str8_lit("tag_check"), 0)) - { - result.v[idx].kind = MG_ColumnKind_CheckForTag; - } - if(md_node_has_tag(hdr, str8_lit("tag_child"), 0)) - { - String8 tag_name = md_tag_from_string(hdr, str8_lit("tag_child"), 0)->first->string; - result.v[idx].kind = MG_ColumnKind_TagChild; - result.v[idx].tag_name = tag_name; - } - idx += 1; - } - return result; -} - -internal U64 -mg_column_index_from_name(MG_ColumnDescArray descs, String8 name) -{ - U64 result = 0; - for(U64 idx = 0; idx < descs.count; idx += 1) - { - if(str8_match(descs.v[idx].name, name, 0)) - { - result = idx; - break; - } - } - return result; -} - -internal String8 -mg_string_from_row_desc_idx(MD_Node *row_parent, MG_ColumnDescArray descs, U64 idx) -{ - String8 result = {0}; - - // rjf: grab relevant column description - MG_ColumnDesc *desc = 0; - if(0 <= idx && idx < descs.count) - { - desc = descs.v + idx; - } - - // rjf: grab node - if(desc != 0) - { - switch(desc->kind) - { - default: break; - - case MG_ColumnKind_DirectCell: - { - // rjf: determine grid idx (shifted by synthetic columns) - U64 cell_idx = idx; - for(U64 col_idx = 0; col_idx < descs.count && col_idx < idx; col_idx += 1) - { - if(descs.v[col_idx].kind != MG_ColumnKind_DirectCell) - { - cell_idx -= 1; - } - } - MD_Node *node = md_child_from_index(row_parent, cell_idx); - result = node->string; - }break; - - case MG_ColumnKind_CheckForTag: - { - String8 tag_name = desc->name; - MD_Node *tag = md_tag_from_string(row_parent, tag_name, 0); - result = md_node_is_nil(tag) ? str8_lit("0") : str8_lit("1"); - }break; - - case MG_ColumnKind_TagChild: - { - String8 tag_name = desc->tag_name; - MD_Node *tag = md_tag_from_string(row_parent, tag_name, 0); - result = tag->first->string; - }break; - } - } - - return result; -} - -internal S64 -mg_eval_table_expand_expr__numeric(MG_StrExpr *expr, MG_TableExpandInfo *info) -{ - S64 result = 0; - MG_StrExprOp op = expr->op; - - switch(op) - { - default: - { - if(MG_StrExprOp_FirstString <= op && op <= MG_StrExprOp_LastString) - { - Temp scratch = scratch_begin(0, 0); - String8List result_strs = {0}; - mg_eval_table_expand_expr__string(scratch.arena, expr, info, &result_strs); - String8 result_str = str8_list_join(scratch.arena, &result_strs, 0); - try_s64_from_str8_c_rules(result_str, &result); - scratch_end(scratch); - } - }break; - - case MG_StrExprOp_Null: - { - try_s64_from_str8_c_rules(expr->node->string, &result); - }break; - - //- rjf: numeric arithmetic binary ops - case MG_StrExprOp_Add: - case MG_StrExprOp_Subtract: - case MG_StrExprOp_Multiply: - case MG_StrExprOp_Divide: - case MG_StrExprOp_Modulo: - case MG_StrExprOp_LeftShift: - case MG_StrExprOp_RightShift: - case MG_StrExprOp_BitwiseAnd: - case MG_StrExprOp_BitwiseOr: - case MG_StrExprOp_BitwiseXor: - case MG_StrExprOp_BooleanAnd: - case MG_StrExprOp_BooleanOr: - { - S64 left_val = mg_eval_table_expand_expr__numeric(expr->left, info); - S64 right_val = mg_eval_table_expand_expr__numeric(expr->right, info); - switch(op) - { - default:break; - case MG_StrExprOp_Add: result = left_val+right_val; break; - case MG_StrExprOp_Subtract: result = left_val-right_val; break; - case MG_StrExprOp_Multiply: result = left_val*right_val; break; - case MG_StrExprOp_Divide: result = left_val/right_val; break; - case MG_StrExprOp_Modulo: result = left_val%right_val; break; - case MG_StrExprOp_LeftShift: result = left_val<>right_val; break; - case MG_StrExprOp_BitwiseAnd: result = left_val&right_val; break; - case MG_StrExprOp_BitwiseOr: result = left_val|right_val; break; - case MG_StrExprOp_BitwiseXor: result = left_val^right_val; break; - case MG_StrExprOp_BooleanAnd: result = left_val&&right_val; break; - case MG_StrExprOp_BooleanOr: result = left_val||right_val; break; - } - }break; - - //- rjf: prefix unary ops - case MG_StrExprOp_BitwiseNegate: - case MG_StrExprOp_BooleanNot: - { - S64 right_val = mg_eval_table_expand_expr__numeric(expr->left, info); - switch(op) - { - default:break; - case MG_StrExprOp_BitwiseNegate: result = (S64)(~((U64)right_val)); break; - case MG_StrExprOp_BooleanNot: result = !right_val; - } - }break; - - //- rjf: comparisons - case MG_StrExprOp_Equals: - case MG_StrExprOp_DoesNotEqual: - { - Temp scratch = scratch_begin(0, 0); - String8List left_strs = {0}; - String8List right_strs = {0}; - mg_eval_table_expand_expr__string(scratch.arena, expr->left, info, &left_strs); - mg_eval_table_expand_expr__string(scratch.arena, expr->right, info, &right_strs); - String8 left_str = str8_list_join(scratch.arena, &left_strs, 0); - String8 right_str = str8_list_join(scratch.arena, &right_strs, 0); - B32 match = str8_match(left_str, right_str, 0); - result = (op == MG_StrExprOp_Equals ? match : !match); - scratch_end(scratch); - }break; - } - - return result; -} - -internal void -mg_eval_table_expand_expr__string(Arena *arena, MG_StrExpr *expr, MG_TableExpandInfo *info, String8List *out) -{ - MG_StrExprOp op = expr->op; - - switch(op) - { - default: - { - if(MG_StrExprOp_FirstNumeric <= op && op <= MG_StrExprOp_LastNumeric) - { - S64 numeric_eval = mg_eval_table_expand_expr__numeric(expr, info); - String8 numeric_eval_stringized = {0}; - if(md_node_has_tag(md_root_from_node(expr->node), str8_lit("hex"), 0)) - { - numeric_eval_stringized = push_str8f(arena, "0x%I64x", numeric_eval); - } - else - { - numeric_eval_stringized = push_str8f(arena, "%I64d", numeric_eval); - } - str8_list_push(arena, out, numeric_eval_stringized); - } - }break; - - case MG_StrExprOp_Null: - { - str8_list_push(arena, out, expr->node->string); - }break; - - case MG_StrExprOp_Dot: - { - // rjf: grab left/right - MG_StrExpr *left_expr = expr->left; - MD_Node *left_node = left_expr->node; - MG_StrExpr *right_expr = expr->right; - MD_Node *right_node = right_expr->node; - - // rjf: grab table name (LHS of .) and column lookup string (RHS of .) - String8 expand_label = left_node->string; - String8 column_lookup = right_node->string; - - // rjf: find which task corresponds to this table - U64 row_idx = 0; - MG_NodeGrid *grid = 0; - MG_ColumnDescArray column_descs = {0}; - { - for(MG_TableExpandTask *task = info->first_expand_task; task != 0; task = task->next) - { - if(str8_match(expand_label, task->expansion_label, 0)) - { - row_idx = task->idx; - grid = task->grid; - column_descs = task->column_descs; - break; - } - } - } - - // rjf: grab row parent - MD_Node *row_parent = &md_nil_node; - if(grid && (0 <= row_idx && row_idx < grid->row_parents.count)) - { - row_parent = grid->row_parents.v[row_idx]; - } - - // rjf: get string for this table lookup - String8 lookup_string = {0}; - { - U64 column_idx = 0; - - if(str8_match(column_lookup, str8_lit("_it"), 0)) - { - lookup_string = push_str8f(arena, "%I64u", row_idx); - } - else - { - // NOTE(rjf): numeric column lookup (column index) - if(right_node->flags & MD_NodeFlag_Numeric) - { - try_u64_from_str8_c_rules(column_lookup, &column_idx); - } - - // NOTE(rjf): string column lookup (column name) - if(right_node->flags & (MD_NodeFlag_Identifier|MD_NodeFlag_StringLiteral)) - { - column_idx = mg_column_index_from_name(column_descs, column_lookup); - } - - lookup_string = mg_string_from_row_desc_idx(row_parent, column_descs, column_idx); - if(str8_match(lookup_string, str8_lit("--"), 0)) - { - lookup_string = info->missing_value_fallback; - } - } - } - - // rjf: push lookup string - { - str8_list_push(arena, out, lookup_string); - } - }break; - - case MG_StrExprOp_ExpandIfTrue: - { - S64 bool_value = mg_eval_table_expand_expr__numeric(expr->left, info); - if(bool_value) - { - mg_eval_table_expand_expr__string(arena, expr->right, info, out); - } - }break; - - case MG_StrExprOp_Concat: - { - mg_eval_table_expand_expr__string(arena, expr->left, info, out); - mg_eval_table_expand_expr__string(arena, expr->right, info, out); - }break; - - case MG_StrExprOp_BumpToColumn: - { - S64 column = mg_eval_table_expand_expr__numeric(expr->left, info); - S64 current_column = out->total_size; - S64 spaces_to_push = column - current_column; - if(spaces_to_push > 0) - { - String8 str = {0}; - str.size = spaces_to_push; - str.str = push_array(arena, U8, spaces_to_push); - for(S64 idx = 0; idx < spaces_to_push; idx += 1) - { - str.str[idx] = ' '; - } - str8_list_push(arena, out, str); - } - }break; - } -} - -internal void -mg_loop_table_column_expansion(Arena *arena, String8 strexpr, MG_TableExpandInfo *info, MG_TableExpandTask *task, String8List *out) -{ - Temp scratch = scratch_begin(&arena, 1); - for(U64 it_idx = 0; it_idx < task->count; it_idx += 1) - { - task->idx = it_idx; - - //- rjf: iterate all further dimensions, if there's left in the chain - if(task->next) - { - mg_loop_table_column_expansion(arena, strexpr, info, task->next, out); - } - - //- rjf: if this is the last task in the chain, perform expansion - else - { - String8List expansion_strs = {0}; - U64 start = 0; - for(U64 char_idx = 0; char_idx <= strexpr.size;) - { - // rjf: push plain text parts of strexpr - if(char_idx == strexpr.size || strexpr.str[char_idx] == '$') - { - String8 plain_text_substr = str8_substr(strexpr, r1u64(start, char_idx)); - start = char_idx; - if(plain_text_substr.size != 0) - { - str8_list_push(arena, &expansion_strs, plain_text_substr); - } - } - - // rjf: handle expansion expression - if(strexpr.str[char_idx] == '$') - { - String8 string = str8_skip(strexpr, char_idx+1); - Rng1U64 expr_range = {0}; - S64 paren_nest = 0; - for(U64 idx = 0; idx < string.size; idx += 1) - { - if(string.str[idx] == '(') - { - paren_nest += 1; - if(paren_nest == 1) - { - expr_range.min = idx; - } - } - if(string.str[idx] == ')') - { - paren_nest -= 1; - if(paren_nest == 0) - { - expr_range.max = idx+1; - break; - } - } - } - String8 expr_string = str8_substr(string, expr_range); - MD_TokenizeResult expr_tokenize = md_tokenize_from_text(scratch.arena, expr_string); - MD_ParseResult expr_base_parse = md_parse_from_text_tokens(scratch.arena, str8_lit(""), expr_string, expr_tokenize.tokens); - MG_StrExprParseResult expr_parse = mg_str_expr_parse_from_root(scratch.arena, expr_base_parse.root->first); - mg_eval_table_expand_expr__string(arena, expr_parse.root, info, &expansion_strs); - char_idx = start = char_idx + 1 + expr_range.max; - } - else - { - char_idx += 1; - } - } - String8 expansion_str = str8_list_join(arena, &expansion_strs, 0); - if(expansion_str.size != 0) - { - str8_list_push(arena, out, expansion_str); - } - } - } - - scratch_end(scratch); -} - -internal String8List -mg_string_list_from_table_gen(Arena *arena, MG_Map grid_name_map, MG_Map grid_column_desc_map, String8 fallback, MD_Node *gen) -{ - String8List result = {0}; - Temp scratch = scratch_begin(&arena, 1); - if(md_node_is_nil(gen->first) && gen->string.size != 0) - { - str8_list_push(arena, &result, gen->string); - str8_list_push(arena, &result, str8_lit("\n")); - } - else for(MD_EachNode(strexpr_node, gen->first)) - { - // rjf: build task list - MG_TableExpandTask *first_task = 0; - MG_TableExpandTask *last_task = 0; - for(MD_EachNode(tag, strexpr_node->first_tag)) - { - if(str8_match(tag->string, str8_lit("expand"), 0)) - { - // rjf: grab args for this expansion - MD_Node *table_name_node = md_child_from_index(tag, 0); - MD_Node *expand_label_node = md_child_from_index(tag, 1); - String8 table_name = table_name_node->string; - String8 expand_label = expand_label_node->string; - - // rjf: lookup table / column descriptions - MG_NodeGrid *grid = mg_map_ptr_from_string(&grid_name_map, table_name); - MG_ColumnDescArray *column_descs = mg_map_ptr_from_string(&grid_column_desc_map, table_name); - - // rjf: figure out row count - U64 grid_row_count = 0; - if(grid != 0) - { - grid_row_count = grid->cells.count / grid->y_stride; - } - - // rjf: push task for this expansion - if(grid != 0) - { - MG_TableExpandTask *task = push_array(scratch.arena, MG_TableExpandTask, 1); - task->expansion_label = expand_label; - task->grid = grid; - task->column_descs = *column_descs; - task->count = grid_row_count; - task->idx = 0; - SLLQueuePush(first_task, last_task, task); - } - } - } - - // rjf: do expansion generation, OR just push this string if we have no expansions - { - MG_TableExpandInfo info = {first_task, fallback}; - if(first_task != 0) - { - mg_loop_table_column_expansion(arena, strexpr_node->string, &info, first_task, &result); - } - else - { - str8_list_push(arena, &result, strexpr_node->string); - } - } - } - scratch_end(scratch); - return result; -} - -//////////////////////////////// -//~ rjf: Layer Lookup Functions - -internal String8 -mg_layer_key_from_path(String8 path) -{ - Temp scratch = scratch_begin(0, 0); - U64 src_folder_pos = 0; - for(U64 next_src_folder_pos = 0; - next_src_folder_pos < path.size; - next_src_folder_pos = str8_find_needle(path, next_src_folder_pos+1, str8_lit("src"), 0)) - { - src_folder_pos = next_src_folder_pos; - } - String8List path_parts = str8_split_path(scratch.arena, str8_chop_last_slash(str8_skip(path, src_folder_pos+4))); - StringJoin join = {0}; - join.sep = str8_lit("/"); - String8 key = str8_list_join(mg_arena, &path_parts, &join); - scratch_end(scratch); - return key; -} - -internal MG_Layer * -mg_layer_from_key(String8 key) -{ - U64 hash = mg_hash_from_string(key); - U64 slot_idx = hash%mg_state->slots_count; - MG_LayerSlot *slot = &mg_state->slots[slot_idx]; - MG_Layer *layer = 0; - for(MG_LayerNode *n = slot->first; n != 0; n = n->next) - { - if(str8_match(n->v.key, key, 0)) - { - layer = &n->v; - break; - } - } - if(layer == 0) - { - MG_LayerNode *n = push_array(mg_arena, MG_LayerNode, 1); - SLLQueuePush(slot->first, slot->last, n); - n->v.key = push_str8_copy(mg_arena, key); - layer = &n->v; - } - return layer; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: String Expression Operator Tables + +read_only global String8 mg_str_expr_op_symbol_string_table[MG_StrExprOp_COUNT] = +{ + str8_lit_comp(""), + str8_lit_comp("."), // MG_StrExprOp_Dot + str8_lit_comp("->"), // MG_StrExprOp_ExpandIfTrue + str8_lit_comp(".."), // MG_StrExprOp_Concat + str8_lit_comp("=>"), // MG_StrExprOp_BumpToColumn + str8_lit_comp("+"), // MG_StrExprOp_Add + str8_lit_comp("-"), // MG_StrExprOp_Subtract + str8_lit_comp("*"), // MG_StrExprOp_Multiply + str8_lit_comp("/"), // MG_StrExprOp_Divide + str8_lit_comp("%"), // MG_StrExprOp_Modulo + str8_lit_comp("<<"), // MG_StrExprOp_LeftShift + str8_lit_comp(">>"), // MG_StrExprOp_RightShift + str8_lit_comp("&"), // MG_StrExprOp_BitwiseAnd + str8_lit_comp("|"), // MG_StrExprOp_BitwiseOr + str8_lit_comp("^"), // MG_StrExprOp_BitwiseXor + str8_lit_comp("~"), // MG_StrExprOp_BitwiseNegate + str8_lit_comp("&&"), // MG_StrExprOp_BooleanAnd + str8_lit_comp("||"), // MG_StrExprOp_BooleanOr + str8_lit_comp("!"), // MG_StrExprOp_BooleanNot + str8_lit_comp("=="), // MG_StrExprOp_Equals + str8_lit_comp("!="), // MG_StrExprOp_DoesNotEqual +}; + +read_only global S8 mg_str_expr_op_precedence_table[MG_StrExprOp_COUNT] = +{ + 0, + 20, // MG_StrExprOp_Dot + 1, // MG_StrExprOp_ExpandIfTrue + 2, // MG_StrExprOp_Concat + 12, // MG_StrExprOp_BumpToColumn + 5, // MG_StrExprOp_Add + 5, // MG_StrExprOp_Subtract + 6, // MG_StrExprOp_Multiply + 6, // MG_StrExprOp_Divide + 6, // MG_StrExprOp_Modulo + 7, // MG_StrExprOp_LeftShift + 7, // MG_StrExprOp_RightShift + 8, // MG_StrExprOp_BitwiseAnd + 10, // MG_StrExprOp_BitwiseOr + 9, // MG_StrExprOp_BitwiseXor + 11, // MG_StrExprOp_BitwiseNegate + 3, // MG_StrExprOp_BooleanAnd + 3, // MG_StrExprOp_BooleanOr + 11, // MG_StrExprOp_BooleanNot + 4, // MG_StrExprOp_Equals + 4, // MG_StrExprOp_DoesNotEqual +}; + +read_only global MG_StrExprOpKind mg_str_expr_op_kind_table[MG_StrExprOp_COUNT] = +{ + MG_StrExprOpKind_Null, + MG_StrExprOpKind_Binary, // MG_StrExprOp_Dot + MG_StrExprOpKind_Binary, // MG_StrExprOp_ExpandIfTrue + MG_StrExprOpKind_Binary, // MG_StrExprOp_Concat + MG_StrExprOpKind_Prefix, // MG_StrExprOp_BumpToColumn + MG_StrExprOpKind_Binary, // MG_StrExprOp_Add + MG_StrExprOpKind_Binary, // MG_StrExprOp_Subtract + MG_StrExprOpKind_Binary, // MG_StrExprOp_Multiply + MG_StrExprOpKind_Binary, // MG_StrExprOp_Divide + MG_StrExprOpKind_Binary, // MG_StrExprOp_Modulo + MG_StrExprOpKind_Binary, // MG_StrExprOp_LeftShift + MG_StrExprOpKind_Binary, // MG_StrExprOp_RightShift + MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseAnd + MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseOr + MG_StrExprOpKind_Binary, // MG_StrExprOp_BitwiseXor + MG_StrExprOpKind_Prefix, // MG_StrExprOp_BitwiseNegate + MG_StrExprOpKind_Binary, // MG_StrExprOp_BooleanAnd + MG_StrExprOpKind_Binary, // MG_StrExprOp_BooleanOr + MG_StrExprOpKind_Prefix, // MG_StrExprOp_BooleanNot + MG_StrExprOpKind_Binary, // MG_StrExprOp_Equals + MG_StrExprOpKind_Binary, // MG_StrExprOp_DoesNotEqual +}; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal U64 +mg_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 TxtPt +mg_txt_pt_from_string_off(String8 string, U64 off) +{ + TxtPt pt = txt_pt(1, 1); + for(U64 idx = 0; idx < string.size && idx < off; idx += 1) + { + if(string.str[idx] == '\n') + { + pt.line += 1; + pt.column = 1; + } + else + { + pt.column += 1; + } + } + return pt; +} + +//////////////////////////////// +//~ rjf: Message Lists + +internal void +mg_msg_list_push(Arena *arena, MG_MsgList *msgs, MG_Msg *msg) +{ + MG_MsgNode *n = push_array(arena, MG_MsgNode, 1); + MemoryCopyStruct(&n->v, msg); + SLLQueuePush(msgs->first, msgs->last, n); + msgs->count += 1; +} + +//////////////////////////////// +//~ rjf: String Escaping + +internal String8 +mg_escaped_from_str8(Arena *arena, String8 string) +{ + // NOTE(rjf): This doesn't handle hex/octal/unicode escape sequences right + // now, just the simple stuff. + Temp scratch = scratch_begin(&arena, 1); + String8List strs = {0}; + U64 start = 0; + for(U64 idx = 0; idx <= string.size; idx += 1) + { + if(idx == string.size || string.str[idx] == '\\' || string.str[idx] == '\r') + { + String8 str = str8_substr(string, r1u64(start, idx)); + if(str.size != 0) + { + str8_list_push(arena, &strs, str); + } + start = idx+1; + } + if(idx < string.size && string.str[idx] == '\\') + { + U8 next_char = string.str[idx+1]; + U8 replace_byte = 0; + switch(next_char) + { + default:{}break; + case 'a': replace_byte = 0x07; break; + case 'b': replace_byte = 0x08; break; + case 'e': replace_byte = 0x1b; break; + case 'f': replace_byte = 0x0c; break; + case 'n': replace_byte = 0x0a; break; + case 'r': replace_byte = 0x0d; break; + case 't': replace_byte = 0x09; break; + case 'v': replace_byte = 0x0b; break; + case '\\':replace_byte = '\\'; break; + case '\'':replace_byte = '\''; break; + case '"': replace_byte = '"'; break; + case '?': replace_byte = '?'; break; + } + String8 replace_string = push_str8_copy(scratch.arena, str8(&replace_byte, 1)); + str8_list_push(scratch.arena, &strs, replace_string); + if(replace_byte == '\\' || replace_byte == '"' || replace_byte == '\'') + { + idx += 1; + start += 1; + } + } + } + String8 result = str8_list_join(arena, &strs, 0); + scratch_end(scratch); + return result; +} + +//////////////////////////////// +//~ rjf: String Wrapping + +internal String8List +mg_wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent) +{ + String8List list = {0}; + Rng1U64 line_range = r1u64(0, 0); + U64 wrapped_indent_level = 0; + static char *spaces = " "; + for (U64 idx = 0; idx <= string.size; idx += 1){ + U8 chr = idx < string.size ? string.str[idx] : 0; + if (chr == '\n'){ + Rng1U64 candidate_line_range = line_range; + candidate_line_range.max = idx; + // NOTE(nick): when wrapping is interrupted with \n we emit a string without including \n + // because later tool_fprint_list inserts separator after each node + // except for last node, so don't strip last \n. + if (idx + 1 == string.size){ + candidate_line_range.max += 1; + } + String8 substr = str8_substr(string, candidate_line_range); + str8_list_push(arena, &list, substr); + line_range = r1u64(idx+1,idx+1); + } + else + if (char_is_space(chr) || chr == 0){ + Rng1U64 candidate_line_range = line_range; + candidate_line_range.max = idx; + String8 substr = str8_substr(string, candidate_line_range); + U64 width_this_line = max_width-wrapped_indent_level; + if (list.node_count == 0){ + width_this_line = first_line_max_width; + } + if (substr.size > width_this_line){ + String8 line = str8_substr(string, line_range); + if (wrapped_indent_level > 0){ + line = push_str8f(arena, "%.*s%S", wrapped_indent_level, spaces, line); + } + str8_list_push(arena, &list, line); + line_range = r1u64(line_range.max+1, candidate_line_range.max); + wrapped_indent_level = ClampTop(64, wrap_indent); + } + else{ + line_range = candidate_line_range; + } + } + } + if (line_range.min < string.size && line_range.max > line_range.min){ + String8 line = str8_substr(string, line_range); + if (wrapped_indent_level > 0){ + line = push_str8f(arena, "%.*s%S", wrapped_indent_level, spaces, line); + } + str8_list_push(arena, &list, line); + } + return list; +} + +//////////////////////////////// +//~ rjf: C-String-Izing + +internal String8 +mg_c_string_literal_from_multiline_string(String8 string) +{ + String8List strings = {0}; + { + str8_list_push(mg_arena, &strings, str8_lit("\"\"\n")); + U64 active_line_start_off = 0; + for(U64 off = 0; off <= string.size; off += 1) + { + B32 is_newline = (off < string.size && (string.str[off] == '\n' || string.str[off] == '\r')); + B32 is_ender = (off >= string.size || is_newline); + if(is_ender) + { + String8 line = str8_substr(string, r1u64(active_line_start_off, off)); + str8_list_push(mg_arena, &strings, str8_lit("\"")); + str8_list_push(mg_arena, &strings, line); + if(is_newline) + { + str8_list_push(mg_arena, &strings, str8_lit("\\n\"\n")); + } + else + { + str8_list_push(mg_arena, &strings, str8_lit("\"\n")); + } + active_line_start_off = off+1; + } + if(is_newline && string.str[off] == '\r') + { + active_line_start_off += 1; + off += 1; + } + } + } + String8 result = str8_list_join(mg_arena, &strings, 0); + return result; +} + +internal String8 +mg_c_array_literal_contents_from_data(String8 data) +{ + Temp scratch = scratch_begin(0, 0); + String8List strings = {0}; + { + for(U64 off = 0; off < data.size;) + { + U64 chunk_size = Min(data.size-off, 64); + U8 *chunk_bytes = data.str+off; + String8 chunk_text_string = {0}; + chunk_text_string.size = chunk_size*5; + chunk_text_string.str = push_array(mg_arena, U8, chunk_text_string.size); + for(U64 byte_idx = 0; byte_idx < chunk_size; byte_idx += 1) + { + String8 byte_str = push_str8f(scratch.arena, "0x%02x,", chunk_bytes[byte_idx]); + MemoryCopy(chunk_text_string.str+byte_idx*5, byte_str.str, byte_str.size); + } + off += chunk_size; + str8_list_push(mg_arena, &strings, chunk_text_string); + str8_list_push(mg_arena, &strings, str8_lit("\n")); + } + } + String8 result = str8_list_join(mg_arena, &strings, 0); + scratch_end(scratch); + return result; +} + +//////////////////////////////// +//~ rjf: Map Functions + +internal MG_Map +mg_push_map(Arena *arena, U64 slot_count) +{ + MG_Map map = {0}; + map.slots_count = slot_count; + map.slots = push_array(arena, MG_MapSlot, map.slots_count); + return map; +} + +internal void * +mg_map_ptr_from_string(MG_Map *map, String8 string) +{ + void *result = 0; + { + U64 hash = mg_hash_from_string(string); + U64 slot_idx = hash%map->slots_count; + MG_MapSlot *slot = &map->slots[slot_idx]; + for(MG_MapNode *n = slot->first; n != 0; n = n->next) + { + if(str8_match(n->key, string, 0)) + { + result = n->val; + break; + } + } + } + return result; +} + +internal void +mg_map_insert_ptr(Arena *arena, MG_Map *map, String8 string, void *val) +{ + U64 hash = mg_hash_from_string(string); + U64 slot_idx = hash%map->slots_count; + MG_MapSlot *slot = &map->slots[slot_idx]; + MG_MapNode *n = push_array(arena, MG_MapNode, 1); + n->key = push_str8_copy(arena, string); + n->val = val; + SLLQueuePush(slot->first, slot->last, n); +} + +//////////////////////////////// +//~ rjf: String Expression Parsing + +internal MG_StrExpr * +mg_push_str_expr(Arena *arena, MG_StrExprOp op, MD_Node *node) +{ + MG_StrExpr *expr = push_array(arena, MG_StrExpr, 1); + MemoryCopyStruct(expr, &mg_str_expr_nil); + expr->op = op; + expr->node = node; + return expr; +} + +internal MG_StrExprParseResult +mg_str_expr_parse_from_first_opl__min_prec(Arena *arena, MD_Node *first, MD_Node *opl, S8 min_prec) +{ + MG_StrExprParseResult parse = {&mg_str_expr_nil}; + { + MD_Node *it = first; + + //- rjf: consume prefix operators + MG_StrExpr *leafmost_op = &mg_str_expr_nil; + for(;it != opl && !md_node_is_nil(it);) + { + MG_StrExprOp found_op = MG_StrExprOp_Null; + for(MG_StrExprOp op = (MG_StrExprOp)(MG_StrExprOp_Null+1); + op < MG_StrExprOp_COUNT; + op = (MG_StrExprOp)(op+1)) + { + if(mg_str_expr_op_kind_table[op] == MG_StrExprOpKind_Prefix && + str8_match(it->string, mg_str_expr_op_symbol_string_table[op], 0) && + mg_str_expr_op_precedence_table[op] >= min_prec) + { + found_op = op; + break; + } + } + if(found_op != MG_StrExprOp_Null) + { + MG_StrExpr *op_expr = mg_push_str_expr(arena, found_op, it); + if(leafmost_op == &mg_str_expr_nil) + { + leafmost_op = op_expr; + } + op_expr->left = parse.root; + parse.root = op_expr; + it = it->next; + } + else + { + break; + } + } + + //- rjf: parse atom + { + MG_StrExpr *atom = &mg_str_expr_nil; + if(it->flags & (MD_NodeFlag_Identifier|MD_NodeFlag_Numeric|MD_NodeFlag_StringLiteral) && + md_node_is_nil(it->first)) + { + atom = mg_push_str_expr(arena, MG_StrExprOp_Null, it); + it = it->next; + } + else if(!md_node_is_nil(it->first)) + { + MG_StrExprParseResult subparse = mg_str_expr_parse_from_first_opl__min_prec(arena, it->first, &md_nil_node, 0); + atom = subparse.root; + md_msg_list_concat_in_place(&parse.msgs, &subparse.msgs); + it = it->next; + } + if(leafmost_op != &mg_str_expr_nil) + { + leafmost_op->left = atom; + } + else + { + parse.root = atom; + } + } + + //- rjf: parse binary operator extensions at this precedence level + for(;it != opl && !md_node_is_nil(it);) + { + // rjf: find binary op kind of `it` + MG_StrExprOp found_op = MG_StrExprOp_Null; + for(MG_StrExprOp op = (MG_StrExprOp)(MG_StrExprOp_Null+1); + op < MG_StrExprOp_COUNT; + op = (MG_StrExprOp)(op+1)) + { + if(mg_str_expr_op_kind_table[op] == MG_StrExprOpKind_Binary && + str8_match(it->string, mg_str_expr_op_symbol_string_table[op], 0) && + mg_str_expr_op_precedence_table[op] >= min_prec) + { + found_op = op; + break; + } + } + + // rjf: good found_op -> build binary expr + if(found_op != MG_StrExprOp_Null) + { + MG_StrExpr *op_expr = mg_push_str_expr(arena, found_op, it); + if(leafmost_op == &mg_str_expr_nil) + { + leafmost_op = op_expr; + } + op_expr->left = parse.root; + parse.root = op_expr; + it = it->next; + } + else + { + break; + } + + // rjf: parse right hand side of binary operator + MG_StrExprParseResult subparse = mg_str_expr_parse_from_first_opl__min_prec(arena, it, opl, mg_str_expr_op_precedence_table[found_op]+1); + parse.root->right = subparse.root; + md_msg_list_concat_in_place(&parse.msgs, &subparse.msgs); + if(subparse.root == &mg_str_expr_nil) + { + md_msg_list_pushf(arena, &parse.msgs, it, MD_MsgKind_Error, "Missing right-hand-side of '%S'.", mg_str_expr_op_symbol_string_table[found_op]); + } + it = subparse.next_node; + } + + // rjf: store next node for more caller-side parsing + parse.next_node = it; + } + return parse; +} + +internal MG_StrExprParseResult +mg_str_expr_parse_from_first_opl(Arena *arena, MD_Node *first, MD_Node *opl) +{ + MG_StrExprParseResult parse = mg_str_expr_parse_from_first_opl__min_prec(arena, first, opl, 0); + return parse; +} + +internal MG_StrExprParseResult +mg_str_expr_parse_from_root(Arena *arena, MD_Node *root) +{ + MG_StrExprParseResult parse = mg_str_expr_parse_from_first_opl__min_prec(arena, root->first, &md_nil_node, 0); + return parse; +} + +//////////////////////////////// +//~ rjf: Table Generation Functions + +internal MG_NodeArray +mg_node_array_make(Arena *arena, U64 count) +{ + MG_NodeArray result = {0}; + result.count = count; + result.v = push_array(arena, MD_Node *, result.count); + for(U64 idx = 0; idx < result.count; idx += 1) + { + result.v[idx] = &md_nil_node; + } + return result; +} + +internal MG_NodeArray +mg_child_array_from_node(Arena *arena, MD_Node *node) +{ + MG_NodeArray children = mg_node_array_make(arena, md_child_count_from_node(node)); + U64 idx = 0; + for(MD_EachNode(child, node->first)) + { + children.v[idx] = child; + idx += 1; + } + return children; +} + +internal MG_NodeGrid +mg_node_grid_make_from_node(Arena *arena, MD_Node *root) +{ + MG_NodeGrid grid = {0}; + + // rjf: determine dimensions + U64 row_count = md_child_count_from_node(root); + U64 column_count = 0; + for(MD_EachNode(row, root->first)) + { + U64 cell_count_this_row = md_child_count_from_node(row); + column_count = Max(column_count, cell_count_this_row); + } + + // rjf: fill grid + grid.x_stride = 1; + grid.y_stride = column_count; + grid.cells = mg_node_array_make(arena, row_count*column_count); + grid.row_parents = mg_node_array_make(arena, row_count); + + // rjf: fill nodes + { + U64 y = 0; + for(MD_EachNode(row, root->first)) + { + U64 x = 0; + grid.row_parents.v[y] = row; + for(MD_EachNode(cell, row->first)) + { + grid.cells.v[x*grid.x_stride + y*grid.y_stride] = cell; + x += 1; + } + y += 1; + } + } + + return grid; +} + +internal MG_NodeArray +mg_row_from_index(MG_NodeGrid grid, U64 index) +{ + MG_NodeArray result = {0}; + if(0 <= index && index < grid.cells.count / grid.x_stride) + { + result.count = grid.y_stride; + result.v = &grid.cells.v[index*grid.y_stride]; + } + return result; +} + +internal MG_NodeArray +mg_column_from_index(Arena *arena, MG_NodeGrid grid, U64 index) +{ + MG_NodeArray result = {0}; + if(0 <= index && index < grid.y_stride) + { + U64 row_count = grid.cells.count / grid.y_stride; + result = mg_node_array_make(arena, row_count); + U64 idx = 0; + for(U64 row_idx = 0; row_idx < row_count; row_idx += 1, idx += 1) + { + result.v[idx] = grid.cells.v[index*grid.x_stride + row_idx*grid.y_stride]; + } + } + return result; +} + +internal MD_Node * +mg_node_from_grid_xy(MG_NodeGrid grid, U64 x, U64 y) +{ + MD_Node *result = &md_nil_node; + U64 idx = x*grid.x_stride + y*grid.y_stride; + if(0 <= idx && idx < grid.cells.count) + { + result = grid.cells.v[idx]; + } + return result; +} + +internal MG_ColumnDescArray +mg_column_desc_array_make(Arena *arena, U64 count, MG_ColumnDesc *descs) +{ + MG_ColumnDescArray result = {0}; + result.count = count; + result.v = push_array(arena, MG_ColumnDesc, result.count); + MemoryCopy(result.v, descs, sizeof(*result.v)*result.count); + return result; +} + +internal MG_ColumnDescArray +mg_column_desc_array_from_tag(Arena *arena, MD_Node *tag) +{ + MG_ColumnDescArray result = {0}; + result.count = md_child_count_from_node(tag); + result.v = push_array(arena, MG_ColumnDesc, result.count); + U64 idx = 0; + for(MD_EachNode(hdr, tag->first)) + { + result.v[idx].name = push_str8_copy(arena, hdr->string); + result.v[idx].kind = MG_ColumnKind_DirectCell; + if(md_node_has_tag(hdr, str8_lit("tag_check"), 0)) + { + result.v[idx].kind = MG_ColumnKind_CheckForTag; + } + if(md_node_has_tag(hdr, str8_lit("tag_child"), 0)) + { + String8 tag_name = md_tag_from_string(hdr, str8_lit("tag_child"), 0)->first->string; + result.v[idx].kind = MG_ColumnKind_TagChild; + result.v[idx].tag_name = tag_name; + } + idx += 1; + } + return result; +} + +internal U64 +mg_column_index_from_name(MG_ColumnDescArray descs, String8 name) +{ + U64 result = 0; + for(U64 idx = 0; idx < descs.count; idx += 1) + { + if(str8_match(descs.v[idx].name, name, 0)) + { + result = idx; + break; + } + } + return result; +} + +internal String8 +mg_string_from_row_desc_idx(MD_Node *row_parent, MG_ColumnDescArray descs, U64 idx) +{ + String8 result = {0}; + + // rjf: grab relevant column description + MG_ColumnDesc *desc = 0; + if(0 <= idx && idx < descs.count) + { + desc = descs.v + idx; + } + + // rjf: grab node + if(desc != 0) + { + switch(desc->kind) + { + default: break; + + case MG_ColumnKind_DirectCell: + { + // rjf: determine grid idx (shifted by synthetic columns) + U64 cell_idx = idx; + for(U64 col_idx = 0; col_idx < descs.count && col_idx < idx; col_idx += 1) + { + if(descs.v[col_idx].kind != MG_ColumnKind_DirectCell) + { + cell_idx -= 1; + } + } + MD_Node *node = md_child_from_index(row_parent, cell_idx); + result = node->string; + }break; + + case MG_ColumnKind_CheckForTag: + { + String8 tag_name = desc->name; + MD_Node *tag = md_tag_from_string(row_parent, tag_name, 0); + result = md_node_is_nil(tag) ? str8_lit("0") : str8_lit("1"); + }break; + + case MG_ColumnKind_TagChild: + { + String8 tag_name = desc->tag_name; + MD_Node *tag = md_tag_from_string(row_parent, tag_name, 0); + result = tag->first->string; + }break; + } + } + + return result; +} + +internal S64 +mg_eval_table_expand_expr__numeric(MG_StrExpr *expr, MG_TableExpandInfo *info) +{ + S64 result = 0; + MG_StrExprOp op = expr->op; + + switch(op) + { + default: + { + if(MG_StrExprOp_FirstString <= op && op <= MG_StrExprOp_LastString) + { + Temp scratch = scratch_begin(0, 0); + String8List result_strs = {0}; + mg_eval_table_expand_expr__string(scratch.arena, expr, info, &result_strs); + String8 result_str = str8_list_join(scratch.arena, &result_strs, 0); + try_s64_from_str8_c_rules(result_str, &result); + scratch_end(scratch); + } + }break; + + case MG_StrExprOp_Null: + { + try_s64_from_str8_c_rules(expr->node->string, &result); + }break; + + //- rjf: numeric arithmetic binary ops + case MG_StrExprOp_Add: + case MG_StrExprOp_Subtract: + case MG_StrExprOp_Multiply: + case MG_StrExprOp_Divide: + case MG_StrExprOp_Modulo: + case MG_StrExprOp_LeftShift: + case MG_StrExprOp_RightShift: + case MG_StrExprOp_BitwiseAnd: + case MG_StrExprOp_BitwiseOr: + case MG_StrExprOp_BitwiseXor: + case MG_StrExprOp_BooleanAnd: + case MG_StrExprOp_BooleanOr: + { + S64 left_val = mg_eval_table_expand_expr__numeric(expr->left, info); + S64 right_val = mg_eval_table_expand_expr__numeric(expr->right, info); + switch(op) + { + default:break; + case MG_StrExprOp_Add: result = left_val+right_val; break; + case MG_StrExprOp_Subtract: result = left_val-right_val; break; + case MG_StrExprOp_Multiply: result = left_val*right_val; break; + case MG_StrExprOp_Divide: result = left_val/right_val; break; + case MG_StrExprOp_Modulo: result = left_val%right_val; break; + case MG_StrExprOp_LeftShift: result = left_val<>right_val; break; + case MG_StrExprOp_BitwiseAnd: result = left_val&right_val; break; + case MG_StrExprOp_BitwiseOr: result = left_val|right_val; break; + case MG_StrExprOp_BitwiseXor: result = left_val^right_val; break; + case MG_StrExprOp_BooleanAnd: result = left_val&&right_val; break; + case MG_StrExprOp_BooleanOr: result = left_val||right_val; break; + } + }break; + + //- rjf: prefix unary ops + case MG_StrExprOp_BitwiseNegate: + case MG_StrExprOp_BooleanNot: + { + S64 right_val = mg_eval_table_expand_expr__numeric(expr->left, info); + switch(op) + { + default:break; + case MG_StrExprOp_BitwiseNegate: result = (S64)(~((U64)right_val)); break; + case MG_StrExprOp_BooleanNot: result = !right_val; + } + }break; + + //- rjf: comparisons + case MG_StrExprOp_Equals: + case MG_StrExprOp_DoesNotEqual: + { + Temp scratch = scratch_begin(0, 0); + String8List left_strs = {0}; + String8List right_strs = {0}; + mg_eval_table_expand_expr__string(scratch.arena, expr->left, info, &left_strs); + mg_eval_table_expand_expr__string(scratch.arena, expr->right, info, &right_strs); + String8 left_str = str8_list_join(scratch.arena, &left_strs, 0); + String8 right_str = str8_list_join(scratch.arena, &right_strs, 0); + B32 match = str8_match(left_str, right_str, 0); + result = (op == MG_StrExprOp_Equals ? match : !match); + scratch_end(scratch); + }break; + } + + return result; +} + +internal void +mg_eval_table_expand_expr__string(Arena *arena, MG_StrExpr *expr, MG_TableExpandInfo *info, String8List *out) +{ + MG_StrExprOp op = expr->op; + + switch(op) + { + default: + { + if(MG_StrExprOp_FirstNumeric <= op && op <= MG_StrExprOp_LastNumeric) + { + S64 numeric_eval = mg_eval_table_expand_expr__numeric(expr, info); + String8 numeric_eval_stringized = {0}; + if(md_node_has_tag(md_root_from_node(expr->node), str8_lit("hex"), 0)) + { + numeric_eval_stringized = push_str8f(arena, "0x%I64x", numeric_eval); + } + else + { + numeric_eval_stringized = push_str8f(arena, "%I64d", numeric_eval); + } + str8_list_push(arena, out, numeric_eval_stringized); + } + }break; + + case MG_StrExprOp_Null: + { + str8_list_push(arena, out, expr->node->string); + }break; + + case MG_StrExprOp_Dot: + { + // rjf: grab left/right + MG_StrExpr *left_expr = expr->left; + MD_Node *left_node = left_expr->node; + MG_StrExpr *right_expr = expr->right; + MD_Node *right_node = right_expr->node; + + // rjf: grab table name (LHS of .) and column lookup string (RHS of .) + String8 expand_label = left_node->string; + String8 column_lookup = right_node->string; + + // rjf: find which task corresponds to this table + U64 row_idx = 0; + MG_NodeGrid *grid = 0; + MG_ColumnDescArray column_descs = {0}; + { + for(MG_TableExpandTask *task = info->first_expand_task; task != 0; task = task->next) + { + if(str8_match(expand_label, task->expansion_label, 0)) + { + row_idx = task->idx; + grid = task->grid; + column_descs = task->column_descs; + break; + } + } + } + + // rjf: grab row parent + MD_Node *row_parent = &md_nil_node; + if(grid && (0 <= row_idx && row_idx < grid->row_parents.count)) + { + row_parent = grid->row_parents.v[row_idx]; + } + + // rjf: get string for this table lookup + String8 lookup_string = {0}; + { + U64 column_idx = 0; + + if(str8_match(column_lookup, str8_lit("_it"), 0)) + { + lookup_string = push_str8f(arena, "%I64u", row_idx); + } + else + { + // NOTE(rjf): numeric column lookup (column index) + if(right_node->flags & MD_NodeFlag_Numeric) + { + try_u64_from_str8_c_rules(column_lookup, &column_idx); + } + + // NOTE(rjf): string column lookup (column name) + if(right_node->flags & (MD_NodeFlag_Identifier|MD_NodeFlag_StringLiteral)) + { + column_idx = mg_column_index_from_name(column_descs, column_lookup); + } + + lookup_string = mg_string_from_row_desc_idx(row_parent, column_descs, column_idx); + if(str8_match(lookup_string, str8_lit("--"), 0)) + { + lookup_string = info->missing_value_fallback; + } + } + } + + // rjf: push lookup string + { + str8_list_push(arena, out, lookup_string); + } + }break; + + case MG_StrExprOp_ExpandIfTrue: + { + S64 bool_value = mg_eval_table_expand_expr__numeric(expr->left, info); + if(bool_value) + { + mg_eval_table_expand_expr__string(arena, expr->right, info, out); + } + }break; + + case MG_StrExprOp_Concat: + { + mg_eval_table_expand_expr__string(arena, expr->left, info, out); + mg_eval_table_expand_expr__string(arena, expr->right, info, out); + }break; + + case MG_StrExprOp_BumpToColumn: + { + S64 column = mg_eval_table_expand_expr__numeric(expr->left, info); + S64 current_column = out->total_size; + S64 spaces_to_push = column - current_column; + if(spaces_to_push > 0) + { + String8 str = {0}; + str.size = spaces_to_push; + str.str = push_array(arena, U8, spaces_to_push); + for(S64 idx = 0; idx < spaces_to_push; idx += 1) + { + str.str[idx] = ' '; + } + str8_list_push(arena, out, str); + } + }break; + } +} + +internal void +mg_loop_table_column_expansion(Arena *arena, String8 strexpr, MG_TableExpandInfo *info, MG_TableExpandTask *task, String8List *out) +{ + Temp scratch = scratch_begin(&arena, 1); + for(U64 it_idx = 0; it_idx < task->count; it_idx += 1) + { + task->idx = it_idx; + + //- rjf: iterate all further dimensions, if there's left in the chain + if(task->next) + { + mg_loop_table_column_expansion(arena, strexpr, info, task->next, out); + } + + //- rjf: if this is the last task in the chain, perform expansion + else + { + String8List expansion_strs = {0}; + U64 start = 0; + for(U64 char_idx = 0; char_idx <= strexpr.size;) + { + // rjf: push plain text parts of strexpr + if(char_idx == strexpr.size || strexpr.str[char_idx] == '$') + { + String8 plain_text_substr = str8_substr(strexpr, r1u64(start, char_idx)); + start = char_idx; + if(plain_text_substr.size != 0) + { + str8_list_push(arena, &expansion_strs, plain_text_substr); + } + } + + // rjf: handle expansion expression + if(strexpr.str[char_idx] == '$') + { + String8 string = str8_skip(strexpr, char_idx+1); + Rng1U64 expr_range = {0}; + S64 paren_nest = 0; + for(U64 idx = 0; idx < string.size; idx += 1) + { + if(string.str[idx] == '(') + { + paren_nest += 1; + if(paren_nest == 1) + { + expr_range.min = idx; + } + } + if(string.str[idx] == ')') + { + paren_nest -= 1; + if(paren_nest == 0) + { + expr_range.max = idx+1; + break; + } + } + } + String8 expr_string = str8_substr(string, expr_range); + MD_TokenizeResult expr_tokenize = md_tokenize_from_text(scratch.arena, expr_string); + MD_ParseResult expr_base_parse = md_parse_from_text_tokens(scratch.arena, str8_lit(""), expr_string, expr_tokenize.tokens); + MG_StrExprParseResult expr_parse = mg_str_expr_parse_from_root(scratch.arena, expr_base_parse.root->first); + mg_eval_table_expand_expr__string(arena, expr_parse.root, info, &expansion_strs); + char_idx = start = char_idx + 1 + expr_range.max; + } + else + { + char_idx += 1; + } + } + String8 expansion_str = str8_list_join(arena, &expansion_strs, 0); + if(expansion_str.size != 0) + { + str8_list_push(arena, out, expansion_str); + } + } + } + + scratch_end(scratch); +} + +internal String8List +mg_string_list_from_table_gen(Arena *arena, MG_Map grid_name_map, MG_Map grid_column_desc_map, String8 fallback, MD_Node *gen) +{ + String8List result = {0}; + Temp scratch = scratch_begin(&arena, 1); + if(md_node_is_nil(gen->first) && gen->string.size != 0) + { + str8_list_push(arena, &result, gen->string); + str8_list_push(arena, &result, str8_lit("\n")); + } + else for(MD_EachNode(strexpr_node, gen->first)) + { + // rjf: build task list + MG_TableExpandTask *first_task = 0; + MG_TableExpandTask *last_task = 0; + for(MD_EachNode(tag, strexpr_node->first_tag)) + { + if(str8_match(tag->string, str8_lit("expand"), 0)) + { + // rjf: grab args for this expansion + MD_Node *table_name_node = md_child_from_index(tag, 0); + MD_Node *expand_label_node = md_child_from_index(tag, 1); + String8 table_name = table_name_node->string; + String8 expand_label = expand_label_node->string; + + // rjf: lookup table / column descriptions + MG_NodeGrid *grid = mg_map_ptr_from_string(&grid_name_map, table_name); + MG_ColumnDescArray *column_descs = mg_map_ptr_from_string(&grid_column_desc_map, table_name); + + // rjf: figure out row count + U64 grid_row_count = 0; + if(grid != 0) + { + grid_row_count = grid->cells.count / grid->y_stride; + } + + // rjf: push task for this expansion + if(grid != 0) + { + MG_TableExpandTask *task = push_array(scratch.arena, MG_TableExpandTask, 1); + task->expansion_label = expand_label; + task->grid = grid; + task->column_descs = *column_descs; + task->count = grid_row_count; + task->idx = 0; + SLLQueuePush(first_task, last_task, task); + } + } + } + + // rjf: do expansion generation, OR just push this string if we have no expansions + { + MG_TableExpandInfo info = {first_task, fallback}; + if(first_task != 0) + { + mg_loop_table_column_expansion(arena, strexpr_node->string, &info, first_task, &result); + } + else + { + str8_list_push(arena, &result, strexpr_node->string); + } + } + } + scratch_end(scratch); + return result; +} + +//////////////////////////////// +//~ rjf: Layer Lookup Functions + +internal String8 +mg_layer_key_from_path(String8 path) +{ + Temp scratch = scratch_begin(0, 0); + U64 src_folder_pos = 0; + for(U64 next_src_folder_pos = 0; + next_src_folder_pos < path.size; + next_src_folder_pos = str8_find_needle(path, next_src_folder_pos+1, str8_lit("src"), 0)) + { + src_folder_pos = next_src_folder_pos; + } + String8List path_parts = str8_split_path(scratch.arena, str8_chop_last_slash(str8_skip(path, src_folder_pos+4))); + StringJoin join = {0}; + join.sep = str8_lit("/"); + String8 key = str8_list_join(mg_arena, &path_parts, &join); + scratch_end(scratch); + return key; +} + +internal MG_Layer * +mg_layer_from_key(String8 key) +{ + U64 hash = mg_hash_from_string(key); + U64 slot_idx = hash%mg_state->slots_count; + MG_LayerSlot *slot = &mg_state->slots[slot_idx]; + MG_Layer *layer = 0; + for(MG_LayerNode *n = slot->first; n != 0; n = n->next) + { + if(str8_match(n->v.key, key, 0)) + { + layer = &n->v; + break; + } + } + if(layer == 0) + { + MG_LayerNode *n = push_array(mg_arena, MG_LayerNode, 1); + SLLQueuePush(slot->first, slot->last, n); + n->v.key = push_str8_copy(mg_arena, key); + layer = &n->v; + } + return layer; +} diff --git a/src/metagen/metagen.h b/src/metagen/metagen.h index 339397c1..6280cbed 100644 --- a/src/metagen/metagen.h +++ b/src/metagen/metagen.h @@ -1,329 +1,329 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef METAGEN_H -#define METAGEN_H - -//////////////////////////////// -//~ rjf: Message Type - -typedef struct MG_Msg MG_Msg; -struct MG_Msg -{ - String8 location; - String8 kind; - String8 msg; -}; - -typedef struct MG_MsgNode MG_MsgNode; -struct MG_MsgNode -{ - MG_MsgNode *next; - MG_Msg v; -}; - -typedef struct MG_MsgList MG_MsgList; -struct MG_MsgList -{ - MG_MsgNode *first; - MG_MsgNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Parse Artifact Types - -typedef struct MG_FileParse MG_FileParse; -struct MG_FileParse -{ - MD_Node *root; -}; - -typedef struct MG_FileParseNode MG_FileParseNode; -struct MG_FileParseNode -{ - MG_FileParseNode *next; - MG_FileParse v; -}; - -typedef struct MG_FileParseList MG_FileParseList; -struct MG_FileParseList -{ - MG_FileParseNode *first; - MG_FileParseNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Map Type - -typedef struct MG_MapNode MG_MapNode; -struct MG_MapNode -{ - MG_MapNode *next; - String8 key; - void *val; -}; - -typedef struct MG_MapSlot MG_MapSlot; -struct MG_MapSlot -{ - MG_MapNode *first; - MG_MapNode *last; -}; - -typedef struct MG_Map MG_Map; -struct MG_Map -{ - MG_MapSlot *slots; - U64 slots_count; -}; - -//////////////////////////////// -//~ rjf: String Expression Types - -typedef enum MG_StrExprOpKind -{ - MG_StrExprOpKind_Null, - MG_StrExprOpKind_Prefix, - MG_StrExprOpKind_Postfix, - MG_StrExprOpKind_Binary, - MG_StrExprOpKind_COUNT -} -MG_StrExprOpKind; - -typedef enum MG_StrExprOp -{ - MG_StrExprOp_Null, - -#define MG_StrExprOp_FirstString MG_StrExprOp_Dot - MG_StrExprOp_Dot, - MG_StrExprOp_ExpandIfTrue, - MG_StrExprOp_Concat, - MG_StrExprOp_BumpToColumn, -#define MG_StrExprOp_LastString MG_StrExprOp_BumpToColumn - -#define MG_StrExprOp_FirstNumeric MG_StrExprOp_Add - MG_StrExprOp_Add, - MG_StrExprOp_Subtract, - MG_StrExprOp_Multiply, - MG_StrExprOp_Divide, - MG_StrExprOp_Modulo, - MG_StrExprOp_LeftShift, - MG_StrExprOp_RightShift, - MG_StrExprOp_BitwiseAnd, - MG_StrExprOp_BitwiseOr, - MG_StrExprOp_BitwiseXor, - MG_StrExprOp_BitwiseNegate, - MG_StrExprOp_BooleanAnd, - MG_StrExprOp_BooleanOr, - MG_StrExprOp_BooleanNot, - MG_StrExprOp_Equals, - MG_StrExprOp_DoesNotEqual, -#define MG_StrExprOp_LastNumeric MG_StrExprOp_DoesNotEqual - - MG_StrExprOp_COUNT, -} -MG_StrExprOp; - -typedef struct MG_StrExpr MG_StrExpr; -struct MG_StrExpr -{ - MG_StrExpr *parent; - MG_StrExpr *left; - MG_StrExpr *right; - MG_StrExprOp op; - MD_Node *node; -}; - -typedef struct MG_StrExprParseResult MG_StrExprParseResult; -struct MG_StrExprParseResult -{ - MG_StrExpr *root; - MD_MsgList msgs; - MD_Node *next_node; -}; - -//////////////////////////////// -//~ rjf: Table Generation Types - -typedef struct MG_NodeArray MG_NodeArray; -struct MG_NodeArray -{ - MD_Node **v; - U64 count; -}; - -typedef struct MG_NodeGrid MG_NodeGrid; -struct MG_NodeGrid -{ - U64 x_stride; - U64 y_stride; - MG_NodeArray cells; - MG_NodeArray row_parents; -}; - -typedef enum MG_ColumnKind -{ - MG_ColumnKind_DirectCell, - MG_ColumnKind_CheckForTag, - MG_ColumnKind_TagChild, - MG_ColumnKind_COUNT -} -MG_ColumnKind; - -typedef struct MG_ColumnDesc MG_ColumnDesc; -struct MG_ColumnDesc -{ - String8 name; - MG_ColumnKind kind; - String8 tag_name; -}; - -typedef struct MG_ColumnDescArray MG_ColumnDescArray; -struct MG_ColumnDescArray -{ - U64 count; - MG_ColumnDesc *v; -}; - -typedef struct MG_TableExpandTask MG_TableExpandTask; -struct MG_TableExpandTask -{ - MG_TableExpandTask *next; - String8 expansion_label; - MG_NodeGrid *grid; - MG_ColumnDescArray column_descs; - U64 count; - U64 idx; -}; - -typedef struct MG_TableExpandInfo MG_TableExpandInfo; -struct MG_TableExpandInfo -{ - MG_TableExpandTask *first_expand_task; - String8 missing_value_fallback; -}; - -//////////////////////////////// -//~ rjf: Main Output Path Types - -typedef struct MG_Layer MG_Layer; -struct MG_Layer -{ - String8 key; - B32 is_library; - String8 gen_folder_name; - String8 h_name_override; - String8 c_name_override; - String8List enums; - String8List structs; - String8List h_functions; - String8List h_tables; - String8List h_catchall; - String8List h_header; - String8List h_footer; - String8List c_functions; - String8List c_tables; - String8List c_catchall; - String8List c_header; - String8List c_footer; -}; - -typedef struct MG_LayerNode MG_LayerNode; -struct MG_LayerNode -{ - MG_LayerNode *next; - MG_Layer v; -}; - -typedef struct MG_LayerSlot MG_LayerSlot; -struct MG_LayerSlot -{ - MG_LayerNode *first; - MG_LayerNode *last; -}; - -typedef struct MG_State MG_State; -struct MG_State -{ - U64 slots_count; - MG_LayerSlot *slots; -}; - -//////////////////////////////// -//~ rjf: Globals - -global Arena *mg_arena = 0; -global MG_State *mg_state = 0; -read_only global MG_StrExpr mg_str_expr_nil = {&mg_str_expr_nil, &mg_str_expr_nil, &mg_str_expr_nil}; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal U64 mg_hash_from_string(String8 string); -internal TxtPt mg_txt_pt_from_string_off(String8 string, U64 off); - -//////////////////////////////// -//~ rjf: Message Lists - -internal void mg_msg_list_push(Arena *arena, MG_MsgList *msgs, MG_Msg *msg); - -//////////////////////////////// -//~ rjf: String Escaping - -internal String8 mg_escaped_from_str8(Arena *arena, String8 string); - -//////////////////////////////// -//~ rjf: String Wrapping - -internal String8List mg_wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent); - -//////////////////////////////// -//~ rjf: C-String-Izing - -internal String8 mg_c_string_literal_from_multiline_string(String8 string); -internal String8 mg_c_array_literal_contents_from_data(String8 data); - -//////////////////////////////// -//~ rjf: Map Functions - -internal MG_Map mg_push_map(Arena *arena, U64 slot_count); -internal void *mg_map_ptr_from_string(MG_Map *map, String8 string); -internal void mg_map_insert_ptr(Arena *arena, MG_Map *map, String8 string, void *val); - -//////////////////////////////// -//~ rjf: String Expression Parsing - -internal MG_StrExpr *mg_push_str_expr(Arena *arena, MG_StrExprOp op, MD_Node *node); -internal MG_StrExprParseResult mg_str_expr_parse_from_first_opl__min_prec(Arena *arena, MD_Node *first, MD_Node *opl, S8 min_prec); -internal MG_StrExprParseResult mg_str_expr_parse_from_first_opl(Arena *arena, MD_Node *first, MD_Node *opl); -internal MG_StrExprParseResult mg_str_expr_parse_from_root(Arena *arena, MD_Node *root); - -//////////////////////////////// -//~ rjf: Table Generation Functions - -internal MG_NodeArray mg_node_array_make(Arena *arena, U64 count); -internal MG_NodeArray mg_child_array_from_node(Arena *arena, MD_Node *node); -internal MG_NodeGrid mg_node_grid_make_from_node(Arena *arena, MD_Node *root); -internal MG_NodeArray mg_row_from_index(MG_NodeGrid grid, U64 index); -internal MG_NodeArray mg_column_from_index(Arena *arena, MG_NodeGrid grid, U64 index); -internal MD_Node *mg_node_from_grid_xy(MG_NodeGrid grid, U64 x, U64 y); - -internal MG_ColumnDescArray mg_column_desc_array_make(Arena *arena, U64 count, MG_ColumnDesc *descs); -internal MG_ColumnDescArray mg_column_desc_array_from_tag(Arena *arena, MD_Node *tag); -internal U64 mg_column_index_from_name(MG_ColumnDescArray descs, String8 name); -internal String8 mg_string_from_row_desc_idx(MD_Node *row_parent, MG_ColumnDescArray descs, U64 idx); - -internal S64 mg_eval_table_expand_expr__numeric(MG_StrExpr *expr, MG_TableExpandInfo *info); -internal void mg_eval_table_expand_expr__string(Arena *arena, MG_StrExpr *expr, MG_TableExpandInfo *info, String8List *out); -internal void mg_loop_table_column_expansion(Arena *arena, String8 strexpr, MG_TableExpandInfo *info, MG_TableExpandTask *task, String8List *out); -internal String8List mg_string_list_from_table_gen(Arena *arena, MG_Map grid_name_map, MG_Map grid_column_desc_map, String8 fallback, MD_Node *gen); - -//////////////////////////////// -//~ rjf: Layer Lookup Functions - -internal String8 mg_layer_key_from_path(String8 path); -internal MG_Layer *mg_layer_from_key(String8 key); - -#endif //METAGEN_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef METAGEN_H +#define METAGEN_H + +//////////////////////////////// +//~ rjf: Message Type + +typedef struct MG_Msg MG_Msg; +struct MG_Msg +{ + String8 location; + String8 kind; + String8 msg; +}; + +typedef struct MG_MsgNode MG_MsgNode; +struct MG_MsgNode +{ + MG_MsgNode *next; + MG_Msg v; +}; + +typedef struct MG_MsgList MG_MsgList; +struct MG_MsgList +{ + MG_MsgNode *first; + MG_MsgNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Parse Artifact Types + +typedef struct MG_FileParse MG_FileParse; +struct MG_FileParse +{ + MD_Node *root; +}; + +typedef struct MG_FileParseNode MG_FileParseNode; +struct MG_FileParseNode +{ + MG_FileParseNode *next; + MG_FileParse v; +}; + +typedef struct MG_FileParseList MG_FileParseList; +struct MG_FileParseList +{ + MG_FileParseNode *first; + MG_FileParseNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Map Type + +typedef struct MG_MapNode MG_MapNode; +struct MG_MapNode +{ + MG_MapNode *next; + String8 key; + void *val; +}; + +typedef struct MG_MapSlot MG_MapSlot; +struct MG_MapSlot +{ + MG_MapNode *first; + MG_MapNode *last; +}; + +typedef struct MG_Map MG_Map; +struct MG_Map +{ + MG_MapSlot *slots; + U64 slots_count; +}; + +//////////////////////////////// +//~ rjf: String Expression Types + +typedef enum MG_StrExprOpKind +{ + MG_StrExprOpKind_Null, + MG_StrExprOpKind_Prefix, + MG_StrExprOpKind_Postfix, + MG_StrExprOpKind_Binary, + MG_StrExprOpKind_COUNT +} +MG_StrExprOpKind; + +typedef enum MG_StrExprOp +{ + MG_StrExprOp_Null, + +#define MG_StrExprOp_FirstString MG_StrExprOp_Dot + MG_StrExprOp_Dot, + MG_StrExprOp_ExpandIfTrue, + MG_StrExprOp_Concat, + MG_StrExprOp_BumpToColumn, +#define MG_StrExprOp_LastString MG_StrExprOp_BumpToColumn + +#define MG_StrExprOp_FirstNumeric MG_StrExprOp_Add + MG_StrExprOp_Add, + MG_StrExprOp_Subtract, + MG_StrExprOp_Multiply, + MG_StrExprOp_Divide, + MG_StrExprOp_Modulo, + MG_StrExprOp_LeftShift, + MG_StrExprOp_RightShift, + MG_StrExprOp_BitwiseAnd, + MG_StrExprOp_BitwiseOr, + MG_StrExprOp_BitwiseXor, + MG_StrExprOp_BitwiseNegate, + MG_StrExprOp_BooleanAnd, + MG_StrExprOp_BooleanOr, + MG_StrExprOp_BooleanNot, + MG_StrExprOp_Equals, + MG_StrExprOp_DoesNotEqual, +#define MG_StrExprOp_LastNumeric MG_StrExprOp_DoesNotEqual + + MG_StrExprOp_COUNT, +} +MG_StrExprOp; + +typedef struct MG_StrExpr MG_StrExpr; +struct MG_StrExpr +{ + MG_StrExpr *parent; + MG_StrExpr *left; + MG_StrExpr *right; + MG_StrExprOp op; + MD_Node *node; +}; + +typedef struct MG_StrExprParseResult MG_StrExprParseResult; +struct MG_StrExprParseResult +{ + MG_StrExpr *root; + MD_MsgList msgs; + MD_Node *next_node; +}; + +//////////////////////////////// +//~ rjf: Table Generation Types + +typedef struct MG_NodeArray MG_NodeArray; +struct MG_NodeArray +{ + MD_Node **v; + U64 count; +}; + +typedef struct MG_NodeGrid MG_NodeGrid; +struct MG_NodeGrid +{ + U64 x_stride; + U64 y_stride; + MG_NodeArray cells; + MG_NodeArray row_parents; +}; + +typedef enum MG_ColumnKind +{ + MG_ColumnKind_DirectCell, + MG_ColumnKind_CheckForTag, + MG_ColumnKind_TagChild, + MG_ColumnKind_COUNT +} +MG_ColumnKind; + +typedef struct MG_ColumnDesc MG_ColumnDesc; +struct MG_ColumnDesc +{ + String8 name; + MG_ColumnKind kind; + String8 tag_name; +}; + +typedef struct MG_ColumnDescArray MG_ColumnDescArray; +struct MG_ColumnDescArray +{ + U64 count; + MG_ColumnDesc *v; +}; + +typedef struct MG_TableExpandTask MG_TableExpandTask; +struct MG_TableExpandTask +{ + MG_TableExpandTask *next; + String8 expansion_label; + MG_NodeGrid *grid; + MG_ColumnDescArray column_descs; + U64 count; + U64 idx; +}; + +typedef struct MG_TableExpandInfo MG_TableExpandInfo; +struct MG_TableExpandInfo +{ + MG_TableExpandTask *first_expand_task; + String8 missing_value_fallback; +}; + +//////////////////////////////// +//~ rjf: Main Output Path Types + +typedef struct MG_Layer MG_Layer; +struct MG_Layer +{ + String8 key; + B32 is_library; + String8 gen_folder_name; + String8 h_name_override; + String8 c_name_override; + String8List enums; + String8List structs; + String8List h_functions; + String8List h_tables; + String8List h_catchall; + String8List h_header; + String8List h_footer; + String8List c_functions; + String8List c_tables; + String8List c_catchall; + String8List c_header; + String8List c_footer; +}; + +typedef struct MG_LayerNode MG_LayerNode; +struct MG_LayerNode +{ + MG_LayerNode *next; + MG_Layer v; +}; + +typedef struct MG_LayerSlot MG_LayerSlot; +struct MG_LayerSlot +{ + MG_LayerNode *first; + MG_LayerNode *last; +}; + +typedef struct MG_State MG_State; +struct MG_State +{ + U64 slots_count; + MG_LayerSlot *slots; +}; + +//////////////////////////////// +//~ rjf: Globals + +global Arena *mg_arena = 0; +global MG_State *mg_state = 0; +read_only global MG_StrExpr mg_str_expr_nil = {&mg_str_expr_nil, &mg_str_expr_nil, &mg_str_expr_nil}; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal U64 mg_hash_from_string(String8 string); +internal TxtPt mg_txt_pt_from_string_off(String8 string, U64 off); + +//////////////////////////////// +//~ rjf: Message Lists + +internal void mg_msg_list_push(Arena *arena, MG_MsgList *msgs, MG_Msg *msg); + +//////////////////////////////// +//~ rjf: String Escaping + +internal String8 mg_escaped_from_str8(Arena *arena, String8 string); + +//////////////////////////////// +//~ rjf: String Wrapping + +internal String8List mg_wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent); + +//////////////////////////////// +//~ rjf: C-String-Izing + +internal String8 mg_c_string_literal_from_multiline_string(String8 string); +internal String8 mg_c_array_literal_contents_from_data(String8 data); + +//////////////////////////////// +//~ rjf: Map Functions + +internal MG_Map mg_push_map(Arena *arena, U64 slot_count); +internal void *mg_map_ptr_from_string(MG_Map *map, String8 string); +internal void mg_map_insert_ptr(Arena *arena, MG_Map *map, String8 string, void *val); + +//////////////////////////////// +//~ rjf: String Expression Parsing + +internal MG_StrExpr *mg_push_str_expr(Arena *arena, MG_StrExprOp op, MD_Node *node); +internal MG_StrExprParseResult mg_str_expr_parse_from_first_opl__min_prec(Arena *arena, MD_Node *first, MD_Node *opl, S8 min_prec); +internal MG_StrExprParseResult mg_str_expr_parse_from_first_opl(Arena *arena, MD_Node *first, MD_Node *opl); +internal MG_StrExprParseResult mg_str_expr_parse_from_root(Arena *arena, MD_Node *root); + +//////////////////////////////// +//~ rjf: Table Generation Functions + +internal MG_NodeArray mg_node_array_make(Arena *arena, U64 count); +internal MG_NodeArray mg_child_array_from_node(Arena *arena, MD_Node *node); +internal MG_NodeGrid mg_node_grid_make_from_node(Arena *arena, MD_Node *root); +internal MG_NodeArray mg_row_from_index(MG_NodeGrid grid, U64 index); +internal MG_NodeArray mg_column_from_index(Arena *arena, MG_NodeGrid grid, U64 index); +internal MD_Node *mg_node_from_grid_xy(MG_NodeGrid grid, U64 x, U64 y); + +internal MG_ColumnDescArray mg_column_desc_array_make(Arena *arena, U64 count, MG_ColumnDesc *descs); +internal MG_ColumnDescArray mg_column_desc_array_from_tag(Arena *arena, MD_Node *tag); +internal U64 mg_column_index_from_name(MG_ColumnDescArray descs, String8 name); +internal String8 mg_string_from_row_desc_idx(MD_Node *row_parent, MG_ColumnDescArray descs, U64 idx); + +internal S64 mg_eval_table_expand_expr__numeric(MG_StrExpr *expr, MG_TableExpandInfo *info); +internal void mg_eval_table_expand_expr__string(Arena *arena, MG_StrExpr *expr, MG_TableExpandInfo *info, String8List *out); +internal void mg_loop_table_column_expansion(Arena *arena, String8 strexpr, MG_TableExpandInfo *info, MG_TableExpandTask *task, String8List *out); +internal String8List mg_string_list_from_table_gen(Arena *arena, MG_Map grid_name_map, MG_Map grid_column_desc_map, String8 fallback, MD_Node *gen); + +//////////////////////////////// +//~ rjf: Layer Lookup Functions + +internal String8 mg_layer_key_from_path(String8 path); +internal MG_Layer *mg_layer_from_key(String8 key); + +#endif //METAGEN_H diff --git a/src/msf/msf.c b/src/msf/msf.c index 70f7533f..8d1ce1a7 100644 --- a/src/msf/msf.c +++ b/src/msf/msf.c @@ -1,286 +1,286 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: MSF Parser Functions - -internal MSF_Parsed* -msf_parsed_from_data(Arena *arena, String8 msf_data) -{ - Temp scratch = scratch_begin(&arena, 1); - MSF_Parsed *result = 0; - - //- determine msf type - U32 index_size = 0; - if(msf_data.size >= MSF_MIN_SIZE) - { - if(str8_match(msf_data, str8_lit(msf_msf20_magic), StringMatchFlag_RightSideSloppy)) - { - index_size = 2; - } - else if(str8_match(msf_data, str8_lit(msf_msf70_magic), StringMatchFlag_RightSideSloppy)) - { - index_size = 4; - } - } - - if(index_size == 2 || index_size == 4) - { - //- extract info from header - U32 block_size_raw = 0; - U32 whole_file_block_count_raw = 0; - U32 directory_size_raw = 0; - U32 directory_super_map_raw = 0; - if(index_size == 2) - { - MSF_Header20 *header = (MSF_Header20*)(msf_data.str + MSF_MSF20_MAGIC_SIZE); - block_size_raw = header->block_size; - whole_file_block_count_raw = header->block_count; - directory_size_raw = header->directory_size; - } - else if(index_size == 4) - { - MSF_Header70 *header = (MSF_Header70*)(msf_data.str + MSF_MSF70_MAGIC_SIZE); - block_size_raw = header->block_size; - whole_file_block_count_raw = header->block_count; - directory_size_raw = header->directory_size; - directory_super_map_raw = header->directory_super_map; - } - - //- setup important sizes & counts - - // (blocks) - U32 block_size = ClampTop(block_size_raw, msf_data.size); - - // (whole file block count) - U32 whole_file_block_count_max = CeilIntegerDiv(msf_data.size, block_size); - U32 whole_file_block_count = ClampTop(whole_file_block_count_raw, whole_file_block_count_max); - - // (directory) - U32 directory_size = ClampTop(directory_size_raw, msf_data.size); - U32 block_count_in_directory = CeilIntegerDiv(directory_size, block_size); - - // (map) - U32 directory_map_size = block_count_in_directory*index_size; - U32 block_count_in_directory_map = CeilIntegerDiv(directory_map_size, block_size); - - // Layout of the "directory": - // - // super map: [s1, s2, s3, ...] - // map: s1 -> [i1, i2, i3, ...]; s2 -> [...]; s3 -> [...]; ... - // directory: i1 -> [data]; i2 -> [data]; i3 -> [data]; ... i1 -> [data]; ... - // - // The "data" in the directory describes streams: - // PDB20: - // struct Pdb20StreamSize{ - // U32 size; - // U32 unknown; // looks like kind codes or revision counters or something - // } - // struct{ - // U32 stream_count; - // Pdb20StreamSize stream_sizes[stream_count]; - // U16 stream_indices[stream_count][...]; - // } - // - // PDB70: - // struct{ - // U32 stream_count; - // U32 stream_sizes[stream_count]; - // U32 stream_indices[stream_count][...]; - // } - - //- parse stream directory - U8 *directory_buf = push_array(scratch.arena, U8, directory_size); - B32 got_directory = 1; - { - U32 directory_super_map_dummy = 0; - U32 *directory_super_map = 0; - U32 directory_map_block_skip_size = 0; - if (index_size == 2){ - directory_super_map = &directory_super_map_dummy; - directory_map_block_skip_size = - MSF_MSF20_MAGIC_SIZE + OffsetOf(MSF_Header20, directory_map); - } - else{ - U64 super_map_off = - MSF_MSF70_MAGIC_SIZE + OffsetOf(MSF_Header70, directory_super_map); - directory_super_map = (U32*)(msf_data.str + super_map_off); - } - - U32 max_index_count_in_map_block = (block_size - directory_map_block_skip_size)/index_size; - - // for each index in super map ... - U8 *out_ptr = directory_buf; - U32 *super_map_ptr = directory_super_map; - for (U32 i = 0; i < block_count_in_directory_map; i += 1, super_map_ptr += 1){ - U32 directory_map_block_index = *super_map_ptr; - if (directory_map_block_index >= whole_file_block_count){ - got_directory = 0; - goto parse_directory_done; - } - - U64 directory_map_block_off = (U64)(directory_map_block_index)*block_size; - U8 *directory_map_block_base = (msf_data.str + directory_map_block_off); - - // clamp index count by end of directory - U32 index_count = 0; - { - U32 directory_pos = (U32)(out_ptr - directory_buf); - U32 remaining_size = directory_size - directory_pos; - U32 remaining_map_block_count = CeilIntegerDiv(remaining_size, block_size); - index_count = ClampTop(max_index_count_in_map_block, remaining_map_block_count); - } - - // for each index in map ... - U8 *map_ptr = directory_map_block_base + directory_map_block_skip_size; - for (U32 j = 0; j < index_count; j += 1, map_ptr += index_size){ - - // read index - U32 directory_block_index = 0; - if (index_size == 4){ - directory_block_index = *(U32*)(map_ptr); - } - else{ - directory_block_index = *(U16*)(map_ptr); - } - if (directory_block_index >= whole_file_block_count){ - got_directory = 0; - goto parse_directory_done; - } - - U64 directory_block_off = (U64)(directory_block_index)*block_size; - U8 *directory_block_base = (msf_data.str + directory_block_off); - - // clamp copy size by end of directory - U32 copy_size = 0; - { - U32 directory_pos = (U32)(out_ptr - directory_buf); - U32 remaining_size = directory_size - directory_pos; - copy_size = ClampTop(block_size, remaining_size); - } - - // copy block data - MemoryCopy(out_ptr, directory_block_base, copy_size); - out_ptr += copy_size; - } - - } - - parse_directory_done:; - } - - //- parse streams from directory - U32 stream_count = 0; - B32 got_streams = 0; - String8 *streams = 0; - - if(got_directory) - { - got_streams = 1; - - // read stream count - U32 stream_count_raw = *(U32*)(directory_buf); - - // setup counts, sizes, and offsets - U32 size_of_stream_entry = 4; - if (index_size == 2){ - size_of_stream_entry = 8; - } - U32 stream_count_max = (directory_size - 4)/size_of_stream_entry; - U32 stream_count__inner = ClampTop(stream_count_raw, stream_count_max); - U32 all_stream_entries_off = 4; - U32 all_indices_off = all_stream_entries_off + stream_count__inner*size_of_stream_entry; - - // set output buffer and count - stream_count = stream_count__inner; - streams = push_array(arena, String8, stream_count); - - // iterate sizes and indices in lock step - U32 entry_cursor = all_stream_entries_off; - U32 index_cursor = all_indices_off; - String8 *stream_ptr = streams; - for (U32 i = 0; i < stream_count; i += 1) - { - // read stream size - U32 stream_size_raw = *(U32*)(directory_buf + entry_cursor); - if (stream_size_raw == 0xffffffff){ - stream_size_raw = 0; - } - - // compute block count - U32 stream_block_count_raw = CeilIntegerDiv(stream_size_raw, block_size); - U32 stream_block_count_max = (directory_size - index_cursor)/index_size;; - U32 stream_block_count = ClampTop(stream_block_count_raw, stream_block_count_max); - U32 stream_size = ClampTop(stream_size_raw, stream_block_count*block_size); - - // copy stream data - U8 *stream_buf = push_array(arena, U8, stream_size); - stream_ptr->str = stream_buf; - stream_ptr->size = stream_size; - - U32 sub_index_cursor = index_cursor; - U8 *stream_out_ptr = stream_buf; - for (U32 i = 0; i < stream_block_count; i += 1, sub_index_cursor += index_size){ - - // read index - U32 stream_block_index = 0; - if (index_size == 4){ - stream_block_index = *(U32*)(directory_buf + sub_index_cursor); - } - else{ - stream_block_index = *(U16*)(directory_buf + sub_index_cursor); - } - if (stream_block_index >= whole_file_block_count){ - got_streams = 0; - goto parse_streams_done; - } - - U64 stream_block_off = (U64)(stream_block_index)*block_size; - U8 *stream_block_base = (msf_data.str + stream_block_off); - - // clamp copy size by end of stream - U32 copy_size = 0; - { - U32 stream_pos = (U32)(stream_out_ptr - stream_buf); - U32 remaining_size = stream_size - stream_pos; - copy_size = ClampTop(block_size, remaining_size); - } - - // copy block data - MemoryCopy(stream_out_ptr, stream_block_base, copy_size); - stream_out_ptr += copy_size; - } - - // advance cursors - entry_cursor += size_of_stream_entry; - index_cursor = sub_index_cursor; - stream_ptr += 1; - } - - parse_streams_done:; - } - - if(got_streams) - { - result = push_array(arena, MSF_Parsed, 1); - result->streams = streams; - result->stream_count = stream_count; - result->block_size = block_size; - result->block_count = whole_file_block_count; - } - } - - scratch_end(scratch); - return result; -} - -internal String8 -msf_data_from_stream(MSF_Parsed *msf, MSF_StreamNumber sn) -{ - String8 result = {0}; - if(sn < msf->stream_count) - { - result = msf->streams[sn]; - } - return(result); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: MSF Parser Functions + +internal MSF_Parsed* +msf_parsed_from_data(Arena *arena, String8 msf_data) +{ + Temp scratch = scratch_begin(&arena, 1); + MSF_Parsed *result = 0; + + //- determine msf type + U32 index_size = 0; + if(msf_data.size >= MSF_MIN_SIZE) + { + if(str8_match(msf_data, str8_lit(msf_msf20_magic), StringMatchFlag_RightSideSloppy)) + { + index_size = 2; + } + else if(str8_match(msf_data, str8_lit(msf_msf70_magic), StringMatchFlag_RightSideSloppy)) + { + index_size = 4; + } + } + + if(index_size == 2 || index_size == 4) + { + //- extract info from header + U32 block_size_raw = 0; + U32 whole_file_block_count_raw = 0; + U32 directory_size_raw = 0; + U32 directory_super_map_raw = 0; + if(index_size == 2) + { + MSF_Header20 *header = (MSF_Header20*)(msf_data.str + MSF_MSF20_MAGIC_SIZE); + block_size_raw = header->block_size; + whole_file_block_count_raw = header->block_count; + directory_size_raw = header->directory_size; + } + else if(index_size == 4) + { + MSF_Header70 *header = (MSF_Header70*)(msf_data.str + MSF_MSF70_MAGIC_SIZE); + block_size_raw = header->block_size; + whole_file_block_count_raw = header->block_count; + directory_size_raw = header->directory_size; + directory_super_map_raw = header->directory_super_map; + } + + //- setup important sizes & counts + + // (blocks) + U32 block_size = ClampTop(block_size_raw, msf_data.size); + + // (whole file block count) + U32 whole_file_block_count_max = CeilIntegerDiv(msf_data.size, block_size); + U32 whole_file_block_count = ClampTop(whole_file_block_count_raw, whole_file_block_count_max); + + // (directory) + U32 directory_size = ClampTop(directory_size_raw, msf_data.size); + U32 block_count_in_directory = CeilIntegerDiv(directory_size, block_size); + + // (map) + U32 directory_map_size = block_count_in_directory*index_size; + U32 block_count_in_directory_map = CeilIntegerDiv(directory_map_size, block_size); + + // Layout of the "directory": + // + // super map: [s1, s2, s3, ...] + // map: s1 -> [i1, i2, i3, ...]; s2 -> [...]; s3 -> [...]; ... + // directory: i1 -> [data]; i2 -> [data]; i3 -> [data]; ... i1 -> [data]; ... + // + // The "data" in the directory describes streams: + // PDB20: + // struct Pdb20StreamSize{ + // U32 size; + // U32 unknown; // looks like kind codes or revision counters or something + // } + // struct{ + // U32 stream_count; + // Pdb20StreamSize stream_sizes[stream_count]; + // U16 stream_indices[stream_count][...]; + // } + // + // PDB70: + // struct{ + // U32 stream_count; + // U32 stream_sizes[stream_count]; + // U32 stream_indices[stream_count][...]; + // } + + //- parse stream directory + U8 *directory_buf = push_array(scratch.arena, U8, directory_size); + B32 got_directory = 1; + { + U32 directory_super_map_dummy = 0; + U32 *directory_super_map = 0; + U32 directory_map_block_skip_size = 0; + if (index_size == 2){ + directory_super_map = &directory_super_map_dummy; + directory_map_block_skip_size = + MSF_MSF20_MAGIC_SIZE + OffsetOf(MSF_Header20, directory_map); + } + else{ + U64 super_map_off = + MSF_MSF70_MAGIC_SIZE + OffsetOf(MSF_Header70, directory_super_map); + directory_super_map = (U32*)(msf_data.str + super_map_off); + } + + U32 max_index_count_in_map_block = (block_size - directory_map_block_skip_size)/index_size; + + // for each index in super map ... + U8 *out_ptr = directory_buf; + U32 *super_map_ptr = directory_super_map; + for (U32 i = 0; i < block_count_in_directory_map; i += 1, super_map_ptr += 1){ + U32 directory_map_block_index = *super_map_ptr; + if (directory_map_block_index >= whole_file_block_count){ + got_directory = 0; + goto parse_directory_done; + } + + U64 directory_map_block_off = (U64)(directory_map_block_index)*block_size; + U8 *directory_map_block_base = (msf_data.str + directory_map_block_off); + + // clamp index count by end of directory + U32 index_count = 0; + { + U32 directory_pos = (U32)(out_ptr - directory_buf); + U32 remaining_size = directory_size - directory_pos; + U32 remaining_map_block_count = CeilIntegerDiv(remaining_size, block_size); + index_count = ClampTop(max_index_count_in_map_block, remaining_map_block_count); + } + + // for each index in map ... + U8 *map_ptr = directory_map_block_base + directory_map_block_skip_size; + for (U32 j = 0; j < index_count; j += 1, map_ptr += index_size){ + + // read index + U32 directory_block_index = 0; + if (index_size == 4){ + directory_block_index = *(U32*)(map_ptr); + } + else{ + directory_block_index = *(U16*)(map_ptr); + } + if (directory_block_index >= whole_file_block_count){ + got_directory = 0; + goto parse_directory_done; + } + + U64 directory_block_off = (U64)(directory_block_index)*block_size; + U8 *directory_block_base = (msf_data.str + directory_block_off); + + // clamp copy size by end of directory + U32 copy_size = 0; + { + U32 directory_pos = (U32)(out_ptr - directory_buf); + U32 remaining_size = directory_size - directory_pos; + copy_size = ClampTop(block_size, remaining_size); + } + + // copy block data + MemoryCopy(out_ptr, directory_block_base, copy_size); + out_ptr += copy_size; + } + + } + + parse_directory_done:; + } + + //- parse streams from directory + U32 stream_count = 0; + B32 got_streams = 0; + String8 *streams = 0; + + if(got_directory) + { + got_streams = 1; + + // read stream count + U32 stream_count_raw = *(U32*)(directory_buf); + + // setup counts, sizes, and offsets + U32 size_of_stream_entry = 4; + if (index_size == 2){ + size_of_stream_entry = 8; + } + U32 stream_count_max = (directory_size - 4)/size_of_stream_entry; + U32 stream_count__inner = ClampTop(stream_count_raw, stream_count_max); + U32 all_stream_entries_off = 4; + U32 all_indices_off = all_stream_entries_off + stream_count__inner*size_of_stream_entry; + + // set output buffer and count + stream_count = stream_count__inner; + streams = push_array(arena, String8, stream_count); + + // iterate sizes and indices in lock step + U32 entry_cursor = all_stream_entries_off; + U32 index_cursor = all_indices_off; + String8 *stream_ptr = streams; + for (U32 i = 0; i < stream_count; i += 1) + { + // read stream size + U32 stream_size_raw = *(U32*)(directory_buf + entry_cursor); + if (stream_size_raw == 0xffffffff){ + stream_size_raw = 0; + } + + // compute block count + U32 stream_block_count_raw = CeilIntegerDiv(stream_size_raw, block_size); + U32 stream_block_count_max = (directory_size - index_cursor)/index_size;; + U32 stream_block_count = ClampTop(stream_block_count_raw, stream_block_count_max); + U32 stream_size = ClampTop(stream_size_raw, stream_block_count*block_size); + + // copy stream data + U8 *stream_buf = push_array(arena, U8, stream_size); + stream_ptr->str = stream_buf; + stream_ptr->size = stream_size; + + U32 sub_index_cursor = index_cursor; + U8 *stream_out_ptr = stream_buf; + for (U32 i = 0; i < stream_block_count; i += 1, sub_index_cursor += index_size){ + + // read index + U32 stream_block_index = 0; + if (index_size == 4){ + stream_block_index = *(U32*)(directory_buf + sub_index_cursor); + } + else{ + stream_block_index = *(U16*)(directory_buf + sub_index_cursor); + } + if (stream_block_index >= whole_file_block_count){ + got_streams = 0; + goto parse_streams_done; + } + + U64 stream_block_off = (U64)(stream_block_index)*block_size; + U8 *stream_block_base = (msf_data.str + stream_block_off); + + // clamp copy size by end of stream + U32 copy_size = 0; + { + U32 stream_pos = (U32)(stream_out_ptr - stream_buf); + U32 remaining_size = stream_size - stream_pos; + copy_size = ClampTop(block_size, remaining_size); + } + + // copy block data + MemoryCopy(stream_out_ptr, stream_block_base, copy_size); + stream_out_ptr += copy_size; + } + + // advance cursors + entry_cursor += size_of_stream_entry; + index_cursor = sub_index_cursor; + stream_ptr += 1; + } + + parse_streams_done:; + } + + if(got_streams) + { + result = push_array(arena, MSF_Parsed, 1); + result->streams = streams; + result->stream_count = stream_count; + result->block_size = block_size; + result->block_count = whole_file_block_count; + } + } + + scratch_end(scratch); + return result; +} + +internal String8 +msf_data_from_stream(MSF_Parsed *msf, MSF_StreamNumber sn) +{ + String8 result = {0}; + if(sn < msf->stream_count) + { + result = msf->streams[sn]; + } + return(result); +} diff --git a/src/msf/msf.h b/src/msf/msf.h index 77f7eebf..3250da05 100644 --- a/src/msf/msf.h +++ b/src/msf/msf.h @@ -1,65 +1,65 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef MSF_H -#define MSF_H - -//////////////////////////////// -//~ rjf: MSF Format Types - -#define MSF_INVALID_STREAM_NUMBER 0xFFFF -typedef U16 MSF_StreamNumber; - -static char msf_msf20_magic[] = "Microsoft C/C++ program database 2.00\r\n\x1aJG\0\0"; -static char msf_msf70_magic[] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0"; - -#define MSF_MSF20_MAGIC_SIZE 44 -#define MSF_MSF70_MAGIC_SIZE 32 -#define MSF_MAX_MAGIC_SIZE 44 - -typedef struct MSF_Header20 MSF_Header20; -struct MSF_Header20 -{ - U32 block_size; - U16 free_block_map_block; - U16 block_count; - U32 directory_size; - U32 unknown; - U16 directory_map; -}; - -typedef struct MSF_Header70 MSF_Header70; -struct MSF_Header70 -{ - U32 block_size; - U32 free_block_map_block; - U32 block_count; - U32 directory_size; - U32 unknown; - U32 directory_super_map; -}; - -// magic(20) + header(20) = 44 + 20 = 64 -// magic(70) + header(70) = 32 + 24 = 56 - -#define MSF_MIN_SIZE 64 - -//////////////////////////////// -//~ rjf: MSF Parser Helper Types - -typedef struct MSF_Parsed MSF_Parsed; -struct MSF_Parsed -{ - String8 *streams; - U64 stream_count; - U64 block_size; - U64 block_count; -}; - -//////////////////////////////// -//~ rjf: MSF Parser Functions - -internal MSF_Parsed* msf_parsed_from_data(Arena *arena, String8 msf_data); -internal String8 msf_data_from_stream(MSF_Parsed *msf, MSF_StreamNumber sn); - -#endif // MSF_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef MSF_H +#define MSF_H + +//////////////////////////////// +//~ rjf: MSF Format Types + +#define MSF_INVALID_STREAM_NUMBER 0xFFFF +typedef U16 MSF_StreamNumber; + +static char msf_msf20_magic[] = "Microsoft C/C++ program database 2.00\r\n\x1aJG\0\0"; +static char msf_msf70_magic[] = "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0"; + +#define MSF_MSF20_MAGIC_SIZE 44 +#define MSF_MSF70_MAGIC_SIZE 32 +#define MSF_MAX_MAGIC_SIZE 44 + +typedef struct MSF_Header20 MSF_Header20; +struct MSF_Header20 +{ + U32 block_size; + U16 free_block_map_block; + U16 block_count; + U32 directory_size; + U32 unknown; + U16 directory_map; +}; + +typedef struct MSF_Header70 MSF_Header70; +struct MSF_Header70 +{ + U32 block_size; + U32 free_block_map_block; + U32 block_count; + U32 directory_size; + U32 unknown; + U32 directory_super_map; +}; + +// magic(20) + header(20) = 44 + 20 = 64 +// magic(70) + header(70) = 32 + 24 = 56 + +#define MSF_MIN_SIZE 64 + +//////////////////////////////// +//~ rjf: MSF Parser Helper Types + +typedef struct MSF_Parsed MSF_Parsed; +struct MSF_Parsed +{ + String8 *streams; + U64 stream_count; + U64 block_size; + U64 block_count; +}; + +//////////////////////////////// +//~ rjf: MSF Parser Functions + +internal MSF_Parsed* msf_parsed_from_data(Arena *arena, String8 msf_data); +internal String8 msf_data_from_stream(MSF_Parsed *msf, MSF_StreamNumber sn); + +#endif // MSF_H diff --git a/src/mule/inline_body.cpp b/src/mule/inline_body.cpp index a813d05f..f20fb7b6 100644 --- a/src/mule/inline_body.cpp +++ b/src/mule/inline_body.cpp @@ -1,8 +1,8 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -bias = (bias^x)&7; -x -= bias; -x *= 2; -x *= x; +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +bias = (bias^x)&7; +x -= bias; +x *= 2; +x *= x; x += bias; \ No newline at end of file diff --git a/src/mule/mule_c.c b/src/mule/mule_c.c index e307760e..2836ec39 100644 --- a/src/mule/mule_c.c +++ b/src/mule/mule_c.c @@ -1,106 +1,106 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -/* -* Program to run in debugger organized to provide tests for -* single threaded stepping, breakpoints, evaluation. -*/ - -//////////////////////////////// -// NOTE(allen): Complex Types - -#include - -void -c_type_coverage_eval_tests(void){ -#if _WIN32 - _Fcomplex x = _FCbuild(0.f, 1.f); - _Dcomplex y = _Cbuild(0.f, -1.f); - -#else - float complex x = 0.f + 1.f*I; - double complex y = 0.0 - 1.0*I; - -#endif -} - -//////////////////////////////// -// NOTE(allen): Reuse Type Names From Another Module - -#include - -typedef struct Basics{ - double a; - float b; - unsigned long long c; - long long d; - unsigned int e; - int f; - unsigned short g; - short h; - unsigned char i; - char j; - - int z; -} Basics; - -typedef struct Basics_Stdint{ - double x1; - float x2; - uint64_t x3; - int64_t x4; - uint32_t x5; - int32_t x6; - uint16_t x7; - int16_t x8; - uint8_t x9; - int8_t x0; -} Basics_Stdint; - -typedef struct Pair{ - int i; - float f; -} Pair; - -void -c_versions_of_same_types(void){ - Basics basics = { 1.5f, 1.50000000000001, -1, 1, -2, 2, -4, 4, -8, 8, }; - Basics_Stdint basics_stdint = { 1.5f, 1.50000000000001, -1, 1, -2, 2, -4, 4, -8, 8, }; - Pair memory_[] = { - {100, 1.f}, - {101, 2.f}, - {102, 4.f}, - {103, 8.f}, - {104, 16.f}, - {105, 32.f}, - }; - - int x = memory_[3].i + basics.f; -} - -//////////////////////////////// -//~ NOTE(rjf): Bitfields - -typedef struct TypeWithBitfield TypeWithBitfield; -struct TypeWithBitfield -{ - int v : 14; - int w : 4; - int x : 32; - int y : 4; - int z : 10; -}; - -void -c_type_with_bitfield_usage(void) -{ - TypeWithBitfield b = {0}; - b.v = 100; - b.w = 6; - b.x = 434512; - b.y = 7; - b.z = 12; - int x = (b.v + b.x); - int y = (b.y - b.z); - int z = (b.w) + 5; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +/* +* Program to run in debugger organized to provide tests for +* single threaded stepping, breakpoints, evaluation. +*/ + +//////////////////////////////// +// NOTE(allen): Complex Types + +#include + +void +c_type_coverage_eval_tests(void){ +#if _WIN32 + _Fcomplex x = _FCbuild(0.f, 1.f); + _Dcomplex y = _Cbuild(0.f, -1.f); + +#else + float complex x = 0.f + 1.f*I; + double complex y = 0.0 - 1.0*I; + +#endif +} + +//////////////////////////////// +// NOTE(allen): Reuse Type Names From Another Module + +#include + +typedef struct Basics{ + double a; + float b; + unsigned long long c; + long long d; + unsigned int e; + int f; + unsigned short g; + short h; + unsigned char i; + char j; + + int z; +} Basics; + +typedef struct Basics_Stdint{ + double x1; + float x2; + uint64_t x3; + int64_t x4; + uint32_t x5; + int32_t x6; + uint16_t x7; + int16_t x8; + uint8_t x9; + int8_t x0; +} Basics_Stdint; + +typedef struct Pair{ + int i; + float f; +} Pair; + +void +c_versions_of_same_types(void){ + Basics basics = { 1.5f, 1.50000000000001, -1, 1, -2, 2, -4, 4, -8, 8, }; + Basics_Stdint basics_stdint = { 1.5f, 1.50000000000001, -1, 1, -2, 2, -4, 4, -8, 8, }; + Pair memory_[] = { + {100, 1.f}, + {101, 2.f}, + {102, 4.f}, + {103, 8.f}, + {104, 16.f}, + {105, 32.f}, + }; + + int x = memory_[3].i + basics.f; +} + +//////////////////////////////// +//~ NOTE(rjf): Bitfields + +typedef struct TypeWithBitfield TypeWithBitfield; +struct TypeWithBitfield +{ + int v : 14; + int w : 4; + int x : 32; + int y : 4; + int z : 10; +}; + +void +c_type_with_bitfield_usage(void) +{ + TypeWithBitfield b = {0}; + b.v = 100; + b.w = 6; + b.x = 434512; + b.y = 7; + b.z = 12; + int x = (b.v + b.x); + int y = (b.y - b.z); + int z = (b.w) + 5; +} diff --git a/src/mule/mule_c.h b/src/mule/mule_c.h index 792972c0..1bf51721 100644 --- a/src/mule/mule_c.h +++ b/src/mule/mule_c.h @@ -1,10 +1,10 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -/* -* Program to run in debugger organized to provide tests for -* single threaded stepping, breakpoints, evaluation. -*/ - -void c_type_coverage_eval_tests(void); +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +/* +* Program to run in debugger organized to provide tests for +* single threaded stepping, breakpoints, evaluation. +*/ + +void c_type_coverage_eval_tests(void); void c_type_with_bitfield_usage(void); \ No newline at end of file diff --git a/src/mule/mule_hotload_main.c b/src/mule/mule_hotload_main.c index 04cfb1bd..cd3c7440 100644 --- a/src/mule/mule_hotload_main.c +++ b/src/mule/mule_hotload_main.c @@ -1,48 +1,48 @@ -#include -#include - -int main(int argc, char **argv) -{ - int lib_loaded = 0; - HANDLE lib = {0}; - FILETIME lib_last_filetime = {0}; - int (*get_number)(void) = 0; - for(;;) - { - //- rjf: hot-load dll - { - HANDLE file = CreateFileA("mule_hotload_module.dll", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - FILETIME modified = {0}; - if(GetFileTime(file, 0, 0, &modified) && - CompareFileTime(&lib_last_filetime, &modified) == -1) - { - for(int reloaded = 0; !reloaded;) - { - if(lib_loaded) - { - FreeLibrary(lib); - lib_loaded = 0; - } - BOOL copy_worked = CopyFile("mule_hotload_module.dll", "mule_hotload_module_temp.dll", 0); - lib = LoadLibraryA("mule_hotload_module_temp.dll"); - if(lib != INVALID_HANDLE_VALUE) - { - reloaded = 1; - lib_last_filetime = modified; - get_number = (int(*)(void))GetProcAddress(lib, "get_number"); - lib_loaded = 1; - } - } - } - CloseHandle(file); - } - int number = get_number(); - printf("got a number: %i\n", number); - if(number == 0) - { - break; - } - Sleep(1000); - } - return 0; -} +#include +#include + +int main(int argc, char **argv) +{ + int lib_loaded = 0; + HANDLE lib = {0}; + FILETIME lib_last_filetime = {0}; + int (*get_number)(void) = 0; + for(;;) + { + //- rjf: hot-load dll + { + HANDLE file = CreateFileA("mule_hotload_module.dll", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + FILETIME modified = {0}; + if(GetFileTime(file, 0, 0, &modified) && + CompareFileTime(&lib_last_filetime, &modified) == -1) + { + for(int reloaded = 0; !reloaded;) + { + if(lib_loaded) + { + FreeLibrary(lib); + lib_loaded = 0; + } + BOOL copy_worked = CopyFile("mule_hotload_module.dll", "mule_hotload_module_temp.dll", 0); + lib = LoadLibraryA("mule_hotload_module_temp.dll"); + if(lib != INVALID_HANDLE_VALUE) + { + reloaded = 1; + lib_last_filetime = modified; + get_number = (int(*)(void))GetProcAddress(lib, "get_number"); + lib_loaded = 1; + } + } + } + CloseHandle(file); + } + int number = get_number(); + printf("got a number: %i\n", number); + if(number == 0) + { + break; + } + Sleep(1000); + } + return 0; +} diff --git a/src/mule/mule_hotload_module_main.c b/src/mule/mule_hotload_module_main.c index dfe9e941..1ca5b8c1 100644 --- a/src/mule/mule_hotload_module_main.c +++ b/src/mule/mule_hotload_module_main.c @@ -1,13 +1,13 @@ -__declspec(dllexport) int -get_number(void) -{ - int sum = 0; - for(int i = 0; i < 100; i += 1) - { - sum += i; - sum += i; - sum += 1; - } - sum = 1000; - return sum; -} +__declspec(dllexport) int +get_number(void) +{ + int sum = 0; + for(int i = 0; i < 100; i += 1) + { + sum += i; + sum += i; + sum += 1; + } + sum = 1000; + return sum; +} diff --git a/src/mule/mule_module.cpp b/src/mule/mule_module.cpp index 6fd7170f..fcfa305c 100644 --- a/src/mule/mule_module.cpp +++ b/src/mule/mule_module.cpp @@ -1,42 +1,42 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#if _WIN32 -#define export_function extern "C" __declspec(dllexport) -#else -#define export_function extern "C" -#endif - -#if _WIN32 -# define thread_var __declspec(thread) -#else -# define thread_var __thread -#endif - -typedef struct Basics Basics; -struct Basics -{ - int a; - int b; - int c; - int d; -}; - -thread_var float tls_a = 1.015625f; -thread_var int tls_b = -100; - -export_function void -dll_tls_eval_test(void) -{ - tls_a *= 1.5f; - tls_b *= -2; -} - -export_function void -dll_type_eval_tests(void) -{ - Basics basics1 = {1, 2, 3, 4}; - Basics basics2 = {4, 5, 6, 7}; - int x = 0; - (void)x; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#if _WIN32 +#define export_function extern "C" __declspec(dllexport) +#else +#define export_function extern "C" +#endif + +#if _WIN32 +# define thread_var __declspec(thread) +#else +# define thread_var __thread +#endif + +typedef struct Basics Basics; +struct Basics +{ + int a; + int b; + int c; + int d; +}; + +thread_var float tls_a = 1.015625f; +thread_var int tls_b = -100; + +export_function void +dll_tls_eval_test(void) +{ + tls_a *= 1.5f; + tls_b *= -2; +} + +export_function void +dll_type_eval_tests(void) +{ + Basics basics1 = {1, 2, 3, 4}; + Basics basics2 = {4, 5, 6, 7}; + int x = 0; + (void)x; +} diff --git a/src/mule/mule_o2.cpp b/src/mule/mule_o2.cpp index 618a642f..b9e86d19 100644 --- a/src/mule/mule_o2.cpp +++ b/src/mule/mule_o2.cpp @@ -1,119 +1,119 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -static int important_s32 = 0; -static float important_f32 = 0; - -#if _WIN32 -#include -#endif - -static void -do_something_with_intermediate_values(void) -{ - static int another_important_s32 = 0; - static float another_important_f32 = 0; - - another_important_s32 = (int)important_f32; - another_important_f32 = (float)important_s32; - -#if _WIN32 - char buffer[256] = "Hello, World!\n"; - buffer[0] += important_s32 + another_important_s32; - buffer[1] += (int)another_important_f32 * important_f32; - OutputDebugStringA(buffer); -#endif -} - -static void -store_important_s32(int *ptr) -{ - important_s32 = *ptr; -} - -static void -store_important_f32(float *ptr) -{ - important_f32 = *ptr; -} - -void -optimized_build_eval_tests(void) -{ - int simple_sum = 0; - for(int i = 0; i < 10000; i += 1) - { - simple_sum += i; - } - store_important_s32(&simple_sum); - - do_something_with_intermediate_values(); - - static struct {float x, y;} vec2s[] = - { - { 10.f, 76.f }, - { 40.f, 50.f }, - { -230.f, 20.f }, - { 27.f, 27.f }, - { 57.f, -57.f }, - { -37.f, 97.f }, - { 99.f, 67.f }, - { 99.f, 37.f }, - { 99.f, 57.f }, - }; - { - struct{float x, y;}sum = {0}; - int count = sizeof(vec2s)/sizeof(vec2s[0]); - for(int i = 0; i < count; i += 1) - { - sum.x += vec2s[i].x; - sum.y += vec2s[i].y; - } - struct{float x, y;}avg = {sum.x/count, sum.y/count}; - float f32 = avg.x * avg.y; - store_important_f32(&f32); - } - - do_something_with_intermediate_values(); - - int factorial = 1; - for(int i = 10; i > 0; i -= 1) - { - factorial *= i; - } - store_important_s32(&factorial); - - do_something_with_intermediate_values(); -} - -//////////////////////////////// -// NOTE(allen): Struct Parameters Eval - -struct OptimizedBasics{ - char a; - unsigned char b; - short c; - unsigned short d; - int e; - unsigned int f; - long long g; - unsigned long long h; - float i; - double j; -}; - -static void -optimized_struct_parameter_helper(int *ptr, OptimizedBasics basics) -{ - basics.a += *ptr; - basics.a += 1; - basics.a += 1; -} - -void -optimized_struct_parameters_eval_tests(void) -{ - int x = 10; - OptimizedBasics basics = {-1, 1, -2, 2, -4, 4, -8, 8, 1.5f, 1.50000000000001}; - optimized_struct_parameter_helper(&x, basics); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +static int important_s32 = 0; +static float important_f32 = 0; + +#if _WIN32 +#include +#endif + +static void +do_something_with_intermediate_values(void) +{ + static int another_important_s32 = 0; + static float another_important_f32 = 0; + + another_important_s32 = (int)important_f32; + another_important_f32 = (float)important_s32; + +#if _WIN32 + char buffer[256] = "Hello, World!\n"; + buffer[0] += important_s32 + another_important_s32; + buffer[1] += (int)another_important_f32 * important_f32; + OutputDebugStringA(buffer); +#endif +} + +static void +store_important_s32(int *ptr) +{ + important_s32 = *ptr; +} + +static void +store_important_f32(float *ptr) +{ + important_f32 = *ptr; +} + +void +optimized_build_eval_tests(void) +{ + int simple_sum = 0; + for(int i = 0; i < 10000; i += 1) + { + simple_sum += i; + } + store_important_s32(&simple_sum); + + do_something_with_intermediate_values(); + + static struct {float x, y;} vec2s[] = + { + { 10.f, 76.f }, + { 40.f, 50.f }, + { -230.f, 20.f }, + { 27.f, 27.f }, + { 57.f, -57.f }, + { -37.f, 97.f }, + { 99.f, 67.f }, + { 99.f, 37.f }, + { 99.f, 57.f }, + }; + { + struct{float x, y;}sum = {0}; + int count = sizeof(vec2s)/sizeof(vec2s[0]); + for(int i = 0; i < count; i += 1) + { + sum.x += vec2s[i].x; + sum.y += vec2s[i].y; + } + struct{float x, y;}avg = {sum.x/count, sum.y/count}; + float f32 = avg.x * avg.y; + store_important_f32(&f32); + } + + do_something_with_intermediate_values(); + + int factorial = 1; + for(int i = 10; i > 0; i -= 1) + { + factorial *= i; + } + store_important_s32(&factorial); + + do_something_with_intermediate_values(); +} + +//////////////////////////////// +// NOTE(allen): Struct Parameters Eval + +struct OptimizedBasics{ + char a; + unsigned char b; + short c; + unsigned short d; + int e; + unsigned int f; + long long g; + unsigned long long h; + float i; + double j; +}; + +static void +optimized_struct_parameter_helper(int *ptr, OptimizedBasics basics) +{ + basics.a += *ptr; + basics.a += 1; + basics.a += 1; +} + +void +optimized_struct_parameters_eval_tests(void) +{ + int x = 10; + OptimizedBasics basics = {-1, 1, -2, 2, -4, 4, -8, 8, 1.5f, 1.50000000000001}; + optimized_struct_parameter_helper(&x, basics); +} diff --git a/src/mule/mule_peb_trample.c b/src/mule/mule_peb_trample.c index a0aaa2c9..67bc9fdb 100644 --- a/src/mule/mule_peb_trample.c +++ b/src/mule/mule_peb_trample.c @@ -1,61 +1,61 @@ -#include -#include -#include "mule_peb_trample_reload.c" - -static void -HideModuleFromWindowsReload(HMODULE ModuleToFlush) -{ - /* NOTE(casey): Normally you cannot "reload" an executable module with the same name, - because Windows checks a linked list of loaded modules and assumes that if - it's already loaded, it doesn't need to reload it, even though it may have to because - it has changed on disk. - - This solution to that problem comes from some excellent spelunking by Martins Mozeiko, - who figured out that you could overwrite the filenames Windows stores in your process's - loaded module table, thus thwarting the Windows filename check against loaded modules, - allowing you to reload an existing module that has changed without requiring it to - have a different filename! - */ - - PEB *Peb = (PEB *)__readgsqword(offsetof(TEB, ProcessEnvironmentBlock)); - LIST_ENTRY *Head = &Peb->Ldr->InMemoryOrderModuleList; - for(LIST_ENTRY *Entry = Head->Flink; - Entry != Head; - Entry = Entry->Flink) - { - LDR_DATA_TABLE_ENTRY *Mod = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); - if(Mod->DllBase == ModuleToFlush) - { - ZeroMemory(Mod->FullDllName.Buffer, Mod->FullDllName.Length); - Mod->DllBase = 0; - break; - } - } -} - -int main(int argument_count, char **arguments) -{ - char *exe_name = arguments[0]; - HANDLE last_module = GetModuleHandle(0); - int (*loop_iteration_function)(int it) = (int (*)(int))GetProcAddress(last_module, "loop_iteration"); - FILETIME last_filetime = {0}; - int should_exit = 0; - for(int it = 0; !should_exit; it += 1) - { - int result = loop_iteration_function(it); - printf("%i\n", result); - Sleep(50); - FILETIME current_filetime = {0}; - HANDLE current_exe_file = CreateFile(exe_name, 0, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - GetFileTime(current_exe_file, 0, 0, ¤t_filetime); - CloseHandle(current_exe_file); - if(it != 0 && CompareFileTime(&last_filetime, ¤t_filetime) < 0) - { - HideModuleFromWindowsReload(last_module); - last_module = LoadLibrary(arguments[0]); - loop_iteration_function = (int (*)(int))GetProcAddress(last_module, "loop_iteration"); - } - last_filetime = current_filetime; - } - return 0; -} +#include +#include +#include "mule_peb_trample_reload.c" + +static void +HideModuleFromWindowsReload(HMODULE ModuleToFlush) +{ + /* NOTE(casey): Normally you cannot "reload" an executable module with the same name, + because Windows checks a linked list of loaded modules and assumes that if + it's already loaded, it doesn't need to reload it, even though it may have to because + it has changed on disk. + + This solution to that problem comes from some excellent spelunking by Martins Mozeiko, + who figured out that you could overwrite the filenames Windows stores in your process's + loaded module table, thus thwarting the Windows filename check against loaded modules, + allowing you to reload an existing module that has changed without requiring it to + have a different filename! + */ + + PEB *Peb = (PEB *)__readgsqword(offsetof(TEB, ProcessEnvironmentBlock)); + LIST_ENTRY *Head = &Peb->Ldr->InMemoryOrderModuleList; + for(LIST_ENTRY *Entry = Head->Flink; + Entry != Head; + Entry = Entry->Flink) + { + LDR_DATA_TABLE_ENTRY *Mod = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); + if(Mod->DllBase == ModuleToFlush) + { + ZeroMemory(Mod->FullDllName.Buffer, Mod->FullDllName.Length); + Mod->DllBase = 0; + break; + } + } +} + +int main(int argument_count, char **arguments) +{ + char *exe_name = arguments[0]; + HANDLE last_module = GetModuleHandle(0); + int (*loop_iteration_function)(int it) = (int (*)(int))GetProcAddress(last_module, "loop_iteration"); + FILETIME last_filetime = {0}; + int should_exit = 0; + for(int it = 0; !should_exit; it += 1) + { + int result = loop_iteration_function(it); + printf("%i\n", result); + Sleep(50); + FILETIME current_filetime = {0}; + HANDLE current_exe_file = CreateFile(exe_name, 0, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + GetFileTime(current_exe_file, 0, 0, ¤t_filetime); + CloseHandle(current_exe_file); + if(it != 0 && CompareFileTime(&last_filetime, ¤t_filetime) < 0) + { + HideModuleFromWindowsReload(last_module); + last_module = LoadLibrary(arguments[0]); + loop_iteration_function = (int (*)(int))GetProcAddress(last_module, "loop_iteration"); + } + last_filetime = current_filetime; + } + return 0; +} diff --git a/src/mule/mule_peb_trample_reload.c b/src/mule/mule_peb_trample_reload.c index 09d1f525..b0198c47 100644 --- a/src/mule/mule_peb_trample_reload.c +++ b/src/mule/mule_peb_trample_reload.c @@ -1,13 +1,13 @@ -__declspec(dllexport) int -loop_iteration(int it) -{ - //return 111; -#if 1 - int sum = 0; - for(int i = 0; i < 1000; i += 1) - { - sum += it*i; - } - return sum; -#endif -} +__declspec(dllexport) int +loop_iteration(int it) +{ + //return 111; +#if 1 + int sum = 0; + for(int i = 0; i < 1000; i += 1) + { + sum += it*i; + } + return sum; +#endif +} diff --git a/src/mutable_text/mutable_text.h b/src/mutable_text/mutable_text.h index 716faa6d..505caac6 100644 --- a/src/mutable_text/mutable_text.h +++ b/src/mutable_text/mutable_text.h @@ -1,96 +1,96 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef MUTABLE_TEXT_H -#define MUTABLE_TEXT_H - -//////////////////////////////// -//~ rjf: Cache Types - -typedef struct MTX_Node MTX_Node; -struct MTX_Node -{ - MTX_Node *next; - MTX_Node *prev; - U128 key; -}; - -typedef struct MTX_Slot MTX_Slot; -struct MTX_Slot -{ - MTX_Node *first; - MTX_Node *last; -}; - -typedef struct MTX_Stripe MTX_Stripe; -struct MTX_Stripe -{ - Arena *arena; - MTX_Node *free_node; - OS_Handle rw_mutex; -}; - -//////////////////////////////// -//~ rjf: Mutation Thread Types - -typedef struct MTX_Op MTX_Op; -struct MTX_Op -{ - Rng1U64 range; - String8 replace; -}; - -typedef struct MTX_MutThread MTX_MutThread; -struct MTX_MutThread -{ - U64 ring_size; - U8 *ring_base; - U64 ring_read_pos; - U64 ring_write_pos; - OS_Handle cv; - OS_Handle mutex; - OS_Handle thread; -}; - -//////////////////////////////// -//~ rjf: Shared State - -typedef struct MTX_Shared MTX_Shared; -struct MTX_Shared -{ - Arena *arena; - - // rjf: buffer cache - U64 slots_count; - U64 stripes_count; - MTX_Slot *slots; - MTX_Stripe *stripes; - - // rjf: mut threads - U64 mut_threads_count; - MTX_MutThread *mut_threads; -}; - -//////////////////////////////// -//~ rjf: Globals - -global MTX_Shared *mtx_shared = 0; - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void mtx_init(void); - -//////////////////////////////// -//~ rjf: Buffer Operations - -internal void mtx_push_op(U128 buffer_key, MTX_Op op); - -//////////////////////////////// -//~ rjf: Mutation Threads - -internal void mtx_enqueue_op(MTX_MutThread *thread, U128 buffer_key, MTX_Op op); -internal void mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, U128 *buffer_key_out, MTX_Op *op_out); -internal void mtx_mut_thread__entry_point(void *p); - -#endif // MUTABLE_TEXT_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef MUTABLE_TEXT_H +#define MUTABLE_TEXT_H + +//////////////////////////////// +//~ rjf: Cache Types + +typedef struct MTX_Node MTX_Node; +struct MTX_Node +{ + MTX_Node *next; + MTX_Node *prev; + U128 key; +}; + +typedef struct MTX_Slot MTX_Slot; +struct MTX_Slot +{ + MTX_Node *first; + MTX_Node *last; +}; + +typedef struct MTX_Stripe MTX_Stripe; +struct MTX_Stripe +{ + Arena *arena; + MTX_Node *free_node; + OS_Handle rw_mutex; +}; + +//////////////////////////////// +//~ rjf: Mutation Thread Types + +typedef struct MTX_Op MTX_Op; +struct MTX_Op +{ + Rng1U64 range; + String8 replace; +}; + +typedef struct MTX_MutThread MTX_MutThread; +struct MTX_MutThread +{ + U64 ring_size; + U8 *ring_base; + U64 ring_read_pos; + U64 ring_write_pos; + OS_Handle cv; + OS_Handle mutex; + OS_Handle thread; +}; + +//////////////////////////////// +//~ rjf: Shared State + +typedef struct MTX_Shared MTX_Shared; +struct MTX_Shared +{ + Arena *arena; + + // rjf: buffer cache + U64 slots_count; + U64 stripes_count; + MTX_Slot *slots; + MTX_Stripe *stripes; + + // rjf: mut threads + U64 mut_threads_count; + MTX_MutThread *mut_threads; +}; + +//////////////////////////////// +//~ rjf: Globals + +global MTX_Shared *mtx_shared = 0; + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void mtx_init(void); + +//////////////////////////////// +//~ rjf: Buffer Operations + +internal void mtx_push_op(U128 buffer_key, MTX_Op op); + +//////////////////////////////// +//~ rjf: Mutation Threads + +internal void mtx_enqueue_op(MTX_MutThread *thread, U128 buffer_key, MTX_Op op); +internal void mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, U128 *buffer_key_out, MTX_Op *op_out); +internal void mtx_mut_thread__entry_point(void *p); + +#endif // MUTABLE_TEXT_H diff --git a/src/natvis/base.natvis b/src/natvis/base.natvis index c606aad1..ad19ed09 100644 --- a/src/natvis/base.natvis +++ b/src/natvis/base.natvis @@ -1,161 +1,161 @@ - - - - {size,str} - size,str - - - - {{ string={string.size,string.str} next={next} }} - - - - empty - {{node count={node_count} total size={total_size} first={first->string} last={last->string} }} - - node_count - total_size - - node_count - first - next - string - - - - - - {{ count={count} pointer={strings} }} - - count - - count - strings - - - - - - - x - y - x*x + y*y - - - - - - x - y - x*x + y*y - - - - - - x - y - x*x + y*y - - - - - - x - y - z - x*x + y*y + z*z - - - - - - x - y - z - x*x + y*y + z*z - - - - - - x - y - z - w - x*x + y*y + z*z + w*w - - - - - - x - y - z - w - x*x + y*y + z*z + w*w - - - - - {{ min={min} max={max} [dim]={max - min} }} - - min - max - max - min - - - - - {{ min={min} max={max} [dim]={max - min} }} - - min - max - max - min - - - - - {{ min={min} max={max} [dim]={max - min} }} - - min - max - max - min - - - - - {{ min={min} max={max} [dim]={max - min} }} - - min - max - max - min - - - - - {{ min={min} max={max} [dim]={max - min} }} - - min - max - max - min - - - - - {{ name={string} hash={hash} value_string={value_string} value_stirngs={value_strings} }} - - - - {{ count={count} first={first} }} - - count - - count - first - next - this - - - - + + + + {size,str} + size,str + + + + {{ string={string.size,string.str} next={next} }} + + + + empty + {{node count={node_count} total size={total_size} first={first->string} last={last->string} }} + + node_count + total_size + + node_count + first + next + string + + + + + + {{ count={count} pointer={strings} }} + + count + + count + strings + + + + + + + x + y + x*x + y*y + + + + + + x + y + x*x + y*y + + + + + + x + y + x*x + y*y + + + + + + x + y + z + x*x + y*y + z*z + + + + + + x + y + z + x*x + y*y + z*z + + + + + + x + y + z + w + x*x + y*y + z*z + w*w + + + + + + x + y + z + w + x*x + y*y + z*z + w*w + + + + + {{ min={min} max={max} [dim]={max - min} }} + + min + max + max - min + + + + + {{ min={min} max={max} [dim]={max - min} }} + + min + max + max - min + + + + + {{ min={min} max={max} [dim]={max - min} }} + + min + max + max - min + + + + + {{ min={min} max={max} [dim]={max - min} }} + + min + max + max - min + + + + + {{ min={min} max={max} [dim]={max - min} }} + + min + max + max - min + + + + + {{ name={string} hash={hash} value_string={value_string} value_stirngs={value_strings} }} + + + + {{ count={count} first={first} }} + + count + + count + first + next + this + + + + diff --git a/src/os/gfx/os_gfx.mdesk b/src/os/gfx/os_gfx.mdesk index bb399f3c..c86bdea0 100644 --- a/src/os/gfx/os_gfx.mdesk +++ b/src/os/gfx/os_gfx.mdesk @@ -1,172 +1,172 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Tables - -@table(name, display_string, cfg_string) -OS_KeyTable: -{ - {Null "Invalid Key" "null" } - {Esc "Escape" "esc" } - {F1 "F1" "f1" } - {F2 "F2" "f2" } - {F3 "F3" "f3" } - {F4 "F4" "f4" } - {F5 "F5" "f5" } - {F6 "F6" "f6" } - {F7 "F7" "f7" } - {F8 "F8" "f8" } - {F9 "F9" "f9" } - {F10 "F10" "f10" } - {F11 "F11" "f11" } - {F12 "F12" "f12" } - {F13 "F13" "f13" } - {F14 "F14" "f14" } - {F15 "F15" "f15" } - {F16 "F16" "f16" } - {F17 "F17" "f17" } - {F18 "F18" "f18" } - {F19 "F19" "f19" } - {F20 "F20" "f20" } - {F21 "F21" "f21" } - {F22 "F22" "f22" } - {F23 "F23" "f23" } - {F24 "F24" "f24" } - {Tick "Tick" "tick" } - {0 "0" "0" } - {1 "1" "1" } - {2 "2" "2" } - {3 "3" "3" } - {4 "4" "4" } - {5 "5" "5" } - {6 "6" "6" } - {7 "7" "7" } - {8 "8" "8" } - {9 "9" "9" } - {Minus "Minus" "minus" } - {Equal "Equal" "equal" } - {Backspace "Backspace" "backspace" } - {Tab "Tab" "tab" } - {Q "Q" "q" } - {W "W" "w" } - {E "E" "e" } - {R "R" "r" } - {T "T" "t" } - {Y "Y" "y" } - {U "U" "u" } - {I "I" "i" } - {O "O" "o" } - {P "P" "p" } - {LeftBracket "Left Bracket" "left_bracket" } - {RightBracket "Right Bracket" "right_bracket" } - {BackSlash "Back Slash" "backslash" } - {CapsLock "Caps Lock" "caps_lock" } - {A "A" "a" } - {S "S" "s" } - {D "D" "d" } - {F "F" "f" } - {G "G" "g" } - {H "H" "h" } - {J "J" "j" } - {K "K" "k" } - {L "L" "l" } - {Semicolon "Semicolon" "semicolon" } - {Quote "Quote" "quote" } - {Return "Return" "return" } - {Shift "Shift" "shift" } - {Z "Z" "z" } - {X "X" "x" } - {C "C" "c" } - {V "V" "v" } - {B "B" "b" } - {N "N" "n" } - {M "M" "m" } - {Comma "Comma" "comma" } - {Period "Period" "period" } - {Slash "Slash" "slash" } - {Ctrl "Ctrl" "ctrl" } - {Alt "Alt" "alt" } - {Space "Space" "space" } - {Menu "Menu" "menu" } - {ScrollLock "Scroll Lock" "scroll_lock" } - {Pause "Pause" "pause" } - {Insert "Insert" "insert" } - {Home "Home" "home" } - {PageUp "Page Up" "page_up" } - {Delete "Delete" "delete" } - {End "End" "end" } - {PageDown "Page Down" "page_down" } - {Up "Up" "up" } - {Left "Left" "left" } - {Down "Down" "down" } - {Right "Right" "right" } - {Ex0 "Ex0" "ex0" } - {Ex1 "Ex1" "ex1" } - {Ex2 "Ex2" "ex2" } - {Ex3 "Ex3" "ex3" } - {Ex4 "Ex4" "ex4" } - {Ex5 "Ex5" "ex5" } - {Ex6 "Ex6" "ex6" } - {Ex7 "Ex7" "ex7" } - {Ex8 "Ex8" "ex8" } - {Ex9 "Ex9" "ex9" } - {Ex10 "Ex10" "ex10" } - {Ex11 "Ex11" "ex11" } - {Ex12 "Ex12" "ex12" } - {Ex13 "Ex13" "ex13" } - {Ex14 "Ex14" "ex14" } - {Ex15 "Ex15" "ex15" } - {Ex16 "Ex16" "ex16" } - {Ex17 "Ex17" "ex17" } - {Ex18 "Ex18" "ex18" } - {Ex19 "Ex19" "ex19" } - {Ex20 "Ex20" "ex20" } - {Ex21 "Ex21" "ex21" } - {Ex22 "Ex22" "ex22" } - {Ex23 "Ex23" "ex23" } - {Ex24 "Ex24" "ex24" } - {Ex25 "Ex25" "ex25" } - {Ex26 "Ex26" "ex26" } - {Ex27 "Ex27" "ex27" } - {Ex28 "Ex28" "ex28" } - {Ex29 "Ex29" "ex29" } - {NumLock "Num Lock" "num_lock" } - {NumSlash "Numpad Slash" "numpad_slash" } - {NumStar "Numpad Star" "numpad_star" } - {NumMinus "Numpad Minus" "numpad_minus" } - {NumPlus "Numpad Plus" "numpad_plus" } - {NumPeriod "Numpad Period" "numpad_period" } - {Num0 "Numpad 0" "numpad_0" } - {Num1 "Numpad 1" "numpad_1" } - {Num2 "Numpad 2" "numpad_2" } - {Num3 "Numpad 3" "numpad_3" } - {Num4 "Numpad 4" "numpad_4" } - {Num5 "Numpad 5" "numpad_5" } - {Num6 "Numpad 6" "numpad_6" } - {Num7 "Numpad 7" "numpad_7" } - {Num8 "Numpad 8" "numpad_8" } - {Num9 "Numpad 9" "numpad_9" } - {LeftMouseButton "Left Mouse Button" "left_mouse" } - {MiddleMouseButton "Middle Mouse Button" "middle_mouse" } - {RightMouseButton "Right Mouse Button" "right_mouse" } -} - -//////////////////////////////// -//~ rjf: Generators - -@enum OS_Key: -{ - @expand(OS_KeyTable a) `$(a.name)`, - COUNT, -} - -@data(String8) os_g_key_display_string_table: -{ - @expand(OS_KeyTable a) `str8_lit_comp("$(a.display_string)")`; -} - -@data(String8) os_g_key_cfg_string_table: -{ - @expand(OS_KeyTable a) `str8_lit_comp("$(a.cfg_string)")`; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Tables + +@table(name, display_string, cfg_string) +OS_KeyTable: +{ + {Null "Invalid Key" "null" } + {Esc "Escape" "esc" } + {F1 "F1" "f1" } + {F2 "F2" "f2" } + {F3 "F3" "f3" } + {F4 "F4" "f4" } + {F5 "F5" "f5" } + {F6 "F6" "f6" } + {F7 "F7" "f7" } + {F8 "F8" "f8" } + {F9 "F9" "f9" } + {F10 "F10" "f10" } + {F11 "F11" "f11" } + {F12 "F12" "f12" } + {F13 "F13" "f13" } + {F14 "F14" "f14" } + {F15 "F15" "f15" } + {F16 "F16" "f16" } + {F17 "F17" "f17" } + {F18 "F18" "f18" } + {F19 "F19" "f19" } + {F20 "F20" "f20" } + {F21 "F21" "f21" } + {F22 "F22" "f22" } + {F23 "F23" "f23" } + {F24 "F24" "f24" } + {Tick "Tick" "tick" } + {0 "0" "0" } + {1 "1" "1" } + {2 "2" "2" } + {3 "3" "3" } + {4 "4" "4" } + {5 "5" "5" } + {6 "6" "6" } + {7 "7" "7" } + {8 "8" "8" } + {9 "9" "9" } + {Minus "Minus" "minus" } + {Equal "Equal" "equal" } + {Backspace "Backspace" "backspace" } + {Tab "Tab" "tab" } + {Q "Q" "q" } + {W "W" "w" } + {E "E" "e" } + {R "R" "r" } + {T "T" "t" } + {Y "Y" "y" } + {U "U" "u" } + {I "I" "i" } + {O "O" "o" } + {P "P" "p" } + {LeftBracket "Left Bracket" "left_bracket" } + {RightBracket "Right Bracket" "right_bracket" } + {BackSlash "Back Slash" "backslash" } + {CapsLock "Caps Lock" "caps_lock" } + {A "A" "a" } + {S "S" "s" } + {D "D" "d" } + {F "F" "f" } + {G "G" "g" } + {H "H" "h" } + {J "J" "j" } + {K "K" "k" } + {L "L" "l" } + {Semicolon "Semicolon" "semicolon" } + {Quote "Quote" "quote" } + {Return "Return" "return" } + {Shift "Shift" "shift" } + {Z "Z" "z" } + {X "X" "x" } + {C "C" "c" } + {V "V" "v" } + {B "B" "b" } + {N "N" "n" } + {M "M" "m" } + {Comma "Comma" "comma" } + {Period "Period" "period" } + {Slash "Slash" "slash" } + {Ctrl "Ctrl" "ctrl" } + {Alt "Alt" "alt" } + {Space "Space" "space" } + {Menu "Menu" "menu" } + {ScrollLock "Scroll Lock" "scroll_lock" } + {Pause "Pause" "pause" } + {Insert "Insert" "insert" } + {Home "Home" "home" } + {PageUp "Page Up" "page_up" } + {Delete "Delete" "delete" } + {End "End" "end" } + {PageDown "Page Down" "page_down" } + {Up "Up" "up" } + {Left "Left" "left" } + {Down "Down" "down" } + {Right "Right" "right" } + {Ex0 "Ex0" "ex0" } + {Ex1 "Ex1" "ex1" } + {Ex2 "Ex2" "ex2" } + {Ex3 "Ex3" "ex3" } + {Ex4 "Ex4" "ex4" } + {Ex5 "Ex5" "ex5" } + {Ex6 "Ex6" "ex6" } + {Ex7 "Ex7" "ex7" } + {Ex8 "Ex8" "ex8" } + {Ex9 "Ex9" "ex9" } + {Ex10 "Ex10" "ex10" } + {Ex11 "Ex11" "ex11" } + {Ex12 "Ex12" "ex12" } + {Ex13 "Ex13" "ex13" } + {Ex14 "Ex14" "ex14" } + {Ex15 "Ex15" "ex15" } + {Ex16 "Ex16" "ex16" } + {Ex17 "Ex17" "ex17" } + {Ex18 "Ex18" "ex18" } + {Ex19 "Ex19" "ex19" } + {Ex20 "Ex20" "ex20" } + {Ex21 "Ex21" "ex21" } + {Ex22 "Ex22" "ex22" } + {Ex23 "Ex23" "ex23" } + {Ex24 "Ex24" "ex24" } + {Ex25 "Ex25" "ex25" } + {Ex26 "Ex26" "ex26" } + {Ex27 "Ex27" "ex27" } + {Ex28 "Ex28" "ex28" } + {Ex29 "Ex29" "ex29" } + {NumLock "Num Lock" "num_lock" } + {NumSlash "Numpad Slash" "numpad_slash" } + {NumStar "Numpad Star" "numpad_star" } + {NumMinus "Numpad Minus" "numpad_minus" } + {NumPlus "Numpad Plus" "numpad_plus" } + {NumPeriod "Numpad Period" "numpad_period" } + {Num0 "Numpad 0" "numpad_0" } + {Num1 "Numpad 1" "numpad_1" } + {Num2 "Numpad 2" "numpad_2" } + {Num3 "Numpad 3" "numpad_3" } + {Num4 "Numpad 4" "numpad_4" } + {Num5 "Numpad 5" "numpad_5" } + {Num6 "Numpad 6" "numpad_6" } + {Num7 "Numpad 7" "numpad_7" } + {Num8 "Numpad 8" "numpad_8" } + {Num9 "Numpad 9" "numpad_9" } + {LeftMouseButton "Left Mouse Button" "left_mouse" } + {MiddleMouseButton "Middle Mouse Button" "middle_mouse" } + {RightMouseButton "Right Mouse Button" "right_mouse" } +} + +//////////////////////////////// +//~ rjf: Generators + +@enum OS_Key: +{ + @expand(OS_KeyTable a) `$(a.name)`, + COUNT, +} + +@data(String8) os_g_key_display_string_table: +{ + @expand(OS_KeyTable a) `str8_lit_comp("$(a.display_string)")`; +} + +@data(String8) os_g_key_cfg_string_table: +{ + @expand(OS_KeyTable a) `str8_lit_comp("$(a.cfg_string)")`; +} diff --git a/src/os/gfx/stub/os_gfx_stub.h b/src/os/gfx/stub/os_gfx_stub.h index 98ebc908..e18b6f64 100644 --- a/src/os/gfx/stub/os_gfx_stub.h +++ b/src/os/gfx/stub/os_gfx_stub.h @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef OS_GFX_STUB_H -#define OS_GFX_STUB_H - -#endif // OS_GFX_STUB_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef OS_GFX_STUB_H +#define OS_GFX_STUB_H + +#endif // OS_GFX_STUB_H diff --git a/src/path/path.h b/src/path/path.h index 6455386c..4bd4650d 100644 --- a/src/path/path.h +++ b/src/path/path.h @@ -1,16 +1,16 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef PATH_H -#define PATH_H - -//////////////////////////////// -//~ allen: Path Helper Functions - -internal StringMatchFlags path_match_flags_from_os(OperatingSystem os); -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); -internal String8List path_normalized_list_from_string(Arena *arena, String8 path, PathStyle *style_out); -internal String8 path_normalized_from_string(Arena *arena, String8 path); - -#endif //PATH_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef PATH_H +#define PATH_H + +//////////////////////////////// +//~ allen: Path Helper Functions + +internal StringMatchFlags path_match_flags_from_os(OperatingSystem os); +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); +internal String8List path_normalized_list_from_string(Arena *arena, String8 path, PathStyle *style_out); +internal String8 path_normalized_from_string(Arena *arena, String8 path); + +#endif //PATH_H diff --git a/src/pdb/pdb.h b/src/pdb/pdb.h index e86f6851..187ff2a7 100644 --- a/src/pdb/pdb.h +++ b/src/pdb/pdb.h @@ -1,462 +1,462 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef PDB_H -#define PDB_H - -// https://github.com/microsoft/microsoft-pdb/tree/master/PDB - -//////////////////////////////// -//~ PDB Format Types - -typedef U32 PDB_Version; -enum{ - PDB_Version_VC2 = 19941610, - PDB_Version_VC4 = 19950623, - PDB_Version_VC41 = 19950814, - PDB_Version_VC50 = 19960307, - PDB_Version_VC98 = 19970604, - PDB_Version_VC70_DEP = 19990604, - PDB_Version_VC70 = 20000404, - PDB_Version_VC80 = 20030901, - PDB_Version_VC110 = 20091201, - PDB_Version_VC140 = 20140508 -}; - -typedef U16 PDB_ModIndex; -typedef U32 PDB_StringIndex; - -typedef enum PDB_FixedStream{ - PDB_FixedStream_PdbInfo = 1, - PDB_FixedStream_Tpi = 2, - PDB_FixedStream_Dbi = 3, - PDB_FixedStream_Ipi = 4 -} PDB_FixedStream; - -typedef enum PDB_NamedStream{ - PDB_NamedStream_HEADER_BLOCK, - PDB_NamedStream_STRTABLE, - PDB_NamedStream_LINK_INFO, - PDB_NamedStream_COUNT -} PDB_NamedStream; - -typedef struct PDB_InfoHeader{ - PDB_Version version; - U32 time; - U32 age; -} PDB_InfoHeader; - -enum{ - PDB_StrtblHeader_MAGIC = 0xEFFEEFFE -}; - -typedef struct PDB_StrtblHeader{ - U32 magic; - U32 version; -} PDB_StrtblHeader; - -//////////////////////////////// -//~ PDB Format DBI Types - -typedef U32 PDB_DbiStream; -enum{ - PDB_DbiStream_FPO, - PDB_DbiStream_EXCEPTION, - PDB_DbiStream_FIXUP, - PDB_DbiStream_OMAP_TO_SRC, - PDB_DbiStream_OMAP_FROM_SRC, - PDB_DbiStream_SECTION_HEADER, - PDB_DbiStream_TOKEN_RDI_MAP, - PDB_DbiStream_XDATA, - PDB_DbiStream_PDATA, - PDB_DbiStream_NEW_FPO, - PDB_DbiStream_SECTION_HEADER_ORIG, - PDB_DbiStream_COUNT -}; - -typedef U32 PDB_DbiHeaderSignature; -enum{ - PDB_DbiHeaderSignature_V1 = 0xFFFFFFFF -}; - -typedef U32 PDB_DbiVersion; -enum{ - PDB_DbiVersion_41 = 930803, - PDB_DbiVersion_50 = 19960307, - PDB_DbiVersion_60 = 19970606, - PDB_DbiVersion_70 = 19990903, - PDB_DbiVersion_110 = 20091201, -}; - -typedef U16 PDB_DbiBuildNumber; -#define PDB_DbiBuildNumberNewFormatFlag 0x8000 -#define PDB_DbiBuildNumberMinor(bn) ((bn)&0xFF) -#define PDB_DbiBuildNumberMajor(bn) (((bn) >> 8)&0x7F) -#define PDB_DbiBuildNumberNewFormat(bn) (!!((bn)&PDB_DbiBuildNumberNewFormatFlag)) -#define PDB_DbiBuildNumber(maj, min) \ -(PDB_DbiBuildNumberNewFormatFlag | ((min)&0xFF) | (((maj)&0x7F) << 16)) - -typedef U16 PDB_DbiHeaderFlags; -enum{ - PDB_DbiHeaderFlag_Incremental = 0x1, - PDB_DbiHeaderFlag_Stripped = 0x2, - PDB_DbiHeaderFlag_CTypes = 0x4 -}; - -typedef struct PDB_DbiHeader{ - PDB_DbiHeaderSignature sig; - PDB_DbiVersion version; - U32 age; - MSF_StreamNumber gsi_sn; - PDB_DbiBuildNumber build_number; - - MSF_StreamNumber psi_sn; - U16 pdb_version; - - MSF_StreamNumber sym_sn; - U16 pdb_version2; - - U32 module_info_size; - U32 sec_con_size; - U32 sec_map_size; - U32 file_info_size; - - U32 tsm_size; - U32 mfc_index; - U32 dbg_header_size; - U32 ec_info_size; - - PDB_DbiHeaderFlags flags; - COFF_MachineType machine; - - U32 reserved; -} PDB_DbiHeader; - -// (this is not "literally" defined by the format - but helpful to have) -typedef enum PDB_DbiRange{ - PDB_DbiRange_ModuleInfo, - PDB_DbiRange_SecCon, - PDB_DbiRange_SecMap, - PDB_DbiRange_FileInfo, - PDB_DbiRange_TSM, - PDB_DbiRange_EcInfo, - PDB_DbiRange_DbgHeader, - PDB_DbiRange_COUNT -} PDB_DbiRange; - -// "ModuleInfo" DBI range - -typedef U32 PDB_DbiSectionContribVersion; -#define PDB_DbiSectionContribVersion_1 (0xeffe0000u + 19970605u) -#define PDB_DbiSectionContribVersion_2 (0xeffe0000u + 20140516u) - -typedef struct PDB_DbiSectionContrib40{ - CV_SectionIndex sec; - U32 sec_off; - U32 size; - U32 flags; - PDB_ModIndex mod; -} PDB_DbiSectionContrib40; - -typedef struct PDB_DbiSectionContrib{ - PDB_DbiSectionContrib40 base; - U32 data_crc; - U32 reloc_crc; -} PDB_DbiSectionContrib; - -typedef struct PDB_DbiSectionContrib2{ - PDB_DbiSectionContrib40 base; - U32 data_crc; - U32 reloc_crc; - U32 sec_coff; -} PDB_DbiSectionContrib2; - -typedef struct PDB_DbiCompUnitHeader{ - U32 unused; - PDB_DbiSectionContrib contribution; - U16 flags; // unknown - - MSF_StreamNumber sn; - U32 symbols_size; - U32 c11_lines_size; - U32 c13_lines_size; - - U16 num_contrib_files; - U16 unused2; - U32 file_names_offset; - - PDB_StringIndex src_file; - PDB_StringIndex pdb_file; - - // U8[] module_name (null terminated) - // U8[] obj_name (null terminated) -} PDB_DbiCompUnitHeader; - -// (this is not "literally" defined by the format - but helpful to have) -typedef enum{ - PDB_DbiCompUnitRange_Symbols, - PDB_DbiCompUnitRange_C11, - PDB_DbiCompUnitRange_C13, - PDB_DbiCompUnitRange_COUNT -} PDB_DbiCompUnitRange; - -//////////////////////////////// -//~ PDB Format TPI Types - -typedef U32 PDB_TpiVersion; -enum{ - PDB_TpiVersion_INTV_VC2 = 920924, - PDB_TpiVersion_IMPV40 = 19950410, - PDB_TpiVersion_IMPV41 = 19951122, - PDB_TpiVersion_IMPV50_INTERIM = 19960307, - PDB_TpiVersion_IMPV50 = 19961031, - PDB_TpiVersion_IMPV70 = 19990903, - PDB_TpiVersion_IMPV80 = 20040203, -}; - -typedef struct PDB_TpiHeader{ - // (HDR) - PDB_TpiVersion version; - U32 header_size; - U32 ti_lo; - U32 ti_hi; - U32 leaf_data_size; - - // (PdbTpiHash) - MSF_StreamNumber hash_sn; - MSF_StreamNumber hash_sn_aux; - U32 hash_key_size; - U32 hash_bucket_count; - U32 hash_vals_off; - U32 hash_vals_size; - U32 itype_off; - U32 itype_size; - U32 hash_adj_off; - U32 hash_adj_size; -} PDB_TpiHeader; - -typedef struct PDB_TpiOffHint{ - CV_TypeId itype; - U32 off; -} PDB_TpiOffHint; - - -//////////////////////////////// -//~ PDB Format GSI Types - -typedef U32 PDB_GsiSignature; -enum{ - PDB_GsiSignature_Basic = 0xffffffff, -}; - -typedef U32 PDB_GsiVersion; -enum{ - PDB_GsiVersion_V70 = 0xeffe0000 + 19990810, -}; - -typedef struct PDB_GsiHeader{ - PDB_GsiSignature signature; - PDB_GsiVersion version; - U32 hr_len; - U32 num_buckets; -} PDB_GsiHeader; - -typedef struct PDB_GsiHashRecord{ - U32 symbol_off; - U32 cref; -} PDB_GsiHashRecord; - -typedef struct PDB_PsiHeader{ - U32 sym_hash_size; - U32 addr_map_size; - U32 thunk_count; - U32 thunk_size; - CV_SectionIndex isec_thunk_table; - U16 padding; - U32 sec_thunk_table_off; - U32 sec_count; -} PDB_PsiHeader; - -//////////////////////////////// -//~ PDB Parser Types - -typedef struct PDB_InfoNode{ - struct PDB_InfoNode *next; - String8 string; - MSF_StreamNumber sn; -} PDB_InfoNode; - -typedef struct PDB_Info{ - PDB_InfoNode *first; - PDB_InfoNode *last; - COFF_Guid auth_guid; -} PDB_Info; - -typedef struct PDB_NamedStreamTable{ - MSF_StreamNumber sn[PDB_NamedStream_COUNT]; -} PDB_NamedStreamTable; - -typedef struct PDB_Strtbl{ - String8 data; - U32 bucket_count; - U32 strblock_min; - U32 strblock_max; - U32 buckets_min; - U32 buckets_max; -} PDB_Strtbl; - -typedef struct PDB_DbiParsed{ - String8 data; - COFF_MachineType machine_type; - MSF_StreamNumber gsi_sn; - MSF_StreamNumber psi_sn; - MSF_StreamNumber sym_sn; - - U64 range_off[(U64)(PDB_DbiRange_COUNT) + 1]; - MSF_StreamNumber dbg_streams[PDB_DbiStream_COUNT]; -} PDB_DbiParsed; - -typedef struct PDB_TpiParsed{ - String8 data; - - // leaf info - U64 leaf_first; - U64 leaf_opl; - U32 itype_first; - U32 itype_opl; - - // hash info - MSF_StreamNumber hash_sn; - MSF_StreamNumber hash_sn_aux; - U32 hash_key_size; - U32 hash_bucket_count; - U32 hash_vals_off; - U32 hash_vals_size; - U32 itype_off; - U32 itype_size; - U32 hash_adj_off; - U32 hash_adj_size; - -} PDB_TpiParsed; - -typedef struct PDB_TpiHashBlock{ - struct PDB_TpiHashBlock *next; - U32 local_count; - CV_TypeId itypes[13]; // 13 = (64 - 12)/4 -} PDB_TpiHashBlock; - -typedef struct PDB_TpiHashParsed{ - String8 data; - String8 aux_data; - - PDB_TpiHashBlock **buckets; - U32 bucket_count; - U32 bucket_mask; -} PDB_TpiHashParsed; - -typedef struct PDB_GsiBucket{ - U32 *offs; - U64 count; -} PDB_GsiBucket; - -typedef struct PDB_GsiParsed{ - PDB_GsiBucket buckets[4096]; -} PDB_GsiParsed; - -typedef struct PDB_CompUnit{ - MSF_StreamNumber sn; - U32 range_off[(U32)(PDB_DbiCompUnitRange_COUNT) + 1]; - - String8 obj_name; - String8 group_name; -} PDB_CompUnit; - -typedef struct PDB_CoffSectionArray{ - COFF_SectionHeader *sections; - U64 count; -} PDB_CoffSectionArray; - -typedef struct PDB_CompUnitNode{ - struct PDB_CompUnitNode *next; - PDB_CompUnit unit; -} PDB_CompUnitNode; - -typedef struct PDB_CompUnitArray{ - PDB_CompUnit **units; - U64 count; -} PDB_CompUnitArray; - -typedef struct PDB_CompUnitContribution{ - U32 mod; - U64 voff_first; - U64 voff_opl; -} PDB_CompUnitContribution; - -typedef struct PDB_CompUnitContributionArray{ - PDB_CompUnitContribution *contributions; - U64 count; -} PDB_CompUnitContributionArray; - -//////////////////////////////// -//~ PDB Parser Functions - -internal PDB_Info* pdb_info_from_data(Arena *arena, String8 pdb_info_data); -internal PDB_NamedStreamTable*pdb_named_stream_table_from_info(Arena *arena, PDB_Info *info); -internal PDB_Strtbl* pdb_strtbl_from_data(Arena *arena, String8 strtbl_data); - -internal PDB_DbiParsed* pdb_dbi_from_data(Arena *arena, String8 dbi_data); -internal PDB_TpiParsed* pdb_tpi_from_data(Arena *arena, String8 tpi_data); -internal PDB_TpiHashParsed* pdb_tpi_hash_from_data(Arena *arena, - PDB_Strtbl *strtbl, - PDB_TpiParsed *tpi, - String8 tpi_hash_data, - String8 tpi_hash_aux_data); -internal PDB_GsiParsed* pdb_gsi_from_data(Arena *arena, String8 gsi_data); - -internal PDB_CoffSectionArray*pdb_coff_section_array_from_data(Arena *arena, - String8 section_data); - -internal PDB_CompUnitArray* pdb_comp_unit_array_from_data(Arena *arena, - String8 module_info_data); - -internal PDB_CompUnitContributionArray* -pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 seccontrib_data, - PDB_CoffSectionArray *sections); - -//////////////////////////////// -//~ PDB Definition Functions - -internal U32 pdb_string_hash1(String8 string); - -//////////////////////////////// -//~ PDB Dbi Functions - -internal String8 pdb_data_from_dbi_range(PDB_DbiParsed *dbi, PDB_DbiRange range); -internal String8 pdb_data_from_unit_range(MSF_Parsed *msf, PDB_CompUnit *unit, - PDB_DbiCompUnitRange range); - -//////////////////////////////// -//~ PDB Tpi Functions - -internal String8 pdb_leaf_data_from_tpi(PDB_TpiParsed *tpi); - -internal CV_TypeIdArray pdb_tpi_itypes_from_name(Arena *arena, - PDB_TpiHashParsed *tpi_hash, - CV_LeafParsed *tpi_leaf, - String8 name, - B32 compare_unique_name, - U32 output_cap); - -internal CV_TypeId pdb_tpi_first_itype_from_name(PDB_TpiHashParsed *tpi_hash, - CV_LeafParsed *tpi_leaf, - String8 name, - B32 compare_unique_name); - -//////////////////////////////// -//~ PDB Strtbl Functions - -internal String8 pdb_strtbl_string_from_off(PDB_Strtbl *strtbl, U32 off); -internal String8 pdb_strtbl_string_from_index(PDB_Strtbl *strtbl, - PDB_StringIndex idx); - -#endif // PDB_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef PDB_H +#define PDB_H + +// https://github.com/microsoft/microsoft-pdb/tree/master/PDB + +//////////////////////////////// +//~ PDB Format Types + +typedef U32 PDB_Version; +enum{ + PDB_Version_VC2 = 19941610, + PDB_Version_VC4 = 19950623, + PDB_Version_VC41 = 19950814, + PDB_Version_VC50 = 19960307, + PDB_Version_VC98 = 19970604, + PDB_Version_VC70_DEP = 19990604, + PDB_Version_VC70 = 20000404, + PDB_Version_VC80 = 20030901, + PDB_Version_VC110 = 20091201, + PDB_Version_VC140 = 20140508 +}; + +typedef U16 PDB_ModIndex; +typedef U32 PDB_StringIndex; + +typedef enum PDB_FixedStream{ + PDB_FixedStream_PdbInfo = 1, + PDB_FixedStream_Tpi = 2, + PDB_FixedStream_Dbi = 3, + PDB_FixedStream_Ipi = 4 +} PDB_FixedStream; + +typedef enum PDB_NamedStream{ + PDB_NamedStream_HEADER_BLOCK, + PDB_NamedStream_STRTABLE, + PDB_NamedStream_LINK_INFO, + PDB_NamedStream_COUNT +} PDB_NamedStream; + +typedef struct PDB_InfoHeader{ + PDB_Version version; + U32 time; + U32 age; +} PDB_InfoHeader; + +enum{ + PDB_StrtblHeader_MAGIC = 0xEFFEEFFE +}; + +typedef struct PDB_StrtblHeader{ + U32 magic; + U32 version; +} PDB_StrtblHeader; + +//////////////////////////////// +//~ PDB Format DBI Types + +typedef U32 PDB_DbiStream; +enum{ + PDB_DbiStream_FPO, + PDB_DbiStream_EXCEPTION, + PDB_DbiStream_FIXUP, + PDB_DbiStream_OMAP_TO_SRC, + PDB_DbiStream_OMAP_FROM_SRC, + PDB_DbiStream_SECTION_HEADER, + PDB_DbiStream_TOKEN_RDI_MAP, + PDB_DbiStream_XDATA, + PDB_DbiStream_PDATA, + PDB_DbiStream_NEW_FPO, + PDB_DbiStream_SECTION_HEADER_ORIG, + PDB_DbiStream_COUNT +}; + +typedef U32 PDB_DbiHeaderSignature; +enum{ + PDB_DbiHeaderSignature_V1 = 0xFFFFFFFF +}; + +typedef U32 PDB_DbiVersion; +enum{ + PDB_DbiVersion_41 = 930803, + PDB_DbiVersion_50 = 19960307, + PDB_DbiVersion_60 = 19970606, + PDB_DbiVersion_70 = 19990903, + PDB_DbiVersion_110 = 20091201, +}; + +typedef U16 PDB_DbiBuildNumber; +#define PDB_DbiBuildNumberNewFormatFlag 0x8000 +#define PDB_DbiBuildNumberMinor(bn) ((bn)&0xFF) +#define PDB_DbiBuildNumberMajor(bn) (((bn) >> 8)&0x7F) +#define PDB_DbiBuildNumberNewFormat(bn) (!!((bn)&PDB_DbiBuildNumberNewFormatFlag)) +#define PDB_DbiBuildNumber(maj, min) \ +(PDB_DbiBuildNumberNewFormatFlag | ((min)&0xFF) | (((maj)&0x7F) << 16)) + +typedef U16 PDB_DbiHeaderFlags; +enum{ + PDB_DbiHeaderFlag_Incremental = 0x1, + PDB_DbiHeaderFlag_Stripped = 0x2, + PDB_DbiHeaderFlag_CTypes = 0x4 +}; + +typedef struct PDB_DbiHeader{ + PDB_DbiHeaderSignature sig; + PDB_DbiVersion version; + U32 age; + MSF_StreamNumber gsi_sn; + PDB_DbiBuildNumber build_number; + + MSF_StreamNumber psi_sn; + U16 pdb_version; + + MSF_StreamNumber sym_sn; + U16 pdb_version2; + + U32 module_info_size; + U32 sec_con_size; + U32 sec_map_size; + U32 file_info_size; + + U32 tsm_size; + U32 mfc_index; + U32 dbg_header_size; + U32 ec_info_size; + + PDB_DbiHeaderFlags flags; + COFF_MachineType machine; + + U32 reserved; +} PDB_DbiHeader; + +// (this is not "literally" defined by the format - but helpful to have) +typedef enum PDB_DbiRange{ + PDB_DbiRange_ModuleInfo, + PDB_DbiRange_SecCon, + PDB_DbiRange_SecMap, + PDB_DbiRange_FileInfo, + PDB_DbiRange_TSM, + PDB_DbiRange_EcInfo, + PDB_DbiRange_DbgHeader, + PDB_DbiRange_COUNT +} PDB_DbiRange; + +// "ModuleInfo" DBI range + +typedef U32 PDB_DbiSectionContribVersion; +#define PDB_DbiSectionContribVersion_1 (0xeffe0000u + 19970605u) +#define PDB_DbiSectionContribVersion_2 (0xeffe0000u + 20140516u) + +typedef struct PDB_DbiSectionContrib40{ + CV_SectionIndex sec; + U32 sec_off; + U32 size; + U32 flags; + PDB_ModIndex mod; +} PDB_DbiSectionContrib40; + +typedef struct PDB_DbiSectionContrib{ + PDB_DbiSectionContrib40 base; + U32 data_crc; + U32 reloc_crc; +} PDB_DbiSectionContrib; + +typedef struct PDB_DbiSectionContrib2{ + PDB_DbiSectionContrib40 base; + U32 data_crc; + U32 reloc_crc; + U32 sec_coff; +} PDB_DbiSectionContrib2; + +typedef struct PDB_DbiCompUnitHeader{ + U32 unused; + PDB_DbiSectionContrib contribution; + U16 flags; // unknown + + MSF_StreamNumber sn; + U32 symbols_size; + U32 c11_lines_size; + U32 c13_lines_size; + + U16 num_contrib_files; + U16 unused2; + U32 file_names_offset; + + PDB_StringIndex src_file; + PDB_StringIndex pdb_file; + + // U8[] module_name (null terminated) + // U8[] obj_name (null terminated) +} PDB_DbiCompUnitHeader; + +// (this is not "literally" defined by the format - but helpful to have) +typedef enum{ + PDB_DbiCompUnitRange_Symbols, + PDB_DbiCompUnitRange_C11, + PDB_DbiCompUnitRange_C13, + PDB_DbiCompUnitRange_COUNT +} PDB_DbiCompUnitRange; + +//////////////////////////////// +//~ PDB Format TPI Types + +typedef U32 PDB_TpiVersion; +enum{ + PDB_TpiVersion_INTV_VC2 = 920924, + PDB_TpiVersion_IMPV40 = 19950410, + PDB_TpiVersion_IMPV41 = 19951122, + PDB_TpiVersion_IMPV50_INTERIM = 19960307, + PDB_TpiVersion_IMPV50 = 19961031, + PDB_TpiVersion_IMPV70 = 19990903, + PDB_TpiVersion_IMPV80 = 20040203, +}; + +typedef struct PDB_TpiHeader{ + // (HDR) + PDB_TpiVersion version; + U32 header_size; + U32 ti_lo; + U32 ti_hi; + U32 leaf_data_size; + + // (PdbTpiHash) + MSF_StreamNumber hash_sn; + MSF_StreamNumber hash_sn_aux; + U32 hash_key_size; + U32 hash_bucket_count; + U32 hash_vals_off; + U32 hash_vals_size; + U32 itype_off; + U32 itype_size; + U32 hash_adj_off; + U32 hash_adj_size; +} PDB_TpiHeader; + +typedef struct PDB_TpiOffHint{ + CV_TypeId itype; + U32 off; +} PDB_TpiOffHint; + + +//////////////////////////////// +//~ PDB Format GSI Types + +typedef U32 PDB_GsiSignature; +enum{ + PDB_GsiSignature_Basic = 0xffffffff, +}; + +typedef U32 PDB_GsiVersion; +enum{ + PDB_GsiVersion_V70 = 0xeffe0000 + 19990810, +}; + +typedef struct PDB_GsiHeader{ + PDB_GsiSignature signature; + PDB_GsiVersion version; + U32 hr_len; + U32 num_buckets; +} PDB_GsiHeader; + +typedef struct PDB_GsiHashRecord{ + U32 symbol_off; + U32 cref; +} PDB_GsiHashRecord; + +typedef struct PDB_PsiHeader{ + U32 sym_hash_size; + U32 addr_map_size; + U32 thunk_count; + U32 thunk_size; + CV_SectionIndex isec_thunk_table; + U16 padding; + U32 sec_thunk_table_off; + U32 sec_count; +} PDB_PsiHeader; + +//////////////////////////////// +//~ PDB Parser Types + +typedef struct PDB_InfoNode{ + struct PDB_InfoNode *next; + String8 string; + MSF_StreamNumber sn; +} PDB_InfoNode; + +typedef struct PDB_Info{ + PDB_InfoNode *first; + PDB_InfoNode *last; + COFF_Guid auth_guid; +} PDB_Info; + +typedef struct PDB_NamedStreamTable{ + MSF_StreamNumber sn[PDB_NamedStream_COUNT]; +} PDB_NamedStreamTable; + +typedef struct PDB_Strtbl{ + String8 data; + U32 bucket_count; + U32 strblock_min; + U32 strblock_max; + U32 buckets_min; + U32 buckets_max; +} PDB_Strtbl; + +typedef struct PDB_DbiParsed{ + String8 data; + COFF_MachineType machine_type; + MSF_StreamNumber gsi_sn; + MSF_StreamNumber psi_sn; + MSF_StreamNumber sym_sn; + + U64 range_off[(U64)(PDB_DbiRange_COUNT) + 1]; + MSF_StreamNumber dbg_streams[PDB_DbiStream_COUNT]; +} PDB_DbiParsed; + +typedef struct PDB_TpiParsed{ + String8 data; + + // leaf info + U64 leaf_first; + U64 leaf_opl; + U32 itype_first; + U32 itype_opl; + + // hash info + MSF_StreamNumber hash_sn; + MSF_StreamNumber hash_sn_aux; + U32 hash_key_size; + U32 hash_bucket_count; + U32 hash_vals_off; + U32 hash_vals_size; + U32 itype_off; + U32 itype_size; + U32 hash_adj_off; + U32 hash_adj_size; + +} PDB_TpiParsed; + +typedef struct PDB_TpiHashBlock{ + struct PDB_TpiHashBlock *next; + U32 local_count; + CV_TypeId itypes[13]; // 13 = (64 - 12)/4 +} PDB_TpiHashBlock; + +typedef struct PDB_TpiHashParsed{ + String8 data; + String8 aux_data; + + PDB_TpiHashBlock **buckets; + U32 bucket_count; + U32 bucket_mask; +} PDB_TpiHashParsed; + +typedef struct PDB_GsiBucket{ + U32 *offs; + U64 count; +} PDB_GsiBucket; + +typedef struct PDB_GsiParsed{ + PDB_GsiBucket buckets[4096]; +} PDB_GsiParsed; + +typedef struct PDB_CompUnit{ + MSF_StreamNumber sn; + U32 range_off[(U32)(PDB_DbiCompUnitRange_COUNT) + 1]; + + String8 obj_name; + String8 group_name; +} PDB_CompUnit; + +typedef struct PDB_CoffSectionArray{ + COFF_SectionHeader *sections; + U64 count; +} PDB_CoffSectionArray; + +typedef struct PDB_CompUnitNode{ + struct PDB_CompUnitNode *next; + PDB_CompUnit unit; +} PDB_CompUnitNode; + +typedef struct PDB_CompUnitArray{ + PDB_CompUnit **units; + U64 count; +} PDB_CompUnitArray; + +typedef struct PDB_CompUnitContribution{ + U32 mod; + U64 voff_first; + U64 voff_opl; +} PDB_CompUnitContribution; + +typedef struct PDB_CompUnitContributionArray{ + PDB_CompUnitContribution *contributions; + U64 count; +} PDB_CompUnitContributionArray; + +//////////////////////////////// +//~ PDB Parser Functions + +internal PDB_Info* pdb_info_from_data(Arena *arena, String8 pdb_info_data); +internal PDB_NamedStreamTable*pdb_named_stream_table_from_info(Arena *arena, PDB_Info *info); +internal PDB_Strtbl* pdb_strtbl_from_data(Arena *arena, String8 strtbl_data); + +internal PDB_DbiParsed* pdb_dbi_from_data(Arena *arena, String8 dbi_data); +internal PDB_TpiParsed* pdb_tpi_from_data(Arena *arena, String8 tpi_data); +internal PDB_TpiHashParsed* pdb_tpi_hash_from_data(Arena *arena, + PDB_Strtbl *strtbl, + PDB_TpiParsed *tpi, + String8 tpi_hash_data, + String8 tpi_hash_aux_data); +internal PDB_GsiParsed* pdb_gsi_from_data(Arena *arena, String8 gsi_data); + +internal PDB_CoffSectionArray*pdb_coff_section_array_from_data(Arena *arena, + String8 section_data); + +internal PDB_CompUnitArray* pdb_comp_unit_array_from_data(Arena *arena, + String8 module_info_data); + +internal PDB_CompUnitContributionArray* +pdb_comp_unit_contribution_array_from_data(Arena *arena, String8 seccontrib_data, + PDB_CoffSectionArray *sections); + +//////////////////////////////// +//~ PDB Definition Functions + +internal U32 pdb_string_hash1(String8 string); + +//////////////////////////////// +//~ PDB Dbi Functions + +internal String8 pdb_data_from_dbi_range(PDB_DbiParsed *dbi, PDB_DbiRange range); +internal String8 pdb_data_from_unit_range(MSF_Parsed *msf, PDB_CompUnit *unit, + PDB_DbiCompUnitRange range); + +//////////////////////////////// +//~ PDB Tpi Functions + +internal String8 pdb_leaf_data_from_tpi(PDB_TpiParsed *tpi); + +internal CV_TypeIdArray pdb_tpi_itypes_from_name(Arena *arena, + PDB_TpiHashParsed *tpi_hash, + CV_LeafParsed *tpi_leaf, + String8 name, + B32 compare_unique_name, + U32 output_cap); + +internal CV_TypeId pdb_tpi_first_itype_from_name(PDB_TpiHashParsed *tpi_hash, + CV_LeafParsed *tpi_leaf, + String8 name, + B32 compare_unique_name); + +//////////////////////////////// +//~ PDB Strtbl Functions + +internal String8 pdb_strtbl_string_from_off(PDB_Strtbl *strtbl, U32 off); +internal String8 pdb_strtbl_string_from_index(PDB_Strtbl *strtbl, + PDB_StringIndex idx); + +#endif // PDB_H diff --git a/src/pdb/pdb_stringize.c b/src/pdb/pdb_stringize.c index 7cd3f7e2..75716b5e 100644 --- a/src/pdb/pdb_stringize.c +++ b/src/pdb/pdb_stringize.c @@ -1,26 +1,26 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ PDB Stringize Functions - -internal void -pdb_stringize_tpi_hash(Arena *arena, String8List *out, PDB_TpiHashParsed *hash){ - U32 bucket_count = hash->bucket_count; - str8_list_pushf(arena, out, "bucket_count=%u\n\n", bucket_count); - for (U32 i = 0; i < bucket_count; i += 1){ - if (hash->buckets[i] != 0){ - str8_list_pushf(arena, out, "bucket[%u]:\n", i); - for (PDB_TpiHashBlock *block = hash->buckets[i]; - block != 0; - block = block->next){ - U32 local_count = block->local_count; - CV_TypeId *itype_ptr = block->itypes; - for (U32 j = 0; j < local_count; j += 1, itype_ptr += 1){ - str8_list_pushf(arena, out, " %u\n", *itype_ptr); - } - } - str8_list_push(arena, out, str8_lit("\n")); - } - } -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ PDB Stringize Functions + +internal void +pdb_stringize_tpi_hash(Arena *arena, String8List *out, PDB_TpiHashParsed *hash){ + U32 bucket_count = hash->bucket_count; + str8_list_pushf(arena, out, "bucket_count=%u\n\n", bucket_count); + for (U32 i = 0; i < bucket_count; i += 1){ + if (hash->buckets[i] != 0){ + str8_list_pushf(arena, out, "bucket[%u]:\n", i); + for (PDB_TpiHashBlock *block = hash->buckets[i]; + block != 0; + block = block->next){ + U32 local_count = block->local_count; + CV_TypeId *itype_ptr = block->itypes; + for (U32 j = 0; j < local_count; j += 1, itype_ptr += 1){ + str8_list_pushf(arena, out, " %u\n", *itype_ptr); + } + } + str8_list_push(arena, out, str8_lit("\n")); + } + } +} diff --git a/src/pdb/pdb_stringize.h b/src/pdb/pdb_stringize.h index db0ca656..72df9ee2 100644 --- a/src/pdb/pdb_stringize.h +++ b/src/pdb/pdb_stringize.h @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef PDB_STRINGIZE_H -#define PDB_STRINGIZE_H - -//////////////////////////////// -//~ PDB Stringize Functions - -internal void pdb_stringize_tpi_hash(Arena *arena, String8List *out, PDB_TpiHashParsed *hash); - -#endif // PDB_STRINGIZE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef PDB_STRINGIZE_H +#define PDB_STRINGIZE_H + +//////////////////////////////// +//~ PDB Stringize Functions + +internal void pdb_stringize_tpi_hash(Arena *arena, String8List *out, PDB_TpiHashParsed *hash); + +#endif // PDB_STRINGIZE_H diff --git a/src/pe/dos_program.asm b/src/pe/dos_program.asm index 39c47cc5..775659d0 100644 --- a/src/pe/dos_program.asm +++ b/src/pe/dos_program.asm @@ -1,17 +1,17 @@ -; Copyright (c) 2024 Epic Games Tools -; Licensed under the MIT license (https://opensource.org/license/mit/) -; $ c:\devel\projects\bin\win32\nasm src\pe\dos_program.asm -fbin -o dos_program.bin - -BITS 16 - -SEGMENT CODE - push cs ; copy psp segment address to ds - pop ds - mov dx, msg ; set print string - mov ah, 9h ; print to stdout - int 21h - mov ax, 0x4c01 ; terminate with return code 1 in al - int 0x21 - -msg: DB "This program cannot be run in DOS mode.$",0 -ALIGN 8, DB +; Copyright (c) 2024 Epic Games Tools +; Licensed under the MIT license (https://opensource.org/license/mit/) +; $ c:\devel\projects\bin\win32\nasm src\pe\dos_program.asm -fbin -o dos_program.bin + +BITS 16 + +SEGMENT CODE + push cs ; copy psp segment address to ds + pop ds + mov dx, msg ; set print string + mov ah, 9h ; print to stdout + int 21h + mov ax, 0x4c01 ; terminate with return code 1 in al + int 0x21 + +msg: DB "This program cannot be run in DOS mode.$",0 +ALIGN 8, DB diff --git a/src/pe/pe.c b/src/pe/pe.c index 9291d179..fb236674 100644 --- a/src/pe/pe.c +++ b/src/pe/pe.c @@ -1,815 +1,815 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Basic Enum Functions - -internal U32 -pe_slot_count_from_unwind_op_code(PE_UnwindOpCode opcode) -{ - U32 result = 0; - switch(opcode) - { - case PE_UnwindOpCode_PUSH_NONVOL: result = 1; break; - case PE_UnwindOpCode_ALLOC_LARGE: result = 2; break; - case PE_UnwindOpCode_ALLOC_SMALL: result = 1; break; - case PE_UnwindOpCode_SET_FPREG: result = 1; break; - case PE_UnwindOpCode_SAVE_NONVOL: result = 2; break; - case PE_UnwindOpCode_SAVE_NONVOL_FAR: result = 3; break; - case PE_UnwindOpCode_EPILOG: result = 2; break; - case PE_UnwindOpCode_SPARE_CODE: result = 3; break; - case PE_UnwindOpCode_SAVE_XMM128: result = 2; break; - case PE_UnwindOpCode_SAVE_XMM128_FAR: result = 3; break; - case PE_UnwindOpCode_PUSH_MACHFRAME: result = 1; break; - } - return result; -} - -internal String8 -pe_string_from_windows_subsystem(PE_WindowsSubsystem subsystem) -{ - String8 result = {0}; - switch(subsystem) - { - default:{}break; - case PE_WindowsSubsystem_UNKNOWN: result = str8_lit("UNKNOWN"); break; - case PE_WindowsSubsystem_NATIVE: result = str8_lit("NATIVE"); break; - case PE_WindowsSubsystem_WINDOWS_GUI: result = str8_lit("WINDOWS_GUI"); break; - case PE_WindowsSubsystem_WINDOWS_CUI: result = str8_lit("WINDOWS_CUI"); break; - case PE_WindowsSubsystem_OS2_CUI: result = str8_lit("OS2_CUI"); break; - case PE_WindowsSubsystem_POSIX_CUI: result = str8_lit("POSIX_CUI"); break; - case PE_WindowsSubsystem_NATIVE_WINDOWS: result = str8_lit("NATIVE_WINDOWS"); break; - case PE_WindowsSubsystem_WINDOWS_CE_GUI: result = str8_lit("WINDOWS_CE_GUID"); break; - case PE_WindowsSubsystem_EFI_APPLICATION: result = str8_lit("EFI_APPLICATION"); break; - case PE_WindowsSubsystem_EFI_BOOT_SERVICE_DRIVER: result = str8_lit("EFI_BOOT_SERVICE_DRIVER"); break; - case PE_WindowsSubsystem_EFI_ROM: result = str8_lit("EFI_ROM"); break; - case PE_WindowsSubsystem_XBOX: result = str8_lit("XBOX"); break; - case PE_WindowsSubsystem_WINDOWS_BOOT_APPLICATION: result = str8_lit("WINDOWS_BOOT_APPLICATION"); break; - } - return result; -} - -//////////////////////////////// -//~ rjf: Parser Functions - -internal PE_BinInfo -pe_bin_info_from_data(Arena *arena, String8 data) -{ - PE_BinInfo info = {0}; - B32 valid = 1; - - // rjf: read dos header - PE_DosHeader dos_header = {0}; - str8_deserial_read_struct(data, 0, &dos_header); - - // rjf: bad dos magic -> bad - if(dos_header.magic != PE_DOS_MAGIC) - { - valid = 0; - } - - // rjf: read pe magic - U32 pe_magic = 0; - if(valid) - { - str8_deserial_read_struct(data, dos_header.coff_file_offset, &pe_magic); - } - - // rjf: bad pe magic -> abort - if(pe_magic != PE_MAGIC) - { - valid = 0; - } - - // rjf: read coff header - U32 coff_header_off = dos_header.coff_file_offset + sizeof(pe_magic); - COFF_Header coff_header = {0}; - if(valid) - { - str8_deserial_read_struct(data, coff_header_off, &coff_header); - } - - // rjf: range of optional extension header ("optional" for short) - U32 optional_size = coff_header.optional_header_size; - U64 after_coff_header_off = coff_header_off + sizeof(coff_header); - U64 after_optional_header_off = after_coff_header_off + optional_size; - Rng1U64 optional_range = {0}; - if(valid) - { - optional_range.min = ClampTop(after_coff_header_off, data.size); - optional_range.max = ClampTop(after_optional_header_off, data.size); - } - - // rjf: get sections - U64 sec_array_off = optional_range.max; - U64 sec_array_raw_opl = sec_array_off + coff_header.section_count*sizeof(COFF_SectionHeader); - U64 sec_array_opl = ClampTop(sec_array_raw_opl, data.size); - U64 clamped_sec_count = (sec_array_opl - sec_array_off)/sizeof(COFF_SectionHeader); - COFF_SectionHeader *sections = (COFF_SectionHeader*)(data.str + sec_array_off); - - // rjf: get symbols - U64 symbol_array_off = coff_header.symbol_table_foff; - U64 symbol_count = coff_header.symbol_count; - - // rjf: get string table - U64 string_table_off = symbol_array_off + sizeof(COFF_Symbol16) * symbol_count; - - // rjf: read optional header - U16 optional_magic = 0; - U64 image_base = 0; - U64 entry_point = 0; - U32 data_dir_count = 0; - U64 virt_section_align = 0; - U64 file_section_align = 0; - Rng1U64 *data_dir_franges = 0; - if(valid && optional_size > 0) - { - // rjf: read magic number - str8_deserial_read_struct(data, optional_range.min, &optional_magic); - - // rjf: read info - U32 reported_data_dir_offset = 0; - U32 reported_data_dir_count = 0; - switch(optional_magic) - { - case PE_PE32_MAGIC: - { - PE_OptionalHeader32 pe_optional = {0}; - str8_deserial_read_struct(data, optional_range.min, &pe_optional); - image_base = pe_optional.image_base; - entry_point = pe_optional.entry_point_va; - virt_section_align = pe_optional.section_alignment; - file_section_align = pe_optional.file_alignment; - reported_data_dir_offset = sizeof(pe_optional); - reported_data_dir_count = pe_optional.data_dir_count; - }break; - case PE_PE32PLUS_MAGIC: - { - PE_OptionalHeader32Plus pe_optional = {0}; - str8_deserial_read_struct(data, optional_range.min, &pe_optional); - image_base = pe_optional.image_base; - entry_point = pe_optional.entry_point_va; - virt_section_align = pe_optional.section_alignment; - file_section_align = pe_optional.file_alignment; - reported_data_dir_offset = sizeof(pe_optional); - reported_data_dir_count = pe_optional.data_dir_count; - }break; - } - - // rjf: find file ranges of data directories - U32 data_dir_max = (optional_size - reported_data_dir_offset) / sizeof(PE_DataDirectory); - data_dir_count = ClampTop(reported_data_dir_count, data_dir_max); - - // rjf: convert PE directories to ranges - data_dir_franges = push_array(arena, Rng1U64, data_dir_count); - for(U32 dir_idx = 0; dir_idx < data_dir_count; dir_idx += 1) - { - U64 dir_offset = optional_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*dir_idx; - PE_DataDirectory dir = {0}; - str8_deserial_read_struct(data, dir_offset, &dir); - U64 file_off = coff_foff_from_voff(sections, clamped_sec_count, dir.virt_off); - data_dir_franges[dir_idx] = r1u64(file_off, file_off+dir.virt_size); - } - } - - // rjf: read info about debug file - U32 dbg_time = 0; - U32 dbg_age = 0; - OS_Guid dbg_guid = {0}; - U64 dbg_path_off = 0; - U64 dbg_path_size = 0; - if(valid && PE_DataDirectoryIndex_DEBUG < data_dir_count) - { - // rjf: read debug directory - PE_DebugDirectory dbg_data = {0}; - str8_deserial_read_struct(data, data_dir_franges[PE_DataDirectoryIndex_DEBUG].min, &dbg_data); - - // rjf: extract external file info from codeview header - if(dbg_data.type == PE_DebugDirectoryType_CODEVIEW) - { - U64 cv_offset = dbg_data.foff; - U32 cv_magic = 0; - str8_deserial_read_struct(data, cv_offset, &cv_magic); - switch(cv_magic) - { - default:break; - case PE_CODEVIEW_PDB20_MAGIC: - { - PE_CvHeaderPDB20 cv = {0}; - str8_deserial_read_struct(data, cv_offset, &cv); - dbg_time = cv.time; - dbg_age = cv.age; - dbg_path_off = cv_offset + sizeof(cv); - }break; - case PE_CODEVIEW_PDB70_MAGIC: - { - PE_CvHeaderPDB70 cv = {0}; - str8_deserial_read_struct(data, cv_offset, &cv); - dbg_guid = cv.guid; - dbg_age = cv.age; - dbg_path_off = cv_offset + sizeof(cv); - }break; - } - if(dbg_path_off > 0) - { - U8 *dbg_path_cstring_base = data.str+dbg_path_off; - dbg_path_size = cstring8_length(dbg_path_cstring_base); - } - } - } - - // rjf: extract tls header - PE_TLSHeader64 tls_header = {0}; - if(valid && PE_DataDirectoryIndex_TLS < data_dir_count) - { - Rng1U64 tls_header_frng = data_dir_franges[PE_DataDirectoryIndex_TLS]; - switch(coff_header.machine) - { - default:{}break; - case COFF_MachineType_X86: - { - PE_TLSHeader32 tls_header32 = {0}; - str8_deserial_read_struct(data, tls_header_frng.min, &tls_header32); - tls_header.raw_data_start = (U64)tls_header32.raw_data_start; - tls_header.raw_data_end = (U64)tls_header32.raw_data_end; - tls_header.index_address = (U64)tls_header32.index_address; - tls_header.callbacks_address = (U64)tls_header32.callbacks_address; - tls_header.zero_fill_size = (U64)tls_header32.zero_fill_size; - tls_header.characteristics = (U64)tls_header32.characteristics; - }break; - case COFF_MachineType_X64: - { - str8_deserial_read_struct(data, tls_header_frng.min, &tls_header); - }break; - } - } - - // rjf: fill info - if(valid) - { - info.image_base = image_base; - info.entry_point = entry_point; - info.is_pe32 = (optional_magic == PE_PE32_MAGIC); - info.virt_section_align = virt_section_align; - info.file_section_align = file_section_align; - info.section_array_off = sec_array_off; - info.section_count = clamped_sec_count; - info.symbol_array_off = symbol_array_off; - info.symbol_count = symbol_count; - info.string_table_off = string_table_off; - info.dbg_path_off = dbg_path_off; - info.dbg_path_size = dbg_path_size; - info.dbg_guid = dbg_guid; - info.dbg_age = dbg_age; - info.dbg_time = dbg_time; - info.data_dir_franges = data_dir_franges; - info.data_dir_count = data_dir_count; - switch(coff_header.machine) - { - default:{}break; - case COFF_MachineType_X86: {info.arch = Architecture_x86;}break; - case COFF_MachineType_X64: {info.arch = Architecture_x64;}break; - case COFF_MachineType_ARM: {info.arch = Architecture_arm32;}break; - case COFF_MachineType_ARM64: {info.arch = Architecture_arm64;}break; - } - MemoryCopyStruct(&info.tls_header, &tls_header); - } - return info; -} - -//////////////////////////////// -//~ rjf: Helpers - -internal U64 -pe_intel_pdata_off_from_voff__binary_search(String8 data, PE_BinInfo *bin, U64 voff) -{ - U64 result = 0; - if(bin->arch != Architecture_Null && PE_DataDirectoryIndex_EXCEPTIONS < bin->data_dir_count) - { - Rng1U64 range = bin->data_dir_franges[PE_DataDirectoryIndex_EXCEPTIONS]; - U64 pdata_off = range.min; - U64 pdata_count = (range.max - range.min)/sizeof(PE_IntelPdata); - - // check if this bin includes a pdata array - if(pdata_count > 0 && 0 <= pdata_off && pdata_off < data.size) - { - PE_IntelPdata *pdata_array = (PE_IntelPdata*)(data.str + pdata_off); - if(voff >= pdata_array[0].voff_first) - { - // binary search: - // find max index s.t. pdata_array[index].voff_first <= voff - // we assume (i < j) -> (pdata_array[i].voff_first < pdata_array[j].voff_first) - U64 index = pdata_count; - U64 min = 0; - U64 opl = pdata_count; - for(;;) - { - U64 mid = (min + opl)/2; - PE_IntelPdata *pdata = pdata_array + mid; - if(voff < pdata->voff_first) - { - opl = mid; - } - else if(pdata->voff_first < voff) - { - min = mid; - } - else - { - index = mid; - break; - } - if(min + 1 >= opl) - { - index = min; - break; - } - } - - // if we are in range fill result - { - PE_IntelPdata *pdata = pdata_array + index; - if(pdata->voff_first <= voff && voff < pdata->voff_one_past_last) - { - result = pdata_off + index*sizeof(PE_IntelPdata); - } - } - } - } - } - return(result); -} - -internal void * -pe_ptr_from_voff(String8 data, PE_BinInfo *bin, U64 voff) -{ - // rjf: get the section for this voff - U64 sec_count = bin->section_count; - COFF_SectionHeader *sec_array = (COFF_SectionHeader*)((U8*)data.str + bin->section_array_off); - COFF_SectionHeader *sec_ptr = sec_array; - COFF_SectionHeader *sec = 0; - for(U64 i = 1; i <= sec_count; i += 1, sec_ptr += 1) - { - if(sec_ptr->voff <= voff && voff < sec_ptr->voff + sec_ptr->vsize) - { - sec = sec_ptr; - break; - } - } - - // rjf: adjust to file pointer - void *result = 0; - if(sec != 0 && sec_ptr->fsize > 0) - { - U64 off = voff - sec->voff + sec->foff; - if(off < data.size) - { - result = data.str + off; - } - } - return result; -} - -internal U64 -pe_section_num_from_voff(String8 data, PE_BinInfo *bin, U64 voff) -{ - U64 sec_count = bin->section_count; - COFF_SectionHeader *sec_array = (COFF_SectionHeader*)((U8*)data.str + bin->section_array_off); - COFF_SectionHeader *sec_ptr = sec_array; - U64 result = 0; - for(U64 i = 1; i <= sec_count; i += 1, sec_ptr += 1) - { - if(sec_ptr->voff <= voff && voff < sec_ptr->voff + sec_ptr->vsize) - { - result = i; - break; - } - } - return result; -} - -internal void * -pe_ptr_from_section_num(String8 data, PE_BinInfo *bin, U64 n) -{ - void *result = 0; - U64 sec_count = bin->section_count; - if(1 <= n && n <= sec_count) - { - COFF_SectionHeader *sec_array = (COFF_SectionHeader*)((U8*)data.str + bin->section_array_off); - COFF_SectionHeader *sec = sec_array + n - 1; - if(sec->fsize > 0) - { - result = data.str + sec->foff; - } - } - return(result); -} - -internal U64 -pe_foff_from_voff(String8 data, PE_BinInfo *bin, U64 voff) -{ - U64 foff = 0; - COFF_SectionHeader *sections = (COFF_SectionHeader*)(data.str+bin->section_array_off); - U64 section_count = bin->section_count; - for(U64 sect_idx = 0; sect_idx < section_count; sect_idx += 1) - { - COFF_SectionHeader *sect = §ions[sect_idx]; - if(sect->voff <= voff && voff < sect->voff + sect->vsize) - { - if(!(sect->flags & COFF_SectionFlag_CNT_UNINITIALIZED_DATA)) - { - foff = sect->foff + (voff - sect->voff); - } - break; - } - } - return foff; -} - -internal PE_BaseRelocBlockList -pe_base_reloc_block_list_from_bin(Arena *arena, String8 data, PE_BinInfo *bin) -{ - PE_BaseRelocBlockList list = {0}; - Rng1U64 base_reloc_range = bin->data_dir_franges[PE_DataDirectoryIndex_BASE_RELOC]; - U64 range_dim = dim_1u64(base_reloc_range); - for(U64 off = base_reloc_range.min; off < range_dim;) - { - // rjf: read next entry - U32 page_virt_off = 0; - U32 block_size = 0; - off += str8_deserial_read_struct(data, off, &page_virt_off); - off += str8_deserial_read_struct(data, off, &block_size); - - // rjf: break on sentinel - if(block_size == 0) - { - break; - } - - // rjf: add node - PE_BaseRelocBlockNode *node = push_array(arena, PE_BaseRelocBlockNode, 1); - SLLQueuePush(list.first, list.last, node); - list.count += 1; - - // rjf: fill block - PE_BaseRelocBlock *block = &node->v; - U64 entries_size = block_size - (sizeof(block_size) + sizeof(page_virt_off)); - block->page_virt_off = page_virt_off; - block->entry_count = entries_size / sizeof(U16); - block->entries = push_array(arena, U16, block->entry_count); - off += str8_deserial_read_array(data, off, &block->entries[0], entries_size); - } - return list; -} - -internal Rng1U64 -pe_tls_rng_from_bin_base_vaddr(String8 data, PE_BinInfo *bin, U64 base_vaddr) -{ - U64 result_addr = (bin->tls_header.index_address - bin->image_base); - U64 result_size = sizeof(U32); - if(bin->arch != Architecture_Null) - { - U64 addr_size = bit_size_from_arch(bin->arch)/8; - Temp scratch = scratch_begin(0, 0); - PE_BaseRelocBlockList relocs = pe_base_reloc_block_list_from_bin(scratch.arena, data, bin); - for(PE_BaseRelocBlockNode *n = relocs.first; n != 0; n = n->next) - { - PE_BaseRelocBlock *block = &n->v; - for(U64 ientry = 0; ientry < block->entry_count;) - { - U32 reloc = block->entries[ientry]; - U16 kind = PE_BaseRelocKindFromEntry(reloc); - U16 offset = PE_BaseRelocOffsetFromEntry(reloc); - U64 apply_to_voff = block->page_virt_off + offset; - U64 apply_to_foff = pe_foff_from_voff(data, bin, apply_to_voff); - U64 apply_to = 0; - str8_deserial_read(data, apply_to_foff, &apply_to, addr_size, 1); - if(apply_to == bin->tls_header.index_address) - { - U64 base_diff = base_vaddr-bin->image_base; - switch(kind) - { - default: - { - // NOTE(rjf): these relocs are arm/mips/riscv specific which aren't supported at the moment - }break; - case PE_BaseRelocKind_ABSOLUTE:{}break; - case PE_BaseRelocKind_HIGH: - { - // rjf: relocate high 16-bits. - U64 high_bits = (apply_to & max_U16) << 16; - result_addr = (high_bits + ((base_diff & max_U32) >> 16)) & max_U16; - }break; - case PE_BaseRelocKind_LOW: - { - // rjf: relocate low 16-bits. - U64 low_bits = apply_to & max_U16; - result_addr = (low_bits + (base_diff & max_U32)) & max_U16; - }break; - case PE_BaseRelocKind_HIGHLOW: - { - // rjf: relocate 32-bits. - result_addr = (apply_to & max_U32) + (base_diff & max_U32); - }break; - case PE_BaseRelocKind_HIGHADJ: - { - if(ientry + 1 >= block->entry_count) - { - // NOTE(rjf): malformed relocation, expected two 16-bit entries - break; - } - - // rjf: relocate high bits and adjust sign bit on lower half. - U16 adj_offset = PE_BaseRelocOffsetFromEntry(block->entries[ientry + 1]); - result_addr = (apply_to & max_U16) << 16; - result_addr += adj_offset; - result_addr += (base_diff & max_U32); - result_addr += 0x8000; - result_addr = (result_addr >> 16) & max_U16; - }break; - case PE_BaseRelocKind_DIR64: - { - // rjf: image base relocation. - result_addr = apply_to + base_diff; - }break; - } - - goto dbl_break; - } - - U32 advance = (kind == PE_BaseRelocKind_HIGHADJ) ? 2 : 1; - ientry += advance; - } - } - dbl_break:; - scratch_end(scratch); - } - Rng1U64 result = r1u64(result_addr, result_addr+result_size); - return result; -} - -//////////////////////////////// - -internal B32 -pe_is_res(String8 data) -{ - U8 magic[sizeof(PE_RES_MAGIC)]; MemoryZeroStruct(&magic); - str8_deserial_read_struct(data, 0, &magic); - B32 is_res = MemoryCompare(&PE_RES_MAGIC, &magic, sizeof(magic)) == 0; - return is_res; -} - -internal PE_ResourceNode * -pe_resource_dir_push_dir_node(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, U32 characteristics, COFF_TimeStamp time_stamp, U16 major_version, U16 minor_version) -{ - PE_ResourceList *list = 0; - switch (id.type) { - default: - case COFF_ResourceIDType_NULL: break; - case COFF_ResourceIDType_STRING: list = &dir->named_list; break; - case COFF_ResourceIDType_NUMBER: list = &dir->id_list; break; - } - - PE_ResourceNode *res_node = push_array(arena, PE_ResourceNode, 1); - SLLQueuePush(list->first, list->last, res_node); - list->count += 1; - - PE_ResourceDir *sub_dir = push_array(arena, PE_ResourceDir, 1); - sub_dir->characteristics = characteristics; - sub_dir->time_stamp = time_stamp; - sub_dir->major_version = major_version; - sub_dir->minor_version = minor_version; - - PE_Resource *res = &res_node->data; - res->id = id; - res->kind = PE_ResDataKind_DIR; - res->u.dir = sub_dir; - - return res_node; -} - -internal PE_ResourceNode * -pe_resource_dir_push_entry_node(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, COFF_ResourceID type, U32 data_version, U32 version, COFF_ResourceMemoryFlags memory_flags, String8 data) -{ - PE_ResourceList *list = NULL; - switch (id.type) { - default: - case COFF_ResourceIDType_NULL: break; - case COFF_ResourceIDType_STRING: list = &dir->named_list; break; - case COFF_ResourceIDType_NUMBER: list = &dir->id_list; break; - } - - PE_ResourceNode *res_node = push_array(arena, PE_ResourceNode, 1); - SLLQueuePush(list->first, list->last, res_node); - list->count += 1; - - PE_Resource *res = &res_node->data; - res->id = id; - res->kind = PE_ResDataKind_COFF_RESOURCE; - res->u.coff_res.type = type; - res->u.coff_res.data_version = data_version; - res->u.coff_res.version = version; - res->u.coff_res.memory_flags = memory_flags; - res->u.coff_res.data = data; - - return res_node; -} - -internal PE_Resource * -pe_resource_dir_push_entry(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, COFF_ResourceID type, U32 data_version, U32 version, COFF_ResourceMemoryFlags memory_flags, String8 data) -{ - PE_ResourceNode *node = pe_resource_dir_push_entry_node(arena, dir, id, type, data_version, version, memory_flags, data); - return &node->data; -} - -internal PE_Resource * -pe_resource_dir_push_dir(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, U32 characteristics, COFF_TimeStamp time_stamp, U16 major_version, U16 minor_version) -{ - PE_ResourceNode *dir_node = pe_resource_dir_push_dir_node(arena, dir, id, characteristics, time_stamp, major_version, minor_version); - return &dir_node->data; -} - -internal PE_ResourceNode * -pe_resource_dir_search_node(PE_ResourceDir *dir, COFF_ResourceID id) -{ - for (PE_ResourceNode *i = dir->id_list.first; i != NULL; i = i->next) { - if (coff_resource_id_is_equal(i->data.id, id)) { - return i; - } - } - return NULL; -} - -internal PE_Resource * -pe_resource_dir_search(PE_ResourceDir *dir, COFF_ResourceID id) -{ - PE_ResourceNode *node = pe_resource_dir_search_node(dir, id); - return node ? &node->data : NULL; -} - -internal PE_ResourceArray -pe_resource_list_to_array(Arena *arena, PE_ResourceList *list) -{ - PE_ResourceArray result; - result.count = 0; - result.v = push_array(arena, PE_Resource, list->count); - for (PE_ResourceNode *n = list->first; n != NULL; n = n->next) { - result.v[result.count++] = n->data; - } - return result; -} - -internal void -pe_resource_dir_push_res_file(Arena *arena, PE_ResourceDir *root_dir, String8 res_file) -{ - // parse file into resource list - String8 res_data = str8_substr(res_file, rng_1u64(sizeof(PE_RES_MAGIC), res_file.size)); - COFF_ResourceList list = coff_resource_list_from_data(arena, res_data); - - // move resources to directories based on type - for (COFF_ResourceNode *res_node = list.first; res_node != NULL; res_node = res_node->next) { - COFF_Resource *res = &res_node->data; - - // search existing directories - PE_Resource *dir_res = pe_resource_dir_search(root_dir, res->type); - - // create new directory - if (dir_res == NULL) { - dir_res = pe_resource_dir_push_dir(arena, root_dir, res->type, 0, 0, 0, 0); - } - PE_ResourceDir *dir = dir_res->u.dir; - - // check for name collisions - PE_Resource *check_res = pe_resource_dir_search(dir, res->name); - if (check_res != NULL) { - // TODO: how do we handle name conflicts? - Assert(!"name collision"); - continue; - } - - // push entry - PE_Resource *sub_dir_res = pe_resource_dir_push_dir(arena, dir, res->name, 0, 0, 0, 0); - COFF_ResourceID id; - id.type = COFF_ResourceIDType_NUMBER; - id.u.number = res->language_id; - pe_resource_dir_push_entry(arena, sub_dir_res->u.dir, id, res->type, res->data_version, res->version, res->memory_flags, res->data); - } -} - -internal PE_ResourceDir * -pe_resource_table_from_directory_data(Arena *arena, String8 data) -{ - struct stack_s { - struct stack_s *next; - U64 table_offset; - U64 name_base_offset; - U64 id_base_offset; - PE_ResourceDir *table; - PE_ResourceDir **directory_ptr; - U64 name_ientry; - U64 id_ientry; - U64 name_entry_count; - U64 id_entry_count; - }; - - Temp scratch = scratch_begin(&arena,1); - struct stack_s *bottom_frame = push_array(scratch.arena, struct stack_s, 1); - struct stack_s *stack = bottom_frame; - - while (stack) { - if (stack->table == NULL) { - COFF_ResourceDirTable coff_table = {0}; - str8_deserial_read_struct(data, stack->table_offset, &coff_table); - - PE_ResourceDir *table = push_array(arena, PE_ResourceDir, 1); - table->characteristics = coff_table.characteristics; - table->time_stamp = coff_table.time_stamp; - table->major_version = coff_table.major_version; - table->minor_version = coff_table.minor_version; - - stack->table = table; - stack->name_base_offset = stack->table_offset + sizeof(COFF_ResourceDirTable); - stack->id_base_offset = stack->table_offset + sizeof(COFF_ResourceDirTable) + sizeof(COFF_ResourceDirEntry) * coff_table.name_entry_count; - stack->name_entry_count = coff_table.name_entry_count; - stack->id_entry_count = coff_table.id_entry_count; - - if (stack->directory_ptr) { - *stack->directory_ptr = table; - } - } - - while (stack->name_ientry < stack->name_entry_count) { - U64 entry_offset = stack->name_base_offset + stack->name_ientry * sizeof(COFF_ResourceDirEntry); - ++stack->name_ientry; - - PE_ResourceNode *named_node = push_array(arena, PE_ResourceNode, 1); - SLLQueuePush(stack->table->named_list.first, stack->table->named_list.last, named_node); - ++stack->table->named_list.count; - PE_Resource *entry = &named_node->data; - - COFF_ResourceDirEntry coff_entry = {0}; - str8_deserial_read_struct(data, entry_offset, &coff_entry); - - // NOTE: this is not documented on MSDN but high bit here is set for some reason - U32 name_offset = coff_entry.name.offset & ~COFF_RESOURCE_SUB_DIR_FLAG; - U16 name_size = 0; - str8_deserial_read_struct(data, name_offset, &name_size); - - String8 name_block; - str8_deserial_read_block(data, name_offset + sizeof(name_size), name_size*sizeof(U16), &name_block); - String16 name16 = str16((U16*)name_block.str, name_size); - - B32 is_dir = !!(coff_entry.id.data_entry_offset & COFF_RESOURCE_SUB_DIR_FLAG); - - entry->id.type = COFF_ResourceIDType_STRING; - entry->id.u.string = str8_from_16(arena, name16); - entry->kind = is_dir ? PE_ResDataKind_DIR : PE_ResDataKind_COFF_LEAF; - - if (is_dir) { - struct stack_s *frame = push_array(scratch.arena, struct stack_s, 1); - frame->table_offset = coff_entry.id.sub_dir_offset & ~COFF_RESOURCE_SUB_DIR_FLAG; - frame->directory_ptr = &entry->u.dir; - SLLStackPush(stack, frame); - goto yeild; - } else { - str8_deserial_read_struct(data, coff_entry.id.data_entry_offset, &entry->u.leaf); - } - } - - while (stack->id_ientry < stack->id_entry_count) { - U64 entry_offset = stack->id_base_offset + stack->id_ientry * sizeof(COFF_ResourceDirEntry); - ++stack->id_ientry; - - PE_ResourceNode *id_node = push_array(arena, PE_ResourceNode, 1); - SLLQueuePush(stack->table->id_list.first, stack->table->id_list.last, id_node); - ++stack->table->id_list.count; - PE_Resource *entry = &id_node->data; - - COFF_ResourceDirEntry coff_entry = {0}; - str8_deserial_read_struct(data, entry_offset, &coff_entry); - - B32 is_dir = !!(coff_entry.id.sub_dir_offset & COFF_RESOURCE_SUB_DIR_FLAG); - - entry->id.type = COFF_ResourceIDType_NUMBER; - entry->id.u.number = coff_entry.name.id; - entry->kind = is_dir ? PE_ResDataKind_DIR : PE_ResDataKind_COFF_LEAF; - - if (is_dir) { - struct stack_s *frame = push_array(scratch.arena, struct stack_s, 1); - frame->table_offset = coff_entry.id.sub_dir_offset & ~COFF_RESOURCE_SUB_DIR_FLAG; - frame->directory_ptr = &entry->u.dir; - SLLStackPush(stack, frame); - goto yeild; - } else { - str8_deserial_read_struct(data, coff_entry.id.sub_dir_offset, &entry->u.leaf); - } - } - - SLLStackPop(stack); - - yeild:; - } - - scratch_end(scratch); - return bottom_frame->table; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Basic Enum Functions + +internal U32 +pe_slot_count_from_unwind_op_code(PE_UnwindOpCode opcode) +{ + U32 result = 0; + switch(opcode) + { + case PE_UnwindOpCode_PUSH_NONVOL: result = 1; break; + case PE_UnwindOpCode_ALLOC_LARGE: result = 2; break; + case PE_UnwindOpCode_ALLOC_SMALL: result = 1; break; + case PE_UnwindOpCode_SET_FPREG: result = 1; break; + case PE_UnwindOpCode_SAVE_NONVOL: result = 2; break; + case PE_UnwindOpCode_SAVE_NONVOL_FAR: result = 3; break; + case PE_UnwindOpCode_EPILOG: result = 2; break; + case PE_UnwindOpCode_SPARE_CODE: result = 3; break; + case PE_UnwindOpCode_SAVE_XMM128: result = 2; break; + case PE_UnwindOpCode_SAVE_XMM128_FAR: result = 3; break; + case PE_UnwindOpCode_PUSH_MACHFRAME: result = 1; break; + } + return result; +} + +internal String8 +pe_string_from_windows_subsystem(PE_WindowsSubsystem subsystem) +{ + String8 result = {0}; + switch(subsystem) + { + default:{}break; + case PE_WindowsSubsystem_UNKNOWN: result = str8_lit("UNKNOWN"); break; + case PE_WindowsSubsystem_NATIVE: result = str8_lit("NATIVE"); break; + case PE_WindowsSubsystem_WINDOWS_GUI: result = str8_lit("WINDOWS_GUI"); break; + case PE_WindowsSubsystem_WINDOWS_CUI: result = str8_lit("WINDOWS_CUI"); break; + case PE_WindowsSubsystem_OS2_CUI: result = str8_lit("OS2_CUI"); break; + case PE_WindowsSubsystem_POSIX_CUI: result = str8_lit("POSIX_CUI"); break; + case PE_WindowsSubsystem_NATIVE_WINDOWS: result = str8_lit("NATIVE_WINDOWS"); break; + case PE_WindowsSubsystem_WINDOWS_CE_GUI: result = str8_lit("WINDOWS_CE_GUID"); break; + case PE_WindowsSubsystem_EFI_APPLICATION: result = str8_lit("EFI_APPLICATION"); break; + case PE_WindowsSubsystem_EFI_BOOT_SERVICE_DRIVER: result = str8_lit("EFI_BOOT_SERVICE_DRIVER"); break; + case PE_WindowsSubsystem_EFI_ROM: result = str8_lit("EFI_ROM"); break; + case PE_WindowsSubsystem_XBOX: result = str8_lit("XBOX"); break; + case PE_WindowsSubsystem_WINDOWS_BOOT_APPLICATION: result = str8_lit("WINDOWS_BOOT_APPLICATION"); break; + } + return result; +} + +//////////////////////////////// +//~ rjf: Parser Functions + +internal PE_BinInfo +pe_bin_info_from_data(Arena *arena, String8 data) +{ + PE_BinInfo info = {0}; + B32 valid = 1; + + // rjf: read dos header + PE_DosHeader dos_header = {0}; + str8_deserial_read_struct(data, 0, &dos_header); + + // rjf: bad dos magic -> bad + if(dos_header.magic != PE_DOS_MAGIC) + { + valid = 0; + } + + // rjf: read pe magic + U32 pe_magic = 0; + if(valid) + { + str8_deserial_read_struct(data, dos_header.coff_file_offset, &pe_magic); + } + + // rjf: bad pe magic -> abort + if(pe_magic != PE_MAGIC) + { + valid = 0; + } + + // rjf: read coff header + U32 coff_header_off = dos_header.coff_file_offset + sizeof(pe_magic); + COFF_Header coff_header = {0}; + if(valid) + { + str8_deserial_read_struct(data, coff_header_off, &coff_header); + } + + // rjf: range of optional extension header ("optional" for short) + U32 optional_size = coff_header.optional_header_size; + U64 after_coff_header_off = coff_header_off + sizeof(coff_header); + U64 after_optional_header_off = after_coff_header_off + optional_size; + Rng1U64 optional_range = {0}; + if(valid) + { + optional_range.min = ClampTop(after_coff_header_off, data.size); + optional_range.max = ClampTop(after_optional_header_off, data.size); + } + + // rjf: get sections + U64 sec_array_off = optional_range.max; + U64 sec_array_raw_opl = sec_array_off + coff_header.section_count*sizeof(COFF_SectionHeader); + U64 sec_array_opl = ClampTop(sec_array_raw_opl, data.size); + U64 clamped_sec_count = (sec_array_opl - sec_array_off)/sizeof(COFF_SectionHeader); + COFF_SectionHeader *sections = (COFF_SectionHeader*)(data.str + sec_array_off); + + // rjf: get symbols + U64 symbol_array_off = coff_header.symbol_table_foff; + U64 symbol_count = coff_header.symbol_count; + + // rjf: get string table + U64 string_table_off = symbol_array_off + sizeof(COFF_Symbol16) * symbol_count; + + // rjf: read optional header + U16 optional_magic = 0; + U64 image_base = 0; + U64 entry_point = 0; + U32 data_dir_count = 0; + U64 virt_section_align = 0; + U64 file_section_align = 0; + Rng1U64 *data_dir_franges = 0; + if(valid && optional_size > 0) + { + // rjf: read magic number + str8_deserial_read_struct(data, optional_range.min, &optional_magic); + + // rjf: read info + U32 reported_data_dir_offset = 0; + U32 reported_data_dir_count = 0; + switch(optional_magic) + { + case PE_PE32_MAGIC: + { + PE_OptionalHeader32 pe_optional = {0}; + str8_deserial_read_struct(data, optional_range.min, &pe_optional); + image_base = pe_optional.image_base; + entry_point = pe_optional.entry_point_va; + virt_section_align = pe_optional.section_alignment; + file_section_align = pe_optional.file_alignment; + reported_data_dir_offset = sizeof(pe_optional); + reported_data_dir_count = pe_optional.data_dir_count; + }break; + case PE_PE32PLUS_MAGIC: + { + PE_OptionalHeader32Plus pe_optional = {0}; + str8_deserial_read_struct(data, optional_range.min, &pe_optional); + image_base = pe_optional.image_base; + entry_point = pe_optional.entry_point_va; + virt_section_align = pe_optional.section_alignment; + file_section_align = pe_optional.file_alignment; + reported_data_dir_offset = sizeof(pe_optional); + reported_data_dir_count = pe_optional.data_dir_count; + }break; + } + + // rjf: find file ranges of data directories + U32 data_dir_max = (optional_size - reported_data_dir_offset) / sizeof(PE_DataDirectory); + data_dir_count = ClampTop(reported_data_dir_count, data_dir_max); + + // rjf: convert PE directories to ranges + data_dir_franges = push_array(arena, Rng1U64, data_dir_count); + for(U32 dir_idx = 0; dir_idx < data_dir_count; dir_idx += 1) + { + U64 dir_offset = optional_range.min + reported_data_dir_offset + sizeof(PE_DataDirectory)*dir_idx; + PE_DataDirectory dir = {0}; + str8_deserial_read_struct(data, dir_offset, &dir); + U64 file_off = coff_foff_from_voff(sections, clamped_sec_count, dir.virt_off); + data_dir_franges[dir_idx] = r1u64(file_off, file_off+dir.virt_size); + } + } + + // rjf: read info about debug file + U32 dbg_time = 0; + U32 dbg_age = 0; + OS_Guid dbg_guid = {0}; + U64 dbg_path_off = 0; + U64 dbg_path_size = 0; + if(valid && PE_DataDirectoryIndex_DEBUG < data_dir_count) + { + // rjf: read debug directory + PE_DebugDirectory dbg_data = {0}; + str8_deserial_read_struct(data, data_dir_franges[PE_DataDirectoryIndex_DEBUG].min, &dbg_data); + + // rjf: extract external file info from codeview header + if(dbg_data.type == PE_DebugDirectoryType_CODEVIEW) + { + U64 cv_offset = dbg_data.foff; + U32 cv_magic = 0; + str8_deserial_read_struct(data, cv_offset, &cv_magic); + switch(cv_magic) + { + default:break; + case PE_CODEVIEW_PDB20_MAGIC: + { + PE_CvHeaderPDB20 cv = {0}; + str8_deserial_read_struct(data, cv_offset, &cv); + dbg_time = cv.time; + dbg_age = cv.age; + dbg_path_off = cv_offset + sizeof(cv); + }break; + case PE_CODEVIEW_PDB70_MAGIC: + { + PE_CvHeaderPDB70 cv = {0}; + str8_deserial_read_struct(data, cv_offset, &cv); + dbg_guid = cv.guid; + dbg_age = cv.age; + dbg_path_off = cv_offset + sizeof(cv); + }break; + } + if(dbg_path_off > 0) + { + U8 *dbg_path_cstring_base = data.str+dbg_path_off; + dbg_path_size = cstring8_length(dbg_path_cstring_base); + } + } + } + + // rjf: extract tls header + PE_TLSHeader64 tls_header = {0}; + if(valid && PE_DataDirectoryIndex_TLS < data_dir_count) + { + Rng1U64 tls_header_frng = data_dir_franges[PE_DataDirectoryIndex_TLS]; + switch(coff_header.machine) + { + default:{}break; + case COFF_MachineType_X86: + { + PE_TLSHeader32 tls_header32 = {0}; + str8_deserial_read_struct(data, tls_header_frng.min, &tls_header32); + tls_header.raw_data_start = (U64)tls_header32.raw_data_start; + tls_header.raw_data_end = (U64)tls_header32.raw_data_end; + tls_header.index_address = (U64)tls_header32.index_address; + tls_header.callbacks_address = (U64)tls_header32.callbacks_address; + tls_header.zero_fill_size = (U64)tls_header32.zero_fill_size; + tls_header.characteristics = (U64)tls_header32.characteristics; + }break; + case COFF_MachineType_X64: + { + str8_deserial_read_struct(data, tls_header_frng.min, &tls_header); + }break; + } + } + + // rjf: fill info + if(valid) + { + info.image_base = image_base; + info.entry_point = entry_point; + info.is_pe32 = (optional_magic == PE_PE32_MAGIC); + info.virt_section_align = virt_section_align; + info.file_section_align = file_section_align; + info.section_array_off = sec_array_off; + info.section_count = clamped_sec_count; + info.symbol_array_off = symbol_array_off; + info.symbol_count = symbol_count; + info.string_table_off = string_table_off; + info.dbg_path_off = dbg_path_off; + info.dbg_path_size = dbg_path_size; + info.dbg_guid = dbg_guid; + info.dbg_age = dbg_age; + info.dbg_time = dbg_time; + info.data_dir_franges = data_dir_franges; + info.data_dir_count = data_dir_count; + switch(coff_header.machine) + { + default:{}break; + case COFF_MachineType_X86: {info.arch = Architecture_x86;}break; + case COFF_MachineType_X64: {info.arch = Architecture_x64;}break; + case COFF_MachineType_ARM: {info.arch = Architecture_arm32;}break; + case COFF_MachineType_ARM64: {info.arch = Architecture_arm64;}break; + } + MemoryCopyStruct(&info.tls_header, &tls_header); + } + return info; +} + +//////////////////////////////// +//~ rjf: Helpers + +internal U64 +pe_intel_pdata_off_from_voff__binary_search(String8 data, PE_BinInfo *bin, U64 voff) +{ + U64 result = 0; + if(bin->arch != Architecture_Null && PE_DataDirectoryIndex_EXCEPTIONS < bin->data_dir_count) + { + Rng1U64 range = bin->data_dir_franges[PE_DataDirectoryIndex_EXCEPTIONS]; + U64 pdata_off = range.min; + U64 pdata_count = (range.max - range.min)/sizeof(PE_IntelPdata); + + // check if this bin includes a pdata array + if(pdata_count > 0 && 0 <= pdata_off && pdata_off < data.size) + { + PE_IntelPdata *pdata_array = (PE_IntelPdata*)(data.str + pdata_off); + if(voff >= pdata_array[0].voff_first) + { + // binary search: + // find max index s.t. pdata_array[index].voff_first <= voff + // we assume (i < j) -> (pdata_array[i].voff_first < pdata_array[j].voff_first) + U64 index = pdata_count; + U64 min = 0; + U64 opl = pdata_count; + for(;;) + { + U64 mid = (min + opl)/2; + PE_IntelPdata *pdata = pdata_array + mid; + if(voff < pdata->voff_first) + { + opl = mid; + } + else if(pdata->voff_first < voff) + { + min = mid; + } + else + { + index = mid; + break; + } + if(min + 1 >= opl) + { + index = min; + break; + } + } + + // if we are in range fill result + { + PE_IntelPdata *pdata = pdata_array + index; + if(pdata->voff_first <= voff && voff < pdata->voff_one_past_last) + { + result = pdata_off + index*sizeof(PE_IntelPdata); + } + } + } + } + } + return(result); +} + +internal void * +pe_ptr_from_voff(String8 data, PE_BinInfo *bin, U64 voff) +{ + // rjf: get the section for this voff + U64 sec_count = bin->section_count; + COFF_SectionHeader *sec_array = (COFF_SectionHeader*)((U8*)data.str + bin->section_array_off); + COFF_SectionHeader *sec_ptr = sec_array; + COFF_SectionHeader *sec = 0; + for(U64 i = 1; i <= sec_count; i += 1, sec_ptr += 1) + { + if(sec_ptr->voff <= voff && voff < sec_ptr->voff + sec_ptr->vsize) + { + sec = sec_ptr; + break; + } + } + + // rjf: adjust to file pointer + void *result = 0; + if(sec != 0 && sec_ptr->fsize > 0) + { + U64 off = voff - sec->voff + sec->foff; + if(off < data.size) + { + result = data.str + off; + } + } + return result; +} + +internal U64 +pe_section_num_from_voff(String8 data, PE_BinInfo *bin, U64 voff) +{ + U64 sec_count = bin->section_count; + COFF_SectionHeader *sec_array = (COFF_SectionHeader*)((U8*)data.str + bin->section_array_off); + COFF_SectionHeader *sec_ptr = sec_array; + U64 result = 0; + for(U64 i = 1; i <= sec_count; i += 1, sec_ptr += 1) + { + if(sec_ptr->voff <= voff && voff < sec_ptr->voff + sec_ptr->vsize) + { + result = i; + break; + } + } + return result; +} + +internal void * +pe_ptr_from_section_num(String8 data, PE_BinInfo *bin, U64 n) +{ + void *result = 0; + U64 sec_count = bin->section_count; + if(1 <= n && n <= sec_count) + { + COFF_SectionHeader *sec_array = (COFF_SectionHeader*)((U8*)data.str + bin->section_array_off); + COFF_SectionHeader *sec = sec_array + n - 1; + if(sec->fsize > 0) + { + result = data.str + sec->foff; + } + } + return(result); +} + +internal U64 +pe_foff_from_voff(String8 data, PE_BinInfo *bin, U64 voff) +{ + U64 foff = 0; + COFF_SectionHeader *sections = (COFF_SectionHeader*)(data.str+bin->section_array_off); + U64 section_count = bin->section_count; + for(U64 sect_idx = 0; sect_idx < section_count; sect_idx += 1) + { + COFF_SectionHeader *sect = §ions[sect_idx]; + if(sect->voff <= voff && voff < sect->voff + sect->vsize) + { + if(!(sect->flags & COFF_SectionFlag_CNT_UNINITIALIZED_DATA)) + { + foff = sect->foff + (voff - sect->voff); + } + break; + } + } + return foff; +} + +internal PE_BaseRelocBlockList +pe_base_reloc_block_list_from_bin(Arena *arena, String8 data, PE_BinInfo *bin) +{ + PE_BaseRelocBlockList list = {0}; + Rng1U64 base_reloc_range = bin->data_dir_franges[PE_DataDirectoryIndex_BASE_RELOC]; + U64 range_dim = dim_1u64(base_reloc_range); + for(U64 off = base_reloc_range.min; off < range_dim;) + { + // rjf: read next entry + U32 page_virt_off = 0; + U32 block_size = 0; + off += str8_deserial_read_struct(data, off, &page_virt_off); + off += str8_deserial_read_struct(data, off, &block_size); + + // rjf: break on sentinel + if(block_size == 0) + { + break; + } + + // rjf: add node + PE_BaseRelocBlockNode *node = push_array(arena, PE_BaseRelocBlockNode, 1); + SLLQueuePush(list.first, list.last, node); + list.count += 1; + + // rjf: fill block + PE_BaseRelocBlock *block = &node->v; + U64 entries_size = block_size - (sizeof(block_size) + sizeof(page_virt_off)); + block->page_virt_off = page_virt_off; + block->entry_count = entries_size / sizeof(U16); + block->entries = push_array(arena, U16, block->entry_count); + off += str8_deserial_read_array(data, off, &block->entries[0], entries_size); + } + return list; +} + +internal Rng1U64 +pe_tls_rng_from_bin_base_vaddr(String8 data, PE_BinInfo *bin, U64 base_vaddr) +{ + U64 result_addr = (bin->tls_header.index_address - bin->image_base); + U64 result_size = sizeof(U32); + if(bin->arch != Architecture_Null) + { + U64 addr_size = bit_size_from_arch(bin->arch)/8; + Temp scratch = scratch_begin(0, 0); + PE_BaseRelocBlockList relocs = pe_base_reloc_block_list_from_bin(scratch.arena, data, bin); + for(PE_BaseRelocBlockNode *n = relocs.first; n != 0; n = n->next) + { + PE_BaseRelocBlock *block = &n->v; + for(U64 ientry = 0; ientry < block->entry_count;) + { + U32 reloc = block->entries[ientry]; + U16 kind = PE_BaseRelocKindFromEntry(reloc); + U16 offset = PE_BaseRelocOffsetFromEntry(reloc); + U64 apply_to_voff = block->page_virt_off + offset; + U64 apply_to_foff = pe_foff_from_voff(data, bin, apply_to_voff); + U64 apply_to = 0; + str8_deserial_read(data, apply_to_foff, &apply_to, addr_size, 1); + if(apply_to == bin->tls_header.index_address) + { + U64 base_diff = base_vaddr-bin->image_base; + switch(kind) + { + default: + { + // NOTE(rjf): these relocs are arm/mips/riscv specific which aren't supported at the moment + }break; + case PE_BaseRelocKind_ABSOLUTE:{}break; + case PE_BaseRelocKind_HIGH: + { + // rjf: relocate high 16-bits. + U64 high_bits = (apply_to & max_U16) << 16; + result_addr = (high_bits + ((base_diff & max_U32) >> 16)) & max_U16; + }break; + case PE_BaseRelocKind_LOW: + { + // rjf: relocate low 16-bits. + U64 low_bits = apply_to & max_U16; + result_addr = (low_bits + (base_diff & max_U32)) & max_U16; + }break; + case PE_BaseRelocKind_HIGHLOW: + { + // rjf: relocate 32-bits. + result_addr = (apply_to & max_U32) + (base_diff & max_U32); + }break; + case PE_BaseRelocKind_HIGHADJ: + { + if(ientry + 1 >= block->entry_count) + { + // NOTE(rjf): malformed relocation, expected two 16-bit entries + break; + } + + // rjf: relocate high bits and adjust sign bit on lower half. + U16 adj_offset = PE_BaseRelocOffsetFromEntry(block->entries[ientry + 1]); + result_addr = (apply_to & max_U16) << 16; + result_addr += adj_offset; + result_addr += (base_diff & max_U32); + result_addr += 0x8000; + result_addr = (result_addr >> 16) & max_U16; + }break; + case PE_BaseRelocKind_DIR64: + { + // rjf: image base relocation. + result_addr = apply_to + base_diff; + }break; + } + + goto dbl_break; + } + + U32 advance = (kind == PE_BaseRelocKind_HIGHADJ) ? 2 : 1; + ientry += advance; + } + } + dbl_break:; + scratch_end(scratch); + } + Rng1U64 result = r1u64(result_addr, result_addr+result_size); + return result; +} + +//////////////////////////////// + +internal B32 +pe_is_res(String8 data) +{ + U8 magic[sizeof(PE_RES_MAGIC)]; MemoryZeroStruct(&magic); + str8_deserial_read_struct(data, 0, &magic); + B32 is_res = MemoryCompare(&PE_RES_MAGIC, &magic, sizeof(magic)) == 0; + return is_res; +} + +internal PE_ResourceNode * +pe_resource_dir_push_dir_node(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, U32 characteristics, COFF_TimeStamp time_stamp, U16 major_version, U16 minor_version) +{ + PE_ResourceList *list = 0; + switch (id.type) { + default: + case COFF_ResourceIDType_NULL: break; + case COFF_ResourceIDType_STRING: list = &dir->named_list; break; + case COFF_ResourceIDType_NUMBER: list = &dir->id_list; break; + } + + PE_ResourceNode *res_node = push_array(arena, PE_ResourceNode, 1); + SLLQueuePush(list->first, list->last, res_node); + list->count += 1; + + PE_ResourceDir *sub_dir = push_array(arena, PE_ResourceDir, 1); + sub_dir->characteristics = characteristics; + sub_dir->time_stamp = time_stamp; + sub_dir->major_version = major_version; + sub_dir->minor_version = minor_version; + + PE_Resource *res = &res_node->data; + res->id = id; + res->kind = PE_ResDataKind_DIR; + res->u.dir = sub_dir; + + return res_node; +} + +internal PE_ResourceNode * +pe_resource_dir_push_entry_node(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, COFF_ResourceID type, U32 data_version, U32 version, COFF_ResourceMemoryFlags memory_flags, String8 data) +{ + PE_ResourceList *list = NULL; + switch (id.type) { + default: + case COFF_ResourceIDType_NULL: break; + case COFF_ResourceIDType_STRING: list = &dir->named_list; break; + case COFF_ResourceIDType_NUMBER: list = &dir->id_list; break; + } + + PE_ResourceNode *res_node = push_array(arena, PE_ResourceNode, 1); + SLLQueuePush(list->first, list->last, res_node); + list->count += 1; + + PE_Resource *res = &res_node->data; + res->id = id; + res->kind = PE_ResDataKind_COFF_RESOURCE; + res->u.coff_res.type = type; + res->u.coff_res.data_version = data_version; + res->u.coff_res.version = version; + res->u.coff_res.memory_flags = memory_flags; + res->u.coff_res.data = data; + + return res_node; +} + +internal PE_Resource * +pe_resource_dir_push_entry(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, COFF_ResourceID type, U32 data_version, U32 version, COFF_ResourceMemoryFlags memory_flags, String8 data) +{ + PE_ResourceNode *node = pe_resource_dir_push_entry_node(arena, dir, id, type, data_version, version, memory_flags, data); + return &node->data; +} + +internal PE_Resource * +pe_resource_dir_push_dir(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, U32 characteristics, COFF_TimeStamp time_stamp, U16 major_version, U16 minor_version) +{ + PE_ResourceNode *dir_node = pe_resource_dir_push_dir_node(arena, dir, id, characteristics, time_stamp, major_version, minor_version); + return &dir_node->data; +} + +internal PE_ResourceNode * +pe_resource_dir_search_node(PE_ResourceDir *dir, COFF_ResourceID id) +{ + for (PE_ResourceNode *i = dir->id_list.first; i != NULL; i = i->next) { + if (coff_resource_id_is_equal(i->data.id, id)) { + return i; + } + } + return NULL; +} + +internal PE_Resource * +pe_resource_dir_search(PE_ResourceDir *dir, COFF_ResourceID id) +{ + PE_ResourceNode *node = pe_resource_dir_search_node(dir, id); + return node ? &node->data : NULL; +} + +internal PE_ResourceArray +pe_resource_list_to_array(Arena *arena, PE_ResourceList *list) +{ + PE_ResourceArray result; + result.count = 0; + result.v = push_array(arena, PE_Resource, list->count); + for (PE_ResourceNode *n = list->first; n != NULL; n = n->next) { + result.v[result.count++] = n->data; + } + return result; +} + +internal void +pe_resource_dir_push_res_file(Arena *arena, PE_ResourceDir *root_dir, String8 res_file) +{ + // parse file into resource list + String8 res_data = str8_substr(res_file, rng_1u64(sizeof(PE_RES_MAGIC), res_file.size)); + COFF_ResourceList list = coff_resource_list_from_data(arena, res_data); + + // move resources to directories based on type + for (COFF_ResourceNode *res_node = list.first; res_node != NULL; res_node = res_node->next) { + COFF_Resource *res = &res_node->data; + + // search existing directories + PE_Resource *dir_res = pe_resource_dir_search(root_dir, res->type); + + // create new directory + if (dir_res == NULL) { + dir_res = pe_resource_dir_push_dir(arena, root_dir, res->type, 0, 0, 0, 0); + } + PE_ResourceDir *dir = dir_res->u.dir; + + // check for name collisions + PE_Resource *check_res = pe_resource_dir_search(dir, res->name); + if (check_res != NULL) { + // TODO: how do we handle name conflicts? + Assert(!"name collision"); + continue; + } + + // push entry + PE_Resource *sub_dir_res = pe_resource_dir_push_dir(arena, dir, res->name, 0, 0, 0, 0); + COFF_ResourceID id; + id.type = COFF_ResourceIDType_NUMBER; + id.u.number = res->language_id; + pe_resource_dir_push_entry(arena, sub_dir_res->u.dir, id, res->type, res->data_version, res->version, res->memory_flags, res->data); + } +} + +internal PE_ResourceDir * +pe_resource_table_from_directory_data(Arena *arena, String8 data) +{ + struct stack_s { + struct stack_s *next; + U64 table_offset; + U64 name_base_offset; + U64 id_base_offset; + PE_ResourceDir *table; + PE_ResourceDir **directory_ptr; + U64 name_ientry; + U64 id_ientry; + U64 name_entry_count; + U64 id_entry_count; + }; + + Temp scratch = scratch_begin(&arena,1); + struct stack_s *bottom_frame = push_array(scratch.arena, struct stack_s, 1); + struct stack_s *stack = bottom_frame; + + while (stack) { + if (stack->table == NULL) { + COFF_ResourceDirTable coff_table = {0}; + str8_deserial_read_struct(data, stack->table_offset, &coff_table); + + PE_ResourceDir *table = push_array(arena, PE_ResourceDir, 1); + table->characteristics = coff_table.characteristics; + table->time_stamp = coff_table.time_stamp; + table->major_version = coff_table.major_version; + table->minor_version = coff_table.minor_version; + + stack->table = table; + stack->name_base_offset = stack->table_offset + sizeof(COFF_ResourceDirTable); + stack->id_base_offset = stack->table_offset + sizeof(COFF_ResourceDirTable) + sizeof(COFF_ResourceDirEntry) * coff_table.name_entry_count; + stack->name_entry_count = coff_table.name_entry_count; + stack->id_entry_count = coff_table.id_entry_count; + + if (stack->directory_ptr) { + *stack->directory_ptr = table; + } + } + + while (stack->name_ientry < stack->name_entry_count) { + U64 entry_offset = stack->name_base_offset + stack->name_ientry * sizeof(COFF_ResourceDirEntry); + ++stack->name_ientry; + + PE_ResourceNode *named_node = push_array(arena, PE_ResourceNode, 1); + SLLQueuePush(stack->table->named_list.first, stack->table->named_list.last, named_node); + ++stack->table->named_list.count; + PE_Resource *entry = &named_node->data; + + COFF_ResourceDirEntry coff_entry = {0}; + str8_deserial_read_struct(data, entry_offset, &coff_entry); + + // NOTE: this is not documented on MSDN but high bit here is set for some reason + U32 name_offset = coff_entry.name.offset & ~COFF_RESOURCE_SUB_DIR_FLAG; + U16 name_size = 0; + str8_deserial_read_struct(data, name_offset, &name_size); + + String8 name_block; + str8_deserial_read_block(data, name_offset + sizeof(name_size), name_size*sizeof(U16), &name_block); + String16 name16 = str16((U16*)name_block.str, name_size); + + B32 is_dir = !!(coff_entry.id.data_entry_offset & COFF_RESOURCE_SUB_DIR_FLAG); + + entry->id.type = COFF_ResourceIDType_STRING; + entry->id.u.string = str8_from_16(arena, name16); + entry->kind = is_dir ? PE_ResDataKind_DIR : PE_ResDataKind_COFF_LEAF; + + if (is_dir) { + struct stack_s *frame = push_array(scratch.arena, struct stack_s, 1); + frame->table_offset = coff_entry.id.sub_dir_offset & ~COFF_RESOURCE_SUB_DIR_FLAG; + frame->directory_ptr = &entry->u.dir; + SLLStackPush(stack, frame); + goto yeild; + } else { + str8_deserial_read_struct(data, coff_entry.id.data_entry_offset, &entry->u.leaf); + } + } + + while (stack->id_ientry < stack->id_entry_count) { + U64 entry_offset = stack->id_base_offset + stack->id_ientry * sizeof(COFF_ResourceDirEntry); + ++stack->id_ientry; + + PE_ResourceNode *id_node = push_array(arena, PE_ResourceNode, 1); + SLLQueuePush(stack->table->id_list.first, stack->table->id_list.last, id_node); + ++stack->table->id_list.count; + PE_Resource *entry = &id_node->data; + + COFF_ResourceDirEntry coff_entry = {0}; + str8_deserial_read_struct(data, entry_offset, &coff_entry); + + B32 is_dir = !!(coff_entry.id.sub_dir_offset & COFF_RESOURCE_SUB_DIR_FLAG); + + entry->id.type = COFF_ResourceIDType_NUMBER; + entry->id.u.number = coff_entry.name.id; + entry->kind = is_dir ? PE_ResDataKind_DIR : PE_ResDataKind_COFF_LEAF; + + if (is_dir) { + struct stack_s *frame = push_array(scratch.arena, struct stack_s, 1); + frame->table_offset = coff_entry.id.sub_dir_offset & ~COFF_RESOURCE_SUB_DIR_FLAG; + frame->directory_ptr = &entry->u.dir; + SLLStackPush(stack, frame); + goto yeild; + } else { + str8_deserial_read_struct(data, coff_entry.id.sub_dir_offset, &entry->u.leaf); + } + } + + SLLStackPop(stack); + + yeild:; + } + + scratch_end(scratch); + return bottom_frame->table; +} diff --git a/src/pe/pe.h b/src/pe/pe.h index ebb89f4b..e5a863ee 100644 --- a/src/pe/pe.h +++ b/src/pe/pe.h @@ -1,773 +1,773 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef PE_H -#define PE_H - -//////////////////////////////// -//~ rjf: PE Format-Defined Types/Constants - -#pragma pack(push,1) - -typedef struct PE_DosHeader PE_DosHeader; -struct PE_DosHeader -{ - U16 magic; - U16 last_page_size; - U16 page_count; - U16 reloc_count; - U16 paragraph_header_size; - U16 min_paragraph; - U16 max_paragraph; - U16 init_ss; - U16 init_sp; - U16 checksum; - U16 init_ip; - U16 init_cs; - U16 reloc_table_file_off; - U16 overlay_number; - U16 reserved[4]; - U16 oem_id; - U16 oem_info; - U16 reserved2[10]; - U32 coff_file_offset; -}; - -typedef U16 PE_WindowsSubsystem; -enum -{ - PE_WindowsSubsystem_UNKNOWN = 0, - PE_WindowsSubsystem_NATIVE = 1, - PE_WindowsSubsystem_WINDOWS_GUI = 2, - PE_WindowsSubsystem_WINDOWS_CUI = 3, - PE_WindowsSubsystem_OS2_CUI = 5, - PE_WindowsSubsystem_POSIX_CUI = 7, - PE_WindowsSubsystem_NATIVE_WINDOWS = 8, - PE_WindowsSubsystem_WINDOWS_CE_GUI = 9, - PE_WindowsSubsystem_EFI_APPLICATION = 10, - PE_WindowsSubsystem_EFI_BOOT_SERVICE_DRIVER = 11, - PE_WindowsSubsystem_EFI_RUNTIME_DRIVER = 12, - PE_WindowsSubsystem_EFI_ROM = 13, - PE_WindowsSubsystem_XBOX = 14, - PE_WindowsSubsystem_WINDOWS_BOOT_APPLICATION = 16, - PE_WindowsSubsystem_COUNT = 14 -}; - -typedef U16 PE_ImageFileCharacteristics; -enum -{ - PE_ImageFileCharacteristic_STRIPPED = (1 << 0), - PE_ImageFileCharacteristic_EXE = (1 << 1), - PE_ImageFileCharacteristic_NUMS_STRIPPED = (1 << 2), - PE_ImageFileCharacteristic_PE_STRIPPED = (1 << 3), - PE_ImageFileCharacteristic_AGGRESIVE_WS_TRIM = (1 << 4), - PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE = (1 << 5), - PE_ImageFileCharacteristic_UNUSED1 = (1 << 6), - PE_ImageFileCharacteristic_BYTES_RESERVED_LO = (1 << 7), - PE_ImageFileCharacteristic_32BIT_MACHINE = (1 << 8), - PE_ImageFileCharacteristic_DEBUG_STRIPPED = (1 << 9), - PE_ImageFileCharacteristic_FILE_REMOVABLE_RUN_FROM_SWAP = (1 << 10), - PE_ImageFileCharacteristic_NET_RUN_FROM_SWAP = (1 << 11), - PE_ImageFileCharacteristic_FILE_SYSTEM = (1 << 12), - PE_ImageFileCharacteristic_FILE_DLL = (1 << 13), - PE_ImageFileCharacteristic_FILE_UP_SYSTEM_ONLY = (1 << 14), - PE_ImageFileCharacteristic_BYTES_RESERVED_HI = (1 << 15), -}; - -typedef U16 PE_DllCharacteristics; -enum -{ - PE_DllCharacteristic_HIGH_ENTROPY_VA = (1 << 5), - PE_DllCharacteristic_DYNAMIC_BASE = (1 << 6), - PE_DllCharacteristic_FORCE_INTEGRITY = (1 << 7), - PE_DllCharacteristic_NX_COMPAT = (1 << 8), - PE_DllCharacteristic_NO_ISOLATION = (1 << 9), - PE_DllCharacteristic_NO_SEH = (1 << 10), - PE_DllCharacteristic_NO_BIND = (1 << 11), - PE_DllCharacteristic_APPCONTAINER = (1 << 12), - PE_DllCharacteristic_WDM_DRIVER = (1 << 13), - PE_DllCharacteristic_GUARD_CF = (1 << 14), - PE_DllCharacteristic_TERMINAL_SERVER_AWARE = (1 << 15), -}; - -typedef struct PE_OptionalHeader32 PE_OptionalHeader32; -struct PE_OptionalHeader32 -{ - U16 magic; - U8 major_linker_version; - U8 minor_linker_version; - U32 sizeof_code; - U32 sizeof_inited_data; - U32 sizeof_uninited_data; - U32 entry_point_va; - U32 code_base; - U32 data_base; - U32 image_base; - U32 section_alignment; - U32 file_alignment; - U16 major_os_ver; - U16 minor_os_ver; - U16 major_img_ver; - U16 minor_img_ver; - U16 major_subsystem_ver; - U16 minor_subsystem_ver; - U32 win32_version_value; - U32 sizeof_image; - U32 sizeof_headers; - U32 check_sum; - PE_WindowsSubsystem subsystem; - PE_DllCharacteristics dll_characteristics; - U32 sizeof_stack_reserve; - U32 sizeof_stack_commit; - U32 sizeof_heap_reserve; - U32 sizeof_heap_commit; - U32 loader_flags; - U32 data_dir_count; -}; - -typedef struct PE_OptionalHeader32Plus PE_OptionalHeader32Plus; -struct PE_OptionalHeader32Plus -{ - U16 magic; - U8 major_linker_version; - U8 minor_linker_version; - U32 sizeof_code; - U32 sizeof_inited_data; - U32 sizeof_uninited_data; - U32 entry_point_va; - U32 code_base; - U64 image_base; - U32 section_alignment; - U32 file_alignment; - U16 major_os_ver; - U16 minor_os_ver; - U16 major_img_ver; - U16 minor_img_ver; - U16 major_subsystem_ver; - U16 minor_subsystem_ver; - U32 win32_version_value; - U32 sizeof_image; - U32 sizeof_headers; - U32 check_sum; - PE_WindowsSubsystem subsystem; - PE_DllCharacteristics dll_characteristics; - U64 sizeof_stack_reserve; - U64 sizeof_stack_commit; - U64 sizeof_heap_reserve; - U64 sizeof_heap_commit; - U32 loader_flags; - U32 data_dir_count; -}; - -typedef enum PE_DataDirectoryIndex -{ - PE_DataDirectoryIndex_EXPORT, - PE_DataDirectoryIndex_IMPORT, - PE_DataDirectoryIndex_RESOURCES, - PE_DataDirectoryIndex_EXCEPTIONS, - PE_DataDirectoryIndex_CERT, - PE_DataDirectoryIndex_BASE_RELOC, - PE_DataDirectoryIndex_DEBUG, - PE_DataDirectoryIndex_ARCH, - PE_DataDirectoryIndex_GLOBAL_PTR, - PE_DataDirectoryIndex_TLS, - PE_DataDirectoryIndex_LOAD_CONFIG, - PE_DataDirectoryIndex_BOUND_IMPORT, - PE_DataDirectoryIndex_IMPORT_ADDR, - PE_DataDirectoryIndex_DELAY_IMPORT, - PE_DataDirectoryIndex_COM_DESCRIPTOR, - PE_DataDirectoryIndex_RESERVED, - PE_DataDirectoryIndex_COUNT = 16 -} -PE_DataDirectoryIndex; - -typedef struct PE_DataDirectory PE_DataDirectory; -struct PE_DataDirectory -{ - U32 virt_off; - U32 virt_size; -}; - -typedef U32 PE_DebugDirectoryType; -enum -{ - PE_DebugDirectoryType_UNKNOWN = 0, - PE_DebugDirectoryType_COFF = 1, - PE_DebugDirectoryType_CODEVIEW = 2, - PE_DebugDirectoryType_FPO = 3, - PE_DebugDirectoryType_MISC = 4, - PE_DebugDirectoryType_EXCEPTION = 5, - PE_DebugDirectoryType_FIXUP = 6, - PE_DebugDirectoryType_OMAP_TO_SRC = 7, - PE_DebugDirectoryType_OMAP_FROM_SRC = 8, - PE_DebugDirectoryType_BORLAND = 9, - PE_DebugDirectoryType_RESERVED10 = 10, - PE_DebugDirectoryType_CLSID = 11, - PE_DebugDirectoryType_VC_FEATURE = 12, - PE_DebugDirectoryType_POGO = 13, - PE_DebugDirectoryType_ILTCG = 14, - PE_DebugDirectoryType_MPX = 15, - PE_DebugDirectoryType_REPRO = 16, - PE_DebugDirectoryType_EX_DLLCHARACTERISTICS = 20, - PE_DebugDirectoryType_COUNT = 18 -}; - -typedef U8 PE_FPOFlags; -enum -{ - PE_FPOFlags_HAS_SEH = 0x800, - PE_FPOFlags_USE_BP_REG = 0x1000, - PE_FPOFlags_RESERVED = 0x2000, - PE_FPOFlags_COUNT = 3 -}; - -typedef U16 PE_FPOEncoded; -enum -{ - PE_FPOEncoded_PROLOG_SIZE_SHIFT = 0, PE_FPOEncoded_PROLOG_SIZE_MASK = 0xff, - PE_FPOEncoded_SAVED_REGS_SIZE_SHIFT = 8, PE_FPOEncoded_SAVED_REGS_SIZE_MASK = 0x7, - PE_FPOEncoded_FLAGS_SHIFT = 11, PE_FPOEncoded_FLAGS_MASK = 0x7, - PE_FPOEncoded_FRAME_TYPE_SHIFT = 14, PE_FPOEncoded_FRAME_TYPE_MASK = 0x3, -}; -#define PE_FPOEncoded_Extract_PROLOG_SIZE(f) (U8)(((f) >> PE_FPOEncoded_PROLOG_SIZE_SHIFT) & PE_FPOEncoded_PROLOG_SIZE_MASK) -#define PE_FPOEncoded_Extract_SAVED_REGS_SIZE(f) (U8)(((f) >> PE_FPOEncoded_SAVED_REGS_SIZE_SHIFT) & PE_FPOEncoded_SAVED_REGS_SIZE_MASK) -#define PE_FPOEncoded_Extract_FLAGS(f) (U8)(((f) >> PE_FPOEncoded_FLAGS_SHIFT) & PE_FPOEncoded_FLAGS_MASK) -#define PE_FPOEncoded_Extract_FRAME_TYPE(f) (U8)(((f) >> PE_FPOEncoded_FRAME_TYPE_SHIFT) & PE_FPOEncoded_FRAME_TYPE_MASK) - -typedef U8 PE_FPOType; -enum -{ - PE_FPOType_FPO = 0, - PE_FPOType_TRAP = 1, - PE_FPOType_TSS = 2, - PE_FPOType_NOFPO = 3, - PE_FPOType_COUNT = 4 -}; - -typedef U32 PE_DebugMiscType; -enum -{ - PE_DebugMiscType_NULL, - PE_DebugMiscType_EXE_NAME, - PE_DebugMiscType_COUNT = 2 -}; - -typedef struct PE_DebugDirectory PE_DebugDirectory; -struct PE_DebugDirectory -{ - U32 characteristics; - COFF_TimeStamp time_stamp; - U16 major_ver; - U16 minor_ver; - PE_DebugDirectoryType type; - U32 size; - U32 voff; - U32 foff; -}; - -typedef U32 PE_GlobalFlags; -enum -{ - PE_GlobalFlags_STOP_ON_EXCEPTION = (1 << 0), - PE_GlobalFlags_SHOW_LDR_SNAPS = (1 << 1), - PE_GlobalFlags_DEBUG_INITIAL_COMMAND = (1 << 2), - PE_GlobalFlags_STOP_ON_HUNG_GUI = (1 << 3), - PE_GlobalFlags_HEAP_ENABLE_TAIL_CHECK = (1 << 4), - PE_GlobalFlags_HEAP_ENABLE_FREE_CHECK = (1 << 5), - PE_GlobalFlags_HEAP_VALIDATE_PARAMETERS = (1 << 6), - PE_GlobalFlags_HEAP_VALIDATE_ALL = (1 << 7), - PE_GlobalFlags_APPLICATION_VERIFIER = (1 << 8), - PE_GlobalFlags_POOL_ENABLE_TAGGING = (1 << 10), - PE_GlobalFlags_HEAP_ENABLE_TAGGING = (1 << 11), - PE_GlobalFlags_STACK_TRACE_DB = (1 << 12), - PE_GlobalFlags_KERNEL_STACK_TRACE_DB = (1 << 13), - PE_GlobalFlags_MAINTAIN_OBJECT_TYPELIST = (1 << 14), - PE_GlobalFlags_HEAP_ENABLE_TAG_BY_DLL = (1 << 15), - PE_GlobalFlags_DISABLE_STACK_EXTENSION = (1 << 16), - PE_GlobalFlags_ENABLE_CSRDEBUG = (1 << 17), - PE_GlobalFlags_ENABLE_KDEBUG_SYMBOL_LOAD = (1 << 18), - PE_GlobalFlags_DISABLE_PAGE_KERNEL_STACKS = (1 << 19), - PE_GlobalFlags_ENABLE_SYSTEM_CRIT_BREAKS = (1 << 20), - PE_GlobalFlags_HEAP_DISABLE_COALESCING = (1 << 21), - PE_GlobalFlags_ENABLE_CLOSE_EXCEPTIONS = (1 << 22), - PE_GlobalFlags_ENABLE_EXCEPTION_LOGGING = (1 << 23), - PE_GlobalFlags_ENABLE_HANDLE_TYPE_TAGGING = (1 << 24), - PE_GlobalFlags_HEAP_PAGE_ALLOCS = (1 << 25), - PE_GlobalFlags_DEBUG_INITIAL_COMMAND_EX = (1 << 26), - PE_GlobalFlags_DISABLE_DBGPRINT = (1 << 27), - PE_GlobalFlags_CRITSEC_EVENT_CREATION = (1 << 28), - PE_GlobalFlags_LDR_TOP_DOWN = (1 << 29), - PE_GlobalFlags_ENABLE_HANDLE_EXCEPTIONS = (1 << 30), - PE_GlobalFlags_DISABLE_PROTDLLS = (1 << 31), -}; - -typedef U32 PE_LoadConfigGuardFlags; -enum -{ - PE_LoadConfigGuardFlags_CF_INSTRUMENTED = (1 << 8), - PE_LoadConfigGuardFlags_CFW_INSTRUMENTED = (1 << 9), - PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_PRESENT = (1 << 10), - PE_LoadConfigGuardFlags_SECURITY_COOKIE_UNUSED = (1 << 11), - PE_LoadConfigGuardFlags_PROTECT_DELAYLOAD_IAT = (1 << 12), - PE_LoadConfigGuardFlags_DELAYLOAD_IAT_IN_ITS_OWN_SECTION = (1 << 13), - PE_LoadConfigGuardFlags_CF_EXPORT_SUPPRESSION_INFO_PRESENT = (1 << 14), - PE_LoadConfigGuardFlags_CF_ENABLE_EXPORT_SUPPRESSION = (1 << 15), - PE_LoadConfigGuardFlags_CF_LONGJUMP_TABLE_PRESENT = (1 << 16), - PE_LoadConfigGuardFlags_EH_CONTINUATION_TABLE_PRESENT = (1 << 22), - PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_SHIFT = 28, PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_MASK = 0xf, -}; -#define PE_LoadConfigGuardFlags_Extract_CF_FUNCTION_TABLE_SIZE(f) (U32)(((f) >> PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_SHIFT) & PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_MASK) - -// this is the "MZ" as a 16-bit short -#define PE_DOS_MAGIC 0x5a4d -#define PE_MAGIC 0x00004550u -#define PE_PE32_MAGIC 0x010bu -#define PE_PE32PLUS_MAGIC 0x020bu - -typedef struct PE_MipsPdata PE_MipsPdata; -struct PE_MipsPdata -{ - U32 voff_first; - U32 voff_one_past_last; - U32 voff_exception_handler; - U32 voff_exception_handler_data; - U32 voff_one_past_prolog; -}; - -typedef struct PE_ArmPdata PE_ArmPdata; -struct PE_ArmPdata -{ - U32 voff_first; - // NOTE(allen): - // bits | meaning - // [0:7] | prolog length - // [8:29] | function length - // [30:30] | instructions_are_32bits (otherwise they are 16 bits) - // [31:31] | has_exception_handler - U32 combined; -}; - -typedef struct PE_IntelPdata PE_IntelPdata; -struct PE_IntelPdata -{ - U32 voff_first; - U32 voff_one_past_last; - U32 voff_unwind_info; -}; - -#define PE_CODEVIEW_PDB20_MAGIC 0x3031424e -#define PE_CODEVIEW_PDB70_MAGIC 0x53445352 - -typedef struct PE_CvHeaderPDB20 PE_CvHeaderPDB20; -struct PE_CvHeaderPDB20 -{ - U32 magic; - U32 offset; - U32 time; - U32 age; - // file name packed after struct -}; - -typedef struct PE_CvHeaderPDB70 PE_CvHeaderPDB70; -struct PE_CvHeaderPDB70 -{ - U32 magic; - OS_Guid guid; - U32 age; - // file name packed after struct -}; - -typedef struct PE_ImportEntry PE_ImportEntry; -struct PE_ImportEntry -{ - U32 lookup_table_voff; - COFF_TimeStamp time_stamp; - U32 forwarder_chain; - U32 name_voff; - U32 import_addr_table_voff; -}; - -typedef struct PE_DelayedImportEntry PE_DelayedImportEntry; -struct PE_DelayedImportEntry -{ - // According to PE/COFF spec this field is unused and should be set zero, - // but when I compile mule with MSVC 2019 this is set to 1. - U32 attributes; - U32 name_voff; // Name of the DLL - U32 module_handle_voff; // Place where module handle from LoadLibrary is stored - U32 iat_voff; - U32 name_table_voff; // Array of hint/name or oridnals - U32 bound_table_voff; // (Optional) Points to an array of PE_ThunkData - U32 unload_table_voff; // (Optional) Copy of iat_voff - // 0 not bound - // -1 if bound and real timestamp located in bounded import directory - // Otherwise time when dll was bound - COFF_TimeStamp time_stamp; -}; - -typedef struct PE_ExportTable PE_ExportTable; -struct PE_ExportTable -{ - U32 flags; // must be zero - COFF_TimeStamp time_stamp; // time and date when export table was created - U16 major_ver; // table version, user can change major and minor version - U16 minor_ver; - U32 name_voff; // ASCII name of the dll - U32 ordinal_base; // Starting oridnal number - U32 export_address_table_count; - U32 name_pointer_table_count; - U32 export_address_table_voff; - U32 name_pointer_table_voff; - U32 ordinal_table_voff; -}; - -typedef struct PE_TLSHeader32 PE_TLSHeader32; -struct PE_TLSHeader32 -{ - U32 raw_data_start; // Range of initialized data that is copied for each thread from the image. - U32 raw_data_end; // (Typically points to .tls section) - U32 index_address; // Address where image loader places TLS slot index. - U32 callbacks_address; // Zero terminated list of callbacks used for initializing data with constructors. - U32 zero_fill_size; // Amount of memory to fill with zeroes in TLS. - U32 characteristics; // COFF_SectionFlags but only align flags are used. -}; - -typedef struct PE_TLSHeader64 PE_TLSHeader64; -struct PE_TLSHeader64 -{ - U64 raw_data_start; // Range of initialized data that is copied for each thread from the image. - U64 raw_data_end; // (Typically points to .tls section) - U64 index_address; // Address where image loader places TLS slot index. - U64 callbacks_address; // Zero terminated list of callbacks used for initializing data with constructors. - U32 zero_fill_size; // Amount of memory to fill with zeroes in TLS. - U32 characteristics; // COFF_SectionFlags but only align flags are used. -}; - -#define PE_RES_ALIGN 4u - -global read_only U8 PE_RES_MAGIC[] = -{ - 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x00, 0x00, - 0xFF, 0xFF, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; - -typedef U32 PE_ResourceKind; -enum -{ - PE_ResourceKind_CURSOR = 0x1, - PE_ResourceKind_BITMAP = 0x2, - PE_ResourceKind_ICON = 0x3, - PE_ResourceKind_MENU = 0x4, - PE_ResourceKind_DIALOG = 0x5, - PE_ResourceKind_STRING = 0x6, - PE_ResourceKind_FONTDIR = 0x7, - PE_ResourceKind_FONT = 0x8, - PE_ResourceKind_ACCELERATOR = 0x9, - PE_ResourceKind_RCDATA = 0xA, - PE_ResourceKind_MESSAGETABLE = 0xB, - PE_ResourceKind_GROUP_CURSOR = 0xC, - PE_ResourceKind_GROUP_ICON = 0xE, - PE_ResourceKind_VERSION = 0x10, - PE_ResourceKind_DLGINCLUDE = 0x11, - PE_ResourceKind_PLUGPLAY = 0x13, - PE_ResourceKind_VXD = 0x14, - PE_ResourceKind_ANICURSOR = 0x15, - PE_ResourceKind_ANIICON = 0x16, - PE_ResourceKind_HTML = 0x17, - PE_ResourceKind_MANIFEST = 0x18, - PE_ResourceKind_BITMAP_NEW = 0x2002, - PE_ResourceKind_MENU_NEW = 0x2004, - PE_ResourceKind_DIALOG_NEW = 0x2005, -}; - -typedef enum PE_ResDataKind -{ - PE_ResDataKind_NULL, - PE_ResDataKind_DIR, - PE_ResDataKind_COFF_LEAF, - PE_ResDataKind_COFF_RESOURCE, -} -PE_ResDataKind; - -typedef struct PE_ResourceHeader PE_ResourceHeader; -struct PE_ResourceHeader -{ - COFF_ResourceHeaderPrefix prefix; - U16 type; - U16 pad0; - U16 name; - U16 pad1; - U32 data_version; - COFF_ResourceMemoryFlags memory_flags; - U16 language_id; - U32 version; - U32 characteristics; -}; - -typedef U16 PE_BaseRelocKind; -enum -{ - PE_BaseRelocKind_ABSOLUTE = 0, // No reallocation is applied. Can be used as padding. - PE_BaseRelocKind_HIGH = 1, - PE_BaseRelocKind_LOW = 2, - PE_BaseRelocKind_HIGHLOW = 3, - PE_BaseRelocKind_HIGHADJ = 4, - PE_BaseRelocKind_MIPS_JMPADDR = 5, - PE_BaseRelocKind_ARM_MOV32 = 5, - PE_BaseRelocKind_RISCV_HIGH20 = 5, - // 6 is reserved - PE_BaseRelocKind_THUMB_MOV32 = 7, - PE_BaseRelocKind_RISCV_LOW12I = 7, - PE_BaseRelocKind_RISCV_LOW12S = 8, - PE_BaseRelocKind_LOONGARCH32_MARK_LA = 8, - PE_BaseRelocKind_LOONGARCH64_MARK_LA = 8, - PE_BaseRelocKind_MIPS_JMPADDR16 = 9, - PE_BaseRelocKind_DIR64 = 10, -}; -#define PE_BaseRelocOffsetFromEntry(x) ((x) & 0x1fff) -#define PE_BaseRelocKindFromEntry(x) (((x) >> 12) & 0xf) -#define PE_BaseRelocMake(k, off) ((((U16)(k) & 0xf) << 12) | (U16)((off) & 0x1fff)) - -typedef U32 PE_UnwindOpCode; -enum -{ - PE_UnwindOpCode_PUSH_NONVOL = 0, - PE_UnwindOpCode_ALLOC_LARGE = 1, - PE_UnwindOpCode_ALLOC_SMALL = 2, - PE_UnwindOpCode_SET_FPREG = 3, - PE_UnwindOpCode_SAVE_NONVOL = 4, - PE_UnwindOpCode_SAVE_NONVOL_FAR = 5, - PE_UnwindOpCode_EPILOG = 6, - PE_UnwindOpCode_SPARE_CODE = 7, - PE_UnwindOpCode_SAVE_XMM128 = 8, - PE_UnwindOpCode_SAVE_XMM128_FAR = 9, - PE_UnwindOpCode_PUSH_MACHFRAME = 10, -}; - -typedef U8 PE_UnwindGprRegX64; -enum -{ - PE_UnwindGprRegX64_RAX = 0, - PE_UnwindGprRegX64_RCX = 1, - PE_UnwindGprRegX64_RDX = 2, - PE_UnwindGprRegX64_RBX = 3, - PE_UnwindGprRegX64_RSP = 4, - PE_UnwindGprRegX64_RBP = 5, - PE_UnwindGprRegX64_RSI = 6, - PE_UnwindGprRegX64_RDI = 7, - PE_UnwindGprRegX64_R8 = 8, - PE_UnwindGprRegX64_R9 = 9, - PE_UnwindGprRegX64_R10 = 10, - PE_UnwindGprRegX64_R11 = 11, - PE_UnwindGprRegX64_R12 = 12, - PE_UnwindGprRegX64_R13 = 13, - PE_UnwindGprRegX64_R14 = 14, - PE_UnwindGprRegX64_R15 = 15, -}; - -typedef U8 PE_UnwindInfoFlags; -enum -{ - PE_UnwindInfoFlag_EHANDLER = (1<<0), - PE_UnwindInfoFlag_UHANDLER = (1<<1), - PE_UnwindInfoFlag_FHANDLER = 3, - PE_UnwindInfoFlag_CHAINED = (1<<2), -}; - -#define PE_UNWIND_OPCODE_FROM_FLAGS(f) ((f)&0xF) -#define PE_UNWIND_INFO_FROM_FLAGS(f) (((f) >> 4)&0xF) - -typedef union PE_UnwindCode PE_UnwindCode; -union PE_UnwindCode -{ - struct - { - U8 off_in_prolog; - U8 flags; - }; - U16 u16; -}; - -#define PE_UNWIND_INFO_VERSION_FROM_HDR(x) ((x)&0x7) -#define PE_UNWIND_INFO_FLAGS_FROM_HDR(x) (((x) >> 3)&0x1F) -#define PE_UNWIND_INFO_REG_FROM_FRAME(x) ((x)&0xF) -#define PE_UNWIND_INFO_OFF_FROM_FRAME(x) (((x) >> 4)&0xF) - -typedef struct PE_UnwindInfo PE_UnwindInfo; -struct PE_UnwindInfo -{ - U8 header; - U8 prolog_size; - U8 codes_num; - U8 frame; -}; - -#pragma pack(pop) - -//////////////////////////////// -//~ rjf: DOS Program - -// generated from pe/dos_program.asm -read_only global U8 pe_dos_program_data[] = -{ - 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, - 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, - 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, - 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x24, 0x00, 0x00 -}; -read_only global String8 pe_dos_program = {pe_dos_program_data, sizeof(pe_dos_program_data)}; - -//////////////////////////////// -//~ rjf: Parsed Info Types - -//- rjf: relocation blocks - -typedef struct PE_BaseRelocBlock PE_BaseRelocBlock; -struct PE_BaseRelocBlock -{ - U64 page_virt_off; - U64 entry_count; - U16 *entries; -}; - -typedef struct PE_BaseRelocBlockNode PE_BaseRelocBlockNode; -struct PE_BaseRelocBlockNode -{ - PE_BaseRelocBlockNode *next; - PE_BaseRelocBlock v; -}; - -typedef struct PE_BaseRelocBlockList PE_BaseRelocBlockList; -struct PE_BaseRelocBlockList -{ - PE_BaseRelocBlockNode *first; - PE_BaseRelocBlockNode *last; - U64 count; -}; - -//- rjf: resources - -typedef struct PE_Resource PE_Resource; -struct PE_Resource -{ - COFF_ResourceID id; - PE_ResDataKind kind; - union - { - COFF_ResourceDataEntry leaf; - struct PE_ResourceDir *dir; - struct - { - COFF_ResourceID type; - U32 data_version; - U32 version; - COFF_ResourceMemoryFlags memory_flags; - String8 data; - } - coff_res; - } - u; -}; - -typedef struct PE_ResourceNode PE_ResourceNode; -struct PE_ResourceNode -{ - PE_ResourceNode *next; - PE_Resource data; -}; - -typedef struct PE_ResourceList PE_ResourceList; -struct PE_ResourceList -{ - PE_ResourceNode *first; - PE_ResourceNode *last; - U64 count; -}; - -typedef struct PE_ResourceArray PE_ResourceArray; -struct PE_ResourceArray -{ - PE_Resource *v; - U64 count; -}; - -typedef struct PE_ResourceDir PE_ResourceDir; -struct PE_ResourceDir -{ - U32 characteristics; - COFF_TimeStamp time_stamp; - U16 major_version; - U16 minor_version; - PE_ResourceList named_list; - PE_ResourceList id_list; -}; - -//- rjf: bundle - -typedef struct PE_BinInfo PE_BinInfo; -struct PE_BinInfo -{ - U64 image_base; - U64 entry_point; - B32 is_pe32; - U64 virt_section_align; - U64 file_section_align; - U64 section_array_off; - U64 section_count; - U64 symbol_array_off; - U64 symbol_count; - U64 string_table_off; - U64 dbg_path_off; - U64 dbg_path_size; - OS_Guid dbg_guid; - U32 dbg_age; - U32 dbg_time; - Architecture arch; - Rng1U64 *data_dir_franges; - U32 data_dir_count; - PE_TLSHeader64 tls_header; -}; - -//////////////////////////////// -//~ rjf: Basic Enum Functions - -internal U32 pe_slot_count_from_unwind_op_code(PE_UnwindOpCode opcode); -internal String8 pe_string_from_windows_subsystem(PE_WindowsSubsystem subsystem); - -//////////////////////////////// -//~ rjf: Parser Functions - -internal PE_BinInfo pe_bin_info_from_data(Arena *arena, String8 data); - -//////////////////////////////// -//~ rjf: Helpers - -internal U64 pe_intel_pdata_off_from_voff__binary_search(String8 data, PE_BinInfo *bin, U64 voff); -internal void *pe_ptr_from_voff(String8 data, PE_BinInfo *bin, U64 voff); -internal U64 pe_section_num_from_voff(String8 data, PE_BinInfo *bin, U64 voff); -internal void *pe_ptr_from_section_num(String8 data, PE_BinInfo *bin, U64 n); -internal U64 pe_foff_from_voff(String8 data, PE_BinInfo *bin, U64 voff); -internal PE_BaseRelocBlockList pe_base_reloc_block_list_from_bin(Arena *arena, String8 data, PE_BinInfo *bin); -internal Rng1U64 pe_tls_rng_from_bin_base_vaddr(String8 data, PE_BinInfo *bin, U64 base_vaddr); - -//////////////////////////////// -//~ Resource Helpers - -internal B32 pe_is_res(String8 data); -internal void pe_resource_dir_push_res_file(Arena *arena, PE_ResourceDir *root_dir, String8 res_file); -internal PE_ResourceNode * pe_resource_dir_push_dir_node(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, U32 characteristics, COFF_TimeStamp time_stamp, U16 major_version, U16 minor_version); -internal PE_ResourceNode * pe_resource_dir_push_entry_node(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, COFF_ResourceID type, U32 data_version, U32 version, COFF_ResourceMemoryFlags memory_flags, String8 data); -internal PE_Resource * pe_resource_dir_push_entry(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, COFF_ResourceID type, U32 data_version, U32 version, COFF_ResourceMemoryFlags memory_flags, String8 data); -internal PE_Resource * pe_resource_dir_push_dir(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, U32 characteristics, COFF_TimeStamp time_stamp, U16 major_version, U16 minor_version); -internal PE_ResourceNode * pe_resource_dir_search_node(PE_ResourceDir *dir, COFF_ResourceID id); -internal PE_Resource * pe_resource_dir_search(PE_ResourceDir *dir, COFF_ResourceID id); -internal PE_ResourceArray pe_resource_list_to_array(Arena *arena, PE_ResourceList *list); -internal PE_ResourceDir * pe_resource_table_from_directory_data(Arena *arena, String8 data); - -#endif // PE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef PE_H +#define PE_H + +//////////////////////////////// +//~ rjf: PE Format-Defined Types/Constants + +#pragma pack(push,1) + +typedef struct PE_DosHeader PE_DosHeader; +struct PE_DosHeader +{ + U16 magic; + U16 last_page_size; + U16 page_count; + U16 reloc_count; + U16 paragraph_header_size; + U16 min_paragraph; + U16 max_paragraph; + U16 init_ss; + U16 init_sp; + U16 checksum; + U16 init_ip; + U16 init_cs; + U16 reloc_table_file_off; + U16 overlay_number; + U16 reserved[4]; + U16 oem_id; + U16 oem_info; + U16 reserved2[10]; + U32 coff_file_offset; +}; + +typedef U16 PE_WindowsSubsystem; +enum +{ + PE_WindowsSubsystem_UNKNOWN = 0, + PE_WindowsSubsystem_NATIVE = 1, + PE_WindowsSubsystem_WINDOWS_GUI = 2, + PE_WindowsSubsystem_WINDOWS_CUI = 3, + PE_WindowsSubsystem_OS2_CUI = 5, + PE_WindowsSubsystem_POSIX_CUI = 7, + PE_WindowsSubsystem_NATIVE_WINDOWS = 8, + PE_WindowsSubsystem_WINDOWS_CE_GUI = 9, + PE_WindowsSubsystem_EFI_APPLICATION = 10, + PE_WindowsSubsystem_EFI_BOOT_SERVICE_DRIVER = 11, + PE_WindowsSubsystem_EFI_RUNTIME_DRIVER = 12, + PE_WindowsSubsystem_EFI_ROM = 13, + PE_WindowsSubsystem_XBOX = 14, + PE_WindowsSubsystem_WINDOWS_BOOT_APPLICATION = 16, + PE_WindowsSubsystem_COUNT = 14 +}; + +typedef U16 PE_ImageFileCharacteristics; +enum +{ + PE_ImageFileCharacteristic_STRIPPED = (1 << 0), + PE_ImageFileCharacteristic_EXE = (1 << 1), + PE_ImageFileCharacteristic_NUMS_STRIPPED = (1 << 2), + PE_ImageFileCharacteristic_PE_STRIPPED = (1 << 3), + PE_ImageFileCharacteristic_AGGRESIVE_WS_TRIM = (1 << 4), + PE_ImageFileCharacteristic_LARGE_ADDRESS_AWARE = (1 << 5), + PE_ImageFileCharacteristic_UNUSED1 = (1 << 6), + PE_ImageFileCharacteristic_BYTES_RESERVED_LO = (1 << 7), + PE_ImageFileCharacteristic_32BIT_MACHINE = (1 << 8), + PE_ImageFileCharacteristic_DEBUG_STRIPPED = (1 << 9), + PE_ImageFileCharacteristic_FILE_REMOVABLE_RUN_FROM_SWAP = (1 << 10), + PE_ImageFileCharacteristic_NET_RUN_FROM_SWAP = (1 << 11), + PE_ImageFileCharacteristic_FILE_SYSTEM = (1 << 12), + PE_ImageFileCharacteristic_FILE_DLL = (1 << 13), + PE_ImageFileCharacteristic_FILE_UP_SYSTEM_ONLY = (1 << 14), + PE_ImageFileCharacteristic_BYTES_RESERVED_HI = (1 << 15), +}; + +typedef U16 PE_DllCharacteristics; +enum +{ + PE_DllCharacteristic_HIGH_ENTROPY_VA = (1 << 5), + PE_DllCharacteristic_DYNAMIC_BASE = (1 << 6), + PE_DllCharacteristic_FORCE_INTEGRITY = (1 << 7), + PE_DllCharacteristic_NX_COMPAT = (1 << 8), + PE_DllCharacteristic_NO_ISOLATION = (1 << 9), + PE_DllCharacteristic_NO_SEH = (1 << 10), + PE_DllCharacteristic_NO_BIND = (1 << 11), + PE_DllCharacteristic_APPCONTAINER = (1 << 12), + PE_DllCharacteristic_WDM_DRIVER = (1 << 13), + PE_DllCharacteristic_GUARD_CF = (1 << 14), + PE_DllCharacteristic_TERMINAL_SERVER_AWARE = (1 << 15), +}; + +typedef struct PE_OptionalHeader32 PE_OptionalHeader32; +struct PE_OptionalHeader32 +{ + U16 magic; + U8 major_linker_version; + U8 minor_linker_version; + U32 sizeof_code; + U32 sizeof_inited_data; + U32 sizeof_uninited_data; + U32 entry_point_va; + U32 code_base; + U32 data_base; + U32 image_base; + U32 section_alignment; + U32 file_alignment; + U16 major_os_ver; + U16 minor_os_ver; + U16 major_img_ver; + U16 minor_img_ver; + U16 major_subsystem_ver; + U16 minor_subsystem_ver; + U32 win32_version_value; + U32 sizeof_image; + U32 sizeof_headers; + U32 check_sum; + PE_WindowsSubsystem subsystem; + PE_DllCharacteristics dll_characteristics; + U32 sizeof_stack_reserve; + U32 sizeof_stack_commit; + U32 sizeof_heap_reserve; + U32 sizeof_heap_commit; + U32 loader_flags; + U32 data_dir_count; +}; + +typedef struct PE_OptionalHeader32Plus PE_OptionalHeader32Plus; +struct PE_OptionalHeader32Plus +{ + U16 magic; + U8 major_linker_version; + U8 minor_linker_version; + U32 sizeof_code; + U32 sizeof_inited_data; + U32 sizeof_uninited_data; + U32 entry_point_va; + U32 code_base; + U64 image_base; + U32 section_alignment; + U32 file_alignment; + U16 major_os_ver; + U16 minor_os_ver; + U16 major_img_ver; + U16 minor_img_ver; + U16 major_subsystem_ver; + U16 minor_subsystem_ver; + U32 win32_version_value; + U32 sizeof_image; + U32 sizeof_headers; + U32 check_sum; + PE_WindowsSubsystem subsystem; + PE_DllCharacteristics dll_characteristics; + U64 sizeof_stack_reserve; + U64 sizeof_stack_commit; + U64 sizeof_heap_reserve; + U64 sizeof_heap_commit; + U32 loader_flags; + U32 data_dir_count; +}; + +typedef enum PE_DataDirectoryIndex +{ + PE_DataDirectoryIndex_EXPORT, + PE_DataDirectoryIndex_IMPORT, + PE_DataDirectoryIndex_RESOURCES, + PE_DataDirectoryIndex_EXCEPTIONS, + PE_DataDirectoryIndex_CERT, + PE_DataDirectoryIndex_BASE_RELOC, + PE_DataDirectoryIndex_DEBUG, + PE_DataDirectoryIndex_ARCH, + PE_DataDirectoryIndex_GLOBAL_PTR, + PE_DataDirectoryIndex_TLS, + PE_DataDirectoryIndex_LOAD_CONFIG, + PE_DataDirectoryIndex_BOUND_IMPORT, + PE_DataDirectoryIndex_IMPORT_ADDR, + PE_DataDirectoryIndex_DELAY_IMPORT, + PE_DataDirectoryIndex_COM_DESCRIPTOR, + PE_DataDirectoryIndex_RESERVED, + PE_DataDirectoryIndex_COUNT = 16 +} +PE_DataDirectoryIndex; + +typedef struct PE_DataDirectory PE_DataDirectory; +struct PE_DataDirectory +{ + U32 virt_off; + U32 virt_size; +}; + +typedef U32 PE_DebugDirectoryType; +enum +{ + PE_DebugDirectoryType_UNKNOWN = 0, + PE_DebugDirectoryType_COFF = 1, + PE_DebugDirectoryType_CODEVIEW = 2, + PE_DebugDirectoryType_FPO = 3, + PE_DebugDirectoryType_MISC = 4, + PE_DebugDirectoryType_EXCEPTION = 5, + PE_DebugDirectoryType_FIXUP = 6, + PE_DebugDirectoryType_OMAP_TO_SRC = 7, + PE_DebugDirectoryType_OMAP_FROM_SRC = 8, + PE_DebugDirectoryType_BORLAND = 9, + PE_DebugDirectoryType_RESERVED10 = 10, + PE_DebugDirectoryType_CLSID = 11, + PE_DebugDirectoryType_VC_FEATURE = 12, + PE_DebugDirectoryType_POGO = 13, + PE_DebugDirectoryType_ILTCG = 14, + PE_DebugDirectoryType_MPX = 15, + PE_DebugDirectoryType_REPRO = 16, + PE_DebugDirectoryType_EX_DLLCHARACTERISTICS = 20, + PE_DebugDirectoryType_COUNT = 18 +}; + +typedef U8 PE_FPOFlags; +enum +{ + PE_FPOFlags_HAS_SEH = 0x800, + PE_FPOFlags_USE_BP_REG = 0x1000, + PE_FPOFlags_RESERVED = 0x2000, + PE_FPOFlags_COUNT = 3 +}; + +typedef U16 PE_FPOEncoded; +enum +{ + PE_FPOEncoded_PROLOG_SIZE_SHIFT = 0, PE_FPOEncoded_PROLOG_SIZE_MASK = 0xff, + PE_FPOEncoded_SAVED_REGS_SIZE_SHIFT = 8, PE_FPOEncoded_SAVED_REGS_SIZE_MASK = 0x7, + PE_FPOEncoded_FLAGS_SHIFT = 11, PE_FPOEncoded_FLAGS_MASK = 0x7, + PE_FPOEncoded_FRAME_TYPE_SHIFT = 14, PE_FPOEncoded_FRAME_TYPE_MASK = 0x3, +}; +#define PE_FPOEncoded_Extract_PROLOG_SIZE(f) (U8)(((f) >> PE_FPOEncoded_PROLOG_SIZE_SHIFT) & PE_FPOEncoded_PROLOG_SIZE_MASK) +#define PE_FPOEncoded_Extract_SAVED_REGS_SIZE(f) (U8)(((f) >> PE_FPOEncoded_SAVED_REGS_SIZE_SHIFT) & PE_FPOEncoded_SAVED_REGS_SIZE_MASK) +#define PE_FPOEncoded_Extract_FLAGS(f) (U8)(((f) >> PE_FPOEncoded_FLAGS_SHIFT) & PE_FPOEncoded_FLAGS_MASK) +#define PE_FPOEncoded_Extract_FRAME_TYPE(f) (U8)(((f) >> PE_FPOEncoded_FRAME_TYPE_SHIFT) & PE_FPOEncoded_FRAME_TYPE_MASK) + +typedef U8 PE_FPOType; +enum +{ + PE_FPOType_FPO = 0, + PE_FPOType_TRAP = 1, + PE_FPOType_TSS = 2, + PE_FPOType_NOFPO = 3, + PE_FPOType_COUNT = 4 +}; + +typedef U32 PE_DebugMiscType; +enum +{ + PE_DebugMiscType_NULL, + PE_DebugMiscType_EXE_NAME, + PE_DebugMiscType_COUNT = 2 +}; + +typedef struct PE_DebugDirectory PE_DebugDirectory; +struct PE_DebugDirectory +{ + U32 characteristics; + COFF_TimeStamp time_stamp; + U16 major_ver; + U16 minor_ver; + PE_DebugDirectoryType type; + U32 size; + U32 voff; + U32 foff; +}; + +typedef U32 PE_GlobalFlags; +enum +{ + PE_GlobalFlags_STOP_ON_EXCEPTION = (1 << 0), + PE_GlobalFlags_SHOW_LDR_SNAPS = (1 << 1), + PE_GlobalFlags_DEBUG_INITIAL_COMMAND = (1 << 2), + PE_GlobalFlags_STOP_ON_HUNG_GUI = (1 << 3), + PE_GlobalFlags_HEAP_ENABLE_TAIL_CHECK = (1 << 4), + PE_GlobalFlags_HEAP_ENABLE_FREE_CHECK = (1 << 5), + PE_GlobalFlags_HEAP_VALIDATE_PARAMETERS = (1 << 6), + PE_GlobalFlags_HEAP_VALIDATE_ALL = (1 << 7), + PE_GlobalFlags_APPLICATION_VERIFIER = (1 << 8), + PE_GlobalFlags_POOL_ENABLE_TAGGING = (1 << 10), + PE_GlobalFlags_HEAP_ENABLE_TAGGING = (1 << 11), + PE_GlobalFlags_STACK_TRACE_DB = (1 << 12), + PE_GlobalFlags_KERNEL_STACK_TRACE_DB = (1 << 13), + PE_GlobalFlags_MAINTAIN_OBJECT_TYPELIST = (1 << 14), + PE_GlobalFlags_HEAP_ENABLE_TAG_BY_DLL = (1 << 15), + PE_GlobalFlags_DISABLE_STACK_EXTENSION = (1 << 16), + PE_GlobalFlags_ENABLE_CSRDEBUG = (1 << 17), + PE_GlobalFlags_ENABLE_KDEBUG_SYMBOL_LOAD = (1 << 18), + PE_GlobalFlags_DISABLE_PAGE_KERNEL_STACKS = (1 << 19), + PE_GlobalFlags_ENABLE_SYSTEM_CRIT_BREAKS = (1 << 20), + PE_GlobalFlags_HEAP_DISABLE_COALESCING = (1 << 21), + PE_GlobalFlags_ENABLE_CLOSE_EXCEPTIONS = (1 << 22), + PE_GlobalFlags_ENABLE_EXCEPTION_LOGGING = (1 << 23), + PE_GlobalFlags_ENABLE_HANDLE_TYPE_TAGGING = (1 << 24), + PE_GlobalFlags_HEAP_PAGE_ALLOCS = (1 << 25), + PE_GlobalFlags_DEBUG_INITIAL_COMMAND_EX = (1 << 26), + PE_GlobalFlags_DISABLE_DBGPRINT = (1 << 27), + PE_GlobalFlags_CRITSEC_EVENT_CREATION = (1 << 28), + PE_GlobalFlags_LDR_TOP_DOWN = (1 << 29), + PE_GlobalFlags_ENABLE_HANDLE_EXCEPTIONS = (1 << 30), + PE_GlobalFlags_DISABLE_PROTDLLS = (1 << 31), +}; + +typedef U32 PE_LoadConfigGuardFlags; +enum +{ + PE_LoadConfigGuardFlags_CF_INSTRUMENTED = (1 << 8), + PE_LoadConfigGuardFlags_CFW_INSTRUMENTED = (1 << 9), + PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_PRESENT = (1 << 10), + PE_LoadConfigGuardFlags_SECURITY_COOKIE_UNUSED = (1 << 11), + PE_LoadConfigGuardFlags_PROTECT_DELAYLOAD_IAT = (1 << 12), + PE_LoadConfigGuardFlags_DELAYLOAD_IAT_IN_ITS_OWN_SECTION = (1 << 13), + PE_LoadConfigGuardFlags_CF_EXPORT_SUPPRESSION_INFO_PRESENT = (1 << 14), + PE_LoadConfigGuardFlags_CF_ENABLE_EXPORT_SUPPRESSION = (1 << 15), + PE_LoadConfigGuardFlags_CF_LONGJUMP_TABLE_PRESENT = (1 << 16), + PE_LoadConfigGuardFlags_EH_CONTINUATION_TABLE_PRESENT = (1 << 22), + PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_SHIFT = 28, PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_MASK = 0xf, +}; +#define PE_LoadConfigGuardFlags_Extract_CF_FUNCTION_TABLE_SIZE(f) (U32)(((f) >> PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_SHIFT) & PE_LoadConfigGuardFlags_CF_FUNCTION_TABLE_SIZE_MASK) + +// this is the "MZ" as a 16-bit short +#define PE_DOS_MAGIC 0x5a4d +#define PE_MAGIC 0x00004550u +#define PE_PE32_MAGIC 0x010bu +#define PE_PE32PLUS_MAGIC 0x020bu + +typedef struct PE_MipsPdata PE_MipsPdata; +struct PE_MipsPdata +{ + U32 voff_first; + U32 voff_one_past_last; + U32 voff_exception_handler; + U32 voff_exception_handler_data; + U32 voff_one_past_prolog; +}; + +typedef struct PE_ArmPdata PE_ArmPdata; +struct PE_ArmPdata +{ + U32 voff_first; + // NOTE(allen): + // bits | meaning + // [0:7] | prolog length + // [8:29] | function length + // [30:30] | instructions_are_32bits (otherwise they are 16 bits) + // [31:31] | has_exception_handler + U32 combined; +}; + +typedef struct PE_IntelPdata PE_IntelPdata; +struct PE_IntelPdata +{ + U32 voff_first; + U32 voff_one_past_last; + U32 voff_unwind_info; +}; + +#define PE_CODEVIEW_PDB20_MAGIC 0x3031424e +#define PE_CODEVIEW_PDB70_MAGIC 0x53445352 + +typedef struct PE_CvHeaderPDB20 PE_CvHeaderPDB20; +struct PE_CvHeaderPDB20 +{ + U32 magic; + U32 offset; + U32 time; + U32 age; + // file name packed after struct +}; + +typedef struct PE_CvHeaderPDB70 PE_CvHeaderPDB70; +struct PE_CvHeaderPDB70 +{ + U32 magic; + OS_Guid guid; + U32 age; + // file name packed after struct +}; + +typedef struct PE_ImportEntry PE_ImportEntry; +struct PE_ImportEntry +{ + U32 lookup_table_voff; + COFF_TimeStamp time_stamp; + U32 forwarder_chain; + U32 name_voff; + U32 import_addr_table_voff; +}; + +typedef struct PE_DelayedImportEntry PE_DelayedImportEntry; +struct PE_DelayedImportEntry +{ + // According to PE/COFF spec this field is unused and should be set zero, + // but when I compile mule with MSVC 2019 this is set to 1. + U32 attributes; + U32 name_voff; // Name of the DLL + U32 module_handle_voff; // Place where module handle from LoadLibrary is stored + U32 iat_voff; + U32 name_table_voff; // Array of hint/name or oridnals + U32 bound_table_voff; // (Optional) Points to an array of PE_ThunkData + U32 unload_table_voff; // (Optional) Copy of iat_voff + // 0 not bound + // -1 if bound and real timestamp located in bounded import directory + // Otherwise time when dll was bound + COFF_TimeStamp time_stamp; +}; + +typedef struct PE_ExportTable PE_ExportTable; +struct PE_ExportTable +{ + U32 flags; // must be zero + COFF_TimeStamp time_stamp; // time and date when export table was created + U16 major_ver; // table version, user can change major and minor version + U16 minor_ver; + U32 name_voff; // ASCII name of the dll + U32 ordinal_base; // Starting oridnal number + U32 export_address_table_count; + U32 name_pointer_table_count; + U32 export_address_table_voff; + U32 name_pointer_table_voff; + U32 ordinal_table_voff; +}; + +typedef struct PE_TLSHeader32 PE_TLSHeader32; +struct PE_TLSHeader32 +{ + U32 raw_data_start; // Range of initialized data that is copied for each thread from the image. + U32 raw_data_end; // (Typically points to .tls section) + U32 index_address; // Address where image loader places TLS slot index. + U32 callbacks_address; // Zero terminated list of callbacks used for initializing data with constructors. + U32 zero_fill_size; // Amount of memory to fill with zeroes in TLS. + U32 characteristics; // COFF_SectionFlags but only align flags are used. +}; + +typedef struct PE_TLSHeader64 PE_TLSHeader64; +struct PE_TLSHeader64 +{ + U64 raw_data_start; // Range of initialized data that is copied for each thread from the image. + U64 raw_data_end; // (Typically points to .tls section) + U64 index_address; // Address where image loader places TLS slot index. + U64 callbacks_address; // Zero terminated list of callbacks used for initializing data with constructors. + U32 zero_fill_size; // Amount of memory to fill with zeroes in TLS. + U32 characteristics; // COFF_SectionFlags but only align flags are used. +}; + +#define PE_RES_ALIGN 4u + +global read_only U8 PE_RES_MAGIC[] = +{ + 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, + 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +typedef U32 PE_ResourceKind; +enum +{ + PE_ResourceKind_CURSOR = 0x1, + PE_ResourceKind_BITMAP = 0x2, + PE_ResourceKind_ICON = 0x3, + PE_ResourceKind_MENU = 0x4, + PE_ResourceKind_DIALOG = 0x5, + PE_ResourceKind_STRING = 0x6, + PE_ResourceKind_FONTDIR = 0x7, + PE_ResourceKind_FONT = 0x8, + PE_ResourceKind_ACCELERATOR = 0x9, + PE_ResourceKind_RCDATA = 0xA, + PE_ResourceKind_MESSAGETABLE = 0xB, + PE_ResourceKind_GROUP_CURSOR = 0xC, + PE_ResourceKind_GROUP_ICON = 0xE, + PE_ResourceKind_VERSION = 0x10, + PE_ResourceKind_DLGINCLUDE = 0x11, + PE_ResourceKind_PLUGPLAY = 0x13, + PE_ResourceKind_VXD = 0x14, + PE_ResourceKind_ANICURSOR = 0x15, + PE_ResourceKind_ANIICON = 0x16, + PE_ResourceKind_HTML = 0x17, + PE_ResourceKind_MANIFEST = 0x18, + PE_ResourceKind_BITMAP_NEW = 0x2002, + PE_ResourceKind_MENU_NEW = 0x2004, + PE_ResourceKind_DIALOG_NEW = 0x2005, +}; + +typedef enum PE_ResDataKind +{ + PE_ResDataKind_NULL, + PE_ResDataKind_DIR, + PE_ResDataKind_COFF_LEAF, + PE_ResDataKind_COFF_RESOURCE, +} +PE_ResDataKind; + +typedef struct PE_ResourceHeader PE_ResourceHeader; +struct PE_ResourceHeader +{ + COFF_ResourceHeaderPrefix prefix; + U16 type; + U16 pad0; + U16 name; + U16 pad1; + U32 data_version; + COFF_ResourceMemoryFlags memory_flags; + U16 language_id; + U32 version; + U32 characteristics; +}; + +typedef U16 PE_BaseRelocKind; +enum +{ + PE_BaseRelocKind_ABSOLUTE = 0, // No reallocation is applied. Can be used as padding. + PE_BaseRelocKind_HIGH = 1, + PE_BaseRelocKind_LOW = 2, + PE_BaseRelocKind_HIGHLOW = 3, + PE_BaseRelocKind_HIGHADJ = 4, + PE_BaseRelocKind_MIPS_JMPADDR = 5, + PE_BaseRelocKind_ARM_MOV32 = 5, + PE_BaseRelocKind_RISCV_HIGH20 = 5, + // 6 is reserved + PE_BaseRelocKind_THUMB_MOV32 = 7, + PE_BaseRelocKind_RISCV_LOW12I = 7, + PE_BaseRelocKind_RISCV_LOW12S = 8, + PE_BaseRelocKind_LOONGARCH32_MARK_LA = 8, + PE_BaseRelocKind_LOONGARCH64_MARK_LA = 8, + PE_BaseRelocKind_MIPS_JMPADDR16 = 9, + PE_BaseRelocKind_DIR64 = 10, +}; +#define PE_BaseRelocOffsetFromEntry(x) ((x) & 0x1fff) +#define PE_BaseRelocKindFromEntry(x) (((x) >> 12) & 0xf) +#define PE_BaseRelocMake(k, off) ((((U16)(k) & 0xf) << 12) | (U16)((off) & 0x1fff)) + +typedef U32 PE_UnwindOpCode; +enum +{ + PE_UnwindOpCode_PUSH_NONVOL = 0, + PE_UnwindOpCode_ALLOC_LARGE = 1, + PE_UnwindOpCode_ALLOC_SMALL = 2, + PE_UnwindOpCode_SET_FPREG = 3, + PE_UnwindOpCode_SAVE_NONVOL = 4, + PE_UnwindOpCode_SAVE_NONVOL_FAR = 5, + PE_UnwindOpCode_EPILOG = 6, + PE_UnwindOpCode_SPARE_CODE = 7, + PE_UnwindOpCode_SAVE_XMM128 = 8, + PE_UnwindOpCode_SAVE_XMM128_FAR = 9, + PE_UnwindOpCode_PUSH_MACHFRAME = 10, +}; + +typedef U8 PE_UnwindGprRegX64; +enum +{ + PE_UnwindGprRegX64_RAX = 0, + PE_UnwindGprRegX64_RCX = 1, + PE_UnwindGprRegX64_RDX = 2, + PE_UnwindGprRegX64_RBX = 3, + PE_UnwindGprRegX64_RSP = 4, + PE_UnwindGprRegX64_RBP = 5, + PE_UnwindGprRegX64_RSI = 6, + PE_UnwindGprRegX64_RDI = 7, + PE_UnwindGprRegX64_R8 = 8, + PE_UnwindGprRegX64_R9 = 9, + PE_UnwindGprRegX64_R10 = 10, + PE_UnwindGprRegX64_R11 = 11, + PE_UnwindGprRegX64_R12 = 12, + PE_UnwindGprRegX64_R13 = 13, + PE_UnwindGprRegX64_R14 = 14, + PE_UnwindGprRegX64_R15 = 15, +}; + +typedef U8 PE_UnwindInfoFlags; +enum +{ + PE_UnwindInfoFlag_EHANDLER = (1<<0), + PE_UnwindInfoFlag_UHANDLER = (1<<1), + PE_UnwindInfoFlag_FHANDLER = 3, + PE_UnwindInfoFlag_CHAINED = (1<<2), +}; + +#define PE_UNWIND_OPCODE_FROM_FLAGS(f) ((f)&0xF) +#define PE_UNWIND_INFO_FROM_FLAGS(f) (((f) >> 4)&0xF) + +typedef union PE_UnwindCode PE_UnwindCode; +union PE_UnwindCode +{ + struct + { + U8 off_in_prolog; + U8 flags; + }; + U16 u16; +}; + +#define PE_UNWIND_INFO_VERSION_FROM_HDR(x) ((x)&0x7) +#define PE_UNWIND_INFO_FLAGS_FROM_HDR(x) (((x) >> 3)&0x1F) +#define PE_UNWIND_INFO_REG_FROM_FRAME(x) ((x)&0xF) +#define PE_UNWIND_INFO_OFF_FROM_FRAME(x) (((x) >> 4)&0xF) + +typedef struct PE_UnwindInfo PE_UnwindInfo; +struct PE_UnwindInfo +{ + U8 header; + U8 prolog_size; + U8 codes_num; + U8 frame; +}; + +#pragma pack(pop) + +//////////////////////////////// +//~ rjf: DOS Program + +// generated from pe/dos_program.asm +read_only global U8 pe_dos_program_data[] = +{ + 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4, 0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, + 0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, + 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20, + 0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x24, 0x00, 0x00 +}; +read_only global String8 pe_dos_program = {pe_dos_program_data, sizeof(pe_dos_program_data)}; + +//////////////////////////////// +//~ rjf: Parsed Info Types + +//- rjf: relocation blocks + +typedef struct PE_BaseRelocBlock PE_BaseRelocBlock; +struct PE_BaseRelocBlock +{ + U64 page_virt_off; + U64 entry_count; + U16 *entries; +}; + +typedef struct PE_BaseRelocBlockNode PE_BaseRelocBlockNode; +struct PE_BaseRelocBlockNode +{ + PE_BaseRelocBlockNode *next; + PE_BaseRelocBlock v; +}; + +typedef struct PE_BaseRelocBlockList PE_BaseRelocBlockList; +struct PE_BaseRelocBlockList +{ + PE_BaseRelocBlockNode *first; + PE_BaseRelocBlockNode *last; + U64 count; +}; + +//- rjf: resources + +typedef struct PE_Resource PE_Resource; +struct PE_Resource +{ + COFF_ResourceID id; + PE_ResDataKind kind; + union + { + COFF_ResourceDataEntry leaf; + struct PE_ResourceDir *dir; + struct + { + COFF_ResourceID type; + U32 data_version; + U32 version; + COFF_ResourceMemoryFlags memory_flags; + String8 data; + } + coff_res; + } + u; +}; + +typedef struct PE_ResourceNode PE_ResourceNode; +struct PE_ResourceNode +{ + PE_ResourceNode *next; + PE_Resource data; +}; + +typedef struct PE_ResourceList PE_ResourceList; +struct PE_ResourceList +{ + PE_ResourceNode *first; + PE_ResourceNode *last; + U64 count; +}; + +typedef struct PE_ResourceArray PE_ResourceArray; +struct PE_ResourceArray +{ + PE_Resource *v; + U64 count; +}; + +typedef struct PE_ResourceDir PE_ResourceDir; +struct PE_ResourceDir +{ + U32 characteristics; + COFF_TimeStamp time_stamp; + U16 major_version; + U16 minor_version; + PE_ResourceList named_list; + PE_ResourceList id_list; +}; + +//- rjf: bundle + +typedef struct PE_BinInfo PE_BinInfo; +struct PE_BinInfo +{ + U64 image_base; + U64 entry_point; + B32 is_pe32; + U64 virt_section_align; + U64 file_section_align; + U64 section_array_off; + U64 section_count; + U64 symbol_array_off; + U64 symbol_count; + U64 string_table_off; + U64 dbg_path_off; + U64 dbg_path_size; + OS_Guid dbg_guid; + U32 dbg_age; + U32 dbg_time; + Architecture arch; + Rng1U64 *data_dir_franges; + U32 data_dir_count; + PE_TLSHeader64 tls_header; +}; + +//////////////////////////////// +//~ rjf: Basic Enum Functions + +internal U32 pe_slot_count_from_unwind_op_code(PE_UnwindOpCode opcode); +internal String8 pe_string_from_windows_subsystem(PE_WindowsSubsystem subsystem); + +//////////////////////////////// +//~ rjf: Parser Functions + +internal PE_BinInfo pe_bin_info_from_data(Arena *arena, String8 data); + +//////////////////////////////// +//~ rjf: Helpers + +internal U64 pe_intel_pdata_off_from_voff__binary_search(String8 data, PE_BinInfo *bin, U64 voff); +internal void *pe_ptr_from_voff(String8 data, PE_BinInfo *bin, U64 voff); +internal U64 pe_section_num_from_voff(String8 data, PE_BinInfo *bin, U64 voff); +internal void *pe_ptr_from_section_num(String8 data, PE_BinInfo *bin, U64 n); +internal U64 pe_foff_from_voff(String8 data, PE_BinInfo *bin, U64 voff); +internal PE_BaseRelocBlockList pe_base_reloc_block_list_from_bin(Arena *arena, String8 data, PE_BinInfo *bin); +internal Rng1U64 pe_tls_rng_from_bin_base_vaddr(String8 data, PE_BinInfo *bin, U64 base_vaddr); + +//////////////////////////////// +//~ Resource Helpers + +internal B32 pe_is_res(String8 data); +internal void pe_resource_dir_push_res_file(Arena *arena, PE_ResourceDir *root_dir, String8 res_file); +internal PE_ResourceNode * pe_resource_dir_push_dir_node(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, U32 characteristics, COFF_TimeStamp time_stamp, U16 major_version, U16 minor_version); +internal PE_ResourceNode * pe_resource_dir_push_entry_node(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, COFF_ResourceID type, U32 data_version, U32 version, COFF_ResourceMemoryFlags memory_flags, String8 data); +internal PE_Resource * pe_resource_dir_push_entry(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, COFF_ResourceID type, U32 data_version, U32 version, COFF_ResourceMemoryFlags memory_flags, String8 data); +internal PE_Resource * pe_resource_dir_push_dir(Arena *arena, PE_ResourceDir *dir, COFF_ResourceID id, U32 characteristics, COFF_TimeStamp time_stamp, U16 major_version, U16 minor_version); +internal PE_ResourceNode * pe_resource_dir_search_node(PE_ResourceDir *dir, COFF_ResourceID id); +internal PE_Resource * pe_resource_dir_search(PE_ResourceDir *dir, COFF_ResourceID id); +internal PE_ResourceArray pe_resource_list_to_array(Arena *arena, PE_ResourceList *list); +internal PE_ResourceDir * pe_resource_table_from_directory_data(Arena *arena, String8 data); + +#endif // PE_H diff --git a/src/rdi_dump/rdi_dump.c b/src/rdi_dump/rdi_dump.c index 25d57c73..c2206b54 100644 --- a/src/rdi_dump/rdi_dump.c +++ b/src/rdi_dump/rdi_dump.c @@ -1,788 +1,788 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: RDI Enum -> String Functions - -internal String8 -rdi_string_from_data_section_kind(RDI_SectionKind v) -{ - String8 result = str8_lit(""); - switch(v) - { - default:{}break; -#define X(name, lower, type) case RDI_SectionKind_##name:{result = str8_lit(#name);}break; - RDI_SectionKind_XList -#undef X - } - return result; -} - -internal String8 -rdi_string_from_arch(RDI_Arch v) -{ - String8 result = str8_lit(""); - switch(v) - { - default:{}break; -#define X(name) case RDI_Arch_##name:{result = str8_lit(#name);}break; - RDI_Arch_XList -#undef X - } - return result; -} - -internal String8 -rdi_string_from_language(RDI_Language v) -{ - String8 result = str8_lit(""); - switch(v) - { - default:{}break; -#define X(name) case RDI_Language_##name:{result = str8_lit(#name);}break; - RDI_Language_XList -#undef X - } - return result; -} - -internal String8 -rdi_string_from_type_kind(RDI_TypeKind v) -{ - String8 result = str8_lit(""); - switch(v) - { - default:{}break; -#define X(name) case RDI_TypeKind_##name:{result = str8_lit(#name);}break; - RDI_TypeKind_XList -#undef X - } - return result; -} - -internal String8 -rdi_string_from_member_kind(RDI_MemberKind v) -{ - String8 result = str8_lit(""); - switch(v) - { - default:{}break; -#define X(name) case RDI_MemberKind_##name:{result = str8_lit(#name);}break; - RDI_MemberKind_XList -#undef X - } - return result; -} - -internal String8 -rdi_string_from_local_kind(RDI_LocalKind v) -{ - String8 result = str8_lit(""); - switch(v) - { - default:{}break; -#define X(name) case RDI_LocalKind_##name:{result = str8_lit(#name);}break; - RDI_LocalKind_XList -#undef X - } - return result; -} - -//////////////////////////////// -//~ rjf: RDI Flags -> String Functions - -internal void -rdi_stringize_binary_section_flags(Arena *arena, String8List *out, RDI_BinarySectionFlags flags) -{ - if(flags == 0) { str8_list_push(arena, out, str8_lit("0")); } -#define X(name) if(flags & RDI_BinarySectionFlag_##name) { str8_list_push(arena, out, str8_lit(#name " ")); } - RDI_BinarySectionFlags_XList; -#undef X -} - -internal void -rdi_stringize_type_modifier_flags(Arena *arena, String8List *out, - RDI_TypeModifierFlags flags) -{ - if(flags == 0) { str8_list_push(arena, out, str8_lit("0")); } -#define X(name) if(flags & RDI_TypeModifierFlag_##name) { str8_list_push(arena, out, str8_lit(#name " ")); } - RDI_TypeModifierFlags_XList; -#undef X -} - -internal void -rdi_stringize_udt_flags(Arena *arena, String8List *out, RDI_UDTFlags flags) -{ - if(flags == 0) { str8_list_push(arena, out, str8_lit("0")); } -#define X(name) if(flags & RDI_UDTFlag_##name) { str8_list_push(arena, out, str8_lit(#name " ")); } - RDI_UDTFlags_XList; -#undef X -} - -internal void -rdi_stringize_link_flags(Arena *arena, String8List *out, RDI_LinkFlags flags) -{ - if(flags == 0) { str8_list_push(arena, out, str8_lit("0")); } -#define X(name) if(flags & RDI_LinkFlag_##name) { str8_list_push(arena, out, str8_lit(#name " ")); } - RDI_LinkFlags_XList; -#undef X -} - -//////////////////////////////// -//~ rjf: RADDBG Compound Stringize Functions - -global char rdi_stringize_spaces[] = " "; - -internal void -rdi_stringize_data_sections(Arena *arena, String8List *out, RDI_Parsed *rdi, U32 indent_level) -{ - for(U64 idx = 0; idx < rdi->sections_count; idx += 1) - { - RDI_SectionKind kind = (RDI_SectionKind)idx; - RDI_Section *section = &rdi->sections[idx]; - String8 kind_str = rdi_string_from_data_section_kind(kind); - str8_list_pushf(arena, out, "%.*sdata_section[%5I64u] = {0x%08llx, %7u, %7u} %S\n", - indent_level, rdi_stringize_spaces, - idx, section->off, section->encoded_size, section->unpacked_size, kind_str); - } -} - -internal void -rdi_stringize_top_level_info(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_TopLevelInfo *tli, U32 indent_level) -{ - String8 arch_str = rdi_string_from_arch(tli->arch); - String8 exe_name = {0}; - exe_name.str = rdi_string_from_idx(rdi, tli->exe_name_string_idx, &exe_name.size); - String8 producer_name = {0}; - producer_name.str = rdi_string_from_idx(rdi, tli->producer_name_string_idx, &producer_name.size); - str8_list_pushf(arena, out, "%.*sarch=%S\n", indent_level, rdi_stringize_spaces, arch_str); - str8_list_pushf(arena, out, "%.*sexe_name='%S'\n", indent_level, rdi_stringize_spaces, exe_name); - str8_list_pushf(arena, out, "%.*svoff_max=0x%08llx\n", indent_level, rdi_stringize_spaces, tli->voff_max); - str8_list_pushf(arena, out, "%.*sproducer_name='%S'\n", indent_level, rdi_stringize_spaces, producer_name); -} - -internal void -rdi_stringize_binary_section(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_BinarySection *bin_section, U32 indent_level) -{ - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, bin_section->name_string_idx, &name.size); - str8_list_pushf(arena, out, "%.*sname='%.*s'\n", indent_level, rdi_stringize_spaces, str8_varg(name)); - - str8_list_pushf(arena, out, "%.*sflags=", indent_level, rdi_stringize_spaces); - rdi_stringize_binary_section_flags(arena, out, bin_section->flags); - str8_list_pushf(arena, out, "\n"); - - str8_list_pushf(arena, out, "%.*svoff_first=0x%08x\n", indent_level, rdi_stringize_spaces, bin_section->voff_first); - str8_list_pushf(arena, out, "%.*svoff_opl =0x%08x\n", indent_level, rdi_stringize_spaces, bin_section->voff_opl); - str8_list_pushf(arena, out, "%.*sfoff_first=0x%08x\n", indent_level, rdi_stringize_spaces, bin_section->foff_first); - str8_list_pushf(arena, out, "%.*sfoff_opl =0x%08x\n", indent_level, rdi_stringize_spaces, bin_section->foff_opl); -} - -internal void -rdi_stringize_file_path(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_FilePathBundle *bundle, RDI_FilePathNode *file_path, U32 indent_level) -{ - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, file_path->name_string_idx, &name.size); - U32 this_idx = (U32)(file_path - bundle->file_paths); - if(file_path->source_file_idx == 0) - { - str8_list_pushf(arena, out, "%.*s[%u] '%.*s'\n", - indent_level, rdi_stringize_spaces, - this_idx, str8_varg(name)); - } - else - { - str8_list_pushf(arena, out, "%.*s[%u] '%.*s'; source_file=%u\n", - indent_level, rdi_stringize_spaces, - this_idx, str8_varg(name), file_path->source_file_idx); - } - - for(U32 child = file_path->first_child; child != 0;) - { - // get node for child - RDI_FilePathNode *child_node = 0; - if (child < bundle->file_path_count){ - child_node = bundle->file_paths + child; - } - if (child_node == 0){ - break; - } - - // stringize child - rdi_stringize_file_path(arena, out, rdi, bundle, child_node, indent_level + 1); - - // increment iterator - child = child_node->next_sibling; - } -} - -internal void -rdi_stringize_source_file(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_SourceFile *source_file, U32 indent_level) -{ - // normal source path - String8 path = {0}; - path.str = rdi_string_from_idx(rdi, source_file->normal_full_path_string_idx, &path.size); - str8_list_pushf(arena, out, "%.*spath: \"%S\"\n", indent_level, rdi_stringize_spaces, path); - - // rjf: source line map idx - str8_list_pushf(arena, out, "%.*ssource_line_map: %u\n", indent_level, rdi_stringize_spaces, source_file->source_line_map_idx); -} - -internal void -rdi_stringize_line_table(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_LineTable *line_table, U32 indent_level) -{ - // rjf: parse line table - RDI_ParsedLineTable parsed_line_table = {0}; - rdi_parsed_from_line_table(rdi, line_table, &parsed_line_table); - - // rjf: stringize lines - str8_list_pushf(arena, out, "%.*slines:\n", indent_level, rdi_stringize_spaces); - for(U32 i = 0; i < parsed_line_table.count; i += 1) - { - U64 first = parsed_line_table.voffs[i]; - U64 opl = parsed_line_table.voffs[i + 1]; - RDI_Line *line = parsed_line_table.lines + i; - RDI_Column *col = 0; - if(i < parsed_line_table.col_count) - { - col = parsed_line_table.cols + i; - } - if(col == 0) - { - str8_list_pushf(arena, out, "%.*s [0x%08llx,0x%08llx) file=%u; line=%u\n", - indent_level, rdi_stringize_spaces, - first, opl, line->file_idx, line->line_num); - } - else - { - str8_list_pushf(arena, out, "%.*s [0x%08llx,0x%08llx) file=%u; line=%u; columns=[%u,%u)\n", - indent_level, rdi_stringize_spaces, - first, opl, line->file_idx, line->line_num, - col->col_first, col->col_opl); - } - } -} - -internal void -rdi_stringize_source_line_map(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_SourceLineMap *map, U32 indent_level) -{ - RDI_ParsedSourceLineMap line_map = {0}; - rdi_parsed_from_source_line_map(rdi, map, &line_map); - str8_list_pushf(arena, out, "%.*slines:\n", indent_level, rdi_stringize_spaces); - - for(U32 i = 0; i < line_map.count; i += 1) - { - U32 line_num = line_map.nums[i]; - U32 digit_count = 1; - if(line_num > 0) - { - U32 x = line_num; - for(;;) - { - x /= 10; - if(x == 0) - { - break; - } - digit_count += 1; - } - } - - str8_list_pushf(arena, out, "%.*s %u: ", indent_level, rdi_stringize_spaces, line_num); - - U32 first = line_map.ranges[i]; - U32 opl_raw = line_map.ranges[i + 1]; - U32 opl = ClampTop(opl_raw, line_map.voff_count); - for(U32 j = first; j < opl; j += 1) - { - if(j == first) - { - str8_list_pushf(arena, out, "0x%08x\n", line_map.voffs[j]); - } - else - { - str8_list_pushf(arena, out, "%.*s0x%08x\n", - indent_level + digit_count + 3, rdi_stringize_spaces, - line_map.voffs[j]); - } - } - } -} - -internal void -rdi_stringize_unit(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_Unit *unit, U32 indent_level) -{ - String8 unit_name = {0}; - unit_name.str = rdi_string_from_idx(rdi, unit->unit_name_string_idx, &unit_name.size); - String8 compiler_name = {0}; - compiler_name.str = rdi_string_from_idx(rdi, unit->compiler_name_string_idx, &compiler_name.size); - - str8_list_pushf(arena, out, "%.*sunit_name='%.*s'\n", indent_level, rdi_stringize_spaces, str8_varg(unit_name)); - str8_list_pushf(arena, out, "%.*scompiler_name='%.*s'\n", indent_level, rdi_stringize_spaces, str8_varg(compiler_name)); - - str8_list_pushf(arena, out, "%.*ssource_file_path=%u\n", indent_level, rdi_stringize_spaces, unit->source_file_path_node); - str8_list_pushf(arena, out, "%.*sobject_file_path=%u\n", indent_level, rdi_stringize_spaces, unit->object_file_path_node); - str8_list_pushf(arena, out, "%.*sarchive_file_path=%u\n", indent_level, rdi_stringize_spaces, unit->archive_file_path_node); - str8_list_pushf(arena, out, "%.*sbuild_path=%u\n", indent_level, rdi_stringize_spaces, unit->build_path_node); - - String8 language_str = rdi_string_from_language(unit->language); - str8_list_pushf(arena, out, "%.*slanguage=%.*s\n", indent_level, rdi_stringize_spaces, str8_varg(language_str)); - - str8_list_pushf(arena, out, "%.*sline_table_idx=%u\n", indent_level, rdi_stringize_spaces, unit->line_table_idx); -} - -internal void -rdi_stringize_type_node(Arena *arena, String8List *out, RDI_Parsed *rdi, - RDI_TypeNode *type, U32 indent_level){ - RDI_TypeKind kind = type->kind; - String8 type_kind_str = rdi_string_from_type_kind(kind); - - str8_list_pushf(arena, out, "%.*skind=%.*s\n", - indent_level, rdi_stringize_spaces, str8_varg(type_kind_str)); - - switch (type->kind){ - case RDI_TypeKind_Modifier: - { - str8_list_pushf(arena, out, "%.*sflags=", indent_level, rdi_stringize_spaces); - rdi_stringize_type_modifier_flags(arena, out, type->flags); - str8_list_push(arena, out, str8_lit("\n")); - }break; - - default: - { - if (type->flags != 0){ - str8_list_pushf(arena, out, "%.*sflags=%x (missing stringizer path)", - indent_level, rdi_stringize_spaces, type->flags); - } - }break; - } - - str8_list_pushf(arena, out, "%.*sbyte_size=%u\n", - indent_level, rdi_stringize_spaces, type->byte_size); - - if (RDI_TypeKind_FirstBuiltIn <= kind && - kind <= RDI_TypeKind_LastBuiltIn){ - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, type->built_in.name_string_idx, &name.size); - str8_list_pushf(arena, out, "%.*sbuilt_in.name='%.*s'\n", - indent_level, rdi_stringize_spaces, str8_varg(name)); - } - - else if (RDI_TypeKind_FirstConstructed <= kind && - kind <= RDI_TypeKind_LastConstructed){ - str8_list_pushf(arena, out, "%.*sconstructed.direct_type=%u\n", - indent_level, rdi_stringize_spaces, type->constructed.direct_type_idx); - - if (type->kind == RDI_TypeKind_Array){ - str8_list_pushf(arena, out, "%.*sconstructed.array_count=%u\n", - indent_level, rdi_stringize_spaces, type->constructed.count); - } - - if (type->kind == RDI_TypeKind_Function || - type->kind == RDI_TypeKind_Method){ - U32 run_first = type->constructed.param_idx_run_first; - U32 run_count_raw = type->constructed.count; - - U32 run_count = 0; - U32 *run = rdi_idx_run_from_first_count(rdi, run_first, run_count_raw, &run_count); - - U32 this_type_idx = 0; - if (run_count > 0 && type->kind == RDI_TypeKind_Method){ - this_type_idx = run[0]; - run += 1; - run_count -= 1; - } - - if (this_type_idx != 0){ - str8_list_pushf(arena, out, "%.*sconstructed.this_type=%u\n", - indent_level, rdi_stringize_spaces, this_type_idx); - } - - str8_list_pushf(arena, out, "%.*sconstructed.params={", - indent_level, rdi_stringize_spaces); - - if (run_count > 0){ - U32 last = run_count - 1; - for (U32 j = 0; j < last; j += 1){ - str8_list_pushf(arena, out, " %u,", run[j]); - } - str8_list_pushf(arena, out, " %u ", run[last]); - } - - str8_list_push(arena, out, str8_lit("}\n")); - } - } - - else if (RDI_TypeKind_FirstUserDefined <= kind && - kind <= RDI_TypeKind_LastUserDefined){ - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, type->user_defined.name_string_idx, &name.size); - str8_list_pushf(arena, out, "%.*suser_defined.name='%.*s'\n", - indent_level, rdi_stringize_spaces, str8_varg(name)); - str8_list_pushf(arena, out, "%.*suser_defined.direct_type=%u\n", - indent_level, rdi_stringize_spaces, - type->user_defined.direct_type_idx); - str8_list_pushf(arena, out, "%.*suser_defined.udt=%u\n", - indent_level, rdi_stringize_spaces, - type->user_defined.udt_idx); - } - - else if (kind == RDI_TypeKind_Bitfield){ - str8_list_pushf(arena, out, "%.*sbitfield.off=%u\n", - indent_level, rdi_stringize_spaces, type->bitfield.off); - str8_list_pushf(arena, out, "%.*sbitfield.size=%u\n", - indent_level, rdi_stringize_spaces, type->bitfield.size); - } -} - -internal void -rdi_stringize_udt(Arena *arena, String8List *out, RDI_Parsed *rdi, - RDI_UDTMemberBundle *member_bundle, RDI_UDT *udt, - U32 indent_level){ - str8_list_pushf(arena, out, "%.*sself_type=%u\n", - indent_level, rdi_stringize_spaces, udt->self_type_idx); - - str8_list_pushf(arena, out, "%.*sflags=", indent_level, rdi_stringize_spaces); - rdi_stringize_udt_flags(arena, out, udt->flags); - str8_list_push(arena, out, str8_lit("\n")); - - if (udt->file_idx != 0){ - str8_list_pushf(arena, out, "%.*sloc={file=%u; line=%u; col=%u}\n", - indent_level, rdi_stringize_spaces, - udt->file_idx, udt->line, udt->col); - } - - // enum members - if (udt->flags & RDI_UDTFlag_EnumMembers){ - U32 first_raw = udt->member_first; - U32 opl_raw = first_raw + udt->member_count; - U32 opl = ClampTop(opl_raw, member_bundle->enum_member_count); - U32 first = ClampTop(first_raw, opl); - - if (first < opl){ - str8_list_pushf(arena, out, "%.*smembers={\n", indent_level, rdi_stringize_spaces); - RDI_EnumMember *enum_member = member_bundle->enum_members + first; - for (U32 i = first; i < opl; i += 1, enum_member += 1){ - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, enum_member->name_string_idx, &name.size); - str8_list_pushf(arena, out, "%.*s '%.*s' %llu\n", - indent_level, rdi_stringize_spaces, - str8_varg(name), enum_member->val); - } - str8_list_pushf(arena, out, "%.*s}\n", indent_level, rdi_stringize_spaces); - } - } - - // field members - else{ - U32 first_raw = udt->member_first; - U32 opl_raw = first_raw + udt->member_count; - U32 opl = ClampTop(opl_raw, member_bundle->member_count); - U32 first = ClampTop(first_raw, opl); - - if (first < opl){ - str8_list_pushf(arena, out, "%.*smembers={\n", indent_level, rdi_stringize_spaces); - RDI_Member *member = member_bundle->members + first; - for (U32 i = first; i < opl; i += 1, member += 1){ - str8_list_pushf(arena, out, "%.*s {\n", indent_level, rdi_stringize_spaces); - - String8 kind_str = rdi_string_from_member_kind(member->kind); - str8_list_pushf(arena, out, "%.*s kind=%.*s\n", - indent_level, rdi_stringize_spaces, str8_varg(kind_str)); - - if (member->name_string_idx != 0){ - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, member->name_string_idx, &name.size); - str8_list_pushf(arena, out, "%.*s name='%.*s'\n", - indent_level, rdi_stringize_spaces, str8_varg(name)); - } - - str8_list_pushf(arena, out, "%.*s type=%u\n", - indent_level, rdi_stringize_spaces, member->type_idx); - str8_list_pushf(arena, out, "%.*s off=%u\n", - indent_level, rdi_stringize_spaces, member->off); - - str8_list_pushf(arena, out, "%.*s }\n", indent_level, rdi_stringize_spaces); - } - str8_list_pushf(arena, out, "%.*s}\n", indent_level, rdi_stringize_spaces); - } - } -} - -internal void -rdi_stringize_global_variable(Arena *arena, String8List *out, RDI_Parsed *rdi, - RDI_GlobalVariable *global_variable, U32 indent_level){ - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, global_variable->name_string_idx, &name.size); - str8_list_pushf(arena, out, "%.*sname='%.*s'\n", - indent_level, rdi_stringize_spaces, str8_varg(name)); - - str8_list_pushf(arena, out, "%.*slink_flags=", indent_level, rdi_stringize_spaces); - rdi_stringize_link_flags(arena, out, global_variable->link_flags); - str8_list_push(arena, out, str8_lit("\n")); - - str8_list_pushf(arena, out, "%.*svoff=0x%08llx\n", - indent_level, rdi_stringize_spaces, global_variable->voff); - - str8_list_pushf(arena, out, "%.*stype_idx=%u\n", - indent_level, rdi_stringize_spaces, global_variable->type_idx); - - str8_list_pushf(arena, out, "%.*scontainer_idx=%u\n", - indent_level, rdi_stringize_spaces, global_variable->container_idx); -} - -internal void -rdi_stringize_thread_variable(Arena *arena, String8List *out, RDI_Parsed *rdi, - RDI_ThreadVariable *thread_var, - U32 indent_level){ - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, thread_var->name_string_idx, &name.size); - str8_list_pushf(arena, out, "%.*sname='%.*s'\n", - indent_level, rdi_stringize_spaces, str8_varg(name)); - - str8_list_pushf(arena, out, "%.*slink_flags=", indent_level, rdi_stringize_spaces); - rdi_stringize_link_flags(arena, out, thread_var->link_flags); - str8_list_push(arena, out, str8_lit("\n")); - - str8_list_pushf(arena, out, "%.*stls_off=0x%08x\n", - indent_level, rdi_stringize_spaces, thread_var->tls_off); - - str8_list_pushf(arena, out, "%.*stype_idx=%u\n", - indent_level, rdi_stringize_spaces, thread_var->type_idx); - - str8_list_pushf(arena, out, "%.*scontainer_idx=%u\n", - indent_level, rdi_stringize_spaces, thread_var->container_idx); -} - -internal void -rdi_stringize_procedure(Arena *arena, String8List *out, RDI_Parsed *rdi, - RDI_Procedure *proc, U32 indent_level){ - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, proc->name_string_idx, &name.size); - str8_list_pushf(arena, out, "%.*sname='%.*s'\n", - indent_level, rdi_stringize_spaces, str8_varg(name)); - - String8 link_name = {0}; - link_name.str = rdi_string_from_idx(rdi, proc->link_name_string_idx, &link_name.size); - str8_list_pushf(arena, out, "%.*slink_name='%.*s'\n", - indent_level, rdi_stringize_spaces, str8_varg(link_name)); - - str8_list_pushf(arena, out, "%.*slink_flags=", indent_level, rdi_stringize_spaces); - rdi_stringize_link_flags(arena, out, proc->link_flags); - str8_list_push(arena, out, str8_lit("\n")); - - str8_list_pushf(arena, out, "%.*stype_idx=%u\n", - indent_level, rdi_stringize_spaces, proc->type_idx); - - str8_list_pushf(arena, out, "%.*sroot_scope_idx=%u\n", - indent_level, rdi_stringize_spaces, proc->root_scope_idx); - - str8_list_pushf(arena, out, "%.*scontainer_idx=%u\n", - indent_level, rdi_stringize_spaces, proc->container_idx); -} - -internal void -rdi_stringize_scope(Arena *arena, String8List *out, RDI_Parsed *rdi, - RDI_ScopeBundle *bundle, RDI_Scope *scope, U32 indent_level) -{ - - U32 this_idx = (U32)(scope - bundle->scopes); - - str8_list_pushf(arena, out, "%.*s[%u]\n", - indent_level, rdi_stringize_spaces, this_idx); - - str8_list_pushf(arena, out, "%.*s proc_idx=%u\n", - indent_level, rdi_stringize_spaces, scope->proc_idx); - - if(scope->inline_site_idx != 0) - { - str8_list_pushf(arena, out, "%.*s inline_site_idx=%u\n", - indent_level, rdi_stringize_spaces, scope->inline_site_idx); - } - - // voff ranges - { - U32 voff_range_first_raw = scope->voff_range_first; - U32 voff_range_opl_raw = scope->voff_range_opl; - U32 voff_range_opl_clamped = ClampTop(voff_range_opl_raw, bundle->scope_voff_count); - U32 voff_range_first = ClampTop(voff_range_first_raw, voff_range_opl_clamped); - U32 voff_range_opl = voff_range_opl_clamped; - if ((voff_range_opl - voff_range_first) % 2 == 1){ - voff_range_opl -= 1; - } - - U64 *voff_ptr = bundle->scope_voffs + voff_range_first; - - if (voff_range_opl - voff_range_first > 2){ - str8_list_pushf(arena, out, "%.*s voff_ranges={\n", - indent_level, rdi_stringize_spaces); - for (U32 i = voff_range_first; i < voff_range_opl; i += 2, voff_ptr += 2){ - str8_list_pushf(arena, out, "%.*s [0x%08llx, 0x%08llx)\n", - indent_level, rdi_stringize_spaces, - voff_ptr[0], voff_ptr[1]); - } - str8_list_pushf(arena, out, "%.*s }\n", - indent_level, rdi_stringize_spaces); - } - else if (voff_range_opl - voff_range_first == 2){ - str8_list_pushf(arena, out, "%.*s voff_range=[0x%08llx, 0x%08llx)\n", - indent_level, rdi_stringize_spaces, - voff_ptr[0], voff_ptr[1]); - } - } - - // locals - { - U32 local_first = scope->local_first; - U32 local_opl_raw = local_first + scope->local_count; - U32 local_opl = ClampTop(local_opl_raw, bundle->local_count); - - if (local_first < local_opl){ - RDI_Local *local_ptr = bundle->locals + local_first; - for (U32 i = local_first; i < local_opl; i += 1, local_ptr += 1){ - str8_list_pushf(arena, out, "%.*s local[%u]\n", - indent_level, rdi_stringize_spaces, i); - - String8 local_kind_str = rdi_string_from_local_kind(local_ptr->kind); - str8_list_pushf(arena, out, "%.*s kind=%.*s\n", - indent_level, rdi_stringize_spaces, str8_varg(local_kind_str)); - - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, local_ptr->name_string_idx, &name.size); - str8_list_pushf(arena, out, "%.*s name='%.*s'\n", - indent_level, rdi_stringize_spaces, str8_varg(name)); - - str8_list_pushf(arena, out, "%.*s type_idx=%u\n", - indent_level, rdi_stringize_spaces, local_ptr->type_idx); - - U32 location_first = local_ptr->location_first; - U32 location_opl_raw = local_ptr->location_opl; - U32 location_opl = ClampTop(location_opl_raw, bundle->location_block_count); - - if (location_first < location_opl){ - str8_list_pushf(arena, out, "%.*s locations:\n", indent_level, rdi_stringize_spaces); - RDI_LocationBlock *block_ptr = bundle->location_blocks + location_first; - for (U32 j = location_first; j < location_opl; j += 1, block_ptr += 1){ - if (block_ptr->scope_off_first == 0 && block_ptr->scope_off_opl == max_U32){ - str8_list_pushf(arena, out, "%.*s case *always*:\n", indent_level, rdi_stringize_spaces); - } - else{ - str8_list_pushf(arena, out, "%.*s case [0x%08x, 0x%08x):\n", - indent_level, rdi_stringize_spaces, - block_ptr->scope_off_first, block_ptr->scope_off_opl); - } - - if (block_ptr->location_data_off >= bundle->location_data_size){ - str8_list_pushf(arena, out, "%.*s \n", - indent_level, rdi_stringize_spaces); - } - else{ - str8_list_pushf(arena, out, "%.*s ", indent_level, rdi_stringize_spaces); - - U8 *loc_data_opl = bundle->location_data + bundle->location_data_size; - U8 *loc_base_ptr = bundle->location_data + block_ptr->location_data_off; - RDI_LocationKind kind = (RDI_LocationKind)*loc_base_ptr; - switch (kind){ - default: - { - str8_list_pushf(arena, out, "\n"); - }break; - - case RDI_LocationKind_AddrBytecodeStream: - { - str8_list_pushf(arena, out, "AddrBytecodeStream\n"); - str8_list_pushf(arena, out, "%.*s ", indent_level, rdi_stringize_spaces); - U8 *bytecode_ptr = loc_base_ptr + 1; - for (;bytecode_ptr < loc_data_opl && *bytecode_ptr != 0; bytecode_ptr += 1){ - str8_list_pushf(arena, out, "%02x ", *bytecode_ptr); - } - str8_list_pushf(arena, out, "\n"); - }break; - - case RDI_LocationKind_ValBytecodeStream: - { - str8_list_pushf(arena, out, "ValBytecodeStream\n"); - str8_list_pushf(arena, out, "%.*s ", indent_level, rdi_stringize_spaces); - U8 *bytecode_ptr = loc_base_ptr + 1; - for (;bytecode_ptr < loc_data_opl && *bytecode_ptr != 0; bytecode_ptr += 1){ - str8_list_pushf(arena, out, "%02x ", *bytecode_ptr); - } - str8_list_pushf(arena, out, "\n"); - }break; - - case RDI_LocationKind_AddrRegPlusU16: - { - if (loc_base_ptr + sizeof(RDI_LocationRegPlusU16) > loc_data_opl){ - str8_list_pushf(arena, out, "AddrRegPlusU16( )\n"); - } - else{ - RDI_LocationRegPlusU16 *loc = (RDI_LocationRegPlusU16*)loc_base_ptr; - str8_list_pushf(arena, out, "AddrRegPlusU16(reg: %u, off: %u)\n", - loc->reg_code, loc->offset); - } - }break; - - case RDI_LocationKind_AddrAddrRegPlusU16: - { - if (loc_base_ptr + sizeof(RDI_LocationRegPlusU16) > loc_data_opl){ - str8_list_pushf(arena, out, "AddrAddrRegPlusU16( )\n"); - } - else{ - RDI_LocationRegPlusU16 *loc = (RDI_LocationRegPlusU16*)loc_base_ptr; - str8_list_pushf(arena, out, "AddrAddrRegisterPlusU16(reg: %u, off: %u)\n", - loc->reg_code, loc->offset); - } - }break; - - case RDI_LocationKind_ValReg: - { - if (loc_base_ptr + sizeof(RDI_LocationReg) > loc_data_opl){ - str8_list_pushf(arena, out, "ValReg( )\n"); - } - else{ - RDI_LocationReg *loc = (RDI_LocationReg*)loc_base_ptr; - str8_list_pushf(arena, out, "ValReg(reg: %u)\n", loc->reg_code); - } - }break; - } - } - } - } - } - } - } - - // TODO(allen): static locals - - for (U32 child = scope->first_child_scope_idx; - child != 0;){ - // get scope for child - RDI_Scope *child_scope = 0; - if (child < bundle->scope_count){ - child_scope = bundle->scopes + child; - } - if (child_scope == 0){ - break; - } - - // stringize child - rdi_stringize_scope(arena, out, rdi, bundle, child_scope, indent_level + 1); - - // increment iterator - child = child_scope->next_sibling_scope_idx; - } - - str8_list_pushf(arena, out, "%.*s[/%u]\n", - indent_level, rdi_stringize_spaces, this_idx); -} - -internal void -rdi_stringize_inline_site(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_InlineSite *inline_site, U32 indent_level) -{ - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, inline_site->name_string_idx, &name.size); - str8_list_pushf(arena, out, "%.*sname='%S'\n", indent_level, rdi_stringize_spaces, name); - str8_list_pushf(arena, out, "%.*stype_idx=%u\n", indent_level, rdi_stringize_spaces, inline_site->type_idx); - str8_list_pushf(arena, out, "%.*sowner_type_idx=%u\n", indent_level, rdi_stringize_spaces, inline_site->owner_type_idx); - str8_list_pushf(arena, out, "%.*sline_table_idx=%u\n", indent_level, rdi_stringize_spaces, inline_site->line_table_idx); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: RDI Enum -> String Functions + +internal String8 +rdi_string_from_data_section_kind(RDI_SectionKind v) +{ + String8 result = str8_lit(""); + switch(v) + { + default:{}break; +#define X(name, lower, type) case RDI_SectionKind_##name:{result = str8_lit(#name);}break; + RDI_SectionKind_XList +#undef X + } + return result; +} + +internal String8 +rdi_string_from_arch(RDI_Arch v) +{ + String8 result = str8_lit(""); + switch(v) + { + default:{}break; +#define X(name) case RDI_Arch_##name:{result = str8_lit(#name);}break; + RDI_Arch_XList +#undef X + } + return result; +} + +internal String8 +rdi_string_from_language(RDI_Language v) +{ + String8 result = str8_lit(""); + switch(v) + { + default:{}break; +#define X(name) case RDI_Language_##name:{result = str8_lit(#name);}break; + RDI_Language_XList +#undef X + } + return result; +} + +internal String8 +rdi_string_from_type_kind(RDI_TypeKind v) +{ + String8 result = str8_lit(""); + switch(v) + { + default:{}break; +#define X(name) case RDI_TypeKind_##name:{result = str8_lit(#name);}break; + RDI_TypeKind_XList +#undef X + } + return result; +} + +internal String8 +rdi_string_from_member_kind(RDI_MemberKind v) +{ + String8 result = str8_lit(""); + switch(v) + { + default:{}break; +#define X(name) case RDI_MemberKind_##name:{result = str8_lit(#name);}break; + RDI_MemberKind_XList +#undef X + } + return result; +} + +internal String8 +rdi_string_from_local_kind(RDI_LocalKind v) +{ + String8 result = str8_lit(""); + switch(v) + { + default:{}break; +#define X(name) case RDI_LocalKind_##name:{result = str8_lit(#name);}break; + RDI_LocalKind_XList +#undef X + } + return result; +} + +//////////////////////////////// +//~ rjf: RDI Flags -> String Functions + +internal void +rdi_stringize_binary_section_flags(Arena *arena, String8List *out, RDI_BinarySectionFlags flags) +{ + if(flags == 0) { str8_list_push(arena, out, str8_lit("0")); } +#define X(name) if(flags & RDI_BinarySectionFlag_##name) { str8_list_push(arena, out, str8_lit(#name " ")); } + RDI_BinarySectionFlags_XList; +#undef X +} + +internal void +rdi_stringize_type_modifier_flags(Arena *arena, String8List *out, + RDI_TypeModifierFlags flags) +{ + if(flags == 0) { str8_list_push(arena, out, str8_lit("0")); } +#define X(name) if(flags & RDI_TypeModifierFlag_##name) { str8_list_push(arena, out, str8_lit(#name " ")); } + RDI_TypeModifierFlags_XList; +#undef X +} + +internal void +rdi_stringize_udt_flags(Arena *arena, String8List *out, RDI_UDTFlags flags) +{ + if(flags == 0) { str8_list_push(arena, out, str8_lit("0")); } +#define X(name) if(flags & RDI_UDTFlag_##name) { str8_list_push(arena, out, str8_lit(#name " ")); } + RDI_UDTFlags_XList; +#undef X +} + +internal void +rdi_stringize_link_flags(Arena *arena, String8List *out, RDI_LinkFlags flags) +{ + if(flags == 0) { str8_list_push(arena, out, str8_lit("0")); } +#define X(name) if(flags & RDI_LinkFlag_##name) { str8_list_push(arena, out, str8_lit(#name " ")); } + RDI_LinkFlags_XList; +#undef X +} + +//////////////////////////////// +//~ rjf: RADDBG Compound Stringize Functions + +global char rdi_stringize_spaces[] = " "; + +internal void +rdi_stringize_data_sections(Arena *arena, String8List *out, RDI_Parsed *rdi, U32 indent_level) +{ + for(U64 idx = 0; idx < rdi->sections_count; idx += 1) + { + RDI_SectionKind kind = (RDI_SectionKind)idx; + RDI_Section *section = &rdi->sections[idx]; + String8 kind_str = rdi_string_from_data_section_kind(kind); + str8_list_pushf(arena, out, "%.*sdata_section[%5I64u] = {0x%08llx, %7u, %7u} %S\n", + indent_level, rdi_stringize_spaces, + idx, section->off, section->encoded_size, section->unpacked_size, kind_str); + } +} + +internal void +rdi_stringize_top_level_info(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_TopLevelInfo *tli, U32 indent_level) +{ + String8 arch_str = rdi_string_from_arch(tli->arch); + String8 exe_name = {0}; + exe_name.str = rdi_string_from_idx(rdi, tli->exe_name_string_idx, &exe_name.size); + String8 producer_name = {0}; + producer_name.str = rdi_string_from_idx(rdi, tli->producer_name_string_idx, &producer_name.size); + str8_list_pushf(arena, out, "%.*sarch=%S\n", indent_level, rdi_stringize_spaces, arch_str); + str8_list_pushf(arena, out, "%.*sexe_name='%S'\n", indent_level, rdi_stringize_spaces, exe_name); + str8_list_pushf(arena, out, "%.*svoff_max=0x%08llx\n", indent_level, rdi_stringize_spaces, tli->voff_max); + str8_list_pushf(arena, out, "%.*sproducer_name='%S'\n", indent_level, rdi_stringize_spaces, producer_name); +} + +internal void +rdi_stringize_binary_section(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_BinarySection *bin_section, U32 indent_level) +{ + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, bin_section->name_string_idx, &name.size); + str8_list_pushf(arena, out, "%.*sname='%.*s'\n", indent_level, rdi_stringize_spaces, str8_varg(name)); + + str8_list_pushf(arena, out, "%.*sflags=", indent_level, rdi_stringize_spaces); + rdi_stringize_binary_section_flags(arena, out, bin_section->flags); + str8_list_pushf(arena, out, "\n"); + + str8_list_pushf(arena, out, "%.*svoff_first=0x%08x\n", indent_level, rdi_stringize_spaces, bin_section->voff_first); + str8_list_pushf(arena, out, "%.*svoff_opl =0x%08x\n", indent_level, rdi_stringize_spaces, bin_section->voff_opl); + str8_list_pushf(arena, out, "%.*sfoff_first=0x%08x\n", indent_level, rdi_stringize_spaces, bin_section->foff_first); + str8_list_pushf(arena, out, "%.*sfoff_opl =0x%08x\n", indent_level, rdi_stringize_spaces, bin_section->foff_opl); +} + +internal void +rdi_stringize_file_path(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_FilePathBundle *bundle, RDI_FilePathNode *file_path, U32 indent_level) +{ + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, file_path->name_string_idx, &name.size); + U32 this_idx = (U32)(file_path - bundle->file_paths); + if(file_path->source_file_idx == 0) + { + str8_list_pushf(arena, out, "%.*s[%u] '%.*s'\n", + indent_level, rdi_stringize_spaces, + this_idx, str8_varg(name)); + } + else + { + str8_list_pushf(arena, out, "%.*s[%u] '%.*s'; source_file=%u\n", + indent_level, rdi_stringize_spaces, + this_idx, str8_varg(name), file_path->source_file_idx); + } + + for(U32 child = file_path->first_child; child != 0;) + { + // get node for child + RDI_FilePathNode *child_node = 0; + if (child < bundle->file_path_count){ + child_node = bundle->file_paths + child; + } + if (child_node == 0){ + break; + } + + // stringize child + rdi_stringize_file_path(arena, out, rdi, bundle, child_node, indent_level + 1); + + // increment iterator + child = child_node->next_sibling; + } +} + +internal void +rdi_stringize_source_file(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_SourceFile *source_file, U32 indent_level) +{ + // normal source path + String8 path = {0}; + path.str = rdi_string_from_idx(rdi, source_file->normal_full_path_string_idx, &path.size); + str8_list_pushf(arena, out, "%.*spath: \"%S\"\n", indent_level, rdi_stringize_spaces, path); + + // rjf: source line map idx + str8_list_pushf(arena, out, "%.*ssource_line_map: %u\n", indent_level, rdi_stringize_spaces, source_file->source_line_map_idx); +} + +internal void +rdi_stringize_line_table(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_LineTable *line_table, U32 indent_level) +{ + // rjf: parse line table + RDI_ParsedLineTable parsed_line_table = {0}; + rdi_parsed_from_line_table(rdi, line_table, &parsed_line_table); + + // rjf: stringize lines + str8_list_pushf(arena, out, "%.*slines:\n", indent_level, rdi_stringize_spaces); + for(U32 i = 0; i < parsed_line_table.count; i += 1) + { + U64 first = parsed_line_table.voffs[i]; + U64 opl = parsed_line_table.voffs[i + 1]; + RDI_Line *line = parsed_line_table.lines + i; + RDI_Column *col = 0; + if(i < parsed_line_table.col_count) + { + col = parsed_line_table.cols + i; + } + if(col == 0) + { + str8_list_pushf(arena, out, "%.*s [0x%08llx,0x%08llx) file=%u; line=%u\n", + indent_level, rdi_stringize_spaces, + first, opl, line->file_idx, line->line_num); + } + else + { + str8_list_pushf(arena, out, "%.*s [0x%08llx,0x%08llx) file=%u; line=%u; columns=[%u,%u)\n", + indent_level, rdi_stringize_spaces, + first, opl, line->file_idx, line->line_num, + col->col_first, col->col_opl); + } + } +} + +internal void +rdi_stringize_source_line_map(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_SourceLineMap *map, U32 indent_level) +{ + RDI_ParsedSourceLineMap line_map = {0}; + rdi_parsed_from_source_line_map(rdi, map, &line_map); + str8_list_pushf(arena, out, "%.*slines:\n", indent_level, rdi_stringize_spaces); + + for(U32 i = 0; i < line_map.count; i += 1) + { + U32 line_num = line_map.nums[i]; + U32 digit_count = 1; + if(line_num > 0) + { + U32 x = line_num; + for(;;) + { + x /= 10; + if(x == 0) + { + break; + } + digit_count += 1; + } + } + + str8_list_pushf(arena, out, "%.*s %u: ", indent_level, rdi_stringize_spaces, line_num); + + U32 first = line_map.ranges[i]; + U32 opl_raw = line_map.ranges[i + 1]; + U32 opl = ClampTop(opl_raw, line_map.voff_count); + for(U32 j = first; j < opl; j += 1) + { + if(j == first) + { + str8_list_pushf(arena, out, "0x%08x\n", line_map.voffs[j]); + } + else + { + str8_list_pushf(arena, out, "%.*s0x%08x\n", + indent_level + digit_count + 3, rdi_stringize_spaces, + line_map.voffs[j]); + } + } + } +} + +internal void +rdi_stringize_unit(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_Unit *unit, U32 indent_level) +{ + String8 unit_name = {0}; + unit_name.str = rdi_string_from_idx(rdi, unit->unit_name_string_idx, &unit_name.size); + String8 compiler_name = {0}; + compiler_name.str = rdi_string_from_idx(rdi, unit->compiler_name_string_idx, &compiler_name.size); + + str8_list_pushf(arena, out, "%.*sunit_name='%.*s'\n", indent_level, rdi_stringize_spaces, str8_varg(unit_name)); + str8_list_pushf(arena, out, "%.*scompiler_name='%.*s'\n", indent_level, rdi_stringize_spaces, str8_varg(compiler_name)); + + str8_list_pushf(arena, out, "%.*ssource_file_path=%u\n", indent_level, rdi_stringize_spaces, unit->source_file_path_node); + str8_list_pushf(arena, out, "%.*sobject_file_path=%u\n", indent_level, rdi_stringize_spaces, unit->object_file_path_node); + str8_list_pushf(arena, out, "%.*sarchive_file_path=%u\n", indent_level, rdi_stringize_spaces, unit->archive_file_path_node); + str8_list_pushf(arena, out, "%.*sbuild_path=%u\n", indent_level, rdi_stringize_spaces, unit->build_path_node); + + String8 language_str = rdi_string_from_language(unit->language); + str8_list_pushf(arena, out, "%.*slanguage=%.*s\n", indent_level, rdi_stringize_spaces, str8_varg(language_str)); + + str8_list_pushf(arena, out, "%.*sline_table_idx=%u\n", indent_level, rdi_stringize_spaces, unit->line_table_idx); +} + +internal void +rdi_stringize_type_node(Arena *arena, String8List *out, RDI_Parsed *rdi, + RDI_TypeNode *type, U32 indent_level){ + RDI_TypeKind kind = type->kind; + String8 type_kind_str = rdi_string_from_type_kind(kind); + + str8_list_pushf(arena, out, "%.*skind=%.*s\n", + indent_level, rdi_stringize_spaces, str8_varg(type_kind_str)); + + switch (type->kind){ + case RDI_TypeKind_Modifier: + { + str8_list_pushf(arena, out, "%.*sflags=", indent_level, rdi_stringize_spaces); + rdi_stringize_type_modifier_flags(arena, out, type->flags); + str8_list_push(arena, out, str8_lit("\n")); + }break; + + default: + { + if (type->flags != 0){ + str8_list_pushf(arena, out, "%.*sflags=%x (missing stringizer path)", + indent_level, rdi_stringize_spaces, type->flags); + } + }break; + } + + str8_list_pushf(arena, out, "%.*sbyte_size=%u\n", + indent_level, rdi_stringize_spaces, type->byte_size); + + if (RDI_TypeKind_FirstBuiltIn <= kind && + kind <= RDI_TypeKind_LastBuiltIn){ + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, type->built_in.name_string_idx, &name.size); + str8_list_pushf(arena, out, "%.*sbuilt_in.name='%.*s'\n", + indent_level, rdi_stringize_spaces, str8_varg(name)); + } + + else if (RDI_TypeKind_FirstConstructed <= kind && + kind <= RDI_TypeKind_LastConstructed){ + str8_list_pushf(arena, out, "%.*sconstructed.direct_type=%u\n", + indent_level, rdi_stringize_spaces, type->constructed.direct_type_idx); + + if (type->kind == RDI_TypeKind_Array){ + str8_list_pushf(arena, out, "%.*sconstructed.array_count=%u\n", + indent_level, rdi_stringize_spaces, type->constructed.count); + } + + if (type->kind == RDI_TypeKind_Function || + type->kind == RDI_TypeKind_Method){ + U32 run_first = type->constructed.param_idx_run_first; + U32 run_count_raw = type->constructed.count; + + U32 run_count = 0; + U32 *run = rdi_idx_run_from_first_count(rdi, run_first, run_count_raw, &run_count); + + U32 this_type_idx = 0; + if (run_count > 0 && type->kind == RDI_TypeKind_Method){ + this_type_idx = run[0]; + run += 1; + run_count -= 1; + } + + if (this_type_idx != 0){ + str8_list_pushf(arena, out, "%.*sconstructed.this_type=%u\n", + indent_level, rdi_stringize_spaces, this_type_idx); + } + + str8_list_pushf(arena, out, "%.*sconstructed.params={", + indent_level, rdi_stringize_spaces); + + if (run_count > 0){ + U32 last = run_count - 1; + for (U32 j = 0; j < last; j += 1){ + str8_list_pushf(arena, out, " %u,", run[j]); + } + str8_list_pushf(arena, out, " %u ", run[last]); + } + + str8_list_push(arena, out, str8_lit("}\n")); + } + } + + else if (RDI_TypeKind_FirstUserDefined <= kind && + kind <= RDI_TypeKind_LastUserDefined){ + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, type->user_defined.name_string_idx, &name.size); + str8_list_pushf(arena, out, "%.*suser_defined.name='%.*s'\n", + indent_level, rdi_stringize_spaces, str8_varg(name)); + str8_list_pushf(arena, out, "%.*suser_defined.direct_type=%u\n", + indent_level, rdi_stringize_spaces, + type->user_defined.direct_type_idx); + str8_list_pushf(arena, out, "%.*suser_defined.udt=%u\n", + indent_level, rdi_stringize_spaces, + type->user_defined.udt_idx); + } + + else if (kind == RDI_TypeKind_Bitfield){ + str8_list_pushf(arena, out, "%.*sbitfield.off=%u\n", + indent_level, rdi_stringize_spaces, type->bitfield.off); + str8_list_pushf(arena, out, "%.*sbitfield.size=%u\n", + indent_level, rdi_stringize_spaces, type->bitfield.size); + } +} + +internal void +rdi_stringize_udt(Arena *arena, String8List *out, RDI_Parsed *rdi, + RDI_UDTMemberBundle *member_bundle, RDI_UDT *udt, + U32 indent_level){ + str8_list_pushf(arena, out, "%.*sself_type=%u\n", + indent_level, rdi_stringize_spaces, udt->self_type_idx); + + str8_list_pushf(arena, out, "%.*sflags=", indent_level, rdi_stringize_spaces); + rdi_stringize_udt_flags(arena, out, udt->flags); + str8_list_push(arena, out, str8_lit("\n")); + + if (udt->file_idx != 0){ + str8_list_pushf(arena, out, "%.*sloc={file=%u; line=%u; col=%u}\n", + indent_level, rdi_stringize_spaces, + udt->file_idx, udt->line, udt->col); + } + + // enum members + if (udt->flags & RDI_UDTFlag_EnumMembers){ + U32 first_raw = udt->member_first; + U32 opl_raw = first_raw + udt->member_count; + U32 opl = ClampTop(opl_raw, member_bundle->enum_member_count); + U32 first = ClampTop(first_raw, opl); + + if (first < opl){ + str8_list_pushf(arena, out, "%.*smembers={\n", indent_level, rdi_stringize_spaces); + RDI_EnumMember *enum_member = member_bundle->enum_members + first; + for (U32 i = first; i < opl; i += 1, enum_member += 1){ + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, enum_member->name_string_idx, &name.size); + str8_list_pushf(arena, out, "%.*s '%.*s' %llu\n", + indent_level, rdi_stringize_spaces, + str8_varg(name), enum_member->val); + } + str8_list_pushf(arena, out, "%.*s}\n", indent_level, rdi_stringize_spaces); + } + } + + // field members + else{ + U32 first_raw = udt->member_first; + U32 opl_raw = first_raw + udt->member_count; + U32 opl = ClampTop(opl_raw, member_bundle->member_count); + U32 first = ClampTop(first_raw, opl); + + if (first < opl){ + str8_list_pushf(arena, out, "%.*smembers={\n", indent_level, rdi_stringize_spaces); + RDI_Member *member = member_bundle->members + first; + for (U32 i = first; i < opl; i += 1, member += 1){ + str8_list_pushf(arena, out, "%.*s {\n", indent_level, rdi_stringize_spaces); + + String8 kind_str = rdi_string_from_member_kind(member->kind); + str8_list_pushf(arena, out, "%.*s kind=%.*s\n", + indent_level, rdi_stringize_spaces, str8_varg(kind_str)); + + if (member->name_string_idx != 0){ + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, member->name_string_idx, &name.size); + str8_list_pushf(arena, out, "%.*s name='%.*s'\n", + indent_level, rdi_stringize_spaces, str8_varg(name)); + } + + str8_list_pushf(arena, out, "%.*s type=%u\n", + indent_level, rdi_stringize_spaces, member->type_idx); + str8_list_pushf(arena, out, "%.*s off=%u\n", + indent_level, rdi_stringize_spaces, member->off); + + str8_list_pushf(arena, out, "%.*s }\n", indent_level, rdi_stringize_spaces); + } + str8_list_pushf(arena, out, "%.*s}\n", indent_level, rdi_stringize_spaces); + } + } +} + +internal void +rdi_stringize_global_variable(Arena *arena, String8List *out, RDI_Parsed *rdi, + RDI_GlobalVariable *global_variable, U32 indent_level){ + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, global_variable->name_string_idx, &name.size); + str8_list_pushf(arena, out, "%.*sname='%.*s'\n", + indent_level, rdi_stringize_spaces, str8_varg(name)); + + str8_list_pushf(arena, out, "%.*slink_flags=", indent_level, rdi_stringize_spaces); + rdi_stringize_link_flags(arena, out, global_variable->link_flags); + str8_list_push(arena, out, str8_lit("\n")); + + str8_list_pushf(arena, out, "%.*svoff=0x%08llx\n", + indent_level, rdi_stringize_spaces, global_variable->voff); + + str8_list_pushf(arena, out, "%.*stype_idx=%u\n", + indent_level, rdi_stringize_spaces, global_variable->type_idx); + + str8_list_pushf(arena, out, "%.*scontainer_idx=%u\n", + indent_level, rdi_stringize_spaces, global_variable->container_idx); +} + +internal void +rdi_stringize_thread_variable(Arena *arena, String8List *out, RDI_Parsed *rdi, + RDI_ThreadVariable *thread_var, + U32 indent_level){ + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, thread_var->name_string_idx, &name.size); + str8_list_pushf(arena, out, "%.*sname='%.*s'\n", + indent_level, rdi_stringize_spaces, str8_varg(name)); + + str8_list_pushf(arena, out, "%.*slink_flags=", indent_level, rdi_stringize_spaces); + rdi_stringize_link_flags(arena, out, thread_var->link_flags); + str8_list_push(arena, out, str8_lit("\n")); + + str8_list_pushf(arena, out, "%.*stls_off=0x%08x\n", + indent_level, rdi_stringize_spaces, thread_var->tls_off); + + str8_list_pushf(arena, out, "%.*stype_idx=%u\n", + indent_level, rdi_stringize_spaces, thread_var->type_idx); + + str8_list_pushf(arena, out, "%.*scontainer_idx=%u\n", + indent_level, rdi_stringize_spaces, thread_var->container_idx); +} + +internal void +rdi_stringize_procedure(Arena *arena, String8List *out, RDI_Parsed *rdi, + RDI_Procedure *proc, U32 indent_level){ + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, proc->name_string_idx, &name.size); + str8_list_pushf(arena, out, "%.*sname='%.*s'\n", + indent_level, rdi_stringize_spaces, str8_varg(name)); + + String8 link_name = {0}; + link_name.str = rdi_string_from_idx(rdi, proc->link_name_string_idx, &link_name.size); + str8_list_pushf(arena, out, "%.*slink_name='%.*s'\n", + indent_level, rdi_stringize_spaces, str8_varg(link_name)); + + str8_list_pushf(arena, out, "%.*slink_flags=", indent_level, rdi_stringize_spaces); + rdi_stringize_link_flags(arena, out, proc->link_flags); + str8_list_push(arena, out, str8_lit("\n")); + + str8_list_pushf(arena, out, "%.*stype_idx=%u\n", + indent_level, rdi_stringize_spaces, proc->type_idx); + + str8_list_pushf(arena, out, "%.*sroot_scope_idx=%u\n", + indent_level, rdi_stringize_spaces, proc->root_scope_idx); + + str8_list_pushf(arena, out, "%.*scontainer_idx=%u\n", + indent_level, rdi_stringize_spaces, proc->container_idx); +} + +internal void +rdi_stringize_scope(Arena *arena, String8List *out, RDI_Parsed *rdi, + RDI_ScopeBundle *bundle, RDI_Scope *scope, U32 indent_level) +{ + + U32 this_idx = (U32)(scope - bundle->scopes); + + str8_list_pushf(arena, out, "%.*s[%u]\n", + indent_level, rdi_stringize_spaces, this_idx); + + str8_list_pushf(arena, out, "%.*s proc_idx=%u\n", + indent_level, rdi_stringize_spaces, scope->proc_idx); + + if(scope->inline_site_idx != 0) + { + str8_list_pushf(arena, out, "%.*s inline_site_idx=%u\n", + indent_level, rdi_stringize_spaces, scope->inline_site_idx); + } + + // voff ranges + { + U32 voff_range_first_raw = scope->voff_range_first; + U32 voff_range_opl_raw = scope->voff_range_opl; + U32 voff_range_opl_clamped = ClampTop(voff_range_opl_raw, bundle->scope_voff_count); + U32 voff_range_first = ClampTop(voff_range_first_raw, voff_range_opl_clamped); + U32 voff_range_opl = voff_range_opl_clamped; + if ((voff_range_opl - voff_range_first) % 2 == 1){ + voff_range_opl -= 1; + } + + U64 *voff_ptr = bundle->scope_voffs + voff_range_first; + + if (voff_range_opl - voff_range_first > 2){ + str8_list_pushf(arena, out, "%.*s voff_ranges={\n", + indent_level, rdi_stringize_spaces); + for (U32 i = voff_range_first; i < voff_range_opl; i += 2, voff_ptr += 2){ + str8_list_pushf(arena, out, "%.*s [0x%08llx, 0x%08llx)\n", + indent_level, rdi_stringize_spaces, + voff_ptr[0], voff_ptr[1]); + } + str8_list_pushf(arena, out, "%.*s }\n", + indent_level, rdi_stringize_spaces); + } + else if (voff_range_opl - voff_range_first == 2){ + str8_list_pushf(arena, out, "%.*s voff_range=[0x%08llx, 0x%08llx)\n", + indent_level, rdi_stringize_spaces, + voff_ptr[0], voff_ptr[1]); + } + } + + // locals + { + U32 local_first = scope->local_first; + U32 local_opl_raw = local_first + scope->local_count; + U32 local_opl = ClampTop(local_opl_raw, bundle->local_count); + + if (local_first < local_opl){ + RDI_Local *local_ptr = bundle->locals + local_first; + for (U32 i = local_first; i < local_opl; i += 1, local_ptr += 1){ + str8_list_pushf(arena, out, "%.*s local[%u]\n", + indent_level, rdi_stringize_spaces, i); + + String8 local_kind_str = rdi_string_from_local_kind(local_ptr->kind); + str8_list_pushf(arena, out, "%.*s kind=%.*s\n", + indent_level, rdi_stringize_spaces, str8_varg(local_kind_str)); + + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, local_ptr->name_string_idx, &name.size); + str8_list_pushf(arena, out, "%.*s name='%.*s'\n", + indent_level, rdi_stringize_spaces, str8_varg(name)); + + str8_list_pushf(arena, out, "%.*s type_idx=%u\n", + indent_level, rdi_stringize_spaces, local_ptr->type_idx); + + U32 location_first = local_ptr->location_first; + U32 location_opl_raw = local_ptr->location_opl; + U32 location_opl = ClampTop(location_opl_raw, bundle->location_block_count); + + if (location_first < location_opl){ + str8_list_pushf(arena, out, "%.*s locations:\n", indent_level, rdi_stringize_spaces); + RDI_LocationBlock *block_ptr = bundle->location_blocks + location_first; + for (U32 j = location_first; j < location_opl; j += 1, block_ptr += 1){ + if (block_ptr->scope_off_first == 0 && block_ptr->scope_off_opl == max_U32){ + str8_list_pushf(arena, out, "%.*s case *always*:\n", indent_level, rdi_stringize_spaces); + } + else{ + str8_list_pushf(arena, out, "%.*s case [0x%08x, 0x%08x):\n", + indent_level, rdi_stringize_spaces, + block_ptr->scope_off_first, block_ptr->scope_off_opl); + } + + if (block_ptr->location_data_off >= bundle->location_data_size){ + str8_list_pushf(arena, out, "%.*s \n", + indent_level, rdi_stringize_spaces); + } + else{ + str8_list_pushf(arena, out, "%.*s ", indent_level, rdi_stringize_spaces); + + U8 *loc_data_opl = bundle->location_data + bundle->location_data_size; + U8 *loc_base_ptr = bundle->location_data + block_ptr->location_data_off; + RDI_LocationKind kind = (RDI_LocationKind)*loc_base_ptr; + switch (kind){ + default: + { + str8_list_pushf(arena, out, "\n"); + }break; + + case RDI_LocationKind_AddrBytecodeStream: + { + str8_list_pushf(arena, out, "AddrBytecodeStream\n"); + str8_list_pushf(arena, out, "%.*s ", indent_level, rdi_stringize_spaces); + U8 *bytecode_ptr = loc_base_ptr + 1; + for (;bytecode_ptr < loc_data_opl && *bytecode_ptr != 0; bytecode_ptr += 1){ + str8_list_pushf(arena, out, "%02x ", *bytecode_ptr); + } + str8_list_pushf(arena, out, "\n"); + }break; + + case RDI_LocationKind_ValBytecodeStream: + { + str8_list_pushf(arena, out, "ValBytecodeStream\n"); + str8_list_pushf(arena, out, "%.*s ", indent_level, rdi_stringize_spaces); + U8 *bytecode_ptr = loc_base_ptr + 1; + for (;bytecode_ptr < loc_data_opl && *bytecode_ptr != 0; bytecode_ptr += 1){ + str8_list_pushf(arena, out, "%02x ", *bytecode_ptr); + } + str8_list_pushf(arena, out, "\n"); + }break; + + case RDI_LocationKind_AddrRegPlusU16: + { + if (loc_base_ptr + sizeof(RDI_LocationRegPlusU16) > loc_data_opl){ + str8_list_pushf(arena, out, "AddrRegPlusU16( )\n"); + } + else{ + RDI_LocationRegPlusU16 *loc = (RDI_LocationRegPlusU16*)loc_base_ptr; + str8_list_pushf(arena, out, "AddrRegPlusU16(reg: %u, off: %u)\n", + loc->reg_code, loc->offset); + } + }break; + + case RDI_LocationKind_AddrAddrRegPlusU16: + { + if (loc_base_ptr + sizeof(RDI_LocationRegPlusU16) > loc_data_opl){ + str8_list_pushf(arena, out, "AddrAddrRegPlusU16( )\n"); + } + else{ + RDI_LocationRegPlusU16 *loc = (RDI_LocationRegPlusU16*)loc_base_ptr; + str8_list_pushf(arena, out, "AddrAddrRegisterPlusU16(reg: %u, off: %u)\n", + loc->reg_code, loc->offset); + } + }break; + + case RDI_LocationKind_ValReg: + { + if (loc_base_ptr + sizeof(RDI_LocationReg) > loc_data_opl){ + str8_list_pushf(arena, out, "ValReg( )\n"); + } + else{ + RDI_LocationReg *loc = (RDI_LocationReg*)loc_base_ptr; + str8_list_pushf(arena, out, "ValReg(reg: %u)\n", loc->reg_code); + } + }break; + } + } + } + } + } + } + } + + // TODO(allen): static locals + + for (U32 child = scope->first_child_scope_idx; + child != 0;){ + // get scope for child + RDI_Scope *child_scope = 0; + if (child < bundle->scope_count){ + child_scope = bundle->scopes + child; + } + if (child_scope == 0){ + break; + } + + // stringize child + rdi_stringize_scope(arena, out, rdi, bundle, child_scope, indent_level + 1); + + // increment iterator + child = child_scope->next_sibling_scope_idx; + } + + str8_list_pushf(arena, out, "%.*s[/%u]\n", + indent_level, rdi_stringize_spaces, this_idx); +} + +internal void +rdi_stringize_inline_site(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_InlineSite *inline_site, U32 indent_level) +{ + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, inline_site->name_string_idx, &name.size); + str8_list_pushf(arena, out, "%.*sname='%S'\n", indent_level, rdi_stringize_spaces, name); + str8_list_pushf(arena, out, "%.*stype_idx=%u\n", indent_level, rdi_stringize_spaces, inline_site->type_idx); + str8_list_pushf(arena, out, "%.*sowner_type_idx=%u\n", indent_level, rdi_stringize_spaces, inline_site->owner_type_idx); + str8_list_pushf(arena, out, "%.*sline_table_idx=%u\n", indent_level, rdi_stringize_spaces, inline_site->line_table_idx); +} diff --git a/src/rdi_dump/rdi_dump.h b/src/rdi_dump/rdi_dump.h index 7a469d9d..73610c35 100644 --- a/src/rdi_dump/rdi_dump.h +++ b/src/rdi_dump/rdi_dump.h @@ -1,78 +1,78 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RDI_DUMP_H -#define RDI_DUMP_H - -//////////////////////////////// -//~ rjf: RADDBG Stringize Helper Types - -typedef struct RDI_FilePathBundle RDI_FilePathBundle; -struct RDI_FilePathBundle -{ - RDI_FilePathNode *file_paths; - U64 file_path_count; -}; - -typedef struct RDI_UDTMemberBundle RDI_UDTMemberBundle; -struct RDI_UDTMemberBundle -{ - RDI_Member *members; - RDI_EnumMember *enum_members; - U32 member_count; - U32 enum_member_count; -}; - -typedef struct RDI_ScopeBundle RDI_ScopeBundle; -struct RDI_ScopeBundle -{ - RDI_Scope *scopes; - U64 *scope_voffs; - RDI_Local *locals; - RDI_LocationBlock *location_blocks; - U8 *location_data; - U32 scope_count; - U32 scope_voff_count; - U32 local_count; - U32 location_block_count; - U32 location_data_size; -}; - -//////////////////////////////// -//~ rjf: RDI Enum -> String Functions - -internal String8 rdi_string_from_data_section_kind(RDI_SectionKind v); -internal String8 rdi_string_from_arch(RDI_Arch v); -internal String8 rdi_string_from_language(RDI_Language v); -internal String8 rdi_string_from_type_kind(RDI_TypeKind v); -internal String8 rdi_string_from_member_kind(RDI_MemberKind v); -internal String8 rdi_string_from_local_kind(RDI_LocalKind v); - -//////////////////////////////// -//~ rjf: RDI Flags -> String Functions - -internal void rdi_stringize_binary_section_flags(Arena *arena, String8List *out, RDI_BinarySectionFlags flags); -internal void rdi_stringize_type_modifier_flags(Arena *arena, String8List *out, RDI_TypeModifierFlags flags); -internal void rdi_stringize_udt_flags(Arena *arena, String8List *out, RDI_UDTFlags flags); -internal void rdi_stringize_link_flags(Arena *arena, String8List *out, RDI_LinkFlags flags); - -//////////////////////////////// -//~ rjf: RDI Compound Stringize Functions - -internal void rdi_stringize_data_sections(Arena *arena, String8List *out, RDI_Parsed *rdi, U32 indent_level); -internal void rdi_stringize_top_level_info(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_TopLevelInfo *tli, U32 indent_level); -internal void rdi_stringize_binary_section(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_BinarySection *bin_section, U32 indent_level); -internal void rdi_stringize_file_path(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_FilePathBundle *bundle, RDI_FilePathNode *file_path, U32 indent_level); -internal void rdi_stringize_source_file(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_SourceFile *source_file, U32 indent_level); -internal void rdi_stringize_line_table(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_LineTable *line_table, U32 indent_level); -internal void rdi_stringize_source_line_map(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_SourceLineMap *map, U32 indent_level); -internal void rdi_stringize_unit(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_Unit *unit, U32 indent_level); -internal void rdi_stringize_type_node(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_TypeNode *type, U32 indent_level); -internal void rdi_stringize_udt(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_UDTMemberBundle *bundle, RDI_UDT *udt, U32 indent_level); -internal void rdi_stringize_global_variable(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_GlobalVariable *global_variable, U32 indent_level); -internal void rdi_stringize_thread_variable(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_ThreadVariable *thread_var, U32 indent_level); -internal void rdi_stringize_procedure(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_Procedure *proc, U32 indent_level); -internal void rdi_stringize_scope(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_ScopeBundle *bundle, RDI_Scope *scope, U32 indent_level); -internal void rdi_stringize_inline_site(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_InlineSite *inline_site, U32 indent_level); - -#endif // RDI_DUMP_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RDI_DUMP_H +#define RDI_DUMP_H + +//////////////////////////////// +//~ rjf: RADDBG Stringize Helper Types + +typedef struct RDI_FilePathBundle RDI_FilePathBundle; +struct RDI_FilePathBundle +{ + RDI_FilePathNode *file_paths; + U64 file_path_count; +}; + +typedef struct RDI_UDTMemberBundle RDI_UDTMemberBundle; +struct RDI_UDTMemberBundle +{ + RDI_Member *members; + RDI_EnumMember *enum_members; + U32 member_count; + U32 enum_member_count; +}; + +typedef struct RDI_ScopeBundle RDI_ScopeBundle; +struct RDI_ScopeBundle +{ + RDI_Scope *scopes; + U64 *scope_voffs; + RDI_Local *locals; + RDI_LocationBlock *location_blocks; + U8 *location_data; + U32 scope_count; + U32 scope_voff_count; + U32 local_count; + U32 location_block_count; + U32 location_data_size; +}; + +//////////////////////////////// +//~ rjf: RDI Enum -> String Functions + +internal String8 rdi_string_from_data_section_kind(RDI_SectionKind v); +internal String8 rdi_string_from_arch(RDI_Arch v); +internal String8 rdi_string_from_language(RDI_Language v); +internal String8 rdi_string_from_type_kind(RDI_TypeKind v); +internal String8 rdi_string_from_member_kind(RDI_MemberKind v); +internal String8 rdi_string_from_local_kind(RDI_LocalKind v); + +//////////////////////////////// +//~ rjf: RDI Flags -> String Functions + +internal void rdi_stringize_binary_section_flags(Arena *arena, String8List *out, RDI_BinarySectionFlags flags); +internal void rdi_stringize_type_modifier_flags(Arena *arena, String8List *out, RDI_TypeModifierFlags flags); +internal void rdi_stringize_udt_flags(Arena *arena, String8List *out, RDI_UDTFlags flags); +internal void rdi_stringize_link_flags(Arena *arena, String8List *out, RDI_LinkFlags flags); + +//////////////////////////////// +//~ rjf: RDI Compound Stringize Functions + +internal void rdi_stringize_data_sections(Arena *arena, String8List *out, RDI_Parsed *rdi, U32 indent_level); +internal void rdi_stringize_top_level_info(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_TopLevelInfo *tli, U32 indent_level); +internal void rdi_stringize_binary_section(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_BinarySection *bin_section, U32 indent_level); +internal void rdi_stringize_file_path(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_FilePathBundle *bundle, RDI_FilePathNode *file_path, U32 indent_level); +internal void rdi_stringize_source_file(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_SourceFile *source_file, U32 indent_level); +internal void rdi_stringize_line_table(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_LineTable *line_table, U32 indent_level); +internal void rdi_stringize_source_line_map(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_SourceLineMap *map, U32 indent_level); +internal void rdi_stringize_unit(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_Unit *unit, U32 indent_level); +internal void rdi_stringize_type_node(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_TypeNode *type, U32 indent_level); +internal void rdi_stringize_udt(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_UDTMemberBundle *bundle, RDI_UDT *udt, U32 indent_level); +internal void rdi_stringize_global_variable(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_GlobalVariable *global_variable, U32 indent_level); +internal void rdi_stringize_thread_variable(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_ThreadVariable *thread_var, U32 indent_level); +internal void rdi_stringize_procedure(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_Procedure *proc, U32 indent_level); +internal void rdi_stringize_scope(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_ScopeBundle *bundle, RDI_Scope *scope, U32 indent_level); +internal void rdi_stringize_inline_site(Arena *arena, String8List *out, RDI_Parsed *rdi, RDI_InlineSite *inline_site, U32 indent_level); + +#endif // RDI_DUMP_H diff --git a/src/rdi_dump/rdi_dump_main.c b/src/rdi_dump/rdi_dump_main.c index 7f83e9e8..49fab69e 100644 --- a/src/rdi_dump/rdi_dump_main.c +++ b/src/rdi_dump/rdi_dump_main.c @@ -1,519 +1,519 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Build Options - -#define BUILD_VERSION_MAJOR 0 -#define BUILD_VERSION_MINOR 9 -#define BUILD_VERSION_PATCH 11 -#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA" -#define BUILD_TITLE "rdi_dump" -#define BUILD_CONSOLE_INTERFACE 1 - -//////////////////////////////// -//~ rjf: Includes - -//- rjf: [lib] -#include "third_party/rad_lzb_simple/rad_lzb_simple.h" -#include "third_party/rad_lzb_simple/rad_lzb_simple.c" - -//- rjf: [h] -#include "base/base_inc.h" -#include "os/os_inc.h" -#include "rdi_format/rdi_format_local.h" -#include "rdi_dump.h" - -//- rjf: [c] -#include "base/base_inc.c" -#include "os/os_inc.c" -#include "rdi_format/rdi_format_local.c" -#include "rdi_dump.c" - -//////////////////////////////// -//~ rjf: Entry Point - -internal void -entry_point(CmdLine *cmd_line) -{ - ////////////////////////////// - //- rjf: set up - // - Arena *arena = arena_alloc(); - String8List errors = {0}; - - ////////////////////////////// - //- rjf: extract command line parameters - // - typedef U32 DumpFlags; - enum - { - DumpFlag_DataSections = (1<<0), - DumpFlag_TopLevelInfo = (1<<1), - DumpFlag_BinarySections = (1<<2), - DumpFlag_FilePaths = (1<<3), - DumpFlag_SourceFiles = (1<<4), - DumpFlag_LineTables = (1<<5), - DumpFlag_SourceLineMaps = (1<<6), - DumpFlag_Units = (1<<7), - DumpFlag_UnitVMap = (1<<8), - DumpFlag_TypeNodes = (1<<9), - DumpFlag_UDTs = (1<<10), - DumpFlag_GlobalVariables = (1<<11), - DumpFlag_GlobalVMap = (1<<12), - DumpFlag_ThreadVariables = (1<<13), - DumpFlag_Procedures = (1<<14), - DumpFlag_Scopes = (1<<15), - DumpFlag_ScopeVMap = (1<<16), - DumpFlag_InlineSites = (1<<17), - DumpFlag_NameMaps = (1<<18), - DumpFlag_Strings = (1<<19), - }; - String8 input_name = {0}; - DumpFlags dump_flags = (U32)0xffffffff; - { - // rjf: extract input file path - input_name = str8_list_first(&cmd_line->inputs); - - // rjf: extract "only" options - { - String8List dump_options = cmd_line_strings(cmd_line, str8_lit("only")); - if(dump_options.first != 0) - { - dump_flags = 0; - for(String8Node *n = dump_options.first; n != 0; n = n->next) - { - if(0){} - else if(str8_match(n->string, str8_lit("data_sections"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_DataSections; } - else if(str8_match(n->string, str8_lit("top_level_info"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_TopLevelInfo; } - else if(str8_match(n->string, str8_lit("binary_sections"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_BinarySections; } - else if(str8_match(n->string, str8_lit("file_paths"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_FilePaths; } - else if(str8_match(n->string, str8_lit("source_files"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_SourceFiles; } - else if(str8_match(n->string, str8_lit("line_tables"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_LineTables; } - else if(str8_match(n->string, str8_lit("source_line_maps"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_SourceLineMaps; } - else if(str8_match(n->string, str8_lit("units"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_Units; } - else if(str8_match(n->string, str8_lit("unit_vmap"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_UnitVMap; } - else if(str8_match(n->string, str8_lit("type_nodes"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_TypeNodes; } - else if(str8_match(n->string, str8_lit("udt_data"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_UDTs; } - else if(str8_match(n->string, str8_lit("global_variables"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_GlobalVariables; } - else if(str8_match(n->string, str8_lit("global_vmap"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_GlobalVMap; } - else if(str8_match(n->string, str8_lit("thread_variables"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_ThreadVariables; } - else if(str8_match(n->string, str8_lit("procedures"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_Procedures; } - else if(str8_match(n->string, str8_lit("scopes"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_Scopes; } - else if(str8_match(n->string, str8_lit("scope_vmap"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_ScopeVMap; } - else if(str8_match(n->string, str8_lit("inline_sites"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_InlineSites; } - else if(str8_match(n->string, str8_lit("name_maps"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_NameMaps; } - else if(str8_match(n->string, str8_lit("strings"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_Strings; } - } - } - } - } - - ////////////////////////////// - //- rjf: load file - // - String8 input_data = os_data_from_file_path(arena, input_name); - if(input_name.size == 0) - { - str8_list_pushf(arena, &errors, "error (input): No input RDI file specified."); - } - else if(input_data.size == 0) - { - str8_list_pushf(arena, &errors, "error (input): No input RDI file successfully loaded; either the path or file contents are invalid."); - } - - ////////////////////////////// - //- rjf: obtain initial rdi parse - // - RDI_Parsed rdi_ = {0}; - RDI_Parsed *rdi = &rdi_; - RDI_ParseStatus status = rdi_parse(input_data.str, input_data.size, rdi); - - ////////////////////////////// - //- rjf: decompress rdi if necessary - // - { - U64 decompressed_size = rdi_decompressed_size_from_parsed(rdi); - if(decompressed_size > input_data.size) - { - U8 *decompressed_data = push_array_no_zero(arena, U8, decompressed_size); - rdi_decompress_parsed(decompressed_data, decompressed_size, rdi); - status = rdi_parse(decompressed_data, decompressed_size, rdi); - } - } - - ////////////////////////////// - //- rjf: error on bad parse status - // - if(status != RDI_ParseStatus_Good) - { - str8_list_pushf(arena, &errors, "error (input): RDI file could not be successfully decoded."); - } - - ////////////////////////////// - //- rjf: output error strings to stderr - // - for(String8Node *n = errors.first; n != 0; n = n->next) - { - fwrite(n->string.str, 1, n->string.size, stderr); - fprintf(stderr, "\n"); - } - - ////////////////////////////// - //- rjf: build dump strings - // - String8List dump = {0}; - if(errors.node_count == 0) - { - //- rjf: DATA SECTIONS - if(dump_flags & DumpFlag_DataSections) - { - str8_list_pushf(arena, &dump, "# DATA SECTIONS:\n"); - rdi_stringize_data_sections(arena, &dump, rdi, 1); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: TOP LEVEL INFO - if(dump_flags & DumpFlag_TopLevelInfo) - { - str8_list_pushf(arena, &dump, "# TOP LEVEL INFO:\n"); - RDI_TopLevelInfo *tli = rdi_element_from_name_idx(rdi, TopLevelInfo, 0); - rdi_stringize_top_level_info(arena, &dump, rdi, tli, 1); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: BINARY SECTIONS - if(dump_flags & DumpFlag_BinarySections) - { - str8_list_pushf(arena, &dump, "# BINARY SECTIONS:\n"); - U64 count = 0; - RDI_BinarySection *v = rdi_table_from_name(rdi, BinarySections, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " section[%I64u]:\n", idx); - rdi_stringize_binary_section(arena, &dump, rdi, &v[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: FILE PATHS - if(dump_flags & DumpFlag_FilePaths) - { - RDI_FilePathBundle file_path_bundle = {0}; - file_path_bundle.file_paths = rdi_table_from_name(rdi, FilePathNodes, &file_path_bundle.file_path_count); - str8_list_pushf(arena, &dump, "# FILE PATHS\n"); - RDI_FilePathNode *ptr = file_path_bundle.file_paths; - for(U32 i = 0; i < file_path_bundle.file_path_count; i += 1, ptr += 1) - { - if(ptr->parent_path_node == 0) - { - rdi_stringize_file_path(arena, &dump, rdi, &file_path_bundle, ptr, 1); - } - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: SOURCE FILES - if(dump_flags & DumpFlag_SourceFiles) - { - str8_list_pushf(arena, &dump, "# SOURCE FILES\n"); - U64 count = 0; - RDI_SourceFile *v = rdi_table_from_name(rdi, SourceFiles, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " source_file[%I64u]:\n", idx); - rdi_stringize_source_file(arena, &dump, rdi, &v[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: LINE TABLES - if(dump_flags & DumpFlag_LineTables) - { - str8_list_pushf(arena, &dump, "# LINE TABLES\n"); - U64 count = 0; - RDI_LineTable *v = rdi_table_from_name(rdi, LineTables, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " line_table[%I64u]:\n", idx); - rdi_stringize_line_table(arena, &dump, rdi, &v[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: SOURCE LINE MAPS - if(dump_flags & DumpFlag_SourceLineMaps) - { - str8_list_pushf(arena, &dump, "# SOURCE LINE MAPS\n"); - U64 count = 0; - RDI_SourceLineMap *v = rdi_table_from_name(rdi, SourceLineMaps, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " source_line_map[%I64u]:\n", idx); - rdi_stringize_source_line_map(arena, &dump, rdi, &v[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: UNITS - if(dump_flags & DumpFlag_Units) - { - str8_list_pushf(arena, &dump, "# UNITS\n"); - U64 count = 0; - RDI_Unit *v = rdi_table_from_name(rdi, Units, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " unit[%I64u]:\n", idx); - rdi_stringize_unit(arena, &dump, rdi, &v[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: UNIT VMAP - if(dump_flags & DumpFlag_UnitVMap) - { - str8_list_pushf(arena, &dump, "# UNIT VMAP\n"); - U64 count = 0; - RDI_VMapEntry *v = rdi_table_from_name(rdi, UnitVMap, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " 0x%08x: %llu\n", v[idx].voff, v[idx].idx); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: TYPE NODES - if(dump_flags & DumpFlag_TypeNodes) - { - str8_list_pushf(arena, &dump, "# TYPE NODES:\n"); - U64 count = 0; - RDI_TypeNode *v = rdi_table_from_name(rdi, TypeNodes, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " type[%I64u]:\n", idx); - rdi_stringize_type_node(arena, &dump, rdi, &v[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: UDT DATA - if(dump_flags & DumpFlag_UDTs) - { - U64 all_members_count = 0; - RDI_Member *all_members = rdi_table_from_name(rdi, Members, &all_members_count); - U64 all_enum_members_count = 0; - RDI_EnumMember *all_enum_members = rdi_table_from_name(rdi, EnumMembers, &all_enum_members_count); - U64 all_udts_count = 0; - RDI_UDT *all_udts = rdi_table_from_name(rdi, UDTs, &all_udts_count); - RDI_UDTMemberBundle member_bundle = {0}; - { - member_bundle.members = all_members; - member_bundle.enum_members = all_enum_members; - member_bundle.member_count = (RDI_U32)all_members_count; - member_bundle.enum_member_count = (RDI_U32)all_enum_members_count; - } - str8_list_pushf(arena, &dump, "# UDTS:\n"); - for(U64 idx = 0; idx < all_udts_count; idx += 1) - { - str8_list_pushf(arena, &dump, " udt[%I64u]:\n", idx); - rdi_stringize_udt(arena, &dump, rdi, &member_bundle, &all_udts[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: GLOBAL VARIABLES - if(dump_flags & DumpFlag_GlobalVariables) - { - str8_list_pushf(arena, &dump, "# GLOBAL VARIABLES:\n"); - RDI_U64 count = 0; - RDI_GlobalVariable *v = rdi_table_from_name(rdi, GlobalVariables, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " global_variable[%I64u]:\n", idx); - rdi_stringize_global_variable(arena, &dump, rdi, &v[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: GLOBAL VMAP - if(dump_flags & DumpFlag_GlobalVMap) - { - str8_list_pushf(arena, &dump, "# GLOBAL VMAP:\n"); - U64 count = 0; - RDI_VMapEntry *v = rdi_table_from_name(rdi, GlobalVMap, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " 0x%08x: %llu\n", v[idx].voff, v[idx].idx); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: THREAD LOCAL VARIABLES - if(dump_flags & DumpFlag_ThreadVariables) - { - str8_list_pushf(arena, &dump, "# THREAD VARIABLES:\n"); - U64 count = 0; - RDI_ThreadVariable *v = rdi_table_from_name(rdi, ThreadVariables, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " thread_variable[%I64u]:\n", idx); - rdi_stringize_thread_variable(arena, &dump, rdi, &v[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: PROCEDURES - if(dump_flags & DumpFlag_Procedures) - { - str8_list_pushf(arena, &dump, "# PROCEDURES:\n"); - U64 count = 0; - RDI_Procedure *v = rdi_table_from_name(rdi, Procedures, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " procedure[%I64u]:\n", idx); - rdi_stringize_procedure(arena, &dump, rdi, &v[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: SCOPES - if(dump_flags & DumpFlag_Scopes) - { - U64 scopes_count = 0; - RDI_Scope *scopes = rdi_table_from_name(rdi, Scopes, &scopes_count); - U64 scopes_voffs_count = 0; - U64 *scopes_voffs = rdi_table_from_name(rdi, ScopeVOffData, &scopes_voffs_count); - U64 locals_count = 0; - RDI_Local *locals = rdi_table_from_name(rdi, Locals, &locals_count); - U64 location_block_count = 0; - RDI_LocationBlock *location_blocks = rdi_table_from_name(rdi, LocationBlocks, &location_block_count); - U64 location_data_size = 0; - RDI_U8 *location_data = rdi_table_from_name(rdi, LocationData, &location_data_size); - RDI_ScopeBundle scope_bundle = {0}; - { - scope_bundle.scopes = scopes; - scope_bundle.scope_count = scopes_count; - scope_bundle.scope_voffs = scopes_voffs; - scope_bundle.scope_voff_count = scopes_voffs_count; - scope_bundle.locals = locals; - scope_bundle.local_count = locals_count; - scope_bundle.location_blocks = location_blocks; - scope_bundle.location_block_count = location_block_count; - scope_bundle.location_data = location_data; - scope_bundle.location_data_size = location_data_size; - } - str8_list_pushf(arena, &dump, "# SCOPES:\n"); - for(U64 idx = 0; idx < scopes_count; idx += 1) - { - if(scopes[idx].parent_scope_idx == 0) - { - rdi_stringize_scope(arena, &dump, rdi, &scope_bundle, &scopes[idx], 1); - } - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: SCOPE VMAP - if(dump_flags & DumpFlag_ScopeVMap) - { - str8_list_pushf(arena, &dump, "# SCOPE VMAP:\n"); - U64 count = 0; - RDI_VMapEntry *v = rdi_table_from_name(rdi, ScopeVMap, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " 0x%08x: %llu\n", v[idx].voff, v[idx].idx); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: INLINE SITES - if(dump_flags & DumpFlag_InlineSites) - { - str8_list_pushf(arena, &dump, "# INLINE SITES:\n"); - U64 count = 0; - RDI_InlineSite *v = rdi_table_from_name(rdi, InlineSites, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - str8_list_pushf(arena, &dump, " inline_site[%I64u]:\n", idx); - rdi_stringize_inline_site(arena, &dump, rdi, &v[idx], 2); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: NAME MAPS - if(dump_flags & DumpFlag_NameMaps) - { - str8_list_pushf(arena, &dump, "# NAME MAP:\n"); - U64 count = 0; - RDI_NameMap *v = rdi_table_from_name(rdi, NameMaps, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - RDI_ParsedNameMap name_map = {0}; - rdi_parsed_from_name_map(rdi, &v[idx], &name_map); - str8_list_pushf(arena, &dump, " name_map[%I64u]:\n", idx); - RDI_NameMapBucket *bucket = name_map.buckets; - for(U32 j = 0; j < name_map.bucket_count; j += 1, bucket += 1) - { - if(bucket->node_count > 0) - { - str8_list_pushf(arena, &dump, " bucket[%u]:\n", j); - RDI_NameMapNode *node = name_map.nodes + bucket->first_node; - RDI_NameMapNode *node_opl = node + bucket->node_count; - for(;node < node_opl; node += 1) - { - String8 string = {0}; - string.str = rdi_string_from_idx(rdi, node->string_idx, &string.size); - str8_list_pushf(arena, &dump, " match \"%.*s\": ", str8_varg(string)); - if(node->match_count == 1) - { - str8_list_pushf(arena, &dump, "%u", node->match_idx_or_idx_run_first); - } - else - { - RDI_U32 idx_count = 0; - RDI_U32 *idx_run = - rdi_idx_run_from_first_count(rdi, node->match_idx_or_idx_run_first, - node->match_count, &idx_count); - if(idx_count > 0) - { - RDI_U32 last = idx_count - 1; - for(U32 k = 0; k < last; k += 1) - { - str8_list_pushf(arena, &dump, "%u, ", idx_run[k]); - } - str8_list_pushf(arena, &dump, "%u", idx_run[last]); - } - } - str8_list_pushf(arena, &dump, "\n"); - } - } - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - - //- rjf: STRINGS - if(dump_flags & DumpFlag_Strings) - { - str8_list_pushf(arena, &dump, "# STRINGS:\n"); - U64 count = 0; - U32 *v = rdi_table_from_name(rdi, StringTable, &count); - for(U64 idx = 0; idx < count; idx += 1) - { - String8 string = {0}; - string.str = rdi_string_from_idx(rdi, (RDI_U32)idx, &string.size); - str8_list_pushf(arena, &dump, " string[%I64u]: \"%S\"\n", idx, string); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - } - - ////////////////////////////// - //- rjf: write dump to stdout - // - for(String8Node *n = dump.first; n != 0; n = n->next) - { - fwrite(n->string.str, 1, n->string.size, stdout); - } -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Build Options + +#define BUILD_VERSION_MAJOR 0 +#define BUILD_VERSION_MINOR 9 +#define BUILD_VERSION_PATCH 11 +#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA" +#define BUILD_TITLE "rdi_dump" +#define BUILD_CONSOLE_INTERFACE 1 + +//////////////////////////////// +//~ rjf: Includes + +//- rjf: [lib] +#include "third_party/rad_lzb_simple/rad_lzb_simple.h" +#include "third_party/rad_lzb_simple/rad_lzb_simple.c" + +//- rjf: [h] +#include "base/base_inc.h" +#include "os/os_inc.h" +#include "rdi_format/rdi_format_local.h" +#include "rdi_dump.h" + +//- rjf: [c] +#include "base/base_inc.c" +#include "os/os_inc.c" +#include "rdi_format/rdi_format_local.c" +#include "rdi_dump.c" + +//////////////////////////////// +//~ rjf: Entry Point + +internal void +entry_point(CmdLine *cmd_line) +{ + ////////////////////////////// + //- rjf: set up + // + Arena *arena = arena_alloc(); + String8List errors = {0}; + + ////////////////////////////// + //- rjf: extract command line parameters + // + typedef U32 DumpFlags; + enum + { + DumpFlag_DataSections = (1<<0), + DumpFlag_TopLevelInfo = (1<<1), + DumpFlag_BinarySections = (1<<2), + DumpFlag_FilePaths = (1<<3), + DumpFlag_SourceFiles = (1<<4), + DumpFlag_LineTables = (1<<5), + DumpFlag_SourceLineMaps = (1<<6), + DumpFlag_Units = (1<<7), + DumpFlag_UnitVMap = (1<<8), + DumpFlag_TypeNodes = (1<<9), + DumpFlag_UDTs = (1<<10), + DumpFlag_GlobalVariables = (1<<11), + DumpFlag_GlobalVMap = (1<<12), + DumpFlag_ThreadVariables = (1<<13), + DumpFlag_Procedures = (1<<14), + DumpFlag_Scopes = (1<<15), + DumpFlag_ScopeVMap = (1<<16), + DumpFlag_InlineSites = (1<<17), + DumpFlag_NameMaps = (1<<18), + DumpFlag_Strings = (1<<19), + }; + String8 input_name = {0}; + DumpFlags dump_flags = (U32)0xffffffff; + { + // rjf: extract input file path + input_name = str8_list_first(&cmd_line->inputs); + + // rjf: extract "only" options + { + String8List dump_options = cmd_line_strings(cmd_line, str8_lit("only")); + if(dump_options.first != 0) + { + dump_flags = 0; + for(String8Node *n = dump_options.first; n != 0; n = n->next) + { + if(0){} + else if(str8_match(n->string, str8_lit("data_sections"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_DataSections; } + else if(str8_match(n->string, str8_lit("top_level_info"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_TopLevelInfo; } + else if(str8_match(n->string, str8_lit("binary_sections"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_BinarySections; } + else if(str8_match(n->string, str8_lit("file_paths"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_FilePaths; } + else if(str8_match(n->string, str8_lit("source_files"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_SourceFiles; } + else if(str8_match(n->string, str8_lit("line_tables"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_LineTables; } + else if(str8_match(n->string, str8_lit("source_line_maps"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_SourceLineMaps; } + else if(str8_match(n->string, str8_lit("units"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_Units; } + else if(str8_match(n->string, str8_lit("unit_vmap"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_UnitVMap; } + else if(str8_match(n->string, str8_lit("type_nodes"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_TypeNodes; } + else if(str8_match(n->string, str8_lit("udt_data"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_UDTs; } + else if(str8_match(n->string, str8_lit("global_variables"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_GlobalVariables; } + else if(str8_match(n->string, str8_lit("global_vmap"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_GlobalVMap; } + else if(str8_match(n->string, str8_lit("thread_variables"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_ThreadVariables; } + else if(str8_match(n->string, str8_lit("procedures"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_Procedures; } + else if(str8_match(n->string, str8_lit("scopes"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_Scopes; } + else if(str8_match(n->string, str8_lit("scope_vmap"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_ScopeVMap; } + else if(str8_match(n->string, str8_lit("inline_sites"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_InlineSites; } + else if(str8_match(n->string, str8_lit("name_maps"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_NameMaps; } + else if(str8_match(n->string, str8_lit("strings"), StringMatchFlag_CaseInsensitive)) { dump_flags |= DumpFlag_Strings; } + } + } + } + } + + ////////////////////////////// + //- rjf: load file + // + String8 input_data = os_data_from_file_path(arena, input_name); + if(input_name.size == 0) + { + str8_list_pushf(arena, &errors, "error (input): No input RDI file specified."); + } + else if(input_data.size == 0) + { + str8_list_pushf(arena, &errors, "error (input): No input RDI file successfully loaded; either the path or file contents are invalid."); + } + + ////////////////////////////// + //- rjf: obtain initial rdi parse + // + RDI_Parsed rdi_ = {0}; + RDI_Parsed *rdi = &rdi_; + RDI_ParseStatus status = rdi_parse(input_data.str, input_data.size, rdi); + + ////////////////////////////// + //- rjf: decompress rdi if necessary + // + { + U64 decompressed_size = rdi_decompressed_size_from_parsed(rdi); + if(decompressed_size > input_data.size) + { + U8 *decompressed_data = push_array_no_zero(arena, U8, decompressed_size); + rdi_decompress_parsed(decompressed_data, decompressed_size, rdi); + status = rdi_parse(decompressed_data, decompressed_size, rdi); + } + } + + ////////////////////////////// + //- rjf: error on bad parse status + // + if(status != RDI_ParseStatus_Good) + { + str8_list_pushf(arena, &errors, "error (input): RDI file could not be successfully decoded."); + } + + ////////////////////////////// + //- rjf: output error strings to stderr + // + for(String8Node *n = errors.first; n != 0; n = n->next) + { + fwrite(n->string.str, 1, n->string.size, stderr); + fprintf(stderr, "\n"); + } + + ////////////////////////////// + //- rjf: build dump strings + // + String8List dump = {0}; + if(errors.node_count == 0) + { + //- rjf: DATA SECTIONS + if(dump_flags & DumpFlag_DataSections) + { + str8_list_pushf(arena, &dump, "# DATA SECTIONS:\n"); + rdi_stringize_data_sections(arena, &dump, rdi, 1); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: TOP LEVEL INFO + if(dump_flags & DumpFlag_TopLevelInfo) + { + str8_list_pushf(arena, &dump, "# TOP LEVEL INFO:\n"); + RDI_TopLevelInfo *tli = rdi_element_from_name_idx(rdi, TopLevelInfo, 0); + rdi_stringize_top_level_info(arena, &dump, rdi, tli, 1); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: BINARY SECTIONS + if(dump_flags & DumpFlag_BinarySections) + { + str8_list_pushf(arena, &dump, "# BINARY SECTIONS:\n"); + U64 count = 0; + RDI_BinarySection *v = rdi_table_from_name(rdi, BinarySections, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " section[%I64u]:\n", idx); + rdi_stringize_binary_section(arena, &dump, rdi, &v[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: FILE PATHS + if(dump_flags & DumpFlag_FilePaths) + { + RDI_FilePathBundle file_path_bundle = {0}; + file_path_bundle.file_paths = rdi_table_from_name(rdi, FilePathNodes, &file_path_bundle.file_path_count); + str8_list_pushf(arena, &dump, "# FILE PATHS\n"); + RDI_FilePathNode *ptr = file_path_bundle.file_paths; + for(U32 i = 0; i < file_path_bundle.file_path_count; i += 1, ptr += 1) + { + if(ptr->parent_path_node == 0) + { + rdi_stringize_file_path(arena, &dump, rdi, &file_path_bundle, ptr, 1); + } + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: SOURCE FILES + if(dump_flags & DumpFlag_SourceFiles) + { + str8_list_pushf(arena, &dump, "# SOURCE FILES\n"); + U64 count = 0; + RDI_SourceFile *v = rdi_table_from_name(rdi, SourceFiles, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " source_file[%I64u]:\n", idx); + rdi_stringize_source_file(arena, &dump, rdi, &v[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: LINE TABLES + if(dump_flags & DumpFlag_LineTables) + { + str8_list_pushf(arena, &dump, "# LINE TABLES\n"); + U64 count = 0; + RDI_LineTable *v = rdi_table_from_name(rdi, LineTables, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " line_table[%I64u]:\n", idx); + rdi_stringize_line_table(arena, &dump, rdi, &v[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: SOURCE LINE MAPS + if(dump_flags & DumpFlag_SourceLineMaps) + { + str8_list_pushf(arena, &dump, "# SOURCE LINE MAPS\n"); + U64 count = 0; + RDI_SourceLineMap *v = rdi_table_from_name(rdi, SourceLineMaps, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " source_line_map[%I64u]:\n", idx); + rdi_stringize_source_line_map(arena, &dump, rdi, &v[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: UNITS + if(dump_flags & DumpFlag_Units) + { + str8_list_pushf(arena, &dump, "# UNITS\n"); + U64 count = 0; + RDI_Unit *v = rdi_table_from_name(rdi, Units, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " unit[%I64u]:\n", idx); + rdi_stringize_unit(arena, &dump, rdi, &v[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: UNIT VMAP + if(dump_flags & DumpFlag_UnitVMap) + { + str8_list_pushf(arena, &dump, "# UNIT VMAP\n"); + U64 count = 0; + RDI_VMapEntry *v = rdi_table_from_name(rdi, UnitVMap, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " 0x%08x: %llu\n", v[idx].voff, v[idx].idx); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: TYPE NODES + if(dump_flags & DumpFlag_TypeNodes) + { + str8_list_pushf(arena, &dump, "# TYPE NODES:\n"); + U64 count = 0; + RDI_TypeNode *v = rdi_table_from_name(rdi, TypeNodes, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " type[%I64u]:\n", idx); + rdi_stringize_type_node(arena, &dump, rdi, &v[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: UDT DATA + if(dump_flags & DumpFlag_UDTs) + { + U64 all_members_count = 0; + RDI_Member *all_members = rdi_table_from_name(rdi, Members, &all_members_count); + U64 all_enum_members_count = 0; + RDI_EnumMember *all_enum_members = rdi_table_from_name(rdi, EnumMembers, &all_enum_members_count); + U64 all_udts_count = 0; + RDI_UDT *all_udts = rdi_table_from_name(rdi, UDTs, &all_udts_count); + RDI_UDTMemberBundle member_bundle = {0}; + { + member_bundle.members = all_members; + member_bundle.enum_members = all_enum_members; + member_bundle.member_count = (RDI_U32)all_members_count; + member_bundle.enum_member_count = (RDI_U32)all_enum_members_count; + } + str8_list_pushf(arena, &dump, "# UDTS:\n"); + for(U64 idx = 0; idx < all_udts_count; idx += 1) + { + str8_list_pushf(arena, &dump, " udt[%I64u]:\n", idx); + rdi_stringize_udt(arena, &dump, rdi, &member_bundle, &all_udts[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: GLOBAL VARIABLES + if(dump_flags & DumpFlag_GlobalVariables) + { + str8_list_pushf(arena, &dump, "# GLOBAL VARIABLES:\n"); + RDI_U64 count = 0; + RDI_GlobalVariable *v = rdi_table_from_name(rdi, GlobalVariables, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " global_variable[%I64u]:\n", idx); + rdi_stringize_global_variable(arena, &dump, rdi, &v[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: GLOBAL VMAP + if(dump_flags & DumpFlag_GlobalVMap) + { + str8_list_pushf(arena, &dump, "# GLOBAL VMAP:\n"); + U64 count = 0; + RDI_VMapEntry *v = rdi_table_from_name(rdi, GlobalVMap, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " 0x%08x: %llu\n", v[idx].voff, v[idx].idx); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: THREAD LOCAL VARIABLES + if(dump_flags & DumpFlag_ThreadVariables) + { + str8_list_pushf(arena, &dump, "# THREAD VARIABLES:\n"); + U64 count = 0; + RDI_ThreadVariable *v = rdi_table_from_name(rdi, ThreadVariables, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " thread_variable[%I64u]:\n", idx); + rdi_stringize_thread_variable(arena, &dump, rdi, &v[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: PROCEDURES + if(dump_flags & DumpFlag_Procedures) + { + str8_list_pushf(arena, &dump, "# PROCEDURES:\n"); + U64 count = 0; + RDI_Procedure *v = rdi_table_from_name(rdi, Procedures, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " procedure[%I64u]:\n", idx); + rdi_stringize_procedure(arena, &dump, rdi, &v[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: SCOPES + if(dump_flags & DumpFlag_Scopes) + { + U64 scopes_count = 0; + RDI_Scope *scopes = rdi_table_from_name(rdi, Scopes, &scopes_count); + U64 scopes_voffs_count = 0; + U64 *scopes_voffs = rdi_table_from_name(rdi, ScopeVOffData, &scopes_voffs_count); + U64 locals_count = 0; + RDI_Local *locals = rdi_table_from_name(rdi, Locals, &locals_count); + U64 location_block_count = 0; + RDI_LocationBlock *location_blocks = rdi_table_from_name(rdi, LocationBlocks, &location_block_count); + U64 location_data_size = 0; + RDI_U8 *location_data = rdi_table_from_name(rdi, LocationData, &location_data_size); + RDI_ScopeBundle scope_bundle = {0}; + { + scope_bundle.scopes = scopes; + scope_bundle.scope_count = scopes_count; + scope_bundle.scope_voffs = scopes_voffs; + scope_bundle.scope_voff_count = scopes_voffs_count; + scope_bundle.locals = locals; + scope_bundle.local_count = locals_count; + scope_bundle.location_blocks = location_blocks; + scope_bundle.location_block_count = location_block_count; + scope_bundle.location_data = location_data; + scope_bundle.location_data_size = location_data_size; + } + str8_list_pushf(arena, &dump, "# SCOPES:\n"); + for(U64 idx = 0; idx < scopes_count; idx += 1) + { + if(scopes[idx].parent_scope_idx == 0) + { + rdi_stringize_scope(arena, &dump, rdi, &scope_bundle, &scopes[idx], 1); + } + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: SCOPE VMAP + if(dump_flags & DumpFlag_ScopeVMap) + { + str8_list_pushf(arena, &dump, "# SCOPE VMAP:\n"); + U64 count = 0; + RDI_VMapEntry *v = rdi_table_from_name(rdi, ScopeVMap, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " 0x%08x: %llu\n", v[idx].voff, v[idx].idx); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: INLINE SITES + if(dump_flags & DumpFlag_InlineSites) + { + str8_list_pushf(arena, &dump, "# INLINE SITES:\n"); + U64 count = 0; + RDI_InlineSite *v = rdi_table_from_name(rdi, InlineSites, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + str8_list_pushf(arena, &dump, " inline_site[%I64u]:\n", idx); + rdi_stringize_inline_site(arena, &dump, rdi, &v[idx], 2); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: NAME MAPS + if(dump_flags & DumpFlag_NameMaps) + { + str8_list_pushf(arena, &dump, "# NAME MAP:\n"); + U64 count = 0; + RDI_NameMap *v = rdi_table_from_name(rdi, NameMaps, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + RDI_ParsedNameMap name_map = {0}; + rdi_parsed_from_name_map(rdi, &v[idx], &name_map); + str8_list_pushf(arena, &dump, " name_map[%I64u]:\n", idx); + RDI_NameMapBucket *bucket = name_map.buckets; + for(U32 j = 0; j < name_map.bucket_count; j += 1, bucket += 1) + { + if(bucket->node_count > 0) + { + str8_list_pushf(arena, &dump, " bucket[%u]:\n", j); + RDI_NameMapNode *node = name_map.nodes + bucket->first_node; + RDI_NameMapNode *node_opl = node + bucket->node_count; + for(;node < node_opl; node += 1) + { + String8 string = {0}; + string.str = rdi_string_from_idx(rdi, node->string_idx, &string.size); + str8_list_pushf(arena, &dump, " match \"%.*s\": ", str8_varg(string)); + if(node->match_count == 1) + { + str8_list_pushf(arena, &dump, "%u", node->match_idx_or_idx_run_first); + } + else + { + RDI_U32 idx_count = 0; + RDI_U32 *idx_run = + rdi_idx_run_from_first_count(rdi, node->match_idx_or_idx_run_first, + node->match_count, &idx_count); + if(idx_count > 0) + { + RDI_U32 last = idx_count - 1; + for(U32 k = 0; k < last; k += 1) + { + str8_list_pushf(arena, &dump, "%u, ", idx_run[k]); + } + str8_list_pushf(arena, &dump, "%u", idx_run[last]); + } + } + str8_list_pushf(arena, &dump, "\n"); + } + } + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + + //- rjf: STRINGS + if(dump_flags & DumpFlag_Strings) + { + str8_list_pushf(arena, &dump, "# STRINGS:\n"); + U64 count = 0; + U32 *v = rdi_table_from_name(rdi, StringTable, &count); + for(U64 idx = 0; idx < count; idx += 1) + { + String8 string = {0}; + string.str = rdi_string_from_idx(rdi, (RDI_U32)idx, &string.size); + str8_list_pushf(arena, &dump, " string[%I64u]: \"%S\"\n", idx, string); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + } + + ////////////////////////////// + //- rjf: write dump to stdout + // + for(String8Node *n = dump.first; n != 0; n = n->next) + { + fwrite(n->string.str, 1, n->string.size, stdout); + } +} diff --git a/src/rdi_format/rdi_format_local.c b/src/rdi_format/rdi_format_local.c index 661fb0b1..5e134430 100644 --- a/src/rdi_format/rdi_format_local.c +++ b/src/rdi_format/rdi_format_local.c @@ -1,52 +1,52 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#include "lib_rdi_format/rdi_format.c" -#include "lib_rdi_format/rdi_format_parse.c" - -internal void -rdi_decompress_parsed(U8 *decompressed_data, U64 decompressed_size, RDI_Parsed *og_rdi) -{ - // rjf: copy header - RDI_Header *src_header = (RDI_Header *)og_rdi->raw_data; - RDI_Header *dst_header = (RDI_Header *)decompressed_data; - { - MemoryCopy(dst_header, src_header, sizeof(RDI_Header)); - } - - // rjf: copy & adjust sections for decompressed version - if(og_rdi->sections_count != 0) - { - RDI_Section *dsec_base = (RDI_Section *)(decompressed_data + dst_header->data_section_off); - MemoryCopy(dsec_base, (U8 *)og_rdi->raw_data + src_header->data_section_off, sizeof(RDI_Section) * og_rdi->sections_count); - U64 off = dst_header->data_section_off + sizeof(RDI_Section) * og_rdi->sections_count; - off += 7; - off -= off%8; - for(U64 idx = 0; idx < og_rdi->sections_count; idx += 1) - { - dsec_base[idx].encoding = RDI_SectionEncoding_Unpacked; - dsec_base[idx].off = off; - dsec_base[idx].encoded_size = dsec_base[idx].unpacked_size; - off += dsec_base[idx].unpacked_size; - off += 7; - off -= off%8; - } - } - - // rjf: decompress sections into new decompressed file buffer - if(og_rdi->sections_count != 0) - { - RDI_Section *src_first = og_rdi->sections; - RDI_Section *dst_first = (RDI_Section *)(decompressed_data + dst_header->data_section_off); - RDI_Section *src_opl = src_first + og_rdi->sections_count; - RDI_Section *dst_opl = dst_first + og_rdi->sections_count; - for(RDI_Section *src = src_first, *dst = dst_first; - src < src_opl && dst < dst_opl; - src += 1, dst += 1) - { - rr_lzb_simple_decode((U8*)og_rdi->raw_data + src->off, src->encoded_size, - decompressed_data + dst->off, dst->unpacked_size); - } - } -} - +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "lib_rdi_format/rdi_format.c" +#include "lib_rdi_format/rdi_format_parse.c" + +internal void +rdi_decompress_parsed(U8 *decompressed_data, U64 decompressed_size, RDI_Parsed *og_rdi) +{ + // rjf: copy header + RDI_Header *src_header = (RDI_Header *)og_rdi->raw_data; + RDI_Header *dst_header = (RDI_Header *)decompressed_data; + { + MemoryCopy(dst_header, src_header, sizeof(RDI_Header)); + } + + // rjf: copy & adjust sections for decompressed version + if(og_rdi->sections_count != 0) + { + RDI_Section *dsec_base = (RDI_Section *)(decompressed_data + dst_header->data_section_off); + MemoryCopy(dsec_base, (U8 *)og_rdi->raw_data + src_header->data_section_off, sizeof(RDI_Section) * og_rdi->sections_count); + U64 off = dst_header->data_section_off + sizeof(RDI_Section) * og_rdi->sections_count; + off += 7; + off -= off%8; + for(U64 idx = 0; idx < og_rdi->sections_count; idx += 1) + { + dsec_base[idx].encoding = RDI_SectionEncoding_Unpacked; + dsec_base[idx].off = off; + dsec_base[idx].encoded_size = dsec_base[idx].unpacked_size; + off += dsec_base[idx].unpacked_size; + off += 7; + off -= off%8; + } + } + + // rjf: decompress sections into new decompressed file buffer + if(og_rdi->sections_count != 0) + { + RDI_Section *src_first = og_rdi->sections; + RDI_Section *dst_first = (RDI_Section *)(decompressed_data + dst_header->data_section_off); + RDI_Section *src_opl = src_first + og_rdi->sections_count; + RDI_Section *dst_opl = dst_first + og_rdi->sections_count; + for(RDI_Section *src = src_first, *dst = dst_first; + src < src_opl && dst < dst_opl; + src += 1, dst += 1) + { + rr_lzb_simple_decode((U8*)og_rdi->raw_data + src->off, src->encoded_size, + decompressed_data + dst->off, dst->unpacked_size); + } + } +} + diff --git a/src/rdi_format/rdi_format_local.h b/src/rdi_format/rdi_format_local.h index 48f7eea3..5adc29c6 100644 --- a/src/rdi_format/rdi_format_local.h +++ b/src/rdi_format/rdi_format_local.h @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RDI_FORMAT_LOCAL_H -#define RDI_FORMAT_LOCAL_H - -#include "lib_rdi_format/rdi_format.h" -#include "lib_rdi_format/rdi_format_parse.h" - -internal void rdi_decompress_parsed(U8 *decompressed_data, U64 decompressed_size, RDI_Parsed *og_rdi); - -#endif // RDI_FORMAT_LOCAL_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RDI_FORMAT_LOCAL_H +#define RDI_FORMAT_LOCAL_H + +#include "lib_rdi_format/rdi_format.h" +#include "lib_rdi_format/rdi_format_parse.h" + +internal void rdi_decompress_parsed(U8 *decompressed_data, U64 decompressed_size, RDI_Parsed *og_rdi); + +#endif // RDI_FORMAT_LOCAL_H diff --git a/src/rdi_from_dwarf/rdi_dwarf.c b/src/rdi_from_dwarf/rdi_dwarf.c index a9f43b52..25b0a7f2 100644 --- a/src/rdi_from_dwarf/rdi_dwarf.c +++ b/src/rdi_from_dwarf/rdi_dwarf.c @@ -1,1888 +1,1888 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ Dwarf Decode Helpers - -static U64 -dwarf_leb128_decode_U64(U8 *ptr, U8 *opl){ - U64 r = 0; - switch (opl - ptr){ - case 10: r |= ((U64)(ptr[9]&0x7F) << 63); - case 9: r |= ((U64)(ptr[8]&0x7F) << 56); - case 8: r |= ((U64)(ptr[7]&0x7F) << 49); - case 7: r |= ((U64)(ptr[6]&0x7F) << 42); - case 6: r |= ((U64)(ptr[5]&0x7F) << 35); - case 5: r |= ((U64)(ptr[4]&0x7F) << 28); - case 4: r |= ((U64)(ptr[3]&0x7F) << 21); - case 3: r |= ((U64)(ptr[2]&0x7F) << 14); - case 2: r |= ((U64)(ptr[1]&0x7F) << 7); - case 1: r |= ((U64)(ptr[0]&0x7F) ); - case 0: default: break; - } - return(r); -} - -static S64 -dwarf_leb128_decode_S64(U8 *ptr, U8 *opl){ - U64 u = dwarf_leb128_decode_U32(ptr, opl); - U64 s = (U64)(opl - ptr)*7; - B32 neg = ((u & (1llu << s)) != 0); - if (neg){ - switch (opl - ptr){ - case 9: u |= ~0x7FFFFFFFFFFFFFFFllu; break; - case 8: u |= ~0x00FFFFFFFFFFFFFFllu; break; - case 7: u |= ~ 0x01FFFFFFFFFFFFllu; break; - case 6: u |= ~ 0x03FFFFFFFFFFllu; break; - case 5: u |= ~ 0x07FFFFFFFFllu; break; - case 4: u |= ~ 0x0FFFFFFFllu; break; - case 3: u |= ~ 0x1FFFFFllu; break; - case 2: u |= ~ 0x3FFFllu; break; - case 1: u |= ~ 0x7Fllu; break; - } - } - S64 r = (S64)(u); - return(r); -} - -static U32 -dwarf_leb128_decode_U32(U8 *ptr, U8 *opl){ - U32 r = 0; - switch (opl - ptr){ - case 5: r |= ((U32)(ptr[4]&0x7F) << 28); - case 4: r |= ((U32)(ptr[3]&0x7F) << 21); - case 3: r |= ((U32)(ptr[2]&0x7F) << 14); - case 2: r |= ((U32)(ptr[1]&0x7F) << 7); - case 1: r |= ((U32)(ptr[0]&0x7F) ); - case 0: default: break; - } - return(r); -} - - -//////////////////////////////// -//~ Dwarf Parser Functions - -static DWARF_Parsed* -dwarf_parsed_from_elf(Arena *arena, ELF_Parsed *elf){ - DWARF_Parsed *result = 0; - - if (elf != 0){ - //- extract debug info - U32 debug_section_idx[DWARF_SectionCode_COUNT] = {0}; - String8 debug_section_name[DWARF_SectionCode_COUNT] = {0}; - String8 debug_data[DWARF_SectionCode_COUNT] = {0}; - for (U64 i = 1; i < DWARF_SectionCode_COUNT; i += 1){ - DWARF_SectionNameRow *row = dwarf_section_name_table + i; - U32 idx = 0; - for (U32 j = 0; idx == 0 && j < DWARF_SECTION_NAME_VARIANT_COUNT; j += 1){ - idx = elf_section_idx_from_name(elf, row->name[j]); - } - debug_section_idx[i] = idx; - debug_section_name[i] = elf_section_name_from_idx(elf, idx); - debug_data[i] = elf_section_data_from_idx(elf, idx); - } - - //- fill result - { - result = push_array(arena, DWARF_Parsed, 1); - result->elf = elf; - MemoryCopyArray(result->debug_section_idx, debug_section_idx); - MemoryCopyArray(result->debug_section_name, debug_section_name); - MemoryCopyArray(result->debug_data, debug_data); - } - } - - return(result); -} - -static DWARF_IndexParsed* -dwarf_index_from_data(Arena *arena, String8 data){ - DWARF_IndexParsed *result = 0; - // TODO(allen): - return(result); -} - -static DWARF_SupParsed* -dwarf_sup_from_data(Arena *arena, String8 data){ - DWARF_SupParsed *result = 0; - // TODO(allen): - return(result); -} - -static DWARF_InfoParsed* -dwarf_info_from_data(Arena *arena, String8 data){ - // supported version numbers: 4,5 - - - // empty unit list - DWARF_InfoUnit *first = 0; - DWARF_InfoUnit *last = 0; - U64 count = 0; - - // whole section loop - U64 unit_idx = 0; - U8 *ptr = data.str; - U8 *opl = data.str + data.size; - for (;ptr < opl; unit_idx += 1){ - - // remember header offset - U64 hdr_off = (ptr - data.str); - - // initial length - U8 *unit_opl = 0; - B32 is_64bit = 0; - dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); - - // version - U8 version = MemoryConsume(U16, ptr, unit_opl); - - // rest of header depends on version - U64 abbrev_off = 0; - U8 address_size = 0; - U8 unit_type = 0; - U64 unit_dwo_id = 0; - U64 unit_type_signature = 0; - U64 unit_type_offset = 0; - switch (version){ - case 4: - { - // abbrev_off - if (is_64bit){ - abbrev_off = MemoryConsume(U64, ptr, unit_opl); - } - else{ - abbrev_off = MemoryConsume(U32, ptr, unit_opl); - } - - // address_size - address_size = MemoryConsume(U8, ptr, unit_opl); - }break; - - case 5: - { - // unit_type - unit_type = (DWARF_UnitType)MemoryConsume(U8, ptr, unit_opl); - - // address_size - address_size = MemoryConsume(U8, ptr, unit_opl); - - // abbrev_off - if (is_64bit){ - abbrev_off = MemoryConsume(U64, ptr, unit_opl); - } - else{ - abbrev_off = MemoryConsume(U32, ptr, unit_opl); - } - - // rest of header depends on unit_type - switch (unit_type){ - case DWARF_UnitType_skeleton: case DWARF_UnitType_split_compile: - { - unit_dwo_id = MemoryConsume(U64, ptr, unit_opl); - }break; - case DWARF_UnitType_type: case DWARF_UnitType_split_type: - { - unit_type_signature = MemoryConsume(U64, ptr, unit_opl); - if (is_64bit){ - unit_type_offset = MemoryConsume(U64, ptr, unit_opl); - } - else{ - unit_type_offset = MemoryConsume(U32, ptr, unit_opl); - } - }break; - } - }break; - } - - // offset size - U8 offset_size = is_64bit?8:4; - - // unit offsets - U64 base_off = (ptr - data.str); - U64 opl_off = (unit_opl - data.str); - - // emit unit - DWARF_InfoUnit *unit = push_array(arena, DWARF_InfoUnit, 1); - SLLQueuePush(first, last, unit); - count += 1; - - unit->hdr_off = hdr_off; - unit->base_off = base_off; - unit->opl_off = opl_off; - - unit->offset_size = offset_size; - unit->version = version; - unit->unit_type = unit_type; - unit->address_size = address_size; - unit->abbrev_off = abbrev_off; - - switch (unit_type){ - case DWARF_UnitType_skeleton: case DWARF_UnitType_split_compile: - { - unit->dwo_id = unit_dwo_id; - }break; - case DWARF_UnitType_type: case DWARF_UnitType_split_type: - { - unit->type_signature = unit_type_signature; - unit->type_offset = unit_type_offset; - }break; - } - - // advance to end of unit - ptr = unit_opl; - } - - // fill result - DWARF_InfoParsed *result = push_array(arena, DWARF_InfoParsed, 1); - result->unit_first = first; - result->unit_last = last; - result->unit_count = count; - return(result); -} - -static DWARF_PubNamesParsed* -dwarf_pubnames_from_data(Arena *arena, String8 data){ - // supported version numbers: 2 - - - // empty unit list - DWARF_PubNamesUnit *first = 0; - DWARF_PubNamesUnit *last = 0; - U64 count = 0; - - // whole section loop - U64 unit_idx = 0; - U8 *ptr = data.str; - U8 *opl = data.str + data.size; - for (;ptr < opl; unit_idx += 1){ - - // remember header offset - U64 hdr_off = (ptr - data.str); - - // initial length - U8 *unit_opl = 0; - B32 is_64bit = 0; - dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); - - // version - U8 version = MemoryConsume(U16, ptr, unit_opl); - - // info_off - U64 info_off = 0; - if (is_64bit){ - info_off = MemoryConsume(U64, ptr, unit_opl); - } - else{ - info_off = MemoryConsume(U32, ptr, unit_opl); - } - - // info_length - U64 info_length = 0; - if (is_64bit){ - info_length = MemoryConsume(U64, ptr, unit_opl); - } - else{ - info_length = MemoryConsume(U32, ptr, unit_opl); - } - - // offset size - U8 offset_size = is_64bit?8:4; - - // unit offsets - U64 base_off = (ptr - data.str); - U64 opl_off = (unit_opl - data.str); - - // emit unit - DWARF_PubNamesUnit *unit = push_array(arena, DWARF_PubNamesUnit, 1); - SLLQueuePush(first, last, unit); - count += 1; - - unit->hdr_off = hdr_off; - unit->base_off = base_off; - unit->opl_off = opl_off; - - unit->offset_size = offset_size; - unit->version = version; - unit->info_off = info_off; - unit->info_length = info_length; - - // advance to end of unit - ptr = unit_opl; - } - - // fill result - DWARF_PubNamesParsed *result = push_array(arena, DWARF_PubNamesParsed, 1); - result->unit_first = first; - result->unit_last = last; - result->unit_count = count; - return(result); -} - -static DWARF_NamesParsed* -dwarf_names_from_data(Arena *arena, String8 data){ - // supported version numbers: 5 - - - // empty unit list - DWARF_NamesUnit *first = 0; - DWARF_NamesUnit *last = 0; - U64 count = 0; - - // whole section loop - U64 unit_idx = 0; - U8 *ptr = data.str; - U8 *opl = data.str + data.size; - for (;ptr < opl; unit_idx += 1){ - - // remember header offset - U64 hdr_off = (ptr - data.str); - - // initial length - U8 *unit_opl = 0; - B32 is_64bit = 0; - dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); - - // version - U8 version = MemoryConsume(U16, ptr, unit_opl); - - // *padding* - MemoryConsume(U16, ptr, unit_opl); - - // comp_unit_count - U32 comp_unit_count = MemoryConsume(U32, ptr, unit_opl); - - // local_type_unit_count - U32 local_type_unit_count = MemoryConsume(U32, ptr, unit_opl); - - // foreign_type_unit_count - U32 foreign_type_unit_count = MemoryConsume(U32, ptr, unit_opl); - - // bucket_count - U32 bucket_count = MemoryConsume(U32, ptr, unit_opl); - - // name_count - U32 name_count = MemoryConsume(U32, ptr, unit_opl); - - // abbrev_table_size - U32 abbrev_table_size = MemoryConsume(U32, ptr, unit_opl); - - // augmentation_string_size - U32 augmentation_string_size = MemoryConsume(U32, ptr, unit_opl); - - // augmentation_string - U8 *augmentation_string = ptr; - { - U8 *ptr_raw = ptr + augmentation_string_size; - ptr = ClampTop(ptr_raw, unit_opl); - } - - // offset size - U8 offset_size = is_64bit?8:4; - - // unit offsets - U64 base_off = (ptr - data.str); - U64 opl_off = (unit_opl - data.str); - - // emit unit - DWARF_NamesUnit *unit = push_array(arena, DWARF_NamesUnit, 1); - SLLQueuePush(first, last, unit); - count += 1; - - unit->hdr_off = hdr_off; - unit->base_off = base_off; - unit->opl_off = opl_off; - - unit->version = version; - unit->comp_unit_count = comp_unit_count; - unit->local_type_unit_count = local_type_unit_count; - unit->foreign_type_unit_count = foreign_type_unit_count; - unit->bucket_count = bucket_count; - unit->name_count = name_count; - unit->abbrev_table_size = abbrev_table_size; - unit->augmentation_string = str8_cstring_capped(augmentation_string, unit_opl); - - // advance to end of unit - ptr = unit_opl; - } - - // fill result - DWARF_NamesParsed *result = push_array(arena, DWARF_NamesParsed, 1); - result->unit_first = first; - result->unit_last = last; - result->unit_count = count; - return(result); -} - -static DWARF_ArangesParsed* -dwarf_aranges_from_data(Arena *arena, String8 data){ - // supported version numbers: 2 - - - // empty unit list - DWARF_ArangesUnit *first = 0; - DWARF_ArangesUnit *last = 0; - U64 count = 0; - - // whole section loop - U64 unit_idx = 0; - U8 *ptr = data.str; - U8 *opl = data.str + data.size; - for (;ptr < opl; unit_idx += 1){ - - // remember header offset - U64 hdr_off = (ptr - data.str); - - // initial length - U8 *unit_opl = 0; - B32 is_64bit = 0; - dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); - - // version - U8 version = MemoryConsume(U16, ptr, unit_opl); - - // info_off - U64 info_off = 0; - if (is_64bit){ - info_off = MemoryConsume(U64, ptr, unit_opl); - } - else{ - info_off = MemoryConsume(U32, ptr, unit_opl); - } - - // address_size - U8 address_size = MemoryConsume(U8, ptr, unit_opl); - - // segment_selector_size - U8 segment_selector_size = MemoryConsume(U8, ptr, unit_opl); - - // offset size - U8 offset_size = is_64bit?8:4; - - // unit offsets - U64 base_off = (ptr - data.str); - U64 opl_off = (unit_opl - data.str); - - // emit unit - DWARF_ArangesUnit *unit = push_array(arena, DWARF_ArangesUnit, 1); - SLLQueuePush(first, last, unit); - count += 1; - - unit->hdr_off = hdr_off; - unit->base_off = base_off; - unit->opl_off = opl_off; - - unit->version = version; - unit->address_size = address_size; - unit->segment_selector_size = segment_selector_size; - unit->offset_size = offset_size; - unit->info_off = info_off; - - // advance to end of unit - ptr = unit_opl; - } - - // fill result - DWARF_ArangesParsed *result = push_array(arena, DWARF_ArangesParsed, 1); - result->unit_first = first; - result->unit_last = last; - result->unit_count = count; - return(result); -} - -static DWARF_LineParsed* -dwarf_line_from_data(Arena *arena, String8 data){ - // supported version numbers: 4, 5 - - - // empty unit list - DWARF_LineUnit *first = 0; - DWARF_LineUnit *last = 0; - U64 count = 0; - - // whole section loop - U64 unit_idx = 0; - U8 *ptr = data.str; - U8 *opl = data.str + data.size; - for (;ptr < opl; unit_idx += 1){ - - // remember header offset - U64 hdr_off = (ptr - data.str); - - // initial length - U8 *unit_opl = 0; - B32 is_64bit = 0; - dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); - - // version - U8 version = MemoryConsume(U16, ptr, unit_opl); - - // offset size - U8 offset_size = is_64bit?8:4; - - // rest of header depends on version - U8 minimum_instruction_length = 0; - U8 maximum_operations_per_instruction = 0; - U8 default_is_stmt = 0; - S8 line_base = 0; - U8 line_range = 0; - U8 opcode_base = 0; - U8 *standard_opcode_lengths = 0; - // v4 - String8List include_directories = {0}; - DWARF_V4LineFileNamesList file_names = {0}; - // v5 - U8 address_size = 0; - U8 segment_selector_size = 0; - U8 directory_entry_format_count = 0; - DWARF_V5LinePathEntryFormat *directory_entry_format = 0; - U64 directories_count = 0; - U8 file_name_entry_format_count = 0; - DWARF_V5LinePathEntryFormat *file_name_entry_format = 0; - U64 file_names_count = 0; - - switch (version){ - case 4: - { - // header_length - U64 header_length = 0; - if (is_64bit){ - header_length = MemoryConsume(U64, ptr, unit_opl); - } - else{ - header_length = MemoryConsume(U32, ptr, unit_opl); - } - - // header opl - U8 *header_opl_raw = ptr + header_length; - U8 *header_opl = ClampTop(header_opl_raw, unit_opl); - - // minimum_instruction_length - minimum_instruction_length = MemoryConsume(U8, ptr, header_opl); - - // maximum_operations_per_instruction - maximum_operations_per_instruction = MemoryConsume(U8, ptr, header_opl); - - // default_is_stmt - default_is_stmt = MemoryConsume(U8, ptr, header_opl); - - // line_base - line_base = MemoryConsume(S8, ptr, header_opl); - - // line_range - line_range = MemoryConsume(U8, ptr, header_opl); - - // opcode_base - opcode_base = MemoryConsume(U8, ptr, header_opl); - - // standard_opcode_lengths - if (opcode_base > 1){ - standard_opcode_lengths = ptr; - ptr += opcode_base - 1; - } - - // include_directories - for (;ptr < header_opl;){ - // null byte ends entries - if (*ptr == 0){ - ptr += 1; - break; - } - - // extract dir range - U8 *dir_first = ptr; - for (;ptr < header_opl && *ptr != 0;) ptr += 1; - U8 *dir_opl = ptr; - if (ptr < header_opl){ - ptr += 1; - } - - // attach dir to list - String8 dir = str8_range(dir_first, dir_opl); - str8_list_push(arena, &include_directories, dir); - } - - // file_names - for (;ptr < header_opl;){ - // null byte ends entries - if (*ptr == 0){ - ptr += 1; - break; - } - - // extract file_name range - U8 *file_name_first = ptr; - for (;ptr < header_opl && *ptr != 0;) ptr += 1; - U8 *file_name_opl = ptr; - if (ptr < header_opl){ - ptr += 1; - } - - // extract include directory index - U64 include_directory_idx = 0; - DWARF_LEB128_DECODE_ADV(U64, include_directory_idx, ptr, header_opl); - - // extract last modified time - U64 last_modified_time = 0; - DWARF_LEB128_DECODE_ADV(U64, last_modified_time, ptr, header_opl); - - // extract file size - U64 file_size = 0; - DWARF_LEB128_DECODE_ADV(U64, file_size, ptr, header_opl); - - // emit file name entry - DWARF_V4LineFileNamesEntry *entry = push_array(arena, DWARF_V4LineFileNamesEntry, 1); - SLLQueuePush(file_names.first, file_names.last, entry); - file_names.count += 1; - entry->file_name = str8_range(file_name_first, file_name_opl); - entry->include_directory_idx = include_directory_idx; - entry->last_modified_time = last_modified_time; - entry->file_size = file_size; - } - - ptr = header_opl; - }break; - - case 5: - { - // address_size - address_size = MemoryConsume(U8, ptr, unit_opl); - - // segment_selector_size - segment_selector_size = MemoryConsume(U8, ptr, unit_opl); - - // header_length - U64 header_length = 0; - if (is_64bit){ - header_length = MemoryConsume(U64, ptr, unit_opl); - } - else{ - header_length = MemoryConsume(U32, ptr, unit_opl); - } - - // header opl - U8 *header_opl_raw = ptr + header_length; - U8 *header_opl = ClampTop(header_opl_raw, unit_opl); - - // minimum_instruction_length - minimum_instruction_length = MemoryConsume(U8, ptr, header_opl); - - // maximum_operations_per_instruction - maximum_operations_per_instruction = MemoryConsume(U8, ptr, header_opl); - - // default_is_stmt - default_is_stmt = MemoryConsume(U8, ptr, header_opl); - - // line_base - line_base = MemoryConsume(S8, ptr, header_opl); - - // line_range - line_range = MemoryConsume(U8, ptr, header_opl); - - // opcode_base - opcode_base = MemoryConsume(U8, ptr, header_opl); - - // standard_opcode_lengths - if (opcode_base > 1){ - standard_opcode_lengths = ptr; - ptr += opcode_base - 1; - } - - // directory_entry_format_count - directory_entry_format_count = MemoryConsume(U8, ptr, header_opl); - - // directory_entry_format - { - directory_entry_format = push_array(arena, DWARF_V5LinePathEntryFormat, - directory_entry_format_count); - DWARF_V5LinePathEntryFormat *entry = directory_entry_format; - DWARF_V5LinePathEntryFormat *entry_opl = - directory_entry_format + directory_entry_format_count; - for (;entry < entry_opl && ptr < header_opl; entry += 1){ - DWARF_LEB128_DECODE_ADV(U64, entry->content_type, ptr, header_opl); - DWARF_LEB128_DECODE_ADV(U64, entry->form, ptr, header_opl); - } - } - - // directories_count - DWARF_LEB128_DECODE_ADV(U64, directories_count, ptr, header_opl); - - // directories - DWARF_V5Directory *directories = push_array(arena, DWARF_V5Directory, directories_count); - dwarf__line_v5_directories(address_size, offset_size, - directory_entry_format, directory_entry_format_count, - directories, directories_count, - &ptr, header_opl); - - // file_name_entry_format_count - file_name_entry_format_count = MemoryConsume(U8, ptr, header_opl); - - // file_name_entry_format - { - file_name_entry_format = push_array(arena, DWARF_V5LinePathEntryFormat, - file_name_entry_format_count); - DWARF_V5LinePathEntryFormat *entry = file_name_entry_format; - for (;ptr < header_opl; entry += 1){ - DWARF_LEB128_DECODE_ADV(U64, entry->content_type, ptr, header_opl); - DWARF_LEB128_DECODE_ADV(U64, entry->form, ptr, header_opl); - } - } - - // file_names_count - DWARF_LEB128_DECODE_ADV(U64, file_names_count, ptr, header_opl); - - // file_names - DWARF_V5Directory *file_names = push_array(arena, DWARF_V5Directory, file_names_count); - dwarf__line_v5_directories(address_size, offset_size, - directory_entry_format, directory_entry_format_count, - file_names, file_names_count, - &ptr, header_opl); - }break; - } - - // unit offsets - U64 base_off = (ptr - data.str); - U64 opl_off = (unit_opl - data.str); - - // emit unit - DWARF_LineUnit *unit = push_array(arena, DWARF_LineUnit, 1); - SLLQueuePush(first, last, unit); - count += 1; - - unit->hdr_off = hdr_off; - unit->base_off = base_off; - unit->opl_off = opl_off; - - unit->version = version; - - // advance to end of unit - ptr = unit_opl; - } - - // fill result - DWARF_LineParsed *result = push_array(arena, DWARF_LineParsed, 1); - result->unit_first = first; - result->unit_last = last; - result->unit_count = count; - return(result); -} - -static DWARF_MacInfoParsed* -dwarf_mac_info_from_data(Arena *arena, String8 data){ - DWARF_MacInfoParsed *result = 0; - // TODO(allen): - return(result); -} - -static DWARF_MacroParsed* -dwarf_macro_from_data(Arena *arena, String8 data){ - DWARF_MacroParsed *result = 0; - // TODO(allen): - return(result); -} - -static DWARF_FrameParsed* -dwarf_frame_from_data(Arena *arena, String8 data){ - DWARF_FrameParsed *result = 0; - // TODO(allen): - return(result); -} - -static DWARF_RangesParsed* -dwarf_ranges_from_data(Arena *arena, String8 data){ - DWARF_RangesParsed *result = 0; - // TODO(allen): - return(result); -} - -static DWARF_StrOffsetsParsed* -dwarf_str_offsets_from_data(Arena *arena, String8 data){ - DWARF_StrOffsetsParsed *result = 0; - // TODO(allen): - return(result); -} - -static DWARF_AddrParsed* -dwarf_addr_from_data(Arena *arena, String8 data){ - // supported version numbers: 5 - - - // addr unit list - DWARF_AddrUnit *first = 0; - DWARF_AddrUnit *last = 0; - U64 count = 0; - - // whole section loop - U64 unit_idx = 0; - U8 *ptr = data.str; - U8 *opl = data.str + data.size; - for (;ptr < opl; unit_idx += 1){ - - U64 hdr_off = (ptr - data.str); - - // initial length - U8 *unit_opl = 0; - B32 is_64bit = 0; - dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); - - // version - U8 version = MemoryConsume(U16, ptr, unit_opl); - - // address size - U8 address_size = MemoryConsume(U8, ptr, unit_opl); - - // segment selector size - U8 segment_selector_size = MemoryConsume(U8, ptr, unit_opl); - - // offset size - U32 offset_size = is_64bit?8:4; - - // unit offsets - U64 base_off = (ptr - data.str); - U64 opl_off = (unit_opl - data.str); - - // emit addr unit - DWARF_AddrUnit *unit = push_array(arena, DWARF_AddrUnit, 1); - SLLQueuePush(first, last, unit); - count += 1; - - unit->hdr_off = hdr_off; - unit->base_off = base_off; - unit->opl_off = opl_off; - - unit->offset_size = offset_size; - unit->dwarf_version = version; - unit->address_size = address_size; - unit->segment_selector_size = segment_selector_size; - - // advance to next unit - ptr = unit_opl; - } - - // fill result - DWARF_AddrParsed *result = push_array(arena, DWARF_AddrParsed, 1); - result->unit_first = first; - result->unit_last = last; - result->unit_count = count; - return(result); -} - -static DWARF_RngListsParsed* -dwarf_rng_lists_from_data(Arena *arena, String8 data){ - DWARF_RngListsParsed *result = 0; - // TODO(allen): - return(result); -} - -static DWARF_LocListsParsed* -dwarf_loc_lists_from_data(Arena *arena, String8 data){ - DWARF_LocListsParsed *result = 0; - // TODO(allen): - return(result); -} - - -// parse helpers - -static void -dwarf__initial_length(String8 data, U8 **ptr_inout, U8 **unit_opl_out, B32 *is_64bit_out){ - U8 *unit_opl = 0; - B32 is_64bit = 0; - - U8 *opl = data.str + data.size; - U8 *ptr = *ptr_inout; - { - U64 length = 0; - U32 m = MemoryConsume(U32, ptr, opl); - if (m == 0xFFFFFFFF){ - is_64bit = 1; - length = MemoryConsume(U64, ptr, opl); - } - else{ - length = ClampTop(m, 0xFFFFFFF0); - } - if (length > 0){ - U64 unit_opl_off_raw = (ptr - data.str) + length; - U64 unit_opl_off = ClampTop(unit_opl_off_raw, data.size); - unit_opl = data.str + unit_opl_off; - } - else{ - unit_opl = ptr; - } - } - - *ptr_inout = ptr; - *unit_opl_out = unit_opl; - *is_64bit_out = is_64bit; -} - -static void -dwarf__line_v5_directories(U64 address_size, U64 offset_size, - DWARF_V5LinePathEntryFormat *format, U64 format_count, - DWARF_V5Directory *directories_out, U64 dir_count, - U8 **ptr_io, U8 *opl){ - - U8 *ptr = *ptr_io; - - DWARF_V5Directory *directory_ptr = directories_out; - for (U32 i = 0; i < dir_count; i += 1, directory_ptr += 1){ - DWARF_V5LinePathEntryFormat *fmt = format; - for (U32 j = 0; j < format_count; j += 1){ - - // form decode - DWARF_FormDecodeRules rules = - dwarf_form_decode_rule(fmt->form, address_size, offset_size); - - // execute decoding - DWARF_FormDecoded decoded = dwarf_form_decode(&rules, &ptr, opl, 0, 0); - - // store to correct field - U64 *target = 0; - switch (fmt->content_type){ - case DWARF_LineEntryFormat_path: - { - if (decoded.dataptr != 0){ - directory_ptr->path_str = str8(decoded.dataptr, decoded.val); - } - else{ - directory_ptr->path_off = decoded.val; - directory_ptr->path_sec_form = fmt->form; - } - }break; - - case DWARF_LineEntryFormat_directory_index: - { - target = &directory_ptr->directory_index; - }goto v5_directory_u64; - - case DWARF_LineEntryFormat_timestamp: - { - target = &directory_ptr->timestamp; - }goto v5_directory_u64; - - case DWARF_LineEntryFormat_size: - { - target = &directory_ptr->size; - }goto v5_directory_u64; - - v5_directory_u64: - { - if (decoded.dataptr != 0){ - U64 size = ClampTop(decoded.val, 8); - MemoryCopy(target, decoded.dataptr, size); - } - else{ - *target = decoded.val; - } - }break; - - case DWARF_LineEntryFormat_MD5: - { - if (decoded.dataptr != 0){ - U64 size = ClampTop(decoded.val, 16); - MemoryCopy(directory_ptr->md5_checksum, decoded.dataptr, size); - } - }break; - } - } - } - - *ptr_io = ptr; -} - - -// debug sections - -static String8 -dwarf_name_from_debug_section(DWARF_Parsed *dwarf, DWARF_SectionCode sec_code){ - String8 result = str8_lit("invalid_debug_section"); - if (sec_code < DWARF_SectionCode_COUNT){ - if (dwarf->debug_section_idx[sec_code] != 0){ - result = dwarf->debug_section_name[sec_code]; - } - } - return(result); -} - - -// abbrev functions - -static DWARF_AbbrevUnit* -dwarf_abbrev_unit_from_offset(DWARF_AbbrevParsed *abbrev, U64 offset){ - DWARF_AbbrevUnit *result = 0; - for (DWARF_AbbrevUnit *unit = abbrev->unit_first; - unit != 0; - unit = unit->next){ - if (unit->offset == offset){ - result = unit; - break; - } - } - return(result); -} - -static DWARF_AbbrevDecl* -dwarf_abbrev_decl_from_code(DWARF_AbbrevUnit *unit, U32 abbrev_code){ - DWARF_AbbrevDecl *result = 0; - for (DWARF_AbbrevDecl *decl = unit->first; - decl != 0; - decl = decl->next){ - if (decl->abbrev_code == abbrev_code){ - result = decl; - break; - } - } - return(result); -} - -// attribute decoding functions - -static DWARF_AttributeClassFlags -dwarf_attribute_class_from_form(DWARF_AttributeForm form){ - DWARF_AttributeClassFlags result = 0; - switch (form){ -#define X(N,C,f) case C: result = DWARF_AttributeClassFlag_##f; break; - DWARF_AttributeFormXList(X) -#undef X - } - return(result); -} - -static DWARF_AttributeClassFlags -dwarf_attribute_class_from_name(DWARF_AttributeName name){ - DWARF_AttributeClassFlags result = 0; - switch (name){ -#define X(N,C,f1,f2,f3,f4) case C: result = 0\ -|DWARF_AttributeClassFlag_##f1\ -|DWARF_AttributeClassFlag_##f2\ -|DWARF_AttributeClassFlag_##f3\ -|DWARF_AttributeClassFlag_##f4\ -;break; - DWARF_AttributeNameXList(X) -#undef X - } - return(result); -} - -// form decoding functions - -static DWARF_FormDecodeRules -dwarf_form_decode_rule(DWARF_AttributeForm form, U64 address_size, U64 offset_size){ - DWARF_FormDecodeRules result = {0}; - switch (form){ - case DWARF_AttributeForm_addr: result.size = address_size; break; - case DWARF_AttributeForm_addrx: result.uleb128 = 1; break; - case DWARF_AttributeForm_addrx1: result.size = 1; break; - case DWARF_AttributeForm_addrx2: result.size = 2; break; - case DWARF_AttributeForm_addrx3: result.size = 3; break; - case DWARF_AttributeForm_addrx4: result.size = 4; break; - - case DWARF_AttributeForm_sec_offset: result.size = offset_size; break; - - case DWARF_AttributeForm_block1: result.size = 1; result.block = 1; break; - case DWARF_AttributeForm_block2: result.size = 2; result.block = 1; break; - case DWARF_AttributeForm_block4: result.size = 4; result.block = 1; break; - case DWARF_AttributeForm_block: result.uleb128 = 1; result.block = 1; break; - - case DWARF_AttributeForm_data1: result.size = 1; break; - case DWARF_AttributeForm_data2: result.size = 2; break; - case DWARF_AttributeForm_data4: result.size = 4; break; - case DWARF_AttributeForm_data8: result.size = 8; break; - case DWARF_AttributeForm_data16: result.size = 16; break; - - case DWARF_AttributeForm_sdata: result.sleb128 = 1; break; - case DWARF_AttributeForm_udata: result.uleb128 = 1; break; - - case DWARF_AttributeForm_implicit_const: result.in_abbrev = 1; break; - - case DWARF_AttributeForm_exprloc: result.uleb128 = 1; result.block = 1; break; - - case DWARF_AttributeForm_flag: result.size = 1; break; - case DWARF_AttributeForm_flag_present: result.auto_1 = 1; break; - - case DWARF_AttributeForm_loclistx: result.uleb128 = 1; break; - case DWARF_AttributeForm_rnglistx: result.uleb128 = 1; break; - - case DWARF_AttributeForm_ref1: result.size = 1; break; - case DWARF_AttributeForm_ref2: result.size = 2; break; - case DWARF_AttributeForm_ref4: result.size = 4; break; - case DWARF_AttributeForm_ref8: result.size = 8; break; - case DWARF_AttributeForm_ref_udata: result.uleb128 = 1; break; - - case DWARF_AttributeForm_ref_addr: result.size = offset_size; break; - - case DWARF_AttributeForm_ref_sig8: result.size = 8; break; - - case DWARF_AttributeForm_ref_sup4: result.size = 4; break; - case DWARF_AttributeForm_ref_sup8: result.size = 8; break; - - case DWARF_AttributeForm_string: result.null_terminated = 1; break; - - case DWARF_AttributeForm_strp: result.size = offset_size; break; - case DWARF_AttributeForm_line_strp: result.size = offset_size; break; - case DWARF_AttributeForm_strp_sup: result.size = offset_size; break; - - case DWARF_AttributeForm_strx: result.uleb128 = 1; break; - case DWARF_AttributeForm_strx1: result.size = 1; break; - case DWARF_AttributeForm_strx2: result.size = 2; break; - case DWARF_AttributeForm_strx3: result.size = 3; break; - case DWARF_AttributeForm_strx4: result.size = 4; break; - } - - return(result); -} - -static DWARF_FormDecoded -dwarf_form_decode(DWARF_FormDecodeRules *rules, U8 **ptr_io, U8 *opl, - DWARF_AbbrevDecl *abbrev_decl, U32 attrib_i){ - - // local copy of ptr - U8 *ptr = *ptr_io; - - // apply rules - U64 val = 0; - U8 *dataptr = 0; - - B32 success = 1; - if (rules->size > 0){ - if (ptr + rules->size <= opl){ - MemoryCopy(&val, ptr, rules->size); - ptr += rules->size; - } - else{ - success = 0; - } - } - else if (rules->uleb128 || rules->sleb128){ - U8 *val_ptr = ptr; - DWARF_LEB128_ADV(ptr, opl, success); - if (success){ - if (rules->uleb128){ - val = dwarf_leb128_decode_U64(val_ptr, ptr); - } - else{ - val = (U64)dwarf_leb128_decode_S64(val_ptr, ptr); - } - } - } - else if (rules->in_abbrev){ - if (abbrev_decl != 0){ - if (abbrev_decl->implicit_const != 0){ - val = (U64)abbrev_decl->implicit_const[attrib_i]; - } - } - else{ - success = 0; - } - } - else if (rules->auto_1){ - val = 1; - } - if (rules->block){ - dataptr = ptr; - ptr += val; - } - else if (rules->null_terminated){ - dataptr = ptr; - for (;ptr < opl && *ptr != 0;) ptr += 1; - val = (U64)(ptr - dataptr); - if (ptr < opl){ - ptr += 1; - } - } - - // store out ptr - *ptr_io = ptr; - - // fill result - DWARF_FormDecoded result = {0}; - result.val = val; - result.dataptr = dataptr; - result.error = !success; - return(result); -} - - -// string functions - -static String8 -dwarf_string_from_unit_type(DWARF_UnitType type){ - String8 result = str8_lit("unrecognized_type"); - switch (type){ -#define X(N,C) case C: result = str8_lit(#N); break; - DWARF_UnitTypeXList(X) -#undef X - } - return(result); -} - -static String8 -dwarf_string_from_tag(DWARF_Tag tag){ - String8 result = str8_lit("unrecognized_tag"); - switch (tag){ -#define X(N,C) case C: result = str8_lit(#N); break; - DWARF_TagXList(X) -#undef X - } - return(result); -} - -static String8 -dwarf_string_from_attribute_name(DWARF_AttributeName name){ - String8 result = str8_lit("unrecognized_attribute_name"); - switch (name){ -#define X(N,C,f1,f2,f3,f4) case C: result = str8_lit(#N); break; - DWARF_AttributeNameXList(X) -#undef X - } - return(result); -} - -static String8 -dwarf_string_from_attribute_form(DWARF_AttributeForm form){ - String8 result = str8_lit("unrecognized_attribute_form"); - switch (form){ -#define X(N,C,k) case C: result = str8_lit(#N); break; - DWARF_AttributeFormXList(X) -#undef X - } - return(result); -} - -static String8 -dwarf_string_from_line_std_op(DWARF_LineStdOp op){ - String8 result = str8_lit("unrecognized_line_std_op"); - switch (op){ -#define X(N,C) case C: result = str8_lit(#N); break; - DWARF_LineStdOpXList(X) -#undef X - } - return(result); -} - -static String8 -dwarf_string_from_line_ext_op(DWARF_LineExtOp op){ - String8 result = str8_lit("unrecognized_line_ext_op"); - switch (op){ -#define X(N,C) case C: result = str8_lit(#N); break; - DWARF_LineExtOpXList(X) -#undef X - } - return(result); -} - -static String8 -dwarf_string_from_line_entry_format(DWARF_LineEntryFormat format){ - String8 result = str8_lit("unrecognized_line_entry_format"); - switch (format){ -#define X(N,C) case C: result = str8_lit(#N); break; - DWARF_LineEntryFormatXList(X) -#undef X - } - return(result); -} - -static String8 -dwarf_string_from_section_code(DWARF_SectionCode sec_code){ - String8 result = str8_lit("unrecognized_section_code"); - switch (sec_code){ -#define X(Nc,Vf,N0,N1,N2) case DWARF_SectionCode_##Nc: result = str8_lit(#Nc); break; - DWARF_SectionNameXList(X,0,0) -#undef X - } - return(result); -} - - - - - -#if 0 -static DWARF_InfoParsed* -dwarf_info_from_data(Arena *arena, String8 data, DWARF_InfoParams *params, - DWARF_AbbrevParsed *abbrev){ - - // unit index range to extract - U64 unit_idx_min = params->unit_idx_min; - U64 unit_idx_max = params->unit_idx_max; - - // empty unit list - DWARF_InfoUnit *unit_first = 0; - DWARF_InfoUnit *unit_last = 0; - U64 unit_count = 0; - B32 decoding_error = 0; - - // whole section loop - U64 unit_idx = 0; - U8 *ptr = data.str; - U8 *opl = data.str + data.size; - for (;ptr < opl; unit_idx += 1){ - - // early escape on unit idx - if (unit_idx > unit_idx_max){ - break; - } - - // determine whether to full parse this unit - B32 full_parse = (unit_idx_min <= unit_idx); - - // header fields - U8 *unit_opl = 0; - B32 is_64bit = 0; - U16 version = 0; - U64 abbrev_offset = 0; - U32 address_size = 0; - DWARF_UnitType unit_type = DWARF_UnitType_null; - U64 unit_dwo_id = 0; - U64 unit_type_signature = 0; - U64 unit_type_offset = 0; - - // initial length - dwarf__initial_length(&ptr, opl, &unit_opl, &is_64bit); - - // if this is not a full parse we may use - // unit_opl to skip to the next unit now - if (full_parse){ - - // version (part of header) - version = MemoryConsume(U16, ptr, unit_opl); - - // rest of header depends on version - switch (version){ - case 4: - { - // abbrev_offset (part of header) - if (is_64bit){ - abbrev_offset = MemoryConsume(U64, ptr, unit_opl); - } - else{ - abbrev_offset = MemoryConsume(U32, ptr, unit_opl); - } - - // address_size (part of header) - address_size = MemoryConsume(U8, ptr, unit_opl); - }break; - - case 5: - { - // unit_type (part of header) - unit_type = (DWARF_UnitType)MemoryConsume(U8, ptr, unit_opl); - - // address_size (part of header) - address_size = MemoryConsume(U8, ptr, unit_opl); - - // abbrev_offset (part of header) - if (is_64bit){ - abbrev_offset = MemoryConsume(U64, ptr, unit_opl); - } - else{ - abbrev_offset = MemoryConsume(U32, ptr, unit_opl); - } - - // rest of header depends on unit_type - switch (unit_type){ - case DWARF_UnitType_skeleton: - case DWARF_UnitType_split_compile: - { - unit_dwo_id = MemoryConsume(U64, ptr, unit_opl); - }break; - case DWARF_UnitType_type: - case DWARF_UnitType_split_type: - { - unit_type_signature = MemoryConsume(U64, ptr, unit_opl); - if (is_64bit){ - unit_type_offset = MemoryConsume(U64, ptr, unit_opl); - } - else{ - unit_type_offset = MemoryConsume(U32, ptr, unit_opl); - } - }break; - } - }break; - } - - // offset size - U32 offset_size = is_64bit?8:4; - - // find matching abbrev unit - DWARF_AbbrevUnit *abbrev_unit = dwarf_abbrev_unit_from_offset(abbrev, abbrev_offset); - if (abbrev_unit == 0){ - // TODO: preserve error info - decoding_error = 1; - } - - // consume info entries - DWARF_InfoEntry *entry_root = 0; - U64 entry_count = 0; - - DWARF_InfoEntry *entry_consptr = 0; - if (abbrev_unit != 0){ - for (;ptr < unit_opl;){ - B32 success = 1; - - // mark beginning of entry - U8 *entry_start_ptr = ptr; - - // extract abbrev code - U8 *abbrev_code_ptr = ptr; - DWARF_LEB128_ADV(ptr, unit_opl, success); - if (!success){ - // TODO: preserve error info - decoding_error = 1; - goto exit_unit_loop; - } - - U32 abbrev_code = dwarf_leb128_decode_U32(abbrev_code_ptr, ptr); - - // null abbrev code means pop - if (abbrev_code == 0){ - if (entry_consptr == 0){ - goto exit_unit_loop; - } - else{ - entry_consptr = entry_consptr->parent; - goto skip_entry; - } - } - - // get abbrev decl - DWARF_AbbrevDecl *abbrev_decl = dwarf_abbrev_decl_from_code(abbrev_unit, abbrev_code); - if (abbrev_decl == 0){ - // TODO: preserve error info - decoding_error = 1; - goto exit_unit_loop; - } - - // allocate entry - U32 attrib_count = abbrev_decl->attrib_count; - DWARF_InfoEntry *entry = push_array(arena, DWARF_InfoEntry, 1); - DWARF_InfoAttribVal *attrib_vals = - push_array_no_zero(arena, DWARF_InfoAttribVal, attrib_count); - - // save entry offset - entry->info_offset = (U64)(entry_start_ptr - data.str); - - // set root at beginning - if (entry_root == 0){ - entry_root = entry; - } - - // attribute loop - DWARF_AbbrevAttribSpec *attrib_spec = abbrev_decl->attrib_specs; - DWARF_InfoAttribVal *attrib_val = attrib_vals; - for (U32 i = 0; i < attrib_count; i += 1, attrib_spec += 1, attrib_val += 1){ - - // determine decoding rules - U32 size = 0; - B8 uleb128 = 0; - B8 sleb128 = 0; - B8 in_abbrev = 0; - B8 auto_1 = 0; - B8 block = 0; - B8 null_terminated = 0; - { - DWARF_AttributeForm form = attrib_spec->form; - switch (form){ - case DWARF_AttributeForm_addr: size = address_size; break; - case DWARF_AttributeForm_addrx: uleb128 = 1; break; - case DWARF_AttributeForm_addrx1: size = 1; break; - case DWARF_AttributeForm_addrx2: size = 2; break; - case DWARF_AttributeForm_addrx3: size = 3; break; - case DWARF_AttributeForm_addrx4: size = 4; break; - - case DWARF_AttributeForm_sec_offset: size = offset_size; break; - - case DWARF_AttributeForm_block1: size = 1; block = 1; break; - case DWARF_AttributeForm_block2: size = 2; block = 1; break; - case DWARF_AttributeForm_block4: size = 4; block = 1; break; - case DWARF_AttributeForm_block: uleb128 = 1; block = 1; break; - - case DWARF_AttributeForm_data1: size = 1; break; - case DWARF_AttributeForm_data2: size = 2; break; - case DWARF_AttributeForm_data4: size = 4; break; - case DWARF_AttributeForm_data8: size = 8; break; - case DWARF_AttributeForm_data16: size = 16; break; - - case DWARF_AttributeForm_sdata: sleb128 = 1; break; - case DWARF_AttributeForm_udata: uleb128 = 1; break; - - case DWARF_AttributeForm_implicit_const: in_abbrev = 1; break; - - case DWARF_AttributeForm_exprloc: uleb128 = 1; block = 1; break; - - case DWARF_AttributeForm_flag: size = 1; break; - case DWARF_AttributeForm_flag_present: auto_1 = 1; break; - - case DWARF_AttributeForm_loclistx: uleb128 = 1; break; - case DWARF_AttributeForm_rnglistx: uleb128 = 1; break; - - case DWARF_AttributeForm_ref1: size = 1; break; - case DWARF_AttributeForm_ref2: size = 2; break; - case DWARF_AttributeForm_ref4: size = 4; break; - case DWARF_AttributeForm_ref8: size = 8; break; - case DWARF_AttributeForm_ref_udata: uleb128 = 1; break; - - case DWARF_AttributeForm_ref_addr: size = offset_size; break; - - case DWARF_AttributeForm_ref_sig8: size = 8; break; - - case DWARF_AttributeForm_ref_sup4: size = 4; break; - case DWARF_AttributeForm_ref_sup8: size = 8; break; - - case DWARF_AttributeForm_string: null_terminated = 1; break; - - case DWARF_AttributeForm_strp: size = offset_size; break; - case DWARF_AttributeForm_line_strp: size = offset_size; break; - case DWARF_AttributeForm_strp_sup: size = offset_size; break; - - case DWARF_AttributeForm_strx: uleb128 = 1; break; - case DWARF_AttributeForm_strx1: size = 1; break; - case DWARF_AttributeForm_strx2: size = 2; break; - case DWARF_AttributeForm_strx3: size = 3; break; - case DWARF_AttributeForm_strx4: size = 4; break; - } - } - - // execute decoding rules - U64 val = 0; - U8 *dataptr = 0; - { - if (size > 0){ - if (ptr + size <= unit_opl){ - MemoryCopy(&val, ptr, size); - ptr += size; - } - else{ - // TODO: preserve error info - decoding_error = 1; - goto exit_unit_loop; - } - } - else if (uleb128 || sleb128){ - U8 *val_ptr = ptr; - DWARF_LEB128_ADV(ptr, unit_opl, success); - if (!success){ - // TODO: preserve error info - decoding_error = 1; - goto exit_unit_loop; - } - else{ - if (uleb128){ - val = dwarf_leb128_decode_U64(val_ptr, ptr); - } - else{ - val = (U64)dwarf_leb128_decode_S64(val_ptr, ptr); - } - } - } - else if (in_abbrev){ - if (abbrev_decl->implicit_const != 0){ - val = (U64)abbrev_decl->implicit_const[i]; - } - } - else if (auto_1){ - val = 1; - } - if (block){ - dataptr = ptr; - ptr += val; - } - else if (null_terminated){ - dataptr = ptr; - for (;ptr < unit_opl && *ptr != 0;) ptr += 1; - val = (U64)(ptr - dataptr); - } - } - - // save attribute - attrib_val->val = val; - attrib_val->dataptr = dataptr; - } - - // emit entry - if (entry_consptr != 0){ - SLLQueuePush_N(entry_consptr->first_child, entry_consptr->last_child, - entry, next_sibling); - entry_consptr->child_count += 1; - entry->parent = entry_consptr; - } - entry_count += 1; - entry->abbrev_decl = abbrev_decl; - entry->attrib_vals = attrib_vals; - - // move consptr down if has children - if (abbrev_decl->has_children){ - entry_consptr = entry; - } - - skip_entry:; - } - } - exit_unit_loop:; - - // TODO: notice errors, emit them, and exit loop here - if (decoding_error){ - break; - } - - // extract root attributes - U64 language = 0; - U64 str_offsets_base = 0; - U64 line_info_offset = 0; - U64 vbase = 0; - U64 addr_base = 0; - U64 rnglists_base = 0; - U64 loclists_base = 0; - if (entry_root != 0){ - - // pull out attributes - DWARF_AbbrevDecl *root_abbrev_decl = entry_root->abbrev_decl; - DWARF_AbbrevAttribSpec *attrib_specs = root_abbrev_decl->attrib_specs; - DWARF_InfoAttribVal *attrib_vals = entry_root->attrib_vals; - U32 attrib_count = root_abbrev_decl->attrib_count; - - // examine each attribute - DWARF_AbbrevAttribSpec *attrib_spec = attrib_specs; - DWARF_InfoAttribVal *attrib_val = attrib_vals; - for (U32 i = 0; i < attrib_count; i += 1, attrib_spec += 1, attrib_val += 1){ - - // determine if there is a root attribute to extract here - U64 *target_u64 = 0; - switch (attrib_spec->name){ - case DWARF_AttributeName_language: target_u64 = &language; break; - case DWARF_AttributeName_str_offsets_base: target_u64 = &str_offsets_base; break; - case DWARF_AttributeName_stmt_list: target_u64 = &line_info_offset; break; - case DWARF_AttributeName_low_pc: target_u64 = &vbase; break; - case DWARF_AttributeName_addr_base: target_u64 = &addr_base; break; - case DWARF_AttributeName_rnglists_base: target_u64 = &rnglists_base; break; - case DWARF_AttributeName_loclists_base: target_u64 = &loclists_base; break; - } - - // set target from attrib value - if (target_u64 != 0){ - *target_u64 = attrib_val->val; - } - } - } - - // allocate unit - DWARF_InfoUnit *unit = push_array(arena, DWARF_InfoUnit, 1); - - // fill & emit unit - SLLQueuePush(unit_first, unit_last, unit); - unit_count += 1; - // [header] - unit->dwarf_version = version; - unit->offset_size = offset_size; - unit->address_size = address_size; - // [root attributes] - unit->language = (DWARF_Language)language; - unit->str_offsets_base = str_offsets_base; - unit->line_info_offset = line_info_offset; - unit->vbase = vbase; - unit->addr_base = addr_base; - unit->rnglists_base = rnglists_base; - unit->loclists_base = loclists_base; - // [entries] - unit->entry_root = entry_root; - unit->entry_count = entry_count; - - } - - // set ptr to end of this unit - ptr = unit_opl; - } - - // fill result - DWARF_InfoParsed *result = push_array(arena, DWARF_InfoParsed, 1); - result->unit_first = unit_first; - result->unit_last = unit_last; - result->unit_count = unit_count; - result->decoding_error = decoding_error; - return(result); -} - -static DWARF_AbbrevParsed* -dwarf_abbrev_from_data(Arena *arena, String8 data, DWARF_AbbrevParams *params){ - /* .debug_abbrev - ** Layout - ** List(Tag) - ** Tag = { id:ULEB128, tag:ULEB128, has_children:B8, ListNullTerminated(Attribute) } - ** Attribute = { name:ULEB128, form:ULEB128, (val:SLEB128)? } - */ - - // unit index range to extract - U64 unit_idx_min = params->unit_idx_min; - U64 unit_idx_max = params->unit_idx_max; - - // empty unit list - DWARF_AbbrevUnit *unit_first = 0; - DWARF_AbbrevUnit *unit_last = 0; - U64 unit_count = 0; - B32 decoding_error = 0; - - // whole section loop - U64 unit_idx = 0; - U8 *ptr = data.str; - U8 *opl = data.str + data.size; - for (;ptr < opl; unit_idx += 1){ - - // early escape on unit idx - if (unit_idx > unit_idx_max){ - break; - } - - // determine whether to full parse this unit - B32 full_parse = (unit_idx_min <= unit_idx); - - // save unit offset - U64 abbrev_unit_offset = (U64)(ptr - data.str); - - // allocate unit - DWARF_AbbrevUnit *unit = push_array(arena, DWARF_AbbrevUnit, 1); - - // empty abbrev list - DWARF_AbbrevDecl *abbrev_first = 0; - DWARF_AbbrevDecl *abbrev_last = 0; - U64 abbrev_count = 0; - - // abbrev decl loop - for (;ptr < opl;){ - B32 success = 1; - - // mark abbrev_code field - U8 *abbrev_code_ptr = ptr; - DWARF_LEB128_ADV(ptr, opl, success); - - // null abbrev code means end of unit - if (success && *abbrev_code_ptr == 0){ - break; - } - - // mark tag - U8 *tag_ptr = ptr; - DWARF_LEB128_ADV(ptr, opl, success); - U8 *end_tag_ptr = ptr; - - // extract has_children - B8 has_children = 0; - if (ptr < opl){ - has_children = *ptr; - ptr += 1; - } - else{ - success = 0; - } - - // count attributes - U8 *attrib_start_ptr = ptr; - U32 attrib_count = 0; - B32 has_implicit_const = 0; - if (success){ - for (;;){ - // decode normal attribute layout - U8 *attrib_name = ptr; - DWARF_LEB128_ADV(ptr, opl, success); - U8 *attrib_form = ptr; - DWARF_LEB128_ADV(ptr, opl, success); - - // handle special case implicit_const - if (success && *attrib_form == (U8)DWARF_AttributeForm_implicit_const){ - DWARF_LEB128_ADV(ptr, opl, success); - has_implicit_const = 1; - } - - // termination conditions - if (ptr == opl || - (*attrib_name == 0 && *attrib_form == 0)){ - break; - } - - // increment - attrib_count += 1; - } - } - - // build the abbreviation declaration - if (full_parse && success){ - - // allocate abbrev - DWARF_AbbrevDecl *abbrev = push_array(arena, DWARF_AbbrevDecl, 1); - DWARF_AbbrevAttribSpec *attribs = - push_array_no_zero(arena, DWARF_AbbrevAttribSpec, attrib_count); - U64 *implicit_const = 0; - if (has_implicit_const){ - implicit_const = push_array(arena, U64, attrib_count); - } - - // extract abbrev fields - U32 abbrev_code = dwarf_leb128_decode_U32(abbrev_code_ptr, tag_ptr); - U32 tag = dwarf_leb128_decode_U32(tag_ptr, end_tag_ptr); - - U8 *attrib_ptr = attrib_start_ptr; - DWARF_AbbrevAttribSpec *attrib = attribs; - for (U32 i = 0; i < attrib_count; i += 1, attrib += 1){ - // mark attribute fields - U8 *attrib_name = attrib_ptr; - DWARF_LEB128_ADV_NOCAP(attrib_ptr); - U8 *attrib_form = attrib_ptr; - DWARF_LEB128_ADV_NOCAP(attrib_ptr); - - // extract attribute fields - U32 name = dwarf_leb128_decode_U32(attrib_name, attrib_form); - U32 form = dwarf_leb128_decode_U32(attrib_form, attrib_ptr); - - // fill attribute spec - attrib->name = (DWARF_AttributeName)name; - attrib->form = (DWARF_AttributeForm)form; - - // handle special case implicit_const - if (form == DWARF_AttributeForm_implicit_const){ - U8 *attrib_value = attrib_ptr; - DWARF_LEB128_ADV_NOCAP(attrib_ptr); - S64 value = dwarf_leb128_decode_S64(attrib_form, attrib_ptr); - implicit_const[i] = value; - } - } - - // fill abbreviation - SLLQueuePush(abbrev_first, abbrev_last, abbrev); - abbrev_count += 1; - abbrev->abbrev_code = abbrev_code; - abbrev->tag = (DWARF_Tag)tag; - abbrev->has_children = has_children; - abbrev->attrib_count = attrib_count; - abbrev->attrib_specs = attribs; - abbrev->implicit_const = implicit_const; - } - - // handle failure - if (!success){ - // TODO: emit error message - decoding_error = 1; - goto done_parse; - } - } - - // fill unit - if (full_parse){ - SLLQueuePush(unit_first, unit_last, unit); - unit_count += 1; - unit->offset = abbrev_unit_offset; - unit->first = abbrev_first; - unit->last = abbrev_last; - unit->count = abbrev_count; - } - } - - done_parse:; - - // fill result - DWARF_AbbrevParsed *result = push_array(arena, DWARF_AbbrevParsed, 1); - result->unit_first = unit_first; - result->unit_last = unit_last; - result->unit_count = unit_count; - result->decoding_error = decoding_error; - return(result); -} -#endif +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ Dwarf Decode Helpers + +static U64 +dwarf_leb128_decode_U64(U8 *ptr, U8 *opl){ + U64 r = 0; + switch (opl - ptr){ + case 10: r |= ((U64)(ptr[9]&0x7F) << 63); + case 9: r |= ((U64)(ptr[8]&0x7F) << 56); + case 8: r |= ((U64)(ptr[7]&0x7F) << 49); + case 7: r |= ((U64)(ptr[6]&0x7F) << 42); + case 6: r |= ((U64)(ptr[5]&0x7F) << 35); + case 5: r |= ((U64)(ptr[4]&0x7F) << 28); + case 4: r |= ((U64)(ptr[3]&0x7F) << 21); + case 3: r |= ((U64)(ptr[2]&0x7F) << 14); + case 2: r |= ((U64)(ptr[1]&0x7F) << 7); + case 1: r |= ((U64)(ptr[0]&0x7F) ); + case 0: default: break; + } + return(r); +} + +static S64 +dwarf_leb128_decode_S64(U8 *ptr, U8 *opl){ + U64 u = dwarf_leb128_decode_U32(ptr, opl); + U64 s = (U64)(opl - ptr)*7; + B32 neg = ((u & (1llu << s)) != 0); + if (neg){ + switch (opl - ptr){ + case 9: u |= ~0x7FFFFFFFFFFFFFFFllu; break; + case 8: u |= ~0x00FFFFFFFFFFFFFFllu; break; + case 7: u |= ~ 0x01FFFFFFFFFFFFllu; break; + case 6: u |= ~ 0x03FFFFFFFFFFllu; break; + case 5: u |= ~ 0x07FFFFFFFFllu; break; + case 4: u |= ~ 0x0FFFFFFFllu; break; + case 3: u |= ~ 0x1FFFFFllu; break; + case 2: u |= ~ 0x3FFFllu; break; + case 1: u |= ~ 0x7Fllu; break; + } + } + S64 r = (S64)(u); + return(r); +} + +static U32 +dwarf_leb128_decode_U32(U8 *ptr, U8 *opl){ + U32 r = 0; + switch (opl - ptr){ + case 5: r |= ((U32)(ptr[4]&0x7F) << 28); + case 4: r |= ((U32)(ptr[3]&0x7F) << 21); + case 3: r |= ((U32)(ptr[2]&0x7F) << 14); + case 2: r |= ((U32)(ptr[1]&0x7F) << 7); + case 1: r |= ((U32)(ptr[0]&0x7F) ); + case 0: default: break; + } + return(r); +} + + +//////////////////////////////// +//~ Dwarf Parser Functions + +static DWARF_Parsed* +dwarf_parsed_from_elf(Arena *arena, ELF_Parsed *elf){ + DWARF_Parsed *result = 0; + + if (elf != 0){ + //- extract debug info + U32 debug_section_idx[DWARF_SectionCode_COUNT] = {0}; + String8 debug_section_name[DWARF_SectionCode_COUNT] = {0}; + String8 debug_data[DWARF_SectionCode_COUNT] = {0}; + for (U64 i = 1; i < DWARF_SectionCode_COUNT; i += 1){ + DWARF_SectionNameRow *row = dwarf_section_name_table + i; + U32 idx = 0; + for (U32 j = 0; idx == 0 && j < DWARF_SECTION_NAME_VARIANT_COUNT; j += 1){ + idx = elf_section_idx_from_name(elf, row->name[j]); + } + debug_section_idx[i] = idx; + debug_section_name[i] = elf_section_name_from_idx(elf, idx); + debug_data[i] = elf_section_data_from_idx(elf, idx); + } + + //- fill result + { + result = push_array(arena, DWARF_Parsed, 1); + result->elf = elf; + MemoryCopyArray(result->debug_section_idx, debug_section_idx); + MemoryCopyArray(result->debug_section_name, debug_section_name); + MemoryCopyArray(result->debug_data, debug_data); + } + } + + return(result); +} + +static DWARF_IndexParsed* +dwarf_index_from_data(Arena *arena, String8 data){ + DWARF_IndexParsed *result = 0; + // TODO(allen): + return(result); +} + +static DWARF_SupParsed* +dwarf_sup_from_data(Arena *arena, String8 data){ + DWARF_SupParsed *result = 0; + // TODO(allen): + return(result); +} + +static DWARF_InfoParsed* +dwarf_info_from_data(Arena *arena, String8 data){ + // supported version numbers: 4,5 + + + // empty unit list + DWARF_InfoUnit *first = 0; + DWARF_InfoUnit *last = 0; + U64 count = 0; + + // whole section loop + U64 unit_idx = 0; + U8 *ptr = data.str; + U8 *opl = data.str + data.size; + for (;ptr < opl; unit_idx += 1){ + + // remember header offset + U64 hdr_off = (ptr - data.str); + + // initial length + U8 *unit_opl = 0; + B32 is_64bit = 0; + dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); + + // version + U8 version = MemoryConsume(U16, ptr, unit_opl); + + // rest of header depends on version + U64 abbrev_off = 0; + U8 address_size = 0; + U8 unit_type = 0; + U64 unit_dwo_id = 0; + U64 unit_type_signature = 0; + U64 unit_type_offset = 0; + switch (version){ + case 4: + { + // abbrev_off + if (is_64bit){ + abbrev_off = MemoryConsume(U64, ptr, unit_opl); + } + else{ + abbrev_off = MemoryConsume(U32, ptr, unit_opl); + } + + // address_size + address_size = MemoryConsume(U8, ptr, unit_opl); + }break; + + case 5: + { + // unit_type + unit_type = (DWARF_UnitType)MemoryConsume(U8, ptr, unit_opl); + + // address_size + address_size = MemoryConsume(U8, ptr, unit_opl); + + // abbrev_off + if (is_64bit){ + abbrev_off = MemoryConsume(U64, ptr, unit_opl); + } + else{ + abbrev_off = MemoryConsume(U32, ptr, unit_opl); + } + + // rest of header depends on unit_type + switch (unit_type){ + case DWARF_UnitType_skeleton: case DWARF_UnitType_split_compile: + { + unit_dwo_id = MemoryConsume(U64, ptr, unit_opl); + }break; + case DWARF_UnitType_type: case DWARF_UnitType_split_type: + { + unit_type_signature = MemoryConsume(U64, ptr, unit_opl); + if (is_64bit){ + unit_type_offset = MemoryConsume(U64, ptr, unit_opl); + } + else{ + unit_type_offset = MemoryConsume(U32, ptr, unit_opl); + } + }break; + } + }break; + } + + // offset size + U8 offset_size = is_64bit?8:4; + + // unit offsets + U64 base_off = (ptr - data.str); + U64 opl_off = (unit_opl - data.str); + + // emit unit + DWARF_InfoUnit *unit = push_array(arena, DWARF_InfoUnit, 1); + SLLQueuePush(first, last, unit); + count += 1; + + unit->hdr_off = hdr_off; + unit->base_off = base_off; + unit->opl_off = opl_off; + + unit->offset_size = offset_size; + unit->version = version; + unit->unit_type = unit_type; + unit->address_size = address_size; + unit->abbrev_off = abbrev_off; + + switch (unit_type){ + case DWARF_UnitType_skeleton: case DWARF_UnitType_split_compile: + { + unit->dwo_id = unit_dwo_id; + }break; + case DWARF_UnitType_type: case DWARF_UnitType_split_type: + { + unit->type_signature = unit_type_signature; + unit->type_offset = unit_type_offset; + }break; + } + + // advance to end of unit + ptr = unit_opl; + } + + // fill result + DWARF_InfoParsed *result = push_array(arena, DWARF_InfoParsed, 1); + result->unit_first = first; + result->unit_last = last; + result->unit_count = count; + return(result); +} + +static DWARF_PubNamesParsed* +dwarf_pubnames_from_data(Arena *arena, String8 data){ + // supported version numbers: 2 + + + // empty unit list + DWARF_PubNamesUnit *first = 0; + DWARF_PubNamesUnit *last = 0; + U64 count = 0; + + // whole section loop + U64 unit_idx = 0; + U8 *ptr = data.str; + U8 *opl = data.str + data.size; + for (;ptr < opl; unit_idx += 1){ + + // remember header offset + U64 hdr_off = (ptr - data.str); + + // initial length + U8 *unit_opl = 0; + B32 is_64bit = 0; + dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); + + // version + U8 version = MemoryConsume(U16, ptr, unit_opl); + + // info_off + U64 info_off = 0; + if (is_64bit){ + info_off = MemoryConsume(U64, ptr, unit_opl); + } + else{ + info_off = MemoryConsume(U32, ptr, unit_opl); + } + + // info_length + U64 info_length = 0; + if (is_64bit){ + info_length = MemoryConsume(U64, ptr, unit_opl); + } + else{ + info_length = MemoryConsume(U32, ptr, unit_opl); + } + + // offset size + U8 offset_size = is_64bit?8:4; + + // unit offsets + U64 base_off = (ptr - data.str); + U64 opl_off = (unit_opl - data.str); + + // emit unit + DWARF_PubNamesUnit *unit = push_array(arena, DWARF_PubNamesUnit, 1); + SLLQueuePush(first, last, unit); + count += 1; + + unit->hdr_off = hdr_off; + unit->base_off = base_off; + unit->opl_off = opl_off; + + unit->offset_size = offset_size; + unit->version = version; + unit->info_off = info_off; + unit->info_length = info_length; + + // advance to end of unit + ptr = unit_opl; + } + + // fill result + DWARF_PubNamesParsed *result = push_array(arena, DWARF_PubNamesParsed, 1); + result->unit_first = first; + result->unit_last = last; + result->unit_count = count; + return(result); +} + +static DWARF_NamesParsed* +dwarf_names_from_data(Arena *arena, String8 data){ + // supported version numbers: 5 + + + // empty unit list + DWARF_NamesUnit *first = 0; + DWARF_NamesUnit *last = 0; + U64 count = 0; + + // whole section loop + U64 unit_idx = 0; + U8 *ptr = data.str; + U8 *opl = data.str + data.size; + for (;ptr < opl; unit_idx += 1){ + + // remember header offset + U64 hdr_off = (ptr - data.str); + + // initial length + U8 *unit_opl = 0; + B32 is_64bit = 0; + dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); + + // version + U8 version = MemoryConsume(U16, ptr, unit_opl); + + // *padding* + MemoryConsume(U16, ptr, unit_opl); + + // comp_unit_count + U32 comp_unit_count = MemoryConsume(U32, ptr, unit_opl); + + // local_type_unit_count + U32 local_type_unit_count = MemoryConsume(U32, ptr, unit_opl); + + // foreign_type_unit_count + U32 foreign_type_unit_count = MemoryConsume(U32, ptr, unit_opl); + + // bucket_count + U32 bucket_count = MemoryConsume(U32, ptr, unit_opl); + + // name_count + U32 name_count = MemoryConsume(U32, ptr, unit_opl); + + // abbrev_table_size + U32 abbrev_table_size = MemoryConsume(U32, ptr, unit_opl); + + // augmentation_string_size + U32 augmentation_string_size = MemoryConsume(U32, ptr, unit_opl); + + // augmentation_string + U8 *augmentation_string = ptr; + { + U8 *ptr_raw = ptr + augmentation_string_size; + ptr = ClampTop(ptr_raw, unit_opl); + } + + // offset size + U8 offset_size = is_64bit?8:4; + + // unit offsets + U64 base_off = (ptr - data.str); + U64 opl_off = (unit_opl - data.str); + + // emit unit + DWARF_NamesUnit *unit = push_array(arena, DWARF_NamesUnit, 1); + SLLQueuePush(first, last, unit); + count += 1; + + unit->hdr_off = hdr_off; + unit->base_off = base_off; + unit->opl_off = opl_off; + + unit->version = version; + unit->comp_unit_count = comp_unit_count; + unit->local_type_unit_count = local_type_unit_count; + unit->foreign_type_unit_count = foreign_type_unit_count; + unit->bucket_count = bucket_count; + unit->name_count = name_count; + unit->abbrev_table_size = abbrev_table_size; + unit->augmentation_string = str8_cstring_capped(augmentation_string, unit_opl); + + // advance to end of unit + ptr = unit_opl; + } + + // fill result + DWARF_NamesParsed *result = push_array(arena, DWARF_NamesParsed, 1); + result->unit_first = first; + result->unit_last = last; + result->unit_count = count; + return(result); +} + +static DWARF_ArangesParsed* +dwarf_aranges_from_data(Arena *arena, String8 data){ + // supported version numbers: 2 + + + // empty unit list + DWARF_ArangesUnit *first = 0; + DWARF_ArangesUnit *last = 0; + U64 count = 0; + + // whole section loop + U64 unit_idx = 0; + U8 *ptr = data.str; + U8 *opl = data.str + data.size; + for (;ptr < opl; unit_idx += 1){ + + // remember header offset + U64 hdr_off = (ptr - data.str); + + // initial length + U8 *unit_opl = 0; + B32 is_64bit = 0; + dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); + + // version + U8 version = MemoryConsume(U16, ptr, unit_opl); + + // info_off + U64 info_off = 0; + if (is_64bit){ + info_off = MemoryConsume(U64, ptr, unit_opl); + } + else{ + info_off = MemoryConsume(U32, ptr, unit_opl); + } + + // address_size + U8 address_size = MemoryConsume(U8, ptr, unit_opl); + + // segment_selector_size + U8 segment_selector_size = MemoryConsume(U8, ptr, unit_opl); + + // offset size + U8 offset_size = is_64bit?8:4; + + // unit offsets + U64 base_off = (ptr - data.str); + U64 opl_off = (unit_opl - data.str); + + // emit unit + DWARF_ArangesUnit *unit = push_array(arena, DWARF_ArangesUnit, 1); + SLLQueuePush(first, last, unit); + count += 1; + + unit->hdr_off = hdr_off; + unit->base_off = base_off; + unit->opl_off = opl_off; + + unit->version = version; + unit->address_size = address_size; + unit->segment_selector_size = segment_selector_size; + unit->offset_size = offset_size; + unit->info_off = info_off; + + // advance to end of unit + ptr = unit_opl; + } + + // fill result + DWARF_ArangesParsed *result = push_array(arena, DWARF_ArangesParsed, 1); + result->unit_first = first; + result->unit_last = last; + result->unit_count = count; + return(result); +} + +static DWARF_LineParsed* +dwarf_line_from_data(Arena *arena, String8 data){ + // supported version numbers: 4, 5 + + + // empty unit list + DWARF_LineUnit *first = 0; + DWARF_LineUnit *last = 0; + U64 count = 0; + + // whole section loop + U64 unit_idx = 0; + U8 *ptr = data.str; + U8 *opl = data.str + data.size; + for (;ptr < opl; unit_idx += 1){ + + // remember header offset + U64 hdr_off = (ptr - data.str); + + // initial length + U8 *unit_opl = 0; + B32 is_64bit = 0; + dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); + + // version + U8 version = MemoryConsume(U16, ptr, unit_opl); + + // offset size + U8 offset_size = is_64bit?8:4; + + // rest of header depends on version + U8 minimum_instruction_length = 0; + U8 maximum_operations_per_instruction = 0; + U8 default_is_stmt = 0; + S8 line_base = 0; + U8 line_range = 0; + U8 opcode_base = 0; + U8 *standard_opcode_lengths = 0; + // v4 + String8List include_directories = {0}; + DWARF_V4LineFileNamesList file_names = {0}; + // v5 + U8 address_size = 0; + U8 segment_selector_size = 0; + U8 directory_entry_format_count = 0; + DWARF_V5LinePathEntryFormat *directory_entry_format = 0; + U64 directories_count = 0; + U8 file_name_entry_format_count = 0; + DWARF_V5LinePathEntryFormat *file_name_entry_format = 0; + U64 file_names_count = 0; + + switch (version){ + case 4: + { + // header_length + U64 header_length = 0; + if (is_64bit){ + header_length = MemoryConsume(U64, ptr, unit_opl); + } + else{ + header_length = MemoryConsume(U32, ptr, unit_opl); + } + + // header opl + U8 *header_opl_raw = ptr + header_length; + U8 *header_opl = ClampTop(header_opl_raw, unit_opl); + + // minimum_instruction_length + minimum_instruction_length = MemoryConsume(U8, ptr, header_opl); + + // maximum_operations_per_instruction + maximum_operations_per_instruction = MemoryConsume(U8, ptr, header_opl); + + // default_is_stmt + default_is_stmt = MemoryConsume(U8, ptr, header_opl); + + // line_base + line_base = MemoryConsume(S8, ptr, header_opl); + + // line_range + line_range = MemoryConsume(U8, ptr, header_opl); + + // opcode_base + opcode_base = MemoryConsume(U8, ptr, header_opl); + + // standard_opcode_lengths + if (opcode_base > 1){ + standard_opcode_lengths = ptr; + ptr += opcode_base - 1; + } + + // include_directories + for (;ptr < header_opl;){ + // null byte ends entries + if (*ptr == 0){ + ptr += 1; + break; + } + + // extract dir range + U8 *dir_first = ptr; + for (;ptr < header_opl && *ptr != 0;) ptr += 1; + U8 *dir_opl = ptr; + if (ptr < header_opl){ + ptr += 1; + } + + // attach dir to list + String8 dir = str8_range(dir_first, dir_opl); + str8_list_push(arena, &include_directories, dir); + } + + // file_names + for (;ptr < header_opl;){ + // null byte ends entries + if (*ptr == 0){ + ptr += 1; + break; + } + + // extract file_name range + U8 *file_name_first = ptr; + for (;ptr < header_opl && *ptr != 0;) ptr += 1; + U8 *file_name_opl = ptr; + if (ptr < header_opl){ + ptr += 1; + } + + // extract include directory index + U64 include_directory_idx = 0; + DWARF_LEB128_DECODE_ADV(U64, include_directory_idx, ptr, header_opl); + + // extract last modified time + U64 last_modified_time = 0; + DWARF_LEB128_DECODE_ADV(U64, last_modified_time, ptr, header_opl); + + // extract file size + U64 file_size = 0; + DWARF_LEB128_DECODE_ADV(U64, file_size, ptr, header_opl); + + // emit file name entry + DWARF_V4LineFileNamesEntry *entry = push_array(arena, DWARF_V4LineFileNamesEntry, 1); + SLLQueuePush(file_names.first, file_names.last, entry); + file_names.count += 1; + entry->file_name = str8_range(file_name_first, file_name_opl); + entry->include_directory_idx = include_directory_idx; + entry->last_modified_time = last_modified_time; + entry->file_size = file_size; + } + + ptr = header_opl; + }break; + + case 5: + { + // address_size + address_size = MemoryConsume(U8, ptr, unit_opl); + + // segment_selector_size + segment_selector_size = MemoryConsume(U8, ptr, unit_opl); + + // header_length + U64 header_length = 0; + if (is_64bit){ + header_length = MemoryConsume(U64, ptr, unit_opl); + } + else{ + header_length = MemoryConsume(U32, ptr, unit_opl); + } + + // header opl + U8 *header_opl_raw = ptr + header_length; + U8 *header_opl = ClampTop(header_opl_raw, unit_opl); + + // minimum_instruction_length + minimum_instruction_length = MemoryConsume(U8, ptr, header_opl); + + // maximum_operations_per_instruction + maximum_operations_per_instruction = MemoryConsume(U8, ptr, header_opl); + + // default_is_stmt + default_is_stmt = MemoryConsume(U8, ptr, header_opl); + + // line_base + line_base = MemoryConsume(S8, ptr, header_opl); + + // line_range + line_range = MemoryConsume(U8, ptr, header_opl); + + // opcode_base + opcode_base = MemoryConsume(U8, ptr, header_opl); + + // standard_opcode_lengths + if (opcode_base > 1){ + standard_opcode_lengths = ptr; + ptr += opcode_base - 1; + } + + // directory_entry_format_count + directory_entry_format_count = MemoryConsume(U8, ptr, header_opl); + + // directory_entry_format + { + directory_entry_format = push_array(arena, DWARF_V5LinePathEntryFormat, + directory_entry_format_count); + DWARF_V5LinePathEntryFormat *entry = directory_entry_format; + DWARF_V5LinePathEntryFormat *entry_opl = + directory_entry_format + directory_entry_format_count; + for (;entry < entry_opl && ptr < header_opl; entry += 1){ + DWARF_LEB128_DECODE_ADV(U64, entry->content_type, ptr, header_opl); + DWARF_LEB128_DECODE_ADV(U64, entry->form, ptr, header_opl); + } + } + + // directories_count + DWARF_LEB128_DECODE_ADV(U64, directories_count, ptr, header_opl); + + // directories + DWARF_V5Directory *directories = push_array(arena, DWARF_V5Directory, directories_count); + dwarf__line_v5_directories(address_size, offset_size, + directory_entry_format, directory_entry_format_count, + directories, directories_count, + &ptr, header_opl); + + // file_name_entry_format_count + file_name_entry_format_count = MemoryConsume(U8, ptr, header_opl); + + // file_name_entry_format + { + file_name_entry_format = push_array(arena, DWARF_V5LinePathEntryFormat, + file_name_entry_format_count); + DWARF_V5LinePathEntryFormat *entry = file_name_entry_format; + for (;ptr < header_opl; entry += 1){ + DWARF_LEB128_DECODE_ADV(U64, entry->content_type, ptr, header_opl); + DWARF_LEB128_DECODE_ADV(U64, entry->form, ptr, header_opl); + } + } + + // file_names_count + DWARF_LEB128_DECODE_ADV(U64, file_names_count, ptr, header_opl); + + // file_names + DWARF_V5Directory *file_names = push_array(arena, DWARF_V5Directory, file_names_count); + dwarf__line_v5_directories(address_size, offset_size, + directory_entry_format, directory_entry_format_count, + file_names, file_names_count, + &ptr, header_opl); + }break; + } + + // unit offsets + U64 base_off = (ptr - data.str); + U64 opl_off = (unit_opl - data.str); + + // emit unit + DWARF_LineUnit *unit = push_array(arena, DWARF_LineUnit, 1); + SLLQueuePush(first, last, unit); + count += 1; + + unit->hdr_off = hdr_off; + unit->base_off = base_off; + unit->opl_off = opl_off; + + unit->version = version; + + // advance to end of unit + ptr = unit_opl; + } + + // fill result + DWARF_LineParsed *result = push_array(arena, DWARF_LineParsed, 1); + result->unit_first = first; + result->unit_last = last; + result->unit_count = count; + return(result); +} + +static DWARF_MacInfoParsed* +dwarf_mac_info_from_data(Arena *arena, String8 data){ + DWARF_MacInfoParsed *result = 0; + // TODO(allen): + return(result); +} + +static DWARF_MacroParsed* +dwarf_macro_from_data(Arena *arena, String8 data){ + DWARF_MacroParsed *result = 0; + // TODO(allen): + return(result); +} + +static DWARF_FrameParsed* +dwarf_frame_from_data(Arena *arena, String8 data){ + DWARF_FrameParsed *result = 0; + // TODO(allen): + return(result); +} + +static DWARF_RangesParsed* +dwarf_ranges_from_data(Arena *arena, String8 data){ + DWARF_RangesParsed *result = 0; + // TODO(allen): + return(result); +} + +static DWARF_StrOffsetsParsed* +dwarf_str_offsets_from_data(Arena *arena, String8 data){ + DWARF_StrOffsetsParsed *result = 0; + // TODO(allen): + return(result); +} + +static DWARF_AddrParsed* +dwarf_addr_from_data(Arena *arena, String8 data){ + // supported version numbers: 5 + + + // addr unit list + DWARF_AddrUnit *first = 0; + DWARF_AddrUnit *last = 0; + U64 count = 0; + + // whole section loop + U64 unit_idx = 0; + U8 *ptr = data.str; + U8 *opl = data.str + data.size; + for (;ptr < opl; unit_idx += 1){ + + U64 hdr_off = (ptr - data.str); + + // initial length + U8 *unit_opl = 0; + B32 is_64bit = 0; + dwarf__initial_length(data, &ptr, &unit_opl, &is_64bit); + + // version + U8 version = MemoryConsume(U16, ptr, unit_opl); + + // address size + U8 address_size = MemoryConsume(U8, ptr, unit_opl); + + // segment selector size + U8 segment_selector_size = MemoryConsume(U8, ptr, unit_opl); + + // offset size + U32 offset_size = is_64bit?8:4; + + // unit offsets + U64 base_off = (ptr - data.str); + U64 opl_off = (unit_opl - data.str); + + // emit addr unit + DWARF_AddrUnit *unit = push_array(arena, DWARF_AddrUnit, 1); + SLLQueuePush(first, last, unit); + count += 1; + + unit->hdr_off = hdr_off; + unit->base_off = base_off; + unit->opl_off = opl_off; + + unit->offset_size = offset_size; + unit->dwarf_version = version; + unit->address_size = address_size; + unit->segment_selector_size = segment_selector_size; + + // advance to next unit + ptr = unit_opl; + } + + // fill result + DWARF_AddrParsed *result = push_array(arena, DWARF_AddrParsed, 1); + result->unit_first = first; + result->unit_last = last; + result->unit_count = count; + return(result); +} + +static DWARF_RngListsParsed* +dwarf_rng_lists_from_data(Arena *arena, String8 data){ + DWARF_RngListsParsed *result = 0; + // TODO(allen): + return(result); +} + +static DWARF_LocListsParsed* +dwarf_loc_lists_from_data(Arena *arena, String8 data){ + DWARF_LocListsParsed *result = 0; + // TODO(allen): + return(result); +} + + +// parse helpers + +static void +dwarf__initial_length(String8 data, U8 **ptr_inout, U8 **unit_opl_out, B32 *is_64bit_out){ + U8 *unit_opl = 0; + B32 is_64bit = 0; + + U8 *opl = data.str + data.size; + U8 *ptr = *ptr_inout; + { + U64 length = 0; + U32 m = MemoryConsume(U32, ptr, opl); + if (m == 0xFFFFFFFF){ + is_64bit = 1; + length = MemoryConsume(U64, ptr, opl); + } + else{ + length = ClampTop(m, 0xFFFFFFF0); + } + if (length > 0){ + U64 unit_opl_off_raw = (ptr - data.str) + length; + U64 unit_opl_off = ClampTop(unit_opl_off_raw, data.size); + unit_opl = data.str + unit_opl_off; + } + else{ + unit_opl = ptr; + } + } + + *ptr_inout = ptr; + *unit_opl_out = unit_opl; + *is_64bit_out = is_64bit; +} + +static void +dwarf__line_v5_directories(U64 address_size, U64 offset_size, + DWARF_V5LinePathEntryFormat *format, U64 format_count, + DWARF_V5Directory *directories_out, U64 dir_count, + U8 **ptr_io, U8 *opl){ + + U8 *ptr = *ptr_io; + + DWARF_V5Directory *directory_ptr = directories_out; + for (U32 i = 0; i < dir_count; i += 1, directory_ptr += 1){ + DWARF_V5LinePathEntryFormat *fmt = format; + for (U32 j = 0; j < format_count; j += 1){ + + // form decode + DWARF_FormDecodeRules rules = + dwarf_form_decode_rule(fmt->form, address_size, offset_size); + + // execute decoding + DWARF_FormDecoded decoded = dwarf_form_decode(&rules, &ptr, opl, 0, 0); + + // store to correct field + U64 *target = 0; + switch (fmt->content_type){ + case DWARF_LineEntryFormat_path: + { + if (decoded.dataptr != 0){ + directory_ptr->path_str = str8(decoded.dataptr, decoded.val); + } + else{ + directory_ptr->path_off = decoded.val; + directory_ptr->path_sec_form = fmt->form; + } + }break; + + case DWARF_LineEntryFormat_directory_index: + { + target = &directory_ptr->directory_index; + }goto v5_directory_u64; + + case DWARF_LineEntryFormat_timestamp: + { + target = &directory_ptr->timestamp; + }goto v5_directory_u64; + + case DWARF_LineEntryFormat_size: + { + target = &directory_ptr->size; + }goto v5_directory_u64; + + v5_directory_u64: + { + if (decoded.dataptr != 0){ + U64 size = ClampTop(decoded.val, 8); + MemoryCopy(target, decoded.dataptr, size); + } + else{ + *target = decoded.val; + } + }break; + + case DWARF_LineEntryFormat_MD5: + { + if (decoded.dataptr != 0){ + U64 size = ClampTop(decoded.val, 16); + MemoryCopy(directory_ptr->md5_checksum, decoded.dataptr, size); + } + }break; + } + } + } + + *ptr_io = ptr; +} + + +// debug sections + +static String8 +dwarf_name_from_debug_section(DWARF_Parsed *dwarf, DWARF_SectionCode sec_code){ + String8 result = str8_lit("invalid_debug_section"); + if (sec_code < DWARF_SectionCode_COUNT){ + if (dwarf->debug_section_idx[sec_code] != 0){ + result = dwarf->debug_section_name[sec_code]; + } + } + return(result); +} + + +// abbrev functions + +static DWARF_AbbrevUnit* +dwarf_abbrev_unit_from_offset(DWARF_AbbrevParsed *abbrev, U64 offset){ + DWARF_AbbrevUnit *result = 0; + for (DWARF_AbbrevUnit *unit = abbrev->unit_first; + unit != 0; + unit = unit->next){ + if (unit->offset == offset){ + result = unit; + break; + } + } + return(result); +} + +static DWARF_AbbrevDecl* +dwarf_abbrev_decl_from_code(DWARF_AbbrevUnit *unit, U32 abbrev_code){ + DWARF_AbbrevDecl *result = 0; + for (DWARF_AbbrevDecl *decl = unit->first; + decl != 0; + decl = decl->next){ + if (decl->abbrev_code == abbrev_code){ + result = decl; + break; + } + } + return(result); +} + +// attribute decoding functions + +static DWARF_AttributeClassFlags +dwarf_attribute_class_from_form(DWARF_AttributeForm form){ + DWARF_AttributeClassFlags result = 0; + switch (form){ +#define X(N,C,f) case C: result = DWARF_AttributeClassFlag_##f; break; + DWARF_AttributeFormXList(X) +#undef X + } + return(result); +} + +static DWARF_AttributeClassFlags +dwarf_attribute_class_from_name(DWARF_AttributeName name){ + DWARF_AttributeClassFlags result = 0; + switch (name){ +#define X(N,C,f1,f2,f3,f4) case C: result = 0\ +|DWARF_AttributeClassFlag_##f1\ +|DWARF_AttributeClassFlag_##f2\ +|DWARF_AttributeClassFlag_##f3\ +|DWARF_AttributeClassFlag_##f4\ +;break; + DWARF_AttributeNameXList(X) +#undef X + } + return(result); +} + +// form decoding functions + +static DWARF_FormDecodeRules +dwarf_form_decode_rule(DWARF_AttributeForm form, U64 address_size, U64 offset_size){ + DWARF_FormDecodeRules result = {0}; + switch (form){ + case DWARF_AttributeForm_addr: result.size = address_size; break; + case DWARF_AttributeForm_addrx: result.uleb128 = 1; break; + case DWARF_AttributeForm_addrx1: result.size = 1; break; + case DWARF_AttributeForm_addrx2: result.size = 2; break; + case DWARF_AttributeForm_addrx3: result.size = 3; break; + case DWARF_AttributeForm_addrx4: result.size = 4; break; + + case DWARF_AttributeForm_sec_offset: result.size = offset_size; break; + + case DWARF_AttributeForm_block1: result.size = 1; result.block = 1; break; + case DWARF_AttributeForm_block2: result.size = 2; result.block = 1; break; + case DWARF_AttributeForm_block4: result.size = 4; result.block = 1; break; + case DWARF_AttributeForm_block: result.uleb128 = 1; result.block = 1; break; + + case DWARF_AttributeForm_data1: result.size = 1; break; + case DWARF_AttributeForm_data2: result.size = 2; break; + case DWARF_AttributeForm_data4: result.size = 4; break; + case DWARF_AttributeForm_data8: result.size = 8; break; + case DWARF_AttributeForm_data16: result.size = 16; break; + + case DWARF_AttributeForm_sdata: result.sleb128 = 1; break; + case DWARF_AttributeForm_udata: result.uleb128 = 1; break; + + case DWARF_AttributeForm_implicit_const: result.in_abbrev = 1; break; + + case DWARF_AttributeForm_exprloc: result.uleb128 = 1; result.block = 1; break; + + case DWARF_AttributeForm_flag: result.size = 1; break; + case DWARF_AttributeForm_flag_present: result.auto_1 = 1; break; + + case DWARF_AttributeForm_loclistx: result.uleb128 = 1; break; + case DWARF_AttributeForm_rnglistx: result.uleb128 = 1; break; + + case DWARF_AttributeForm_ref1: result.size = 1; break; + case DWARF_AttributeForm_ref2: result.size = 2; break; + case DWARF_AttributeForm_ref4: result.size = 4; break; + case DWARF_AttributeForm_ref8: result.size = 8; break; + case DWARF_AttributeForm_ref_udata: result.uleb128 = 1; break; + + case DWARF_AttributeForm_ref_addr: result.size = offset_size; break; + + case DWARF_AttributeForm_ref_sig8: result.size = 8; break; + + case DWARF_AttributeForm_ref_sup4: result.size = 4; break; + case DWARF_AttributeForm_ref_sup8: result.size = 8; break; + + case DWARF_AttributeForm_string: result.null_terminated = 1; break; + + case DWARF_AttributeForm_strp: result.size = offset_size; break; + case DWARF_AttributeForm_line_strp: result.size = offset_size; break; + case DWARF_AttributeForm_strp_sup: result.size = offset_size; break; + + case DWARF_AttributeForm_strx: result.uleb128 = 1; break; + case DWARF_AttributeForm_strx1: result.size = 1; break; + case DWARF_AttributeForm_strx2: result.size = 2; break; + case DWARF_AttributeForm_strx3: result.size = 3; break; + case DWARF_AttributeForm_strx4: result.size = 4; break; + } + + return(result); +} + +static DWARF_FormDecoded +dwarf_form_decode(DWARF_FormDecodeRules *rules, U8 **ptr_io, U8 *opl, + DWARF_AbbrevDecl *abbrev_decl, U32 attrib_i){ + + // local copy of ptr + U8 *ptr = *ptr_io; + + // apply rules + U64 val = 0; + U8 *dataptr = 0; + + B32 success = 1; + if (rules->size > 0){ + if (ptr + rules->size <= opl){ + MemoryCopy(&val, ptr, rules->size); + ptr += rules->size; + } + else{ + success = 0; + } + } + else if (rules->uleb128 || rules->sleb128){ + U8 *val_ptr = ptr; + DWARF_LEB128_ADV(ptr, opl, success); + if (success){ + if (rules->uleb128){ + val = dwarf_leb128_decode_U64(val_ptr, ptr); + } + else{ + val = (U64)dwarf_leb128_decode_S64(val_ptr, ptr); + } + } + } + else if (rules->in_abbrev){ + if (abbrev_decl != 0){ + if (abbrev_decl->implicit_const != 0){ + val = (U64)abbrev_decl->implicit_const[attrib_i]; + } + } + else{ + success = 0; + } + } + else if (rules->auto_1){ + val = 1; + } + if (rules->block){ + dataptr = ptr; + ptr += val; + } + else if (rules->null_terminated){ + dataptr = ptr; + for (;ptr < opl && *ptr != 0;) ptr += 1; + val = (U64)(ptr - dataptr); + if (ptr < opl){ + ptr += 1; + } + } + + // store out ptr + *ptr_io = ptr; + + // fill result + DWARF_FormDecoded result = {0}; + result.val = val; + result.dataptr = dataptr; + result.error = !success; + return(result); +} + + +// string functions + +static String8 +dwarf_string_from_unit_type(DWARF_UnitType type){ + String8 result = str8_lit("unrecognized_type"); + switch (type){ +#define X(N,C) case C: result = str8_lit(#N); break; + DWARF_UnitTypeXList(X) +#undef X + } + return(result); +} + +static String8 +dwarf_string_from_tag(DWARF_Tag tag){ + String8 result = str8_lit("unrecognized_tag"); + switch (tag){ +#define X(N,C) case C: result = str8_lit(#N); break; + DWARF_TagXList(X) +#undef X + } + return(result); +} + +static String8 +dwarf_string_from_attribute_name(DWARF_AttributeName name){ + String8 result = str8_lit("unrecognized_attribute_name"); + switch (name){ +#define X(N,C,f1,f2,f3,f4) case C: result = str8_lit(#N); break; + DWARF_AttributeNameXList(X) +#undef X + } + return(result); +} + +static String8 +dwarf_string_from_attribute_form(DWARF_AttributeForm form){ + String8 result = str8_lit("unrecognized_attribute_form"); + switch (form){ +#define X(N,C,k) case C: result = str8_lit(#N); break; + DWARF_AttributeFormXList(X) +#undef X + } + return(result); +} + +static String8 +dwarf_string_from_line_std_op(DWARF_LineStdOp op){ + String8 result = str8_lit("unrecognized_line_std_op"); + switch (op){ +#define X(N,C) case C: result = str8_lit(#N); break; + DWARF_LineStdOpXList(X) +#undef X + } + return(result); +} + +static String8 +dwarf_string_from_line_ext_op(DWARF_LineExtOp op){ + String8 result = str8_lit("unrecognized_line_ext_op"); + switch (op){ +#define X(N,C) case C: result = str8_lit(#N); break; + DWARF_LineExtOpXList(X) +#undef X + } + return(result); +} + +static String8 +dwarf_string_from_line_entry_format(DWARF_LineEntryFormat format){ + String8 result = str8_lit("unrecognized_line_entry_format"); + switch (format){ +#define X(N,C) case C: result = str8_lit(#N); break; + DWARF_LineEntryFormatXList(X) +#undef X + } + return(result); +} + +static String8 +dwarf_string_from_section_code(DWARF_SectionCode sec_code){ + String8 result = str8_lit("unrecognized_section_code"); + switch (sec_code){ +#define X(Nc,Vf,N0,N1,N2) case DWARF_SectionCode_##Nc: result = str8_lit(#Nc); break; + DWARF_SectionNameXList(X,0,0) +#undef X + } + return(result); +} + + + + + +#if 0 +static DWARF_InfoParsed* +dwarf_info_from_data(Arena *arena, String8 data, DWARF_InfoParams *params, + DWARF_AbbrevParsed *abbrev){ + + // unit index range to extract + U64 unit_idx_min = params->unit_idx_min; + U64 unit_idx_max = params->unit_idx_max; + + // empty unit list + DWARF_InfoUnit *unit_first = 0; + DWARF_InfoUnit *unit_last = 0; + U64 unit_count = 0; + B32 decoding_error = 0; + + // whole section loop + U64 unit_idx = 0; + U8 *ptr = data.str; + U8 *opl = data.str + data.size; + for (;ptr < opl; unit_idx += 1){ + + // early escape on unit idx + if (unit_idx > unit_idx_max){ + break; + } + + // determine whether to full parse this unit + B32 full_parse = (unit_idx_min <= unit_idx); + + // header fields + U8 *unit_opl = 0; + B32 is_64bit = 0; + U16 version = 0; + U64 abbrev_offset = 0; + U32 address_size = 0; + DWARF_UnitType unit_type = DWARF_UnitType_null; + U64 unit_dwo_id = 0; + U64 unit_type_signature = 0; + U64 unit_type_offset = 0; + + // initial length + dwarf__initial_length(&ptr, opl, &unit_opl, &is_64bit); + + // if this is not a full parse we may use + // unit_opl to skip to the next unit now + if (full_parse){ + + // version (part of header) + version = MemoryConsume(U16, ptr, unit_opl); + + // rest of header depends on version + switch (version){ + case 4: + { + // abbrev_offset (part of header) + if (is_64bit){ + abbrev_offset = MemoryConsume(U64, ptr, unit_opl); + } + else{ + abbrev_offset = MemoryConsume(U32, ptr, unit_opl); + } + + // address_size (part of header) + address_size = MemoryConsume(U8, ptr, unit_opl); + }break; + + case 5: + { + // unit_type (part of header) + unit_type = (DWARF_UnitType)MemoryConsume(U8, ptr, unit_opl); + + // address_size (part of header) + address_size = MemoryConsume(U8, ptr, unit_opl); + + // abbrev_offset (part of header) + if (is_64bit){ + abbrev_offset = MemoryConsume(U64, ptr, unit_opl); + } + else{ + abbrev_offset = MemoryConsume(U32, ptr, unit_opl); + } + + // rest of header depends on unit_type + switch (unit_type){ + case DWARF_UnitType_skeleton: + case DWARF_UnitType_split_compile: + { + unit_dwo_id = MemoryConsume(U64, ptr, unit_opl); + }break; + case DWARF_UnitType_type: + case DWARF_UnitType_split_type: + { + unit_type_signature = MemoryConsume(U64, ptr, unit_opl); + if (is_64bit){ + unit_type_offset = MemoryConsume(U64, ptr, unit_opl); + } + else{ + unit_type_offset = MemoryConsume(U32, ptr, unit_opl); + } + }break; + } + }break; + } + + // offset size + U32 offset_size = is_64bit?8:4; + + // find matching abbrev unit + DWARF_AbbrevUnit *abbrev_unit = dwarf_abbrev_unit_from_offset(abbrev, abbrev_offset); + if (abbrev_unit == 0){ + // TODO: preserve error info + decoding_error = 1; + } + + // consume info entries + DWARF_InfoEntry *entry_root = 0; + U64 entry_count = 0; + + DWARF_InfoEntry *entry_consptr = 0; + if (abbrev_unit != 0){ + for (;ptr < unit_opl;){ + B32 success = 1; + + // mark beginning of entry + U8 *entry_start_ptr = ptr; + + // extract abbrev code + U8 *abbrev_code_ptr = ptr; + DWARF_LEB128_ADV(ptr, unit_opl, success); + if (!success){ + // TODO: preserve error info + decoding_error = 1; + goto exit_unit_loop; + } + + U32 abbrev_code = dwarf_leb128_decode_U32(abbrev_code_ptr, ptr); + + // null abbrev code means pop + if (abbrev_code == 0){ + if (entry_consptr == 0){ + goto exit_unit_loop; + } + else{ + entry_consptr = entry_consptr->parent; + goto skip_entry; + } + } + + // get abbrev decl + DWARF_AbbrevDecl *abbrev_decl = dwarf_abbrev_decl_from_code(abbrev_unit, abbrev_code); + if (abbrev_decl == 0){ + // TODO: preserve error info + decoding_error = 1; + goto exit_unit_loop; + } + + // allocate entry + U32 attrib_count = abbrev_decl->attrib_count; + DWARF_InfoEntry *entry = push_array(arena, DWARF_InfoEntry, 1); + DWARF_InfoAttribVal *attrib_vals = + push_array_no_zero(arena, DWARF_InfoAttribVal, attrib_count); + + // save entry offset + entry->info_offset = (U64)(entry_start_ptr - data.str); + + // set root at beginning + if (entry_root == 0){ + entry_root = entry; + } + + // attribute loop + DWARF_AbbrevAttribSpec *attrib_spec = abbrev_decl->attrib_specs; + DWARF_InfoAttribVal *attrib_val = attrib_vals; + for (U32 i = 0; i < attrib_count; i += 1, attrib_spec += 1, attrib_val += 1){ + + // determine decoding rules + U32 size = 0; + B8 uleb128 = 0; + B8 sleb128 = 0; + B8 in_abbrev = 0; + B8 auto_1 = 0; + B8 block = 0; + B8 null_terminated = 0; + { + DWARF_AttributeForm form = attrib_spec->form; + switch (form){ + case DWARF_AttributeForm_addr: size = address_size; break; + case DWARF_AttributeForm_addrx: uleb128 = 1; break; + case DWARF_AttributeForm_addrx1: size = 1; break; + case DWARF_AttributeForm_addrx2: size = 2; break; + case DWARF_AttributeForm_addrx3: size = 3; break; + case DWARF_AttributeForm_addrx4: size = 4; break; + + case DWARF_AttributeForm_sec_offset: size = offset_size; break; + + case DWARF_AttributeForm_block1: size = 1; block = 1; break; + case DWARF_AttributeForm_block2: size = 2; block = 1; break; + case DWARF_AttributeForm_block4: size = 4; block = 1; break; + case DWARF_AttributeForm_block: uleb128 = 1; block = 1; break; + + case DWARF_AttributeForm_data1: size = 1; break; + case DWARF_AttributeForm_data2: size = 2; break; + case DWARF_AttributeForm_data4: size = 4; break; + case DWARF_AttributeForm_data8: size = 8; break; + case DWARF_AttributeForm_data16: size = 16; break; + + case DWARF_AttributeForm_sdata: sleb128 = 1; break; + case DWARF_AttributeForm_udata: uleb128 = 1; break; + + case DWARF_AttributeForm_implicit_const: in_abbrev = 1; break; + + case DWARF_AttributeForm_exprloc: uleb128 = 1; block = 1; break; + + case DWARF_AttributeForm_flag: size = 1; break; + case DWARF_AttributeForm_flag_present: auto_1 = 1; break; + + case DWARF_AttributeForm_loclistx: uleb128 = 1; break; + case DWARF_AttributeForm_rnglistx: uleb128 = 1; break; + + case DWARF_AttributeForm_ref1: size = 1; break; + case DWARF_AttributeForm_ref2: size = 2; break; + case DWARF_AttributeForm_ref4: size = 4; break; + case DWARF_AttributeForm_ref8: size = 8; break; + case DWARF_AttributeForm_ref_udata: uleb128 = 1; break; + + case DWARF_AttributeForm_ref_addr: size = offset_size; break; + + case DWARF_AttributeForm_ref_sig8: size = 8; break; + + case DWARF_AttributeForm_ref_sup4: size = 4; break; + case DWARF_AttributeForm_ref_sup8: size = 8; break; + + case DWARF_AttributeForm_string: null_terminated = 1; break; + + case DWARF_AttributeForm_strp: size = offset_size; break; + case DWARF_AttributeForm_line_strp: size = offset_size; break; + case DWARF_AttributeForm_strp_sup: size = offset_size; break; + + case DWARF_AttributeForm_strx: uleb128 = 1; break; + case DWARF_AttributeForm_strx1: size = 1; break; + case DWARF_AttributeForm_strx2: size = 2; break; + case DWARF_AttributeForm_strx3: size = 3; break; + case DWARF_AttributeForm_strx4: size = 4; break; + } + } + + // execute decoding rules + U64 val = 0; + U8 *dataptr = 0; + { + if (size > 0){ + if (ptr + size <= unit_opl){ + MemoryCopy(&val, ptr, size); + ptr += size; + } + else{ + // TODO: preserve error info + decoding_error = 1; + goto exit_unit_loop; + } + } + else if (uleb128 || sleb128){ + U8 *val_ptr = ptr; + DWARF_LEB128_ADV(ptr, unit_opl, success); + if (!success){ + // TODO: preserve error info + decoding_error = 1; + goto exit_unit_loop; + } + else{ + if (uleb128){ + val = dwarf_leb128_decode_U64(val_ptr, ptr); + } + else{ + val = (U64)dwarf_leb128_decode_S64(val_ptr, ptr); + } + } + } + else if (in_abbrev){ + if (abbrev_decl->implicit_const != 0){ + val = (U64)abbrev_decl->implicit_const[i]; + } + } + else if (auto_1){ + val = 1; + } + if (block){ + dataptr = ptr; + ptr += val; + } + else if (null_terminated){ + dataptr = ptr; + for (;ptr < unit_opl && *ptr != 0;) ptr += 1; + val = (U64)(ptr - dataptr); + } + } + + // save attribute + attrib_val->val = val; + attrib_val->dataptr = dataptr; + } + + // emit entry + if (entry_consptr != 0){ + SLLQueuePush_N(entry_consptr->first_child, entry_consptr->last_child, + entry, next_sibling); + entry_consptr->child_count += 1; + entry->parent = entry_consptr; + } + entry_count += 1; + entry->abbrev_decl = abbrev_decl; + entry->attrib_vals = attrib_vals; + + // move consptr down if has children + if (abbrev_decl->has_children){ + entry_consptr = entry; + } + + skip_entry:; + } + } + exit_unit_loop:; + + // TODO: notice errors, emit them, and exit loop here + if (decoding_error){ + break; + } + + // extract root attributes + U64 language = 0; + U64 str_offsets_base = 0; + U64 line_info_offset = 0; + U64 vbase = 0; + U64 addr_base = 0; + U64 rnglists_base = 0; + U64 loclists_base = 0; + if (entry_root != 0){ + + // pull out attributes + DWARF_AbbrevDecl *root_abbrev_decl = entry_root->abbrev_decl; + DWARF_AbbrevAttribSpec *attrib_specs = root_abbrev_decl->attrib_specs; + DWARF_InfoAttribVal *attrib_vals = entry_root->attrib_vals; + U32 attrib_count = root_abbrev_decl->attrib_count; + + // examine each attribute + DWARF_AbbrevAttribSpec *attrib_spec = attrib_specs; + DWARF_InfoAttribVal *attrib_val = attrib_vals; + for (U32 i = 0; i < attrib_count; i += 1, attrib_spec += 1, attrib_val += 1){ + + // determine if there is a root attribute to extract here + U64 *target_u64 = 0; + switch (attrib_spec->name){ + case DWARF_AttributeName_language: target_u64 = &language; break; + case DWARF_AttributeName_str_offsets_base: target_u64 = &str_offsets_base; break; + case DWARF_AttributeName_stmt_list: target_u64 = &line_info_offset; break; + case DWARF_AttributeName_low_pc: target_u64 = &vbase; break; + case DWARF_AttributeName_addr_base: target_u64 = &addr_base; break; + case DWARF_AttributeName_rnglists_base: target_u64 = &rnglists_base; break; + case DWARF_AttributeName_loclists_base: target_u64 = &loclists_base; break; + } + + // set target from attrib value + if (target_u64 != 0){ + *target_u64 = attrib_val->val; + } + } + } + + // allocate unit + DWARF_InfoUnit *unit = push_array(arena, DWARF_InfoUnit, 1); + + // fill & emit unit + SLLQueuePush(unit_first, unit_last, unit); + unit_count += 1; + // [header] + unit->dwarf_version = version; + unit->offset_size = offset_size; + unit->address_size = address_size; + // [root attributes] + unit->language = (DWARF_Language)language; + unit->str_offsets_base = str_offsets_base; + unit->line_info_offset = line_info_offset; + unit->vbase = vbase; + unit->addr_base = addr_base; + unit->rnglists_base = rnglists_base; + unit->loclists_base = loclists_base; + // [entries] + unit->entry_root = entry_root; + unit->entry_count = entry_count; + + } + + // set ptr to end of this unit + ptr = unit_opl; + } + + // fill result + DWARF_InfoParsed *result = push_array(arena, DWARF_InfoParsed, 1); + result->unit_first = unit_first; + result->unit_last = unit_last; + result->unit_count = unit_count; + result->decoding_error = decoding_error; + return(result); +} + +static DWARF_AbbrevParsed* +dwarf_abbrev_from_data(Arena *arena, String8 data, DWARF_AbbrevParams *params){ + /* .debug_abbrev + ** Layout + ** List(Tag) + ** Tag = { id:ULEB128, tag:ULEB128, has_children:B8, ListNullTerminated(Attribute) } + ** Attribute = { name:ULEB128, form:ULEB128, (val:SLEB128)? } + */ + + // unit index range to extract + U64 unit_idx_min = params->unit_idx_min; + U64 unit_idx_max = params->unit_idx_max; + + // empty unit list + DWARF_AbbrevUnit *unit_first = 0; + DWARF_AbbrevUnit *unit_last = 0; + U64 unit_count = 0; + B32 decoding_error = 0; + + // whole section loop + U64 unit_idx = 0; + U8 *ptr = data.str; + U8 *opl = data.str + data.size; + for (;ptr < opl; unit_idx += 1){ + + // early escape on unit idx + if (unit_idx > unit_idx_max){ + break; + } + + // determine whether to full parse this unit + B32 full_parse = (unit_idx_min <= unit_idx); + + // save unit offset + U64 abbrev_unit_offset = (U64)(ptr - data.str); + + // allocate unit + DWARF_AbbrevUnit *unit = push_array(arena, DWARF_AbbrevUnit, 1); + + // empty abbrev list + DWARF_AbbrevDecl *abbrev_first = 0; + DWARF_AbbrevDecl *abbrev_last = 0; + U64 abbrev_count = 0; + + // abbrev decl loop + for (;ptr < opl;){ + B32 success = 1; + + // mark abbrev_code field + U8 *abbrev_code_ptr = ptr; + DWARF_LEB128_ADV(ptr, opl, success); + + // null abbrev code means end of unit + if (success && *abbrev_code_ptr == 0){ + break; + } + + // mark tag + U8 *tag_ptr = ptr; + DWARF_LEB128_ADV(ptr, opl, success); + U8 *end_tag_ptr = ptr; + + // extract has_children + B8 has_children = 0; + if (ptr < opl){ + has_children = *ptr; + ptr += 1; + } + else{ + success = 0; + } + + // count attributes + U8 *attrib_start_ptr = ptr; + U32 attrib_count = 0; + B32 has_implicit_const = 0; + if (success){ + for (;;){ + // decode normal attribute layout + U8 *attrib_name = ptr; + DWARF_LEB128_ADV(ptr, opl, success); + U8 *attrib_form = ptr; + DWARF_LEB128_ADV(ptr, opl, success); + + // handle special case implicit_const + if (success && *attrib_form == (U8)DWARF_AttributeForm_implicit_const){ + DWARF_LEB128_ADV(ptr, opl, success); + has_implicit_const = 1; + } + + // termination conditions + if (ptr == opl || + (*attrib_name == 0 && *attrib_form == 0)){ + break; + } + + // increment + attrib_count += 1; + } + } + + // build the abbreviation declaration + if (full_parse && success){ + + // allocate abbrev + DWARF_AbbrevDecl *abbrev = push_array(arena, DWARF_AbbrevDecl, 1); + DWARF_AbbrevAttribSpec *attribs = + push_array_no_zero(arena, DWARF_AbbrevAttribSpec, attrib_count); + U64 *implicit_const = 0; + if (has_implicit_const){ + implicit_const = push_array(arena, U64, attrib_count); + } + + // extract abbrev fields + U32 abbrev_code = dwarf_leb128_decode_U32(abbrev_code_ptr, tag_ptr); + U32 tag = dwarf_leb128_decode_U32(tag_ptr, end_tag_ptr); + + U8 *attrib_ptr = attrib_start_ptr; + DWARF_AbbrevAttribSpec *attrib = attribs; + for (U32 i = 0; i < attrib_count; i += 1, attrib += 1){ + // mark attribute fields + U8 *attrib_name = attrib_ptr; + DWARF_LEB128_ADV_NOCAP(attrib_ptr); + U8 *attrib_form = attrib_ptr; + DWARF_LEB128_ADV_NOCAP(attrib_ptr); + + // extract attribute fields + U32 name = dwarf_leb128_decode_U32(attrib_name, attrib_form); + U32 form = dwarf_leb128_decode_U32(attrib_form, attrib_ptr); + + // fill attribute spec + attrib->name = (DWARF_AttributeName)name; + attrib->form = (DWARF_AttributeForm)form; + + // handle special case implicit_const + if (form == DWARF_AttributeForm_implicit_const){ + U8 *attrib_value = attrib_ptr; + DWARF_LEB128_ADV_NOCAP(attrib_ptr); + S64 value = dwarf_leb128_decode_S64(attrib_form, attrib_ptr); + implicit_const[i] = value; + } + } + + // fill abbreviation + SLLQueuePush(abbrev_first, abbrev_last, abbrev); + abbrev_count += 1; + abbrev->abbrev_code = abbrev_code; + abbrev->tag = (DWARF_Tag)tag; + abbrev->has_children = has_children; + abbrev->attrib_count = attrib_count; + abbrev->attrib_specs = attribs; + abbrev->implicit_const = implicit_const; + } + + // handle failure + if (!success){ + // TODO: emit error message + decoding_error = 1; + goto done_parse; + } + } + + // fill unit + if (full_parse){ + SLLQueuePush(unit_first, unit_last, unit); + unit_count += 1; + unit->offset = abbrev_unit_offset; + unit->first = abbrev_first; + unit->last = abbrev_last; + unit->count = abbrev_count; + } + } + + done_parse:; + + // fill result + DWARF_AbbrevParsed *result = push_array(arena, DWARF_AbbrevParsed, 1); + result->unit_first = unit_first; + result->unit_last = unit_last; + result->unit_count = unit_count; + result->decoding_error = decoding_error; + return(result); +} +#endif diff --git a/src/rdi_from_dwarf/rdi_dwarf.h b/src/rdi_from_dwarf/rdi_dwarf.h index 273ae521..595d4bb8 100644 --- a/src/rdi_from_dwarf/rdi_dwarf.h +++ b/src/rdi_from_dwarf/rdi_dwarf.h @@ -1,1493 +1,1493 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RDI_DWARF_H -#define RDI_DWARF_H - -// https://dwarfstd.org/doc/DWARF4.pdf -// https://dwarfstd.org/doc/DWARF5.pdf - -// TODO(allen): -// [ ] function to parse info just for unit headers & root attributes -// [ ] put together unit info from all sections in one structure -// [ ] actually check version numbers in unit header parsers - -#pragma pack(push,1) - -//////////////////////////////// -//~ Dwarf Format Code X Lists - -// unit type X(name, code) -#define DWARF_UnitTypeXList(X)\ -X(null, 0x00)\ -X(compile, 0x01)\ -X(type, 0x02)\ -X(partial, 0x03)\ -X(skeleton, 0x04)\ -X(split_compile, 0x05)\ -X(split_type, 0x06)\ -X(lo_user, 0x80)\ -X(hi_user, 0xff) - -typedef enum DWARF_UnitType{ -#define X(N,C) DWARF_UnitType_##N = C, - DWARF_UnitTypeXList(X) -#undef X -} DWARF_UnitType; - - -// tag X(name, code) -#define DWARF_TagXList(X)\ -X(null, 0x00)\ -X(array_type, 0x01)\ -X(class_type, 0x02)\ -X(entry_point, 0x03)\ -X(enumeration_type, 0x04)\ -X(formal_parameter, 0x05)\ -X(imported_declaration, 0x08)\ -X(label, 0x0a)\ -X(lexical_block, 0x0b)\ -X(member, 0x0d)\ -X(pointer_type, 0x0f)\ -X(reference_type, 0x10)\ -X(compile_unit, 0x11)\ -X(string_type, 0x12)\ -X(structure_type, 0x13)\ -X(subroutine_type, 0x15)\ -X(typedef, 0x16)\ -X(union_type, 0x17)\ -X(unspecified_parameters, 0x18)\ -X(variant, 0x19)\ -X(common_block, 0x1a)\ -X(common_inclusion, 0x1b)\ -X(inheritance, 0x1c)\ -X(inlined_subroutine, 0x1d)\ -X(module, 0x1e)\ -X(ptr_to_member_type, 0x1f)\ -X(set_type, 0x20)\ -X(subrange_type, 0x21)\ -X(with_stmt, 0x22)\ -X(access_declaration, 0x23)\ -X(base_type, 0x24)\ -X(catch_block, 0x25)\ -X(const_type, 0x26)\ -X(constant, 0x27)\ -X(enumerator, 0x28)\ -X(file_type, 0x29)\ -X(friend, 0x2a)\ -X(namelist, 0x2b)\ -X(namelist_item, 0x2c)\ -X(packed_type, 0x2d)\ -X(subprogram, 0x2e)\ -X(template_type_parameter, 0x2f)\ -X(template_value_parameter, 0x30)\ -X(thrown_type, 0x31)\ -X(try_block, 0x32)\ -X(variant_part, 0x33)\ -X(variable, 0x34)\ -X(volatile_type, 0x35)\ -X(dwarf_procedure, 0x36)\ -X(restrict_type, 0x37)\ -X(interface_type, 0x38)\ -X(namespace, 0x39)\ -X(imported_module, 0x3a)\ -X(unspecified_type, 0x3b)\ -X(partial_unit, 0x3c)\ -X(imported_unit, 0x3d)\ -X(condition, 0x3f)\ -X(shared_type, 0x40)\ -X(type_unit, 0x41)\ -X(rvalue_reference_type, 0x42)\ -X(template_alias, 0x43)\ -X(coarray_type, 0x44)\ -X(generic_subrange, 0x45)\ -X(dynamic_type, 0x46)\ -X(atomic_type, 0x47)\ -X(call_site, 0x48)\ -X(call_site_parameter, 0x49)\ -X(skeleton_unit, 0x4a)\ -X(immutable_type, 0x4b)\ -X(lo_user, 0x4080)\ -X(hi_user, 0xffff) - -typedef enum DWARF_Tag{ -#define X(N,C) DWARF_Tag_##N = C, - DWARF_TagXList(X) -#undef X -} DWARF_Tag; - - -// attribute classes: X(name,code) -#define DWARF_AttributeClassXList(X)\ -X(address, 0)\ -X(addrptr, 1)\ -X(block, 2)\ -X(constant, 3)\ -X(exprloc, 4)\ -X(flag, 5)\ -X(lineptr, 6)\ -X(loclist, 7)\ -X(loclistsptr, 8)\ -X(macptr, 9)\ -X(reference, 10)\ -X(rnglist, 11)\ -X(rnglistsptr, 12)\ -X(string, 13)\ -X(stroffsetsptr, 14) - -typedef U32 DWARF_AttributeClassFlags; -enum{ -#define X(N,C) DWARF_AttributeClassFlag_##N = (1 << C), - DWARF_AttributeClassXList(X) -#undef X - - DWARF_AttributeClassFlag_0 = 0, - DWARF_AttributeClassFlag_specialcase = ~0, - DWARF_AttributeClassFlag_sec_offset_classes = - (DWARF_AttributeClassFlag_addrptr | - DWARF_AttributeClassFlag_lineptr | - DWARF_AttributeClassFlag_loclist | - DWARF_AttributeClassFlag_loclistsptr | - DWARF_AttributeClassFlag_macptr | - DWARF_AttributeClassFlag_rnglist | - DWARF_AttributeClassFlag_rnglistsptr | - DWARF_AttributeClassFlag_stroffsetsptr | - 0), - -}; - - -// attribute name: X(name, code, classflag1, classflag2, classflag3, classflag4) -#define DWARF_AttributeNameXList(X)\ -X(null, 0x00, 0, 0, 0, 0)\ -X(sibling, 0x01, reference, 0, 0, 0)\ -X(location, 0x02, exprloc, loclist, 0, 0)\ -X(name, 0x03, string, 0, 0, 0)\ -X(ordering, 0x09, constant, 0, 0, 0)\ -X(byte_size, 0x0b, constant, exprloc, reference, 0)\ -X(bit_size, 0x0d, constant, exprloc, reference, 0)\ -X(stmt_list, 0x10, lineptr, 0, 0, 0)\ -X(low_pc, 0x11, address, 0, 0, 0)\ -X(high_pc, 0x12, address, constant, 0, 0)\ -X(language, 0x13, constant, 0, 0, 0)\ -X(discr, 0x15, reference, 0, 0, 0)\ -X(discr_value, 0x16, constant, 0, 0, 0)\ -X(visibility, 0x17, constant, 0, 0, 0)\ -X(import, 0x18, reference, 0, 0, 0)\ -X(string_length, 0x19, exprloc, loclist, reference, 0)\ -X(common_reference, 0x1a, reference, 0, 0, 0)\ -X(comp_dir, 0x1b, string, 0, 0, 0)\ -X(const_value, 0x1c, block, constant, string, 0)\ -X(containing_type, 0x1d, reference, 0, 0, 0)\ -X(default_value, 0x1e, constant, reference, flag, 0)\ -X(inline, 0x20, constant, 0, 0, 0)\ -X(is_optional, 0x21, flag, 0, 0, 0)\ -X(lower_bound, 0x22, constant, exprloc, reference, 0)\ -X(producer, 0x25, string, 0, 0, 0)\ -X(prototyped, 0x27, flag, 0, 0, 0)\ -X(return_addr, 0x2a, exprloc, loclist, 0, 0)\ -X(start_scope, 0x2c, constant, rnglist, 0, 0)\ -X(bit_stride, 0x2e, constant, exprloc, reference, 0)\ -X(upper_bound, 0x2f, constant, exprloc, reference, 0)\ -X(abstract_origin, 0x31, reference, 0, 0, 0)\ -X(accessibility, 0x32, constant, 0, 0, 0)\ -X(address_class, 0x33, constant, 0, 0, 0)\ -X(artificial, 0x34, flag, 0, 0, 0)\ -X(base_types, 0x35, reference, 0, 0, 0)\ -X(calling_convention, 0x36, constant, 0, 0, 0)\ -X(count, 0x37, constant, exprloc, reference, 0)\ -X(data_member_location, 0x38, constant, exprloc, loclist, 0)\ -X(decl_column, 0x39, constant, 0, 0, 0)\ -X(decl_file, 0x3a, constant, 0, 0, 0)\ -X(decl_line, 0x3b, constant, 0, 0, 0)\ -X(declaration, 0x3c, flag, 0, 0, 0)\ -X(discr_list, 0x3d, block, 0, 0, 0)\ -X(encoding, 0x3e, constant, 0, 0, 0)\ -X(external, 0x3f, flag, 0, 0, 0)\ -X(frame_base, 0x40, exprloc, loclist, 0, 0)\ -X(friend, 0x41, reference, 0, 0, 0)\ -X(identifier_case, 0x42, constant, 0, 0, 0)\ -X(namelist_item, 0x44, reference, 0, 0, 0)\ -X(priority, 0x45, reference, 0, 0, 0)\ -X(segment, 0x46, exprloc, loclist, 0, 0)\ -X(specification, 0x47, reference, 0, 0, 0)\ -X(static_link, 0x48, exprloc, loclist, 0, 0)\ -X(type, 0x49, reference, 0, 0, 0)\ -X(use_location, 0x4a, exprloc, loclist, 0, 0)\ -X(variable_parameter, 0x4b, flag, 0, 0, 0)\ -X(virtuality, 0x4c, constant, 0, 0, 0)\ -X(vtable_elem_location, 0x4d, exprloc, loclist, 0, 0)\ -X(allocated, 0x4e, constant, exprloc, reference, 0)\ -X(associated, 0x4f, constant, exprloc, reference, 0)\ -X(data_location, 0x50, exprloc, 0, 0, 0)\ -X(byte_stride, 0x51, constant, exprloc, reference, 0)\ -X(entry_pc, 0x52, address, constant, 0, 0)\ -X(use_UTF8, 0x53, flag, 0, 0, 0)\ -X(extension, 0x54, reference, 0, 0, 0)\ -X(ranges, 0x55, rnglist, 0, 0, 0)\ -X(trampoline, 0x56, address, flag, reference, string)\ -X(call_column, 0x57, constant, 0, 0, 0)\ -X(call_file, 0x58, constant, 0, 0, 0)\ -X(call_line, 0x59, constant, 0, 0, 0)\ -X(description, 0x5a, string, 0, 0, 0)\ -X(binary_scale, 0x5b, constant, 0, 0, 0)\ -X(decimal_scale, 0x5c, constant, 0, 0, 0)\ -X(small, 0x5d, reference, 0, 0, 0)\ -X(decimal_sign, 0x5e, constant, 0, 0, 0)\ -X(digit_count, 0x5f, constant, 0, 0, 0)\ -X(picture_string, 0x60, string, 0, 0, 0)\ -X(mutable, 0x61, flag, 0, 0, 0)\ -X(threads_scaled, 0x62, flag, 0, 0, 0)\ -X(explicit, 0x63, flag, 0, 0, 0)\ -X(object_pointer, 0x64, reference, 0, 0, 0)\ -X(endianity, 0x65, constant, 0, 0, 0)\ -X(elemental, 0x66, flag, 0, 0, 0)\ -X(pure, 0x67, flag, 0, 0, 0)\ -X(recursive, 0x68, flag, 0, 0, 0)\ -X(signature, 0x69, reference, 0, 0, 0)\ -X(main_subprogram, 0x6a, flag, 0, 0, 0)\ -X(data_bit_offset, 0x6b, constant, 0, 0, 0)\ -X(const_expr, 0x6c, flag, 0, 0, 0)\ -X(enum_class, 0x6d, flag, 0, 0, 0)\ -X(linkage_name, 0x6e, string, 0, 0, 0)\ -X(string_length_bit_size, 0x6f, constant, 0, 0, 0)\ -X(string_length_byte_size, 0x70, constant, 0, 0, 0)\ -X(rank, 0x71, constant, exprloc, 0, 0)\ -X(str_offsets_base, 0x72, stroffsetsptr, 0, 0, 0)\ -X(addr_base, 0x73, addrptr, 0, 0, 0)\ -X(rnglists_base, 0x74, rnglistsptr, 0, 0, 0)\ -X(dwo_name, 0x76, string, 0, 0, 0)\ -X(reference, 0x77, flag, 0, 0, 0)\ -X(rvalue_reference, 0x78, flag, 0, 0, 0)\ -X(macros, 0x79, macptr, 0, 0, 0)\ -X(call_all_calls, 0x7a, flag, 0, 0, 0)\ -X(call_all_source_calls, 0x7b, flag, 0, 0, 0)\ -X(call_all_tail_calls, 0x7c, flag, 0, 0, 0)\ -X(call_return_pc, 0x7d, address, 0, 0, 0)\ -X(call_value, 0x7e, exprloc, 0, 0, 0)\ -X(call_origin, 0x7f, exprloc, 0, 0, 0)\ -X(call_parameter, 0x80, reference, 0, 0, 0)\ -X(call_pc, 0x81, address, 0, 0, 0)\ -X(call_tail_call, 0x82, flag, 0, 0, 0)\ -X(call_target, 0x83, exprloc, 0, 0, 0)\ -X(call_target_clobbered, 0x84, exprloc, 0, 0, 0)\ -X(call_data_location, 0x85, exprloc, 0, 0, 0)\ -X(call_data_value, 0x86, exprloc, 0, 0, 0)\ -X(noreturn, 0x87, flag, 0, 0, 0)\ -X(alignment, 0x88, constant, 0, 0, 0)\ -X(export_symbols, 0x89, flag, 0, 0, 0)\ -X(deleted, 0x8a, flag, 0, 0, 0)\ -X(defaulted, 0x8b, constant, 0, 0, 0)\ -X(loclists_base, 0x8c, loclistsptr, 0, 0, 0)\ -X(lo_user, 0x2000, 0, 0, 0, 0)\ -X(hi_user, 0x3fff, 0, 0, 0, 0) - -typedef enum DWARF_AttributeName{ -#define X(N,C,f1,f2,f3,f4) DWARF_AttributeName_##N = C, - DWARF_AttributeNameXList(X) -#undef X -} DWARF_AttributeName; - - -// attribute forms: X(name, code, classflag) -#define DWARF_AttributeFormXList(X)\ -X(null, 0x00, 0)\ -X(addr, 0x01, address)\ -X(block2, 0x03, block)\ -X(block4, 0x04, block)\ -X(data2, 0x05, constant)\ -X(data4, 0x06, constant)\ -X(data8, 0x07, constant)\ -X(string, 0x08, string)\ -X(block, 0x09, block)\ -X(block1, 0x0a, block)\ -X(data1, 0x0b, constant)\ -X(flag, 0x0c, flag)\ -X(sdata, 0x0d, constant)\ -X(strp, 0x0e, string)\ -X(udata, 0x0f, constant)\ -X(ref_addr, 0x10, reference)\ -X(ref1, 0x11, reference)\ -X(ref2, 0x12, reference)\ -X(ref4, 0x13, reference)\ -X(ref8, 0x14, reference)\ -X(ref_udata, 0x15, reference)\ -X(indirect, 0x16, specialcase)\ -X(sec_offset, 0x17, sec_offset_classes)\ -X(exprloc, 0x18, exprloc)\ -X(flag_present, 0x19, flag)\ -X(strx, 0x1a, string)\ -X(addrx, 0x1b, address)\ -X(ref_sup4, 0x1c, reference)\ -X(strp_sup, 0x1d, string)\ -X(data16, 0x1e, constant)\ -X(line_strp, 0x1f, string)\ -X(ref_sig8, 0x20, reference)\ -X(implicit_const, 0x21, specialcase)\ -X(loclistx, 0x22, loclist)\ -X(rnglistx, 0x23, rnglist)\ -X(ref_sup8, 0x24, reference)\ -X(strx1, 0x25, string)\ -X(strx2, 0x26, string)\ -X(strx3, 0x27, string)\ -X(strx4, 0x28, string)\ -X(addrx1, 0x29, address)\ -X(addrx2, 0x2a, address)\ -X(addrx3, 0x2b, address)\ -X(addrx4, 0x2c, address) - -typedef enum DWARF_AttributeForm{ -#define X(N,C,f) DWARF_AttributeForm_##N = C, - DWARF_AttributeFormXList(X) -#undef X -} DWARF_AttributeForm; - - -// ops: X(name, code, opnum) -#define DWARF_OpXList(X)\ -X(addr, 0x03, 1)\ -X(deref, 0x06, 0)\ -X(const1u, 0x08, 1)\ -X(const1s, 0x09, 1)\ -X(const2u, 0x0a, 1)\ -X(const2s, 0x0b, 1)\ -X(const4u, 0x0c, 1)\ -X(const4s, 0x0d, 1)\ -X(const8u, 0x0e, 1)\ -X(const8s, 0x0f, 1)\ -X(constu, 0x10, 1)\ -X(consts, 0x11, 1)\ -X(dup, 0x12, 0)\ -X(drop, 0x13, 0)\ -X(over, 0x14, 0)\ -X(pick, 0x15, 1)\ -X(swap, 0x16, 0)\ -X(rot, 0x17, 0)\ -X(xderef, 0x18, 0)\ -X(abs, 0x19, 0)\ -X(and, 0x1a, 0)\ -X(div, 0x1b, 0)\ -X(minus, 0x1c, 0)\ -X(mod, 0x1d, 0)\ -X(mul, 0x1e, 0)\ -X(neg, 0x1f, 0)\ -X(not, 0x20, 0)\ -X(or, 0x21, 0)\ -X(plus, 0x22, 0)\ -X(plus_uconst, 0x23, 1)\ -X(shl, 0x24, 0)\ -X(shr, 0x25, 0)\ -X(shra, 0x26, 0)\ -X(xor, 0x27, 0)\ -X(bra, 0x28, 1)\ -X(eq, 0x29, 0)\ -X(ge, 0x2a, 0)\ -X(gt, 0x2b, 0)\ -X(le, 0x2c, 0)\ -X(lt, 0x2d, 0)\ -X(ne, 0x2e, 0)\ -X(skip, 0x2f, 1)\ -X(lit0, 0x30, 0)\ -X(lit1, 0x31, 0)\ -X(lit31, 0x4f, 0)\ -X(reg0, 0x50, 0)\ -X(reg1, 0x51, 0)\ -X(reg31, 0x6f, 0)\ -X(breg0, 0x70, 1)\ -X(breg1, 0x71, 1)\ -X(breg31, 0x8f, 1)\ -X(regx, 0x90, 1)\ -X(fbreg, 0x91, 1)\ -X(bregx, 0x92, 2)\ -X(piece, 0x93, 1)\ -X(deref_size, 0x94, 1)\ -X(xderef_size, 0x95, 1)\ -X(nop, 0x96, 0)\ -X(push_object_address, 0x97, 0)\ -X(call2, 0x98, 1)\ -X(call4, 0x99, 1)\ -X(call_ref, 0x9a, 1)\ -X(form_tls_address, 0x9b, 0)\ -X(call_frame_cfa, 0x9c, 0)\ -X(bit_piece, 0x9d, 2)\ -X(implicit_value, 0x9e, 2)\ -X(stack_value, 0x9f, 0)\ -X(implicit_pointer, 0xa0, 2)\ -X(addrx, 0xa1, 1)\ -X(constx, 0xa2, 1)\ -X(entry_value, 0xa3, 2)\ -X(const_type, 0xa4, 3)\ -X(regval_type, 0xa5, 2)\ -X(deref_type, 0xa6, 2)\ -X(xderef_type, 0xa7, 2)\ -X(convert, 0xa8, 1)\ -X(reinterpret, 0xa9, 1)\ -X(lo_user, 0xe0, 0)\ -X(hi_user, 0xff, 0) - -typedef enum DWARF_Op{ -#define X(N,C,k) DWARF_Op_##N = C, - DWARF_OpXList(X) -#undef X -} DWARF_Op; - - -// location list entry: X(name, code) -#define DWARF_LocationListEntryXList(X)\ -X(end_of_list, 0x00)\ -X(base_addressx, 0x01)\ -X(startx_endx, 0x02)\ -X(startx_length, 0x03)\ -X(offset_pair, 0x04)\ -X(default_location, 0x05)\ -X(base_address, 0x06)\ -X(start_end, 0x07)\ -X(start_length, 0x08) - -typedef enum DWARF_LocationListEntry{ -#define X(N,C) DWARF_LocationListEntry_##N = C, - DWARF_LocationListEntryXList(X) -#undef X -} DWARF_LocationListEntry; - - -// base type: X(name, code) -#define DWARF_BaseTypeXList(X)\ -X(address, 0x01)\ -X(boolean, 0x02)\ -X(complex_float, 0x03)\ -X(float, 0x04)\ -X(signed, 0x05)\ -X(signed_char, 0x06)\ -X(unsigned, 0x07)\ -X(unsigned_char, 0x08)\ -X(imaginary_float, 0x09)\ -X(packed_decimal, 0x0a)\ -X(numeric_string, 0x0b)\ -X(edited, 0x0c)\ -X(signed_fixed, 0x0d)\ -X(unsigned_fixed, 0x0e)\ -X(decimal_float, 0x0f)\ -X(UTF, 0x10)\ -X(UCS, 0x11)\ -X(ASCII, 0x12)\ -X(lo_user, 0x80)\ -X(hi_user, 0xff) - -typedef enum DWARF_BaseType{ -#define X(N,C) DWARF_BaseType_##N = C, - DWARF_BaseTypeXList(X) -#undef X -} DWARF_BaseType; - - -// decimal sign: X(name, code) -#define DWARF_DecimalSignXList(X)\ -X(unsigned, 0x01)\ -X(leading_overpunch, 0x02)\ -X(trailing_overpunch, 0x03)\ -X(leading_separate, 0x04)\ -X(trailing_separate, 0x05) - -typedef enum DWARF_DecimalSign{ -#define X(N,C) DWARF_DecimalSign_##N = C, - DWARF_DecimalSignXList(X) -#undef X -} DWARF_DecimalSign; - - -// endianity: X(name, code) -#define DWARF_EndianityXList(X)\ -X(default, 0x00)\ -X(big, 0x01)\ -X(little, 0x02)\ -X(lo_user, 0x40)\ -X(hi_user, 0xff) - -typedef enum DWARF_Endianity{ -#define X(N,C) DWARF_Endianity_##N = C, - DWARF_EndianityXList(X) -#undef X -} DWARF_Endianity; - - -// access: X(name, code) -#define DWARF_AccessXList(X)\ -X(public, 0x01)\ -X(protected, 0x02)\ -X(private, 0x03) - -typedef enum DWARF_Access{ -#define X(N,C) DWARF_Access_##N = C, - DWARF_AccessXList(X) -#undef X -} DWARF_Access; - - -// visibility: X(name, code) -#define DWARF_VisibilityXList(X)\ -X(local, 0x01)\ -X(exported, 0x02)\ -X(qualified, 0x03) - -typedef enum DWARF_Visibility{ -#define X(N,C) DWARF_Visibility_##N = C, - DWARF_VisibilityXList(X) -#undef X -} DWARF_Visibility; - - -// virtuality: X(name, code) -#define DWARF_VirtualityXList(X)\ -X(none, 0x00)\ -X(virtual, 0x01)\ -X(pure_virtual, 0x02) - -typedef enum DWARF_Virtuality{ -#define X(N,C) DWARF_Virtuality_##N = C, - DWARF_VirtualityXList(X) -#undef X -} DWARF_Virtuality; - - -// language: X(name, code, deflowerbound) -#define DWARF_LanguageXList(X)\ -X(C89, 0x0001, 0)\ -X(C, 0x0002, 0)\ -X(Ada83, 0x0003, 1)\ -X(C_plus_plus, 0x0004, 0)\ -X(Cobol74, 0x0005, 1)\ -X(Cobol85, 0x0006, 1)\ -X(Fortran77, 0x0007, 1)\ -X(Fortran90, 0x0008, 1)\ -X(Pascal83, 0x0009, 1)\ -X(Modula2, 0x000a, 1)\ -X(Java, 0x000b, 0)\ -X(C99, 0x000c, 0)\ -X(Ada95, 0x000d, 1)\ -X(Fortran95, 0x000e, 1)\ -X(PLI, 0x000f, 1)\ -X(ObjC, 0x0010, 0)\ -X(ObjC_plus_plus, 0x0011, 0)\ -X(UPC, 0x0012, 0)\ -X(D, 0x0013, 0)\ -X(Python, 0x0014, 0)\ -X(OpenCL, 0x0015, 0)\ -X(Go, 0x0016, 0)\ -X(Modula3, 0x0017, 1)\ -X(Haskell, 0x0018, 0)\ -X(C_plus_plus_03, 0x0019, 0)\ -X(C_plus_plus_11, 0x001a, 0)\ -X(OCaml, 0x001b, 0)\ -X(Rust, 0x001c, 0)\ -X(C11, 0x001d, 0)\ -X(Swift, 0x001e, 0)\ -X(Julia, 0x001f, 1)\ -X(Dylan, 0x0020, 0)\ -X(C_plus_plus_14, 0x0021, 0)\ -X(Fortran03, 0x0022, 1)\ -X(Fortran08, 0x0023, 1)\ -X(RenderScript, 0x0024, 0)\ -X(BLISS, 0x0025, 0)\ -X(lo_user, 0x8000, 0)\ -X(hi_user, 0xffff, 0) - -typedef enum DWARF_Language{ -#define X(N,C,k) DWARF_Language_##N = C, - DWARF_LanguageXList(X) -#undef X -} DWARF_Language; - - -// identifier case: X(name, code) -#define DWARF_IdentifierCaseXList(X)\ -X(case_sensitive, 0x00)\ -X(up_case, 0x01)\ -X(down_case, 0x02)\ -X(case_insensitive, 0x03) - -typedef enum DWARF_IdentifierCase{ -#define X(N,C) DWARF_IdentifierCase_##N = C, - DWARF_IdentifierCaseXList(X) -#undef X -} DWARF_IdentifierCase; - - -// calling convention: X(name, code) -#define DWARF_CallingConventionXList(X)\ -X(normal, 0x01)\ -X(program, 0x02)\ -X(nocall, 0x03)\ -X(pass_by_reference, 0x04)\ -X(pass_by_value, 0x05)\ -X(lo_user, 0x40)\ -X(hi_user, 0xff) - -typedef enum DWARF_CallingConvention{ -#define X(N,C) DWARF_CallingConvention_##N = C, - DWARF_CallingConventionXList(X) -#undef X -} DWARF_CallingConvention; - - -// inline: X(name, code) -#define DWARF_InlineXList(X)\ -X(not_inlined, 0x00)\ -X(inlined, 0x01)\ -X(declared_not_inlined, 0x02)\ -X(declared_inlined, 0x03) - -typedef enum DWARF_Inline{ -#define X(N,C) DWARF_Inline_##N = C, - DWARF_InlineXList(X) -#undef X -} DWARF_Inline; - - -// array ordering: X(name, code) -#define DWARF_ArrayOrderingXList(X)\ -X(row_major, 0x00)\ -X(col_major, 0x01) - -typedef enum DWARF_ArrayOrdering{ -#define X(N,C) DWARF_ArrayOrdering_##N = C, - DWARF_ArrayOrderingXList(X) -#undef X -} DWARF_ArrayOrdering; - - -// discriminant: X(name, code) -#define DWARF_DiscriminantXList(X)\ -X(label, 0x00)\ -X(range, 0x01) - -typedef enum DWARF_Discriminant{ -#define X(N,C) DWARF_Discriminant_##N = C, - DWARF_DiscriminantXList(X) -#undef X -} DWARF_Discriminant; - - -// name index: X(name, code) -#define DWARF_NameIndexXList(X)\ -X(compile_unit, 1)\ -X(type_unit, 2)\ -X(die_offset, 3)\ -X(parent, 4)\ -X(type_hash, 5)\ -X(lo_user, 0x2000)\ -X(hi_user, 0x3fff) - -typedef enum DWARF_NameIndex{ -#define X(N,C) DWARF_NameIndex_##N = C, - DWARF_NameIndexXList(X) -#undef X -} DWARF_NameIndex; - - -// defaulted: X(name, code) -#define DWARF_DefaultedXList(X)\ -X(no, 0x00)\ -X(in_class, 0x01)\ -X(out_of_class, 0x02) - -typedef enum DWARF_Defaulted{ -#define X(N,C) DWARF_Defaulted_##N = C, - DWARF_DefaultedXList(X) -#undef X -} DWARF_Defaulted; - -// call frame instruction: X(N, hi2bits, matchlow, low6bits, operand1, operand2) -// "CFA" -#define DWARF_CallFrameInsnXList(X)\ -X(advance_loc, 0x1, 0, 0, NULL, NULL)\ -X(offset, 0x2, 0, 0, ULEB, NULL)\ -X(restore, 0x3, 0, 0, NULL, NULL)\ -X(nop, 0x0, 1, 0, NULL, NULL)\ -X(set_loc, 0x0, 1, 0x01, ADDRESS, NULL)\ -X(advance_loc1, 0x0, 1, 0x02, 1BYTE, NULL)\ -X(advance_loc2, 0x0, 1, 0x03, 2BYTE, NULL)\ -X(advance_loc4, 0x0, 1, 0x04, 4BYTE, NULL)\ -X(offset_extended, 0x0, 1, 0x05, ULEB, ULEB)\ -X(restore_extended, 0x0, 1, 0x06, ULEB, NULL)\ -X(undefined, 0x0, 1, 0x07, ULEB, NULL)\ -X(same_value, 0x0, 1, 0x08, ULEB, NULL)\ -X(register, 0x0, 1, 0x09, ULEB, ULEB)\ -X(remember_state, 0x0, 1, 0x0a, NULL, NULL)\ -X(restore_state, 0x0, 1, 0x0b, NULL, NULL)\ -X(def_cfa, 0x0, 1, 0x0c, ULEB, ULEB)\ -X(def_cfa_register, 0x0, 1, 0x0d, ULEB, NULL)\ -X(def_cfa_offset, 0x0, 1, 0x0e, ULEB, NULL)\ -X(def_cfa_expression,0x0, 1, 0x0f, BLOCK, NULL)\ -X(expression, 0x0, 1, 0x10, ULEB, BLOCK)\ -X(offset_extended_sf,0x0, 1, 0x11, ULEB, SLEB)\ -X(def_cfa_sf, 0x0, 1, 0x12, ULEB, SLEB)\ -X(def_cfa_offset_sf, 0x0, 1, 0x13, SLEB, NULL)\ -X(val_offset, 0x0, 1, 0x14, ULEB, ULEB)\ -X(val_offset_sf, 0x0, 1, 0x15, ULEB, SLEB)\ -X(val_expression, 0x0, 1, 0x16, ULEB, BLOCK)\ -X(lo_user, 0x0, 1, 0x1c, NULL, NULL)\ -X(hi_user, 0x0, 1, 0x3f, NULL, NULL) - -// line number encoding codes -// (DWARF4.pdf + 7.21) (DWARF5.pdf + 7.22) - -// X(name, code) (V4 & V5) -#define DWARF_LineStdOpXList(X) \ -X(copy, 0x01)\ -X(advance_pc, 0x02)\ -X(advance_line, 0x03)\ -X(set_file, 0x04)\ -X(set_column, 0x05)\ -X(negate_stmt, 0x06)\ -X(set_basic_block, 0x07)\ -X(const_add_pc, 0x08)\ -X(fixed_advance_pc, 0x09)\ -X(set_prologue_end, 0x0a)\ -X(set_epilogue_begin, 0x0b)\ -X(set_isa, 0x0c) - -typedef enum DWARF_LineStdOp{ -#define X(N,C) DWARF_LineStdOp_##N = C, - DWARF_LineStdOpXList(X) -#undef X -} DWARF_LineStdOp; - -// X(name, code) (V4 & V5) -#define DWARF_LineExtOpXList(X) \ -X(end_sequence, 0x01)\ -X(set_address, 0x02)\ -X(define_file, 0x03)\ -X(set_discriminator, 0x04)\ -X(lo_user, 0x80)\ -X(hi_user, 0xff) - -typedef enum DWARF_LineExtOp{ -#define X(N,C) DWARF_LineExtOp_##N = C, - DWARF_LineExtOpXList(X) -#undef X -} DWARF_LineExtOp; - -// X(name, code) (V5) -#define DWARF_LineEntryFormatXList(X) \ -X(path, 0x1)\ -X(directory_index, 0x2)\ -X(timestamp, 0x3)\ -X(size, 0x4)\ -X(MD5, 0x5)\ -X(lo_user, 0x2000)\ -X(hi_user, 0x3fff) - -typedef enum DWARF_LineEntryFormat{ -#define X(N,C) DWARF_LineEntryFormat_##N = C, - DWARF_LineEntryFormatXList(X) -#undef X -} DWARF_LineEntryFormat; - -//////////////////////////////// -//~ Dwarf Parser Codes and Data Tables - -#define DWARF_SECTION_NAME_VARIANT_COUNT 3 - -// X(section_code_name, versionflags, section_name0, section_name1, section_name2) -#define DWARF_SectionNameXList(X,V4,V5)\ -X(Null, 0, "", "", "")\ -X(Loc, V4, ".debug_loc", ".debug_loc.dwo", "__debug_loc")\ -X(Str, V4|V5, ".debug_str", ".debug_str.dwo", "__debug_str")\ -X(LineStr, V5, ".debug_line_str", ".debug_line_str.dwo", "__debug_line_str")\ -X(CmpUnitIdx, V5, ".debug_cu_index", ".debug_cu_index.dwo", "__debug_cu_index")\ -X(TypeIdx, V5, ".debug_tu_index", ".debug_tu_index.dwo", "__debug_tu_index")\ -X(Supplement, V5, ".debug_sup", ".debug_sup.dwo", "__debug_sup")\ -X(Info, V4|V5, ".debug_info", ".debug_info.dwo", "__debug_info")\ -X(Abbrev, V4|V5, ".debug_abbrev", ".debug_abbrev.dwo", "__debug_abbrev")\ -X(PubNames, V4, ".debug_pubnames", ".debug_pubnames.dwo", "__debug_pubnames")\ -X(PubTypes, V4, ".debug_pubtypes", ".debug_pubtypes.dwo", "__debug_pubtypes")\ -X(Names, V5, ".debug_names", ".debug_names.dwo", "__debug_names")\ -X(Aranges, V4|V5, ".debug_aranges", ".debug_aranges.dwo", "__debug_aranges")\ -X(Line, V4|V5, ".debug_line", ".debug_line.dwo", "__debug_line")\ -X(MacInfo, V4, ".debug_macinfo", ".debug_macinfo.dwo", "__debug_macinfo")\ -X(Macro, V5, ".debug_macro", ".debug_macro.dwo", "__debug_macro")\ -X(Frame, V4|V5, ".debug_frame", ".debug_frame.dwo", "__debug_frame")\ -X(Ranges, V4, ".debug_ranges", ".debug_ranges.dwo", "__debug_ranges")\ -X(StrOffsets, V5, ".debug_str_offsets", ".debug_str_offsets.dwo", "__debug_str_offsets")\ -X(Addr, V5, ".debug_addr", ".debug_addr.dwo", "__debug_addr")\ -X(RngLists, V5, ".debug_rnglists", ".debug_rnglists.dwo", "__debug_rnglists")\ -X(LocLists, V5, ".debug_loclists", ".debug_loclists.dwo", "__debug_loclists") - - -typedef enum DWARF_SectionCode{ -#define X(c,vf,n0,n1,n2) DWARF_SectionCode_##c, - DWARF_SectionNameXList(X,0,0) -#undef X - DWARF_SectionCode_COUNT -} DWARF_SectionCode; - -typedef struct DWARF_SectionNameRow{ - String8 name[DWARF_SECTION_NAME_VARIANT_COUNT]; -} DWARF_SectionNameRow; - -read_only global DWARF_SectionNameRow dwarf_section_name_table[] = { -#define X(c,vf,n0,n1,n2) \ -{ { str8_lit_comp(n0), str8_lit_comp(n1), str8_lit_comp(n2) } }, - DWARF_SectionNameXList(X,0,0) -#undef X -}; - - -#pragma pack(pop) - - -//////////////////////////////// -//~ Dwarf Parser Types - -typedef struct DWARF_Parsed{ - ELF_Parsed *elf; - U32 debug_section_idx[DWARF_SectionCode_COUNT]; - String8 debug_section_name[DWARF_SectionCode_COUNT]; - String8 debug_data[DWARF_SectionCode_COUNT]; -} DWARF_Parsed; - - -// form decoding - -typedef struct DWARF_FormDecodeRules{ - union{ - // form decode fields - struct{ - U8 size; - B8 uleb128; - B8 sleb128; - B8 in_abbrev; - B8 auto_1; - B8 block; - B8 null_terminated; - }; - - // for alignment and padding to 8 - U64 x; - }; -} DWARF_FormDecodeRules; - -typedef struct DWARF_FormDecoded{ - U64 val; - U8 *dataptr; - B32 error; -} DWARF_FormDecoded; - - -// index section: .debug_cu_index .debug_tu_index -// (DWARF5.pdf + 7.3.5) - -// ** not implemented yet ** - -typedef struct DWARF_IndexParsed{ - U32 dummy; -} DWARF_IndexParsed; - - -// supplementary section: .debug_sup -// (DWARF5.pdf + 7.3.6) - -// ** not implemented yet ** - -typedef struct DWARF_SupParsed{ - U32 dummy; -} DWARF_SupParsed; - - -// info section: .debug_info -// (DWARF4.pdb + 7.5) (DWARF5.pdf + 7.5) - -typedef struct DWARF_InfoAttribVal{ - U64 val; - U8 *dataptr; -} DWARF_InfoAttribVal; - -typedef struct DWARF_InfoEntry{ - struct DWARF_InfoEntry *next_sibling; - struct DWARF_InfoEntry *first_child; - struct DWARF_InfoEntry *last_child; - U64 child_count; - struct DWARF_InfoEntry *parent; - - U64 info_offset; - struct DWARF_AbbrevDecl *abbrev_decl; - DWARF_InfoAttribVal *attrib_vals; -} DWARF_InfoEntry; - -#if 0 -typedef struct DWARF_InfoUnit{ - struct DWARF_InfoUnit *next; - - // header - U32 version; - U32 offset_size; - U32 address_size; - - // root attributes - DWARF_Language language; - U64 line_info_offset; - U64 vbase; - U64 str_offsets_base; - U64 addr_base; - U64 rnglists_base; - U64 loclists_base; - - // info entries - DWARF_InfoEntry *entry_root; - U64 entry_count; -} DWARF_InfoUnit; -#endif - -#if 0 -typedef struct DWARF_InfoParams{ - U64 unit_idx_min; - U64 unit_idx_max; -} DWARF_InfoParams; -#endif - -typedef struct DWARF_InfoUnit{ - struct DWARF_InfoUnit *next; - - U64 hdr_off; - U64 base_off; - U64 opl_off; - - U8 offset_size; - U8 version; - U8 unit_type; // (DWARF_UnitType) - U8 address_size; - U64 abbrev_off; - - union{ - // unit_type: skeleton, split_compile - U64 dwo_id; - // unit_type: type, split_type - struct{ - U64 type_signature; - U64 type_offset; - }; - }; -} DWARF_InfoUnit; - -typedef struct DWARF_InfoParsed{ - DWARF_InfoUnit *unit_first; - DWARF_InfoUnit *unit_last; - U64 unit_count; -} DWARF_InfoParsed; - - -// abbreviations section: .debug_abbrev -// (DWARF4.pdf + 7.5.3) (DWARF5.pdf + 7.5.3) - -typedef struct DWARF_AbbrevAttribSpec{ - DWARF_AttributeName name; - DWARF_AttributeForm form; -} DWARF_AbbrevAttribSpec; - -typedef struct DWARF_AbbrevDecl{ - struct DWARF_AbbrevDecl *next; - U32 abbrev_code; - DWARF_Tag tag; - B8 has_children; - U8 __filler__; - U16 attrib_count; - DWARF_AbbrevAttribSpec *attrib_specs; - S64 *implicit_const; -} DWARF_AbbrevDecl; - -typedef struct DWARF_AbbrevUnit{ - struct DWARF_AbbrevUnit *next; - U64 offset; - DWARF_AbbrevDecl *first; - DWARF_AbbrevDecl *last; - U64 count; -} DWARF_AbbrevUnit; - -#if 0 -typedef struct DWARF_AbbrevParams{ - U64 unit_idx_min; - U64 unit_idx_max; -} DWARF_AbbrevParams; -#endif - -typedef struct DWARF_AbbrevParsed{ - DWARF_AbbrevUnit *unit_first; - DWARF_AbbrevUnit *unit_last; - U64 unit_count; - B32 decoding_error; -} DWARF_AbbrevParsed; - - -// name lookup tables (V4): .debug_pubnames .debug_pubtypes -// (DWARF4.pdf + 7.19) - -typedef struct DWARF_PubNamesUnit{ - struct DWARF_PubNamesUnit *next; - - U64 hdr_off; - U64 base_off; - U64 opl_off; - - U8 offset_size; - U8 version; - U64 info_off; - U64 info_length; -} DWARF_PubNamesUnit; - -typedef struct DWARF_PubNamesParsed{ - DWARF_PubNamesUnit *unit_first; - DWARF_PubNamesUnit *unit_last; - U64 unit_count; -} DWARF_PubNamesParsed; - - -// name lookup tables (V5): .debug_names -// (DWARF5.pdf + 6.1.1.4.1 & 7.19) - -typedef struct DWARF_NamesUnit{ - struct DWARF_NamesUnit *next; - - U64 hdr_off; - U64 base_off; - U64 opl_off; - - U8 version; - U32 comp_unit_count; - U32 local_type_unit_count; - U32 foreign_type_unit_count; - U32 bucket_count; - U32 name_count; - U32 abbrev_table_size; - String8 augmentation_string; - -} DWARF_NamesUnit; - -typedef struct DWARF_NamesParsed{ - DWARF_NamesUnit *unit_first; - DWARF_NamesUnit *unit_last; - U64 unit_count; -} DWARF_NamesParsed; - - -// address range table: .debug_aranges -// (DWARF4.pdf + 7.20) (DWARF5.pdf + 7.21) - -typedef struct DWARF_ArangesUnit{ - struct DWARF_ArangesUnit *next; - - U64 hdr_off; - U64 base_off; - U64 opl_off; - - U8 version; - U8 address_size; - U8 segment_selector_size; - U8 offset_size; - U64 info_off; -} DWARF_ArangesUnit; - -typedef struct DWARF_ArangesParsed{ - DWARF_ArangesUnit *unit_first; - DWARF_ArangesUnit *unit_last; - U64 unit_count; -} DWARF_ArangesParsed; - - -// line number information: .debug_line -// (DWARF4.pdf + 6.2.4 & 7.21) (DWARF5.pdf + 6.2.4 & 7.22) - -typedef struct DWARF_V4LineFileNamesEntry{ - struct DWARF_V4LineFileNamesEntry *next; - String8 file_name; - U64 include_directory_idx; - U64 last_modified_time; - U64 file_size; -} DWARF_V4LineFileNamesEntry; - -typedef struct DWARF_V4LineFileNamesList{ - DWARF_V4LineFileNamesEntry *first; - DWARF_V4LineFileNamesEntry *last; - U64 count; -} DWARF_V4LineFileNamesList; - -typedef struct DWARF_V5LinePathEntryFormat{ - U32 content_type; /* DWARF_LineEntryFormat */ - U32 form; /* DWARF_AttributeForm */ -} DWARF_V5LinePathEntryFormat; - -typedef struct DWARF_V5Directory{ - String8 path_str; - U64 path_off; - U64 path_sec_form; - U64 directory_index; - U64 timestamp; - U64 size; - U8 md5_checksum[16]; -} DWARF_V5Directory; - -typedef struct DWARF_LineUnit{ - struct DWARF_LineUnit *next; - - U64 hdr_off; - U64 base_off; - U64 opl_off; - - U8 version; - -} DWARF_LineUnit; - -typedef struct DWARF_LineParsed{ - DWARF_LineUnit *unit_first; - DWARF_LineUnit *unit_last; - U64 unit_count; -} DWARF_LineParsed; - - -// macro information (V4): .debug_macinfo -// (DWARF4.pdf + 7.22) - -// ** not implemented yet ** - -typedef struct DWARF_MacInfoParsed{ - U32 dummy; -} DWARF_MacInfoParsed; - - -// macro information (V5): .debug_macro -// (DWARF5.pdf + 7.23) - -// ** not implemented yet ** - -typedef struct DWARF_MacroParsed{ - U32 dummy; -} DWARF_MacroParsed; - - -// call frame information: .debug_frame -// (DWARF4.pdf + 7.23) (DWARF5.pdf + 7.24) - -// ** not implemented yet ** - -typedef struct DWARF_FrameParsed{ - U32 dummy; -} DWARF_FrameParsed; - - -// range lists (V4): .debug_ranges -// (DWARF4.pdf + 7.24) - -// ** not implemented yet ** - -typedef struct DWARF_RangesParsed{ - U32 dummy; -} DWARF_RangesParsed; - - -// string offsets table: .debug_str_offsets -// (DWARF5.pdf + 7.26) - -// ** not implemented yet ** - -typedef struct DWARF_StrOffsetsParsed{ - U32 dummy; -} DWARF_StrOffsetsParsed; - - -// address table: .debug_addr -// (DWARF5.pdf + 7.27) - -typedef struct DWARF_AddrUnit{ - struct DWARF_AddrUnit *next; - - U64 hdr_off; - U64 base_off; - U64 opl_off; - - U8 offset_size; - U8 dwarf_version; - U8 address_size; - U8 segment_selector_size; -} DWARF_AddrUnit; - -typedef struct DWARF_AddrParsed{ - DWARF_AddrUnit *unit_first; - DWARF_AddrUnit *unit_last; - U64 unit_count; -} DWARF_AddrParsed; - - -// range lists (V5): .debug_rnglists -// (DWARF5.pdf + 7.28 & 7.25) - -// ** not implemented yet ** - -typedef struct DWARF_RngListsParsed{ - U32 dummy; -} DWARF_RngListsParsed; - - -// location lists: .debug_loclists -// (DWARF5.pdf + 7.29) - -// ** not implemented yet ** - -typedef struct DWARF_LocListsParsed{ - U32 dummy; -} DWARF_LocListsParsed; - - -//////////////////////////////// -//~ Dwarf Decode Helpers - -#define DWARF_LEB128_ADV(p,o,s) do{ for(;; (p)+=1){\ -if ((p) == (o)) { (s)=0; break; } \ -if (((*(p))&0x80) == 0) { (p)+=1; break; } \ -} }while(0) - -#define DWARF_LEB128_ADV_NOCAP(p) for((p)+=1; ((*(p-1))&0x80) != 0; (p)+=1) - -static U64 dwarf_leb128_decode_U64(U8 *ptr, U8 *opl); -static S64 dwarf_leb128_decode_S64(U8 *ptr, U8 *opl); -static U32 dwarf_leb128_decode_U32(U8 *ptr, U8 *opl); - -#define dwarf_leb128_decode(T,ptr,opl) dwarf_leb128_decode_##T(ptr,opl) - -#define DWARF_LEB128_DECODE_ADV(T,x,p,o) do{ \ -U8 *first__ = (p); B32 success__; \ -DWARF_LEB128_ADV(p,o,success__); \ -if (success__) \ -(x) = dwarf_leb128_decode(T,first__, (p)); \ -}while(0) - - -//////////////////////////////// -//~ allen: ELF/DW Unwind Types -// -// TODO(rjf): OLD TYPES FROM UNWINDER CODE. bucketing this here, and deferring dwarf-based -// unwinding info to future DWARF/linux work. -// -#if 0 - -// * applies to (any X: unwind(ELF/DW, X)) - -// EH: Exception Frames -typedef U8 UNW_DW_EhPtrEnc; -enum{ - UNW_DW_EhPtrEnc_TYPE_MASK = 0x0F, - UNW_DW_EhPtrEnc_PTR = 0x00, // Pointer sized unsigned value - UNW_DW_EhPtrEnc_ULEB128 = 0x01, // Unsigned LE base-128 value - UNW_DW_EhPtrEnc_UDATA2 = 0x02, // Unsigned 16-bit value - UNW_DW_EhPtrEnc_UDATA4 = 0x03, // Unsigned 32-bit value - UNW_DW_EhPtrEnc_UDATA8 = 0x04, // Unsigned 64-bit value - UNW_DW_EhPtrEnc_SIGNED = 0x08, // Signed pointer - UNW_DW_EhPtrEnc_SLEB128 = 0x09, // Signed LE base-128 value - UNW_DW_EhPtrEnc_SDATA2 = 0x0A, // Signed 16-bit value - UNW_DW_EhPtrEnc_SDATA4 = 0x0B, // Signed 32-bit value - UNW_DW_EhPtrEnc_SDATA8 = 0x0C, // Signed 64-bit value -}; -enum{ - UNW_DW_EhPtrEnc_MODIF_MASK = 0x70, - UNW_DW_EhPtrEnc_PCREL = 0x10, // Value is relative to the current program counter. - UNW_DW_EhPtrEnc_TEXTREL = 0x20, // Value is relative to the .text section. - UNW_DW_EhPtrEnc_DATAREL = 0x30, // Value is relative to the .got or .eh_frame_hdr section. - UNW_DW_EhPtrEnc_FUNCREL = 0x40, // Value is relative to the function. - UNW_DW_EhPtrEnc_ALIGNED = 0x50, // Value is aligned to an address unit sized boundary. -}; -enum{ - UNW_DW_EhPtrEnc_INDIRECT = 0x80, // This flag indicates that value is stored in virtual memory. - UNW_DW_EhPtrEnc_OMIT = 0xFF, -}; - -typedef struct UNW_DW_EhPtrCtx{ - U64 raw_base_vaddr; // address where pointer is being read - U64 text_vaddr; // base address of section with instructions (used for encoding pointer on SH and IA64) - U64 data_vaddr; // base address of data section (used for encoding pointer on x86-64) - U64 func_vaddr; // base address of function where IP is located -} UNW_DW_EhPtrCtx; - -// CIE: Common Information Entry -typedef struct UNW_DW_CIEUnpacked{ - U8 version; - UNW_DW_EhPtrEnc lsda_encoding; - UNW_DW_EhPtrEnc addr_encoding; - - B8 has_augmentation_size; - U64 augmentation_size; - String8 augmentation; - - U64 code_align_factor; - S64 data_align_factor; - U64 ret_addr_reg; - - U64 handler_ip; - - U64 cfi_range_min; - U64 cfi_range_max; -} UNW_DW_CIEUnpacked; - -typedef struct UNW_DW_CIEUnpackedNode{ - struct UNW_DW_CIEUnpackedNode *next; - UNW_DW_CIEUnpacked cie; - U64 offset; -} UNW_DW_CIEUnpackedNode; - -// FDE: Frame Description Entry -typedef struct UNW_DW_FDEUnpacked{ - U64 ip_voff_min; - U64 ip_voff_max; - U64 lsda_ip; - - U64 cfi_range_min; - U64 cfi_range_max; -} UNW_DW_FDEUnpacked; - -// CFI: Call Frame Information -typedef struct UNW_DW_CFIRecords{ - B32 valid; - UNW_DW_CIEUnpacked cie; - UNW_DW_FDEUnpacked fde; -} UNW_DW_CFIRecords; - -typedef enum UNW_DW_CFICFARule{ - UNW_DW_CFICFARule_REGOFF, - UNW_DW_CFICFARule_EXPR, -} UNW_DW_CFICFARule; - -typedef struct UNW_DW_CFICFACell{ - UNW_DW_CFICFARule rule; - union{ - struct{ - U64 reg_idx; - S64 offset; - }; - U64 expr_min; - U64 expr_max; - }; -} UNW_DW_CFICFACell; - -typedef enum UNW_DW_CFIRegisterRule{ - UNW_DW_CFIRegisterRule_SAME_VALUE, - UNW_DW_CFIRegisterRule_UNDEFINED, - UNW_DW_CFIRegisterRule_OFFSET, - UNW_DW_CFIRegisterRule_VAL_OFFSET, - UNW_DW_CFIRegisterRule_REGISTER, - UNW_DW_CFIRegisterRule_EXPRESSION, - UNW_DW_CFIRegisterRule_VAL_EXPRESSION, -} UNW_DW_CFIRegisterRule; - -typedef struct UNW_DW_CFICell{ - UNW_DW_CFIRegisterRule rule; - union{ - S64 n; - struct{ - U64 expr_min; - U64 expr_max; - }; - }; -} UNW_DW_CFICell; - -typedef struct UNW_DW_CFIRow{ - struct UNW_DW_CFIRow *next; - UNW_DW_CFICell *cells; - UNW_DW_CFICFACell cfa_cell; -} UNW_DW_CFIRow; - -typedef struct UNW_DW_CFIMachine{ - U64 cells_per_row; - UNW_DW_CIEUnpacked *cie; - UNW_DW_EhPtrCtx *ptr_ctx; - UNW_DW_CFIRow *initial_row; - U64 fde_ip; -} UNW_DW_CFIMachine; - -typedef U8 UNW_DW_CFADecode; -enum{ - UNW_DW_CFADecode_NOP = 0x0, - // 1,2,4,8 reserved for literal byte sizes - UNW_DW_CFADecode_ADDRESS = 0x9, - UNW_DW_CFADecode_ULEB128 = 0xA, - UNW_DW_CFADecode_SLEB128 = 0xB, -}; - -typedef U16 UNW_DW_CFAControlBits; -enum{ - UNW_DW_CFAControlBits_DEC1_MASK = 0x00F, - UNW_DW_CFAControlBits_DEC2_MASK = 0x0F0, - UNW_DW_CFAControlBits_IS_REG_0 = 0x100, - UNW_DW_CFAControlBits_IS_REG_1 = 0x200, - UNW_DW_CFAControlBits_IS_REG_2 = 0x400, - UNW_DW_CFAControlBits_NEW_ROW = 0x800, -}; -#endif - -//////////////////////////////// -//~ Dwarf Parser Functions - -static DWARF_Parsed* dwarf_parsed_from_elf(Arena *arena, ELF_Parsed *elf); - -static DWARF_IndexParsed* dwarf_index_from_data(Arena *arena, String8 data); -static DWARF_SupParsed* dwarf_sup_from_data(Arena *arena, String8 data); -static DWARF_InfoParsed* dwarf_info_from_data(Arena *arena, String8 data); -static DWARF_PubNamesParsed* dwarf_pubnames_from_data(Arena *arena, String8 data); -static DWARF_NamesParsed* dwarf_names_from_data(Arena *arena, String8 data); -static DWARF_ArangesParsed* dwarf_aranges_from_data(Arena *arena, String8 data); -static DWARF_LineParsed* dwarf_line_from_data(Arena *arena, String8 data); -static DWARF_MacInfoParsed* dwarf_mac_info_from_data(Arena *arena, String8 data); -static DWARF_MacroParsed* dwarf_macro_from_data(Arena *arena, String8 data); -static DWARF_FrameParsed* dwarf_frame_from_data(Arena *arena, String8 data); -static DWARF_RangesParsed* dwarf_ranges_from_data(Arena *arena, String8 data); -static DWARF_StrOffsetsParsed* dwarf_str_offsets_from_data(Arena *arena, String8 data); -static DWARF_AddrParsed* dwarf_addr_from_data(Arena *arena, String8 data); -static DWARF_RngListsParsed* dwarf_rng_lists_from_data(Arena *arena, String8 data); -static DWARF_LocListsParsed* dwarf_loc_lists_from_data(Arena *arena, String8 data); - - -// parse helpers - -// (DWARF4.pdf + 7.2.2) (DWARF5.pdf + 7.2.2) -static void dwarf__initial_length(String8 data, - U8 **ptr_inout, U8 **unit_opl_out, B32 *is_64bit_out); - -static void -dwarf__line_v5_directories(U64 address_size, U64 offset_size, - DWARF_V5LinePathEntryFormat *format, U64 format_count, - DWARF_V5Directory *directories_out, U64 dir_count, - U8 **ptr_io, U8 *opl); - -// debug sections - -static String8 dwarf_name_from_debug_section(DWARF_Parsed *dwarf, DWARF_SectionCode sec_code); - -// abbrev functions - -static DWARF_AbbrevUnit* dwarf_abbrev_unit_from_offset(DWARF_AbbrevParsed *abbrev, U64 off); -static DWARF_AbbrevDecl* dwarf_abbrev_decl_from_code(DWARF_AbbrevUnit *unit, U32 code); - -// attribute decoding functions - -static DWARF_AttributeClassFlags dwarf_attribute_class_from_form(DWARF_AttributeForm form); -static DWARF_AttributeClassFlags dwarf_attribute_class_from_name(DWARF_AttributeName name); - -// form decoding functions - -static DWARF_FormDecodeRules -dwarf_form_decode_rule(DWARF_AttributeForm form, U64 address_size, U64 offset_size); - -static DWARF_FormDecoded -dwarf_form_decode(DWARF_FormDecodeRules *rules, U8 **ptr_io, U8 *opl, - DWARF_AbbrevDecl *abbrev_decl, U32 attrib_i); - -// string functions - -static String8 dwarf_string_from_unit_type(DWARF_UnitType type); -static String8 dwarf_string_from_tag(DWARF_Tag tag); -static String8 dwarf_string_from_attribute_name(DWARF_AttributeName name); -static String8 dwarf_string_from_attribute_form(DWARF_AttributeForm form); -static String8 dwarf_string_from_line_std_op(DWARF_LineStdOp op); -static String8 dwarf_string_from_line_ext_op(DWARF_LineExtOp op); -static String8 dwarf_string_from_line_entry_format(DWARF_LineEntryFormat format); -static String8 dwarf_string_from_section_code(DWARF_SectionCode sec_code); - -#endif //RDI_DWARF_H - +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RDI_DWARF_H +#define RDI_DWARF_H + +// https://dwarfstd.org/doc/DWARF4.pdf +// https://dwarfstd.org/doc/DWARF5.pdf + +// TODO(allen): +// [ ] function to parse info just for unit headers & root attributes +// [ ] put together unit info from all sections in one structure +// [ ] actually check version numbers in unit header parsers + +#pragma pack(push,1) + +//////////////////////////////// +//~ Dwarf Format Code X Lists + +// unit type X(name, code) +#define DWARF_UnitTypeXList(X)\ +X(null, 0x00)\ +X(compile, 0x01)\ +X(type, 0x02)\ +X(partial, 0x03)\ +X(skeleton, 0x04)\ +X(split_compile, 0x05)\ +X(split_type, 0x06)\ +X(lo_user, 0x80)\ +X(hi_user, 0xff) + +typedef enum DWARF_UnitType{ +#define X(N,C) DWARF_UnitType_##N = C, + DWARF_UnitTypeXList(X) +#undef X +} DWARF_UnitType; + + +// tag X(name, code) +#define DWARF_TagXList(X)\ +X(null, 0x00)\ +X(array_type, 0x01)\ +X(class_type, 0x02)\ +X(entry_point, 0x03)\ +X(enumeration_type, 0x04)\ +X(formal_parameter, 0x05)\ +X(imported_declaration, 0x08)\ +X(label, 0x0a)\ +X(lexical_block, 0x0b)\ +X(member, 0x0d)\ +X(pointer_type, 0x0f)\ +X(reference_type, 0x10)\ +X(compile_unit, 0x11)\ +X(string_type, 0x12)\ +X(structure_type, 0x13)\ +X(subroutine_type, 0x15)\ +X(typedef, 0x16)\ +X(union_type, 0x17)\ +X(unspecified_parameters, 0x18)\ +X(variant, 0x19)\ +X(common_block, 0x1a)\ +X(common_inclusion, 0x1b)\ +X(inheritance, 0x1c)\ +X(inlined_subroutine, 0x1d)\ +X(module, 0x1e)\ +X(ptr_to_member_type, 0x1f)\ +X(set_type, 0x20)\ +X(subrange_type, 0x21)\ +X(with_stmt, 0x22)\ +X(access_declaration, 0x23)\ +X(base_type, 0x24)\ +X(catch_block, 0x25)\ +X(const_type, 0x26)\ +X(constant, 0x27)\ +X(enumerator, 0x28)\ +X(file_type, 0x29)\ +X(friend, 0x2a)\ +X(namelist, 0x2b)\ +X(namelist_item, 0x2c)\ +X(packed_type, 0x2d)\ +X(subprogram, 0x2e)\ +X(template_type_parameter, 0x2f)\ +X(template_value_parameter, 0x30)\ +X(thrown_type, 0x31)\ +X(try_block, 0x32)\ +X(variant_part, 0x33)\ +X(variable, 0x34)\ +X(volatile_type, 0x35)\ +X(dwarf_procedure, 0x36)\ +X(restrict_type, 0x37)\ +X(interface_type, 0x38)\ +X(namespace, 0x39)\ +X(imported_module, 0x3a)\ +X(unspecified_type, 0x3b)\ +X(partial_unit, 0x3c)\ +X(imported_unit, 0x3d)\ +X(condition, 0x3f)\ +X(shared_type, 0x40)\ +X(type_unit, 0x41)\ +X(rvalue_reference_type, 0x42)\ +X(template_alias, 0x43)\ +X(coarray_type, 0x44)\ +X(generic_subrange, 0x45)\ +X(dynamic_type, 0x46)\ +X(atomic_type, 0x47)\ +X(call_site, 0x48)\ +X(call_site_parameter, 0x49)\ +X(skeleton_unit, 0x4a)\ +X(immutable_type, 0x4b)\ +X(lo_user, 0x4080)\ +X(hi_user, 0xffff) + +typedef enum DWARF_Tag{ +#define X(N,C) DWARF_Tag_##N = C, + DWARF_TagXList(X) +#undef X +} DWARF_Tag; + + +// attribute classes: X(name,code) +#define DWARF_AttributeClassXList(X)\ +X(address, 0)\ +X(addrptr, 1)\ +X(block, 2)\ +X(constant, 3)\ +X(exprloc, 4)\ +X(flag, 5)\ +X(lineptr, 6)\ +X(loclist, 7)\ +X(loclistsptr, 8)\ +X(macptr, 9)\ +X(reference, 10)\ +X(rnglist, 11)\ +X(rnglistsptr, 12)\ +X(string, 13)\ +X(stroffsetsptr, 14) + +typedef U32 DWARF_AttributeClassFlags; +enum{ +#define X(N,C) DWARF_AttributeClassFlag_##N = (1 << C), + DWARF_AttributeClassXList(X) +#undef X + + DWARF_AttributeClassFlag_0 = 0, + DWARF_AttributeClassFlag_specialcase = ~0, + DWARF_AttributeClassFlag_sec_offset_classes = + (DWARF_AttributeClassFlag_addrptr | + DWARF_AttributeClassFlag_lineptr | + DWARF_AttributeClassFlag_loclist | + DWARF_AttributeClassFlag_loclistsptr | + DWARF_AttributeClassFlag_macptr | + DWARF_AttributeClassFlag_rnglist | + DWARF_AttributeClassFlag_rnglistsptr | + DWARF_AttributeClassFlag_stroffsetsptr | + 0), + +}; + + +// attribute name: X(name, code, classflag1, classflag2, classflag3, classflag4) +#define DWARF_AttributeNameXList(X)\ +X(null, 0x00, 0, 0, 0, 0)\ +X(sibling, 0x01, reference, 0, 0, 0)\ +X(location, 0x02, exprloc, loclist, 0, 0)\ +X(name, 0x03, string, 0, 0, 0)\ +X(ordering, 0x09, constant, 0, 0, 0)\ +X(byte_size, 0x0b, constant, exprloc, reference, 0)\ +X(bit_size, 0x0d, constant, exprloc, reference, 0)\ +X(stmt_list, 0x10, lineptr, 0, 0, 0)\ +X(low_pc, 0x11, address, 0, 0, 0)\ +X(high_pc, 0x12, address, constant, 0, 0)\ +X(language, 0x13, constant, 0, 0, 0)\ +X(discr, 0x15, reference, 0, 0, 0)\ +X(discr_value, 0x16, constant, 0, 0, 0)\ +X(visibility, 0x17, constant, 0, 0, 0)\ +X(import, 0x18, reference, 0, 0, 0)\ +X(string_length, 0x19, exprloc, loclist, reference, 0)\ +X(common_reference, 0x1a, reference, 0, 0, 0)\ +X(comp_dir, 0x1b, string, 0, 0, 0)\ +X(const_value, 0x1c, block, constant, string, 0)\ +X(containing_type, 0x1d, reference, 0, 0, 0)\ +X(default_value, 0x1e, constant, reference, flag, 0)\ +X(inline, 0x20, constant, 0, 0, 0)\ +X(is_optional, 0x21, flag, 0, 0, 0)\ +X(lower_bound, 0x22, constant, exprloc, reference, 0)\ +X(producer, 0x25, string, 0, 0, 0)\ +X(prototyped, 0x27, flag, 0, 0, 0)\ +X(return_addr, 0x2a, exprloc, loclist, 0, 0)\ +X(start_scope, 0x2c, constant, rnglist, 0, 0)\ +X(bit_stride, 0x2e, constant, exprloc, reference, 0)\ +X(upper_bound, 0x2f, constant, exprloc, reference, 0)\ +X(abstract_origin, 0x31, reference, 0, 0, 0)\ +X(accessibility, 0x32, constant, 0, 0, 0)\ +X(address_class, 0x33, constant, 0, 0, 0)\ +X(artificial, 0x34, flag, 0, 0, 0)\ +X(base_types, 0x35, reference, 0, 0, 0)\ +X(calling_convention, 0x36, constant, 0, 0, 0)\ +X(count, 0x37, constant, exprloc, reference, 0)\ +X(data_member_location, 0x38, constant, exprloc, loclist, 0)\ +X(decl_column, 0x39, constant, 0, 0, 0)\ +X(decl_file, 0x3a, constant, 0, 0, 0)\ +X(decl_line, 0x3b, constant, 0, 0, 0)\ +X(declaration, 0x3c, flag, 0, 0, 0)\ +X(discr_list, 0x3d, block, 0, 0, 0)\ +X(encoding, 0x3e, constant, 0, 0, 0)\ +X(external, 0x3f, flag, 0, 0, 0)\ +X(frame_base, 0x40, exprloc, loclist, 0, 0)\ +X(friend, 0x41, reference, 0, 0, 0)\ +X(identifier_case, 0x42, constant, 0, 0, 0)\ +X(namelist_item, 0x44, reference, 0, 0, 0)\ +X(priority, 0x45, reference, 0, 0, 0)\ +X(segment, 0x46, exprloc, loclist, 0, 0)\ +X(specification, 0x47, reference, 0, 0, 0)\ +X(static_link, 0x48, exprloc, loclist, 0, 0)\ +X(type, 0x49, reference, 0, 0, 0)\ +X(use_location, 0x4a, exprloc, loclist, 0, 0)\ +X(variable_parameter, 0x4b, flag, 0, 0, 0)\ +X(virtuality, 0x4c, constant, 0, 0, 0)\ +X(vtable_elem_location, 0x4d, exprloc, loclist, 0, 0)\ +X(allocated, 0x4e, constant, exprloc, reference, 0)\ +X(associated, 0x4f, constant, exprloc, reference, 0)\ +X(data_location, 0x50, exprloc, 0, 0, 0)\ +X(byte_stride, 0x51, constant, exprloc, reference, 0)\ +X(entry_pc, 0x52, address, constant, 0, 0)\ +X(use_UTF8, 0x53, flag, 0, 0, 0)\ +X(extension, 0x54, reference, 0, 0, 0)\ +X(ranges, 0x55, rnglist, 0, 0, 0)\ +X(trampoline, 0x56, address, flag, reference, string)\ +X(call_column, 0x57, constant, 0, 0, 0)\ +X(call_file, 0x58, constant, 0, 0, 0)\ +X(call_line, 0x59, constant, 0, 0, 0)\ +X(description, 0x5a, string, 0, 0, 0)\ +X(binary_scale, 0x5b, constant, 0, 0, 0)\ +X(decimal_scale, 0x5c, constant, 0, 0, 0)\ +X(small, 0x5d, reference, 0, 0, 0)\ +X(decimal_sign, 0x5e, constant, 0, 0, 0)\ +X(digit_count, 0x5f, constant, 0, 0, 0)\ +X(picture_string, 0x60, string, 0, 0, 0)\ +X(mutable, 0x61, flag, 0, 0, 0)\ +X(threads_scaled, 0x62, flag, 0, 0, 0)\ +X(explicit, 0x63, flag, 0, 0, 0)\ +X(object_pointer, 0x64, reference, 0, 0, 0)\ +X(endianity, 0x65, constant, 0, 0, 0)\ +X(elemental, 0x66, flag, 0, 0, 0)\ +X(pure, 0x67, flag, 0, 0, 0)\ +X(recursive, 0x68, flag, 0, 0, 0)\ +X(signature, 0x69, reference, 0, 0, 0)\ +X(main_subprogram, 0x6a, flag, 0, 0, 0)\ +X(data_bit_offset, 0x6b, constant, 0, 0, 0)\ +X(const_expr, 0x6c, flag, 0, 0, 0)\ +X(enum_class, 0x6d, flag, 0, 0, 0)\ +X(linkage_name, 0x6e, string, 0, 0, 0)\ +X(string_length_bit_size, 0x6f, constant, 0, 0, 0)\ +X(string_length_byte_size, 0x70, constant, 0, 0, 0)\ +X(rank, 0x71, constant, exprloc, 0, 0)\ +X(str_offsets_base, 0x72, stroffsetsptr, 0, 0, 0)\ +X(addr_base, 0x73, addrptr, 0, 0, 0)\ +X(rnglists_base, 0x74, rnglistsptr, 0, 0, 0)\ +X(dwo_name, 0x76, string, 0, 0, 0)\ +X(reference, 0x77, flag, 0, 0, 0)\ +X(rvalue_reference, 0x78, flag, 0, 0, 0)\ +X(macros, 0x79, macptr, 0, 0, 0)\ +X(call_all_calls, 0x7a, flag, 0, 0, 0)\ +X(call_all_source_calls, 0x7b, flag, 0, 0, 0)\ +X(call_all_tail_calls, 0x7c, flag, 0, 0, 0)\ +X(call_return_pc, 0x7d, address, 0, 0, 0)\ +X(call_value, 0x7e, exprloc, 0, 0, 0)\ +X(call_origin, 0x7f, exprloc, 0, 0, 0)\ +X(call_parameter, 0x80, reference, 0, 0, 0)\ +X(call_pc, 0x81, address, 0, 0, 0)\ +X(call_tail_call, 0x82, flag, 0, 0, 0)\ +X(call_target, 0x83, exprloc, 0, 0, 0)\ +X(call_target_clobbered, 0x84, exprloc, 0, 0, 0)\ +X(call_data_location, 0x85, exprloc, 0, 0, 0)\ +X(call_data_value, 0x86, exprloc, 0, 0, 0)\ +X(noreturn, 0x87, flag, 0, 0, 0)\ +X(alignment, 0x88, constant, 0, 0, 0)\ +X(export_symbols, 0x89, flag, 0, 0, 0)\ +X(deleted, 0x8a, flag, 0, 0, 0)\ +X(defaulted, 0x8b, constant, 0, 0, 0)\ +X(loclists_base, 0x8c, loclistsptr, 0, 0, 0)\ +X(lo_user, 0x2000, 0, 0, 0, 0)\ +X(hi_user, 0x3fff, 0, 0, 0, 0) + +typedef enum DWARF_AttributeName{ +#define X(N,C,f1,f2,f3,f4) DWARF_AttributeName_##N = C, + DWARF_AttributeNameXList(X) +#undef X +} DWARF_AttributeName; + + +// attribute forms: X(name, code, classflag) +#define DWARF_AttributeFormXList(X)\ +X(null, 0x00, 0)\ +X(addr, 0x01, address)\ +X(block2, 0x03, block)\ +X(block4, 0x04, block)\ +X(data2, 0x05, constant)\ +X(data4, 0x06, constant)\ +X(data8, 0x07, constant)\ +X(string, 0x08, string)\ +X(block, 0x09, block)\ +X(block1, 0x0a, block)\ +X(data1, 0x0b, constant)\ +X(flag, 0x0c, flag)\ +X(sdata, 0x0d, constant)\ +X(strp, 0x0e, string)\ +X(udata, 0x0f, constant)\ +X(ref_addr, 0x10, reference)\ +X(ref1, 0x11, reference)\ +X(ref2, 0x12, reference)\ +X(ref4, 0x13, reference)\ +X(ref8, 0x14, reference)\ +X(ref_udata, 0x15, reference)\ +X(indirect, 0x16, specialcase)\ +X(sec_offset, 0x17, sec_offset_classes)\ +X(exprloc, 0x18, exprloc)\ +X(flag_present, 0x19, flag)\ +X(strx, 0x1a, string)\ +X(addrx, 0x1b, address)\ +X(ref_sup4, 0x1c, reference)\ +X(strp_sup, 0x1d, string)\ +X(data16, 0x1e, constant)\ +X(line_strp, 0x1f, string)\ +X(ref_sig8, 0x20, reference)\ +X(implicit_const, 0x21, specialcase)\ +X(loclistx, 0x22, loclist)\ +X(rnglistx, 0x23, rnglist)\ +X(ref_sup8, 0x24, reference)\ +X(strx1, 0x25, string)\ +X(strx2, 0x26, string)\ +X(strx3, 0x27, string)\ +X(strx4, 0x28, string)\ +X(addrx1, 0x29, address)\ +X(addrx2, 0x2a, address)\ +X(addrx3, 0x2b, address)\ +X(addrx4, 0x2c, address) + +typedef enum DWARF_AttributeForm{ +#define X(N,C,f) DWARF_AttributeForm_##N = C, + DWARF_AttributeFormXList(X) +#undef X +} DWARF_AttributeForm; + + +// ops: X(name, code, opnum) +#define DWARF_OpXList(X)\ +X(addr, 0x03, 1)\ +X(deref, 0x06, 0)\ +X(const1u, 0x08, 1)\ +X(const1s, 0x09, 1)\ +X(const2u, 0x0a, 1)\ +X(const2s, 0x0b, 1)\ +X(const4u, 0x0c, 1)\ +X(const4s, 0x0d, 1)\ +X(const8u, 0x0e, 1)\ +X(const8s, 0x0f, 1)\ +X(constu, 0x10, 1)\ +X(consts, 0x11, 1)\ +X(dup, 0x12, 0)\ +X(drop, 0x13, 0)\ +X(over, 0x14, 0)\ +X(pick, 0x15, 1)\ +X(swap, 0x16, 0)\ +X(rot, 0x17, 0)\ +X(xderef, 0x18, 0)\ +X(abs, 0x19, 0)\ +X(and, 0x1a, 0)\ +X(div, 0x1b, 0)\ +X(minus, 0x1c, 0)\ +X(mod, 0x1d, 0)\ +X(mul, 0x1e, 0)\ +X(neg, 0x1f, 0)\ +X(not, 0x20, 0)\ +X(or, 0x21, 0)\ +X(plus, 0x22, 0)\ +X(plus_uconst, 0x23, 1)\ +X(shl, 0x24, 0)\ +X(shr, 0x25, 0)\ +X(shra, 0x26, 0)\ +X(xor, 0x27, 0)\ +X(bra, 0x28, 1)\ +X(eq, 0x29, 0)\ +X(ge, 0x2a, 0)\ +X(gt, 0x2b, 0)\ +X(le, 0x2c, 0)\ +X(lt, 0x2d, 0)\ +X(ne, 0x2e, 0)\ +X(skip, 0x2f, 1)\ +X(lit0, 0x30, 0)\ +X(lit1, 0x31, 0)\ +X(lit31, 0x4f, 0)\ +X(reg0, 0x50, 0)\ +X(reg1, 0x51, 0)\ +X(reg31, 0x6f, 0)\ +X(breg0, 0x70, 1)\ +X(breg1, 0x71, 1)\ +X(breg31, 0x8f, 1)\ +X(regx, 0x90, 1)\ +X(fbreg, 0x91, 1)\ +X(bregx, 0x92, 2)\ +X(piece, 0x93, 1)\ +X(deref_size, 0x94, 1)\ +X(xderef_size, 0x95, 1)\ +X(nop, 0x96, 0)\ +X(push_object_address, 0x97, 0)\ +X(call2, 0x98, 1)\ +X(call4, 0x99, 1)\ +X(call_ref, 0x9a, 1)\ +X(form_tls_address, 0x9b, 0)\ +X(call_frame_cfa, 0x9c, 0)\ +X(bit_piece, 0x9d, 2)\ +X(implicit_value, 0x9e, 2)\ +X(stack_value, 0x9f, 0)\ +X(implicit_pointer, 0xa0, 2)\ +X(addrx, 0xa1, 1)\ +X(constx, 0xa2, 1)\ +X(entry_value, 0xa3, 2)\ +X(const_type, 0xa4, 3)\ +X(regval_type, 0xa5, 2)\ +X(deref_type, 0xa6, 2)\ +X(xderef_type, 0xa7, 2)\ +X(convert, 0xa8, 1)\ +X(reinterpret, 0xa9, 1)\ +X(lo_user, 0xe0, 0)\ +X(hi_user, 0xff, 0) + +typedef enum DWARF_Op{ +#define X(N,C,k) DWARF_Op_##N = C, + DWARF_OpXList(X) +#undef X +} DWARF_Op; + + +// location list entry: X(name, code) +#define DWARF_LocationListEntryXList(X)\ +X(end_of_list, 0x00)\ +X(base_addressx, 0x01)\ +X(startx_endx, 0x02)\ +X(startx_length, 0x03)\ +X(offset_pair, 0x04)\ +X(default_location, 0x05)\ +X(base_address, 0x06)\ +X(start_end, 0x07)\ +X(start_length, 0x08) + +typedef enum DWARF_LocationListEntry{ +#define X(N,C) DWARF_LocationListEntry_##N = C, + DWARF_LocationListEntryXList(X) +#undef X +} DWARF_LocationListEntry; + + +// base type: X(name, code) +#define DWARF_BaseTypeXList(X)\ +X(address, 0x01)\ +X(boolean, 0x02)\ +X(complex_float, 0x03)\ +X(float, 0x04)\ +X(signed, 0x05)\ +X(signed_char, 0x06)\ +X(unsigned, 0x07)\ +X(unsigned_char, 0x08)\ +X(imaginary_float, 0x09)\ +X(packed_decimal, 0x0a)\ +X(numeric_string, 0x0b)\ +X(edited, 0x0c)\ +X(signed_fixed, 0x0d)\ +X(unsigned_fixed, 0x0e)\ +X(decimal_float, 0x0f)\ +X(UTF, 0x10)\ +X(UCS, 0x11)\ +X(ASCII, 0x12)\ +X(lo_user, 0x80)\ +X(hi_user, 0xff) + +typedef enum DWARF_BaseType{ +#define X(N,C) DWARF_BaseType_##N = C, + DWARF_BaseTypeXList(X) +#undef X +} DWARF_BaseType; + + +// decimal sign: X(name, code) +#define DWARF_DecimalSignXList(X)\ +X(unsigned, 0x01)\ +X(leading_overpunch, 0x02)\ +X(trailing_overpunch, 0x03)\ +X(leading_separate, 0x04)\ +X(trailing_separate, 0x05) + +typedef enum DWARF_DecimalSign{ +#define X(N,C) DWARF_DecimalSign_##N = C, + DWARF_DecimalSignXList(X) +#undef X +} DWARF_DecimalSign; + + +// endianity: X(name, code) +#define DWARF_EndianityXList(X)\ +X(default, 0x00)\ +X(big, 0x01)\ +X(little, 0x02)\ +X(lo_user, 0x40)\ +X(hi_user, 0xff) + +typedef enum DWARF_Endianity{ +#define X(N,C) DWARF_Endianity_##N = C, + DWARF_EndianityXList(X) +#undef X +} DWARF_Endianity; + + +// access: X(name, code) +#define DWARF_AccessXList(X)\ +X(public, 0x01)\ +X(protected, 0x02)\ +X(private, 0x03) + +typedef enum DWARF_Access{ +#define X(N,C) DWARF_Access_##N = C, + DWARF_AccessXList(X) +#undef X +} DWARF_Access; + + +// visibility: X(name, code) +#define DWARF_VisibilityXList(X)\ +X(local, 0x01)\ +X(exported, 0x02)\ +X(qualified, 0x03) + +typedef enum DWARF_Visibility{ +#define X(N,C) DWARF_Visibility_##N = C, + DWARF_VisibilityXList(X) +#undef X +} DWARF_Visibility; + + +// virtuality: X(name, code) +#define DWARF_VirtualityXList(X)\ +X(none, 0x00)\ +X(virtual, 0x01)\ +X(pure_virtual, 0x02) + +typedef enum DWARF_Virtuality{ +#define X(N,C) DWARF_Virtuality_##N = C, + DWARF_VirtualityXList(X) +#undef X +} DWARF_Virtuality; + + +// language: X(name, code, deflowerbound) +#define DWARF_LanguageXList(X)\ +X(C89, 0x0001, 0)\ +X(C, 0x0002, 0)\ +X(Ada83, 0x0003, 1)\ +X(C_plus_plus, 0x0004, 0)\ +X(Cobol74, 0x0005, 1)\ +X(Cobol85, 0x0006, 1)\ +X(Fortran77, 0x0007, 1)\ +X(Fortran90, 0x0008, 1)\ +X(Pascal83, 0x0009, 1)\ +X(Modula2, 0x000a, 1)\ +X(Java, 0x000b, 0)\ +X(C99, 0x000c, 0)\ +X(Ada95, 0x000d, 1)\ +X(Fortran95, 0x000e, 1)\ +X(PLI, 0x000f, 1)\ +X(ObjC, 0x0010, 0)\ +X(ObjC_plus_plus, 0x0011, 0)\ +X(UPC, 0x0012, 0)\ +X(D, 0x0013, 0)\ +X(Python, 0x0014, 0)\ +X(OpenCL, 0x0015, 0)\ +X(Go, 0x0016, 0)\ +X(Modula3, 0x0017, 1)\ +X(Haskell, 0x0018, 0)\ +X(C_plus_plus_03, 0x0019, 0)\ +X(C_plus_plus_11, 0x001a, 0)\ +X(OCaml, 0x001b, 0)\ +X(Rust, 0x001c, 0)\ +X(C11, 0x001d, 0)\ +X(Swift, 0x001e, 0)\ +X(Julia, 0x001f, 1)\ +X(Dylan, 0x0020, 0)\ +X(C_plus_plus_14, 0x0021, 0)\ +X(Fortran03, 0x0022, 1)\ +X(Fortran08, 0x0023, 1)\ +X(RenderScript, 0x0024, 0)\ +X(BLISS, 0x0025, 0)\ +X(lo_user, 0x8000, 0)\ +X(hi_user, 0xffff, 0) + +typedef enum DWARF_Language{ +#define X(N,C,k) DWARF_Language_##N = C, + DWARF_LanguageXList(X) +#undef X +} DWARF_Language; + + +// identifier case: X(name, code) +#define DWARF_IdentifierCaseXList(X)\ +X(case_sensitive, 0x00)\ +X(up_case, 0x01)\ +X(down_case, 0x02)\ +X(case_insensitive, 0x03) + +typedef enum DWARF_IdentifierCase{ +#define X(N,C) DWARF_IdentifierCase_##N = C, + DWARF_IdentifierCaseXList(X) +#undef X +} DWARF_IdentifierCase; + + +// calling convention: X(name, code) +#define DWARF_CallingConventionXList(X)\ +X(normal, 0x01)\ +X(program, 0x02)\ +X(nocall, 0x03)\ +X(pass_by_reference, 0x04)\ +X(pass_by_value, 0x05)\ +X(lo_user, 0x40)\ +X(hi_user, 0xff) + +typedef enum DWARF_CallingConvention{ +#define X(N,C) DWARF_CallingConvention_##N = C, + DWARF_CallingConventionXList(X) +#undef X +} DWARF_CallingConvention; + + +// inline: X(name, code) +#define DWARF_InlineXList(X)\ +X(not_inlined, 0x00)\ +X(inlined, 0x01)\ +X(declared_not_inlined, 0x02)\ +X(declared_inlined, 0x03) + +typedef enum DWARF_Inline{ +#define X(N,C) DWARF_Inline_##N = C, + DWARF_InlineXList(X) +#undef X +} DWARF_Inline; + + +// array ordering: X(name, code) +#define DWARF_ArrayOrderingXList(X)\ +X(row_major, 0x00)\ +X(col_major, 0x01) + +typedef enum DWARF_ArrayOrdering{ +#define X(N,C) DWARF_ArrayOrdering_##N = C, + DWARF_ArrayOrderingXList(X) +#undef X +} DWARF_ArrayOrdering; + + +// discriminant: X(name, code) +#define DWARF_DiscriminantXList(X)\ +X(label, 0x00)\ +X(range, 0x01) + +typedef enum DWARF_Discriminant{ +#define X(N,C) DWARF_Discriminant_##N = C, + DWARF_DiscriminantXList(X) +#undef X +} DWARF_Discriminant; + + +// name index: X(name, code) +#define DWARF_NameIndexXList(X)\ +X(compile_unit, 1)\ +X(type_unit, 2)\ +X(die_offset, 3)\ +X(parent, 4)\ +X(type_hash, 5)\ +X(lo_user, 0x2000)\ +X(hi_user, 0x3fff) + +typedef enum DWARF_NameIndex{ +#define X(N,C) DWARF_NameIndex_##N = C, + DWARF_NameIndexXList(X) +#undef X +} DWARF_NameIndex; + + +// defaulted: X(name, code) +#define DWARF_DefaultedXList(X)\ +X(no, 0x00)\ +X(in_class, 0x01)\ +X(out_of_class, 0x02) + +typedef enum DWARF_Defaulted{ +#define X(N,C) DWARF_Defaulted_##N = C, + DWARF_DefaultedXList(X) +#undef X +} DWARF_Defaulted; + +// call frame instruction: X(N, hi2bits, matchlow, low6bits, operand1, operand2) +// "CFA" +#define DWARF_CallFrameInsnXList(X)\ +X(advance_loc, 0x1, 0, 0, NULL, NULL)\ +X(offset, 0x2, 0, 0, ULEB, NULL)\ +X(restore, 0x3, 0, 0, NULL, NULL)\ +X(nop, 0x0, 1, 0, NULL, NULL)\ +X(set_loc, 0x0, 1, 0x01, ADDRESS, NULL)\ +X(advance_loc1, 0x0, 1, 0x02, 1BYTE, NULL)\ +X(advance_loc2, 0x0, 1, 0x03, 2BYTE, NULL)\ +X(advance_loc4, 0x0, 1, 0x04, 4BYTE, NULL)\ +X(offset_extended, 0x0, 1, 0x05, ULEB, ULEB)\ +X(restore_extended, 0x0, 1, 0x06, ULEB, NULL)\ +X(undefined, 0x0, 1, 0x07, ULEB, NULL)\ +X(same_value, 0x0, 1, 0x08, ULEB, NULL)\ +X(register, 0x0, 1, 0x09, ULEB, ULEB)\ +X(remember_state, 0x0, 1, 0x0a, NULL, NULL)\ +X(restore_state, 0x0, 1, 0x0b, NULL, NULL)\ +X(def_cfa, 0x0, 1, 0x0c, ULEB, ULEB)\ +X(def_cfa_register, 0x0, 1, 0x0d, ULEB, NULL)\ +X(def_cfa_offset, 0x0, 1, 0x0e, ULEB, NULL)\ +X(def_cfa_expression,0x0, 1, 0x0f, BLOCK, NULL)\ +X(expression, 0x0, 1, 0x10, ULEB, BLOCK)\ +X(offset_extended_sf,0x0, 1, 0x11, ULEB, SLEB)\ +X(def_cfa_sf, 0x0, 1, 0x12, ULEB, SLEB)\ +X(def_cfa_offset_sf, 0x0, 1, 0x13, SLEB, NULL)\ +X(val_offset, 0x0, 1, 0x14, ULEB, ULEB)\ +X(val_offset_sf, 0x0, 1, 0x15, ULEB, SLEB)\ +X(val_expression, 0x0, 1, 0x16, ULEB, BLOCK)\ +X(lo_user, 0x0, 1, 0x1c, NULL, NULL)\ +X(hi_user, 0x0, 1, 0x3f, NULL, NULL) + +// line number encoding codes +// (DWARF4.pdf + 7.21) (DWARF5.pdf + 7.22) + +// X(name, code) (V4 & V5) +#define DWARF_LineStdOpXList(X) \ +X(copy, 0x01)\ +X(advance_pc, 0x02)\ +X(advance_line, 0x03)\ +X(set_file, 0x04)\ +X(set_column, 0x05)\ +X(negate_stmt, 0x06)\ +X(set_basic_block, 0x07)\ +X(const_add_pc, 0x08)\ +X(fixed_advance_pc, 0x09)\ +X(set_prologue_end, 0x0a)\ +X(set_epilogue_begin, 0x0b)\ +X(set_isa, 0x0c) + +typedef enum DWARF_LineStdOp{ +#define X(N,C) DWARF_LineStdOp_##N = C, + DWARF_LineStdOpXList(X) +#undef X +} DWARF_LineStdOp; + +// X(name, code) (V4 & V5) +#define DWARF_LineExtOpXList(X) \ +X(end_sequence, 0x01)\ +X(set_address, 0x02)\ +X(define_file, 0x03)\ +X(set_discriminator, 0x04)\ +X(lo_user, 0x80)\ +X(hi_user, 0xff) + +typedef enum DWARF_LineExtOp{ +#define X(N,C) DWARF_LineExtOp_##N = C, + DWARF_LineExtOpXList(X) +#undef X +} DWARF_LineExtOp; + +// X(name, code) (V5) +#define DWARF_LineEntryFormatXList(X) \ +X(path, 0x1)\ +X(directory_index, 0x2)\ +X(timestamp, 0x3)\ +X(size, 0x4)\ +X(MD5, 0x5)\ +X(lo_user, 0x2000)\ +X(hi_user, 0x3fff) + +typedef enum DWARF_LineEntryFormat{ +#define X(N,C) DWARF_LineEntryFormat_##N = C, + DWARF_LineEntryFormatXList(X) +#undef X +} DWARF_LineEntryFormat; + +//////////////////////////////// +//~ Dwarf Parser Codes and Data Tables + +#define DWARF_SECTION_NAME_VARIANT_COUNT 3 + +// X(section_code_name, versionflags, section_name0, section_name1, section_name2) +#define DWARF_SectionNameXList(X,V4,V5)\ +X(Null, 0, "", "", "")\ +X(Loc, V4, ".debug_loc", ".debug_loc.dwo", "__debug_loc")\ +X(Str, V4|V5, ".debug_str", ".debug_str.dwo", "__debug_str")\ +X(LineStr, V5, ".debug_line_str", ".debug_line_str.dwo", "__debug_line_str")\ +X(CmpUnitIdx, V5, ".debug_cu_index", ".debug_cu_index.dwo", "__debug_cu_index")\ +X(TypeIdx, V5, ".debug_tu_index", ".debug_tu_index.dwo", "__debug_tu_index")\ +X(Supplement, V5, ".debug_sup", ".debug_sup.dwo", "__debug_sup")\ +X(Info, V4|V5, ".debug_info", ".debug_info.dwo", "__debug_info")\ +X(Abbrev, V4|V5, ".debug_abbrev", ".debug_abbrev.dwo", "__debug_abbrev")\ +X(PubNames, V4, ".debug_pubnames", ".debug_pubnames.dwo", "__debug_pubnames")\ +X(PubTypes, V4, ".debug_pubtypes", ".debug_pubtypes.dwo", "__debug_pubtypes")\ +X(Names, V5, ".debug_names", ".debug_names.dwo", "__debug_names")\ +X(Aranges, V4|V5, ".debug_aranges", ".debug_aranges.dwo", "__debug_aranges")\ +X(Line, V4|V5, ".debug_line", ".debug_line.dwo", "__debug_line")\ +X(MacInfo, V4, ".debug_macinfo", ".debug_macinfo.dwo", "__debug_macinfo")\ +X(Macro, V5, ".debug_macro", ".debug_macro.dwo", "__debug_macro")\ +X(Frame, V4|V5, ".debug_frame", ".debug_frame.dwo", "__debug_frame")\ +X(Ranges, V4, ".debug_ranges", ".debug_ranges.dwo", "__debug_ranges")\ +X(StrOffsets, V5, ".debug_str_offsets", ".debug_str_offsets.dwo", "__debug_str_offsets")\ +X(Addr, V5, ".debug_addr", ".debug_addr.dwo", "__debug_addr")\ +X(RngLists, V5, ".debug_rnglists", ".debug_rnglists.dwo", "__debug_rnglists")\ +X(LocLists, V5, ".debug_loclists", ".debug_loclists.dwo", "__debug_loclists") + + +typedef enum DWARF_SectionCode{ +#define X(c,vf,n0,n1,n2) DWARF_SectionCode_##c, + DWARF_SectionNameXList(X,0,0) +#undef X + DWARF_SectionCode_COUNT +} DWARF_SectionCode; + +typedef struct DWARF_SectionNameRow{ + String8 name[DWARF_SECTION_NAME_VARIANT_COUNT]; +} DWARF_SectionNameRow; + +read_only global DWARF_SectionNameRow dwarf_section_name_table[] = { +#define X(c,vf,n0,n1,n2) \ +{ { str8_lit_comp(n0), str8_lit_comp(n1), str8_lit_comp(n2) } }, + DWARF_SectionNameXList(X,0,0) +#undef X +}; + + +#pragma pack(pop) + + +//////////////////////////////// +//~ Dwarf Parser Types + +typedef struct DWARF_Parsed{ + ELF_Parsed *elf; + U32 debug_section_idx[DWARF_SectionCode_COUNT]; + String8 debug_section_name[DWARF_SectionCode_COUNT]; + String8 debug_data[DWARF_SectionCode_COUNT]; +} DWARF_Parsed; + + +// form decoding + +typedef struct DWARF_FormDecodeRules{ + union{ + // form decode fields + struct{ + U8 size; + B8 uleb128; + B8 sleb128; + B8 in_abbrev; + B8 auto_1; + B8 block; + B8 null_terminated; + }; + + // for alignment and padding to 8 + U64 x; + }; +} DWARF_FormDecodeRules; + +typedef struct DWARF_FormDecoded{ + U64 val; + U8 *dataptr; + B32 error; +} DWARF_FormDecoded; + + +// index section: .debug_cu_index .debug_tu_index +// (DWARF5.pdf + 7.3.5) + +// ** not implemented yet ** + +typedef struct DWARF_IndexParsed{ + U32 dummy; +} DWARF_IndexParsed; + + +// supplementary section: .debug_sup +// (DWARF5.pdf + 7.3.6) + +// ** not implemented yet ** + +typedef struct DWARF_SupParsed{ + U32 dummy; +} DWARF_SupParsed; + + +// info section: .debug_info +// (DWARF4.pdb + 7.5) (DWARF5.pdf + 7.5) + +typedef struct DWARF_InfoAttribVal{ + U64 val; + U8 *dataptr; +} DWARF_InfoAttribVal; + +typedef struct DWARF_InfoEntry{ + struct DWARF_InfoEntry *next_sibling; + struct DWARF_InfoEntry *first_child; + struct DWARF_InfoEntry *last_child; + U64 child_count; + struct DWARF_InfoEntry *parent; + + U64 info_offset; + struct DWARF_AbbrevDecl *abbrev_decl; + DWARF_InfoAttribVal *attrib_vals; +} DWARF_InfoEntry; + +#if 0 +typedef struct DWARF_InfoUnit{ + struct DWARF_InfoUnit *next; + + // header + U32 version; + U32 offset_size; + U32 address_size; + + // root attributes + DWARF_Language language; + U64 line_info_offset; + U64 vbase; + U64 str_offsets_base; + U64 addr_base; + U64 rnglists_base; + U64 loclists_base; + + // info entries + DWARF_InfoEntry *entry_root; + U64 entry_count; +} DWARF_InfoUnit; +#endif + +#if 0 +typedef struct DWARF_InfoParams{ + U64 unit_idx_min; + U64 unit_idx_max; +} DWARF_InfoParams; +#endif + +typedef struct DWARF_InfoUnit{ + struct DWARF_InfoUnit *next; + + U64 hdr_off; + U64 base_off; + U64 opl_off; + + U8 offset_size; + U8 version; + U8 unit_type; // (DWARF_UnitType) + U8 address_size; + U64 abbrev_off; + + union{ + // unit_type: skeleton, split_compile + U64 dwo_id; + // unit_type: type, split_type + struct{ + U64 type_signature; + U64 type_offset; + }; + }; +} DWARF_InfoUnit; + +typedef struct DWARF_InfoParsed{ + DWARF_InfoUnit *unit_first; + DWARF_InfoUnit *unit_last; + U64 unit_count; +} DWARF_InfoParsed; + + +// abbreviations section: .debug_abbrev +// (DWARF4.pdf + 7.5.3) (DWARF5.pdf + 7.5.3) + +typedef struct DWARF_AbbrevAttribSpec{ + DWARF_AttributeName name; + DWARF_AttributeForm form; +} DWARF_AbbrevAttribSpec; + +typedef struct DWARF_AbbrevDecl{ + struct DWARF_AbbrevDecl *next; + U32 abbrev_code; + DWARF_Tag tag; + B8 has_children; + U8 __filler__; + U16 attrib_count; + DWARF_AbbrevAttribSpec *attrib_specs; + S64 *implicit_const; +} DWARF_AbbrevDecl; + +typedef struct DWARF_AbbrevUnit{ + struct DWARF_AbbrevUnit *next; + U64 offset; + DWARF_AbbrevDecl *first; + DWARF_AbbrevDecl *last; + U64 count; +} DWARF_AbbrevUnit; + +#if 0 +typedef struct DWARF_AbbrevParams{ + U64 unit_idx_min; + U64 unit_idx_max; +} DWARF_AbbrevParams; +#endif + +typedef struct DWARF_AbbrevParsed{ + DWARF_AbbrevUnit *unit_first; + DWARF_AbbrevUnit *unit_last; + U64 unit_count; + B32 decoding_error; +} DWARF_AbbrevParsed; + + +// name lookup tables (V4): .debug_pubnames .debug_pubtypes +// (DWARF4.pdf + 7.19) + +typedef struct DWARF_PubNamesUnit{ + struct DWARF_PubNamesUnit *next; + + U64 hdr_off; + U64 base_off; + U64 opl_off; + + U8 offset_size; + U8 version; + U64 info_off; + U64 info_length; +} DWARF_PubNamesUnit; + +typedef struct DWARF_PubNamesParsed{ + DWARF_PubNamesUnit *unit_first; + DWARF_PubNamesUnit *unit_last; + U64 unit_count; +} DWARF_PubNamesParsed; + + +// name lookup tables (V5): .debug_names +// (DWARF5.pdf + 6.1.1.4.1 & 7.19) + +typedef struct DWARF_NamesUnit{ + struct DWARF_NamesUnit *next; + + U64 hdr_off; + U64 base_off; + U64 opl_off; + + U8 version; + U32 comp_unit_count; + U32 local_type_unit_count; + U32 foreign_type_unit_count; + U32 bucket_count; + U32 name_count; + U32 abbrev_table_size; + String8 augmentation_string; + +} DWARF_NamesUnit; + +typedef struct DWARF_NamesParsed{ + DWARF_NamesUnit *unit_first; + DWARF_NamesUnit *unit_last; + U64 unit_count; +} DWARF_NamesParsed; + + +// address range table: .debug_aranges +// (DWARF4.pdf + 7.20) (DWARF5.pdf + 7.21) + +typedef struct DWARF_ArangesUnit{ + struct DWARF_ArangesUnit *next; + + U64 hdr_off; + U64 base_off; + U64 opl_off; + + U8 version; + U8 address_size; + U8 segment_selector_size; + U8 offset_size; + U64 info_off; +} DWARF_ArangesUnit; + +typedef struct DWARF_ArangesParsed{ + DWARF_ArangesUnit *unit_first; + DWARF_ArangesUnit *unit_last; + U64 unit_count; +} DWARF_ArangesParsed; + + +// line number information: .debug_line +// (DWARF4.pdf + 6.2.4 & 7.21) (DWARF5.pdf + 6.2.4 & 7.22) + +typedef struct DWARF_V4LineFileNamesEntry{ + struct DWARF_V4LineFileNamesEntry *next; + String8 file_name; + U64 include_directory_idx; + U64 last_modified_time; + U64 file_size; +} DWARF_V4LineFileNamesEntry; + +typedef struct DWARF_V4LineFileNamesList{ + DWARF_V4LineFileNamesEntry *first; + DWARF_V4LineFileNamesEntry *last; + U64 count; +} DWARF_V4LineFileNamesList; + +typedef struct DWARF_V5LinePathEntryFormat{ + U32 content_type; /* DWARF_LineEntryFormat */ + U32 form; /* DWARF_AttributeForm */ +} DWARF_V5LinePathEntryFormat; + +typedef struct DWARF_V5Directory{ + String8 path_str; + U64 path_off; + U64 path_sec_form; + U64 directory_index; + U64 timestamp; + U64 size; + U8 md5_checksum[16]; +} DWARF_V5Directory; + +typedef struct DWARF_LineUnit{ + struct DWARF_LineUnit *next; + + U64 hdr_off; + U64 base_off; + U64 opl_off; + + U8 version; + +} DWARF_LineUnit; + +typedef struct DWARF_LineParsed{ + DWARF_LineUnit *unit_first; + DWARF_LineUnit *unit_last; + U64 unit_count; +} DWARF_LineParsed; + + +// macro information (V4): .debug_macinfo +// (DWARF4.pdf + 7.22) + +// ** not implemented yet ** + +typedef struct DWARF_MacInfoParsed{ + U32 dummy; +} DWARF_MacInfoParsed; + + +// macro information (V5): .debug_macro +// (DWARF5.pdf + 7.23) + +// ** not implemented yet ** + +typedef struct DWARF_MacroParsed{ + U32 dummy; +} DWARF_MacroParsed; + + +// call frame information: .debug_frame +// (DWARF4.pdf + 7.23) (DWARF5.pdf + 7.24) + +// ** not implemented yet ** + +typedef struct DWARF_FrameParsed{ + U32 dummy; +} DWARF_FrameParsed; + + +// range lists (V4): .debug_ranges +// (DWARF4.pdf + 7.24) + +// ** not implemented yet ** + +typedef struct DWARF_RangesParsed{ + U32 dummy; +} DWARF_RangesParsed; + + +// string offsets table: .debug_str_offsets +// (DWARF5.pdf + 7.26) + +// ** not implemented yet ** + +typedef struct DWARF_StrOffsetsParsed{ + U32 dummy; +} DWARF_StrOffsetsParsed; + + +// address table: .debug_addr +// (DWARF5.pdf + 7.27) + +typedef struct DWARF_AddrUnit{ + struct DWARF_AddrUnit *next; + + U64 hdr_off; + U64 base_off; + U64 opl_off; + + U8 offset_size; + U8 dwarf_version; + U8 address_size; + U8 segment_selector_size; +} DWARF_AddrUnit; + +typedef struct DWARF_AddrParsed{ + DWARF_AddrUnit *unit_first; + DWARF_AddrUnit *unit_last; + U64 unit_count; +} DWARF_AddrParsed; + + +// range lists (V5): .debug_rnglists +// (DWARF5.pdf + 7.28 & 7.25) + +// ** not implemented yet ** + +typedef struct DWARF_RngListsParsed{ + U32 dummy; +} DWARF_RngListsParsed; + + +// location lists: .debug_loclists +// (DWARF5.pdf + 7.29) + +// ** not implemented yet ** + +typedef struct DWARF_LocListsParsed{ + U32 dummy; +} DWARF_LocListsParsed; + + +//////////////////////////////// +//~ Dwarf Decode Helpers + +#define DWARF_LEB128_ADV(p,o,s) do{ for(;; (p)+=1){\ +if ((p) == (o)) { (s)=0; break; } \ +if (((*(p))&0x80) == 0) { (p)+=1; break; } \ +} }while(0) + +#define DWARF_LEB128_ADV_NOCAP(p) for((p)+=1; ((*(p-1))&0x80) != 0; (p)+=1) + +static U64 dwarf_leb128_decode_U64(U8 *ptr, U8 *opl); +static S64 dwarf_leb128_decode_S64(U8 *ptr, U8 *opl); +static U32 dwarf_leb128_decode_U32(U8 *ptr, U8 *opl); + +#define dwarf_leb128_decode(T,ptr,opl) dwarf_leb128_decode_##T(ptr,opl) + +#define DWARF_LEB128_DECODE_ADV(T,x,p,o) do{ \ +U8 *first__ = (p); B32 success__; \ +DWARF_LEB128_ADV(p,o,success__); \ +if (success__) \ +(x) = dwarf_leb128_decode(T,first__, (p)); \ +}while(0) + + +//////////////////////////////// +//~ allen: ELF/DW Unwind Types +// +// TODO(rjf): OLD TYPES FROM UNWINDER CODE. bucketing this here, and deferring dwarf-based +// unwinding info to future DWARF/linux work. +// +#if 0 + +// * applies to (any X: unwind(ELF/DW, X)) + +// EH: Exception Frames +typedef U8 UNW_DW_EhPtrEnc; +enum{ + UNW_DW_EhPtrEnc_TYPE_MASK = 0x0F, + UNW_DW_EhPtrEnc_PTR = 0x00, // Pointer sized unsigned value + UNW_DW_EhPtrEnc_ULEB128 = 0x01, // Unsigned LE base-128 value + UNW_DW_EhPtrEnc_UDATA2 = 0x02, // Unsigned 16-bit value + UNW_DW_EhPtrEnc_UDATA4 = 0x03, // Unsigned 32-bit value + UNW_DW_EhPtrEnc_UDATA8 = 0x04, // Unsigned 64-bit value + UNW_DW_EhPtrEnc_SIGNED = 0x08, // Signed pointer + UNW_DW_EhPtrEnc_SLEB128 = 0x09, // Signed LE base-128 value + UNW_DW_EhPtrEnc_SDATA2 = 0x0A, // Signed 16-bit value + UNW_DW_EhPtrEnc_SDATA4 = 0x0B, // Signed 32-bit value + UNW_DW_EhPtrEnc_SDATA8 = 0x0C, // Signed 64-bit value +}; +enum{ + UNW_DW_EhPtrEnc_MODIF_MASK = 0x70, + UNW_DW_EhPtrEnc_PCREL = 0x10, // Value is relative to the current program counter. + UNW_DW_EhPtrEnc_TEXTREL = 0x20, // Value is relative to the .text section. + UNW_DW_EhPtrEnc_DATAREL = 0x30, // Value is relative to the .got or .eh_frame_hdr section. + UNW_DW_EhPtrEnc_FUNCREL = 0x40, // Value is relative to the function. + UNW_DW_EhPtrEnc_ALIGNED = 0x50, // Value is aligned to an address unit sized boundary. +}; +enum{ + UNW_DW_EhPtrEnc_INDIRECT = 0x80, // This flag indicates that value is stored in virtual memory. + UNW_DW_EhPtrEnc_OMIT = 0xFF, +}; + +typedef struct UNW_DW_EhPtrCtx{ + U64 raw_base_vaddr; // address where pointer is being read + U64 text_vaddr; // base address of section with instructions (used for encoding pointer on SH and IA64) + U64 data_vaddr; // base address of data section (used for encoding pointer on x86-64) + U64 func_vaddr; // base address of function where IP is located +} UNW_DW_EhPtrCtx; + +// CIE: Common Information Entry +typedef struct UNW_DW_CIEUnpacked{ + U8 version; + UNW_DW_EhPtrEnc lsda_encoding; + UNW_DW_EhPtrEnc addr_encoding; + + B8 has_augmentation_size; + U64 augmentation_size; + String8 augmentation; + + U64 code_align_factor; + S64 data_align_factor; + U64 ret_addr_reg; + + U64 handler_ip; + + U64 cfi_range_min; + U64 cfi_range_max; +} UNW_DW_CIEUnpacked; + +typedef struct UNW_DW_CIEUnpackedNode{ + struct UNW_DW_CIEUnpackedNode *next; + UNW_DW_CIEUnpacked cie; + U64 offset; +} UNW_DW_CIEUnpackedNode; + +// FDE: Frame Description Entry +typedef struct UNW_DW_FDEUnpacked{ + U64 ip_voff_min; + U64 ip_voff_max; + U64 lsda_ip; + + U64 cfi_range_min; + U64 cfi_range_max; +} UNW_DW_FDEUnpacked; + +// CFI: Call Frame Information +typedef struct UNW_DW_CFIRecords{ + B32 valid; + UNW_DW_CIEUnpacked cie; + UNW_DW_FDEUnpacked fde; +} UNW_DW_CFIRecords; + +typedef enum UNW_DW_CFICFARule{ + UNW_DW_CFICFARule_REGOFF, + UNW_DW_CFICFARule_EXPR, +} UNW_DW_CFICFARule; + +typedef struct UNW_DW_CFICFACell{ + UNW_DW_CFICFARule rule; + union{ + struct{ + U64 reg_idx; + S64 offset; + }; + U64 expr_min; + U64 expr_max; + }; +} UNW_DW_CFICFACell; + +typedef enum UNW_DW_CFIRegisterRule{ + UNW_DW_CFIRegisterRule_SAME_VALUE, + UNW_DW_CFIRegisterRule_UNDEFINED, + UNW_DW_CFIRegisterRule_OFFSET, + UNW_DW_CFIRegisterRule_VAL_OFFSET, + UNW_DW_CFIRegisterRule_REGISTER, + UNW_DW_CFIRegisterRule_EXPRESSION, + UNW_DW_CFIRegisterRule_VAL_EXPRESSION, +} UNW_DW_CFIRegisterRule; + +typedef struct UNW_DW_CFICell{ + UNW_DW_CFIRegisterRule rule; + union{ + S64 n; + struct{ + U64 expr_min; + U64 expr_max; + }; + }; +} UNW_DW_CFICell; + +typedef struct UNW_DW_CFIRow{ + struct UNW_DW_CFIRow *next; + UNW_DW_CFICell *cells; + UNW_DW_CFICFACell cfa_cell; +} UNW_DW_CFIRow; + +typedef struct UNW_DW_CFIMachine{ + U64 cells_per_row; + UNW_DW_CIEUnpacked *cie; + UNW_DW_EhPtrCtx *ptr_ctx; + UNW_DW_CFIRow *initial_row; + U64 fde_ip; +} UNW_DW_CFIMachine; + +typedef U8 UNW_DW_CFADecode; +enum{ + UNW_DW_CFADecode_NOP = 0x0, + // 1,2,4,8 reserved for literal byte sizes + UNW_DW_CFADecode_ADDRESS = 0x9, + UNW_DW_CFADecode_ULEB128 = 0xA, + UNW_DW_CFADecode_SLEB128 = 0xB, +}; + +typedef U16 UNW_DW_CFAControlBits; +enum{ + UNW_DW_CFAControlBits_DEC1_MASK = 0x00F, + UNW_DW_CFAControlBits_DEC2_MASK = 0x0F0, + UNW_DW_CFAControlBits_IS_REG_0 = 0x100, + UNW_DW_CFAControlBits_IS_REG_1 = 0x200, + UNW_DW_CFAControlBits_IS_REG_2 = 0x400, + UNW_DW_CFAControlBits_NEW_ROW = 0x800, +}; +#endif + +//////////////////////////////// +//~ Dwarf Parser Functions + +static DWARF_Parsed* dwarf_parsed_from_elf(Arena *arena, ELF_Parsed *elf); + +static DWARF_IndexParsed* dwarf_index_from_data(Arena *arena, String8 data); +static DWARF_SupParsed* dwarf_sup_from_data(Arena *arena, String8 data); +static DWARF_InfoParsed* dwarf_info_from_data(Arena *arena, String8 data); +static DWARF_PubNamesParsed* dwarf_pubnames_from_data(Arena *arena, String8 data); +static DWARF_NamesParsed* dwarf_names_from_data(Arena *arena, String8 data); +static DWARF_ArangesParsed* dwarf_aranges_from_data(Arena *arena, String8 data); +static DWARF_LineParsed* dwarf_line_from_data(Arena *arena, String8 data); +static DWARF_MacInfoParsed* dwarf_mac_info_from_data(Arena *arena, String8 data); +static DWARF_MacroParsed* dwarf_macro_from_data(Arena *arena, String8 data); +static DWARF_FrameParsed* dwarf_frame_from_data(Arena *arena, String8 data); +static DWARF_RangesParsed* dwarf_ranges_from_data(Arena *arena, String8 data); +static DWARF_StrOffsetsParsed* dwarf_str_offsets_from_data(Arena *arena, String8 data); +static DWARF_AddrParsed* dwarf_addr_from_data(Arena *arena, String8 data); +static DWARF_RngListsParsed* dwarf_rng_lists_from_data(Arena *arena, String8 data); +static DWARF_LocListsParsed* dwarf_loc_lists_from_data(Arena *arena, String8 data); + + +// parse helpers + +// (DWARF4.pdf + 7.2.2) (DWARF5.pdf + 7.2.2) +static void dwarf__initial_length(String8 data, + U8 **ptr_inout, U8 **unit_opl_out, B32 *is_64bit_out); + +static void +dwarf__line_v5_directories(U64 address_size, U64 offset_size, + DWARF_V5LinePathEntryFormat *format, U64 format_count, + DWARF_V5Directory *directories_out, U64 dir_count, + U8 **ptr_io, U8 *opl); + +// debug sections + +static String8 dwarf_name_from_debug_section(DWARF_Parsed *dwarf, DWARF_SectionCode sec_code); + +// abbrev functions + +static DWARF_AbbrevUnit* dwarf_abbrev_unit_from_offset(DWARF_AbbrevParsed *abbrev, U64 off); +static DWARF_AbbrevDecl* dwarf_abbrev_decl_from_code(DWARF_AbbrevUnit *unit, U32 code); + +// attribute decoding functions + +static DWARF_AttributeClassFlags dwarf_attribute_class_from_form(DWARF_AttributeForm form); +static DWARF_AttributeClassFlags dwarf_attribute_class_from_name(DWARF_AttributeName name); + +// form decoding functions + +static DWARF_FormDecodeRules +dwarf_form_decode_rule(DWARF_AttributeForm form, U64 address_size, U64 offset_size); + +static DWARF_FormDecoded +dwarf_form_decode(DWARF_FormDecodeRules *rules, U8 **ptr_io, U8 *opl, + DWARF_AbbrevDecl *abbrev_decl, U32 attrib_i); + +// string functions + +static String8 dwarf_string_from_unit_type(DWARF_UnitType type); +static String8 dwarf_string_from_tag(DWARF_Tag tag); +static String8 dwarf_string_from_attribute_name(DWARF_AttributeName name); +static String8 dwarf_string_from_attribute_form(DWARF_AttributeForm form); +static String8 dwarf_string_from_line_std_op(DWARF_LineStdOp op); +static String8 dwarf_string_from_line_ext_op(DWARF_LineExtOp op); +static String8 dwarf_string_from_line_entry_format(DWARF_LineEntryFormat format); +static String8 dwarf_string_from_section_code(DWARF_SectionCode sec_code); + +#endif //RDI_DWARF_H + diff --git a/src/rdi_from_dwarf/rdi_dwarf_stringize.c b/src/rdi_from_dwarf/rdi_dwarf_stringize.c index 9b0bfdce..67865813 100644 --- a/src/rdi_from_dwarf/rdi_dwarf_stringize.c +++ b/src/rdi_from_dwarf/rdi_dwarf_stringize.c @@ -1,102 +1,102 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ DWARF Stringize Functions - -static char dwarf_spaces[] = " "; - -static void -dwarf_stringize_info(Arena *arena, String8List *out, DWARF_InfoUnit *unit, U32 indent){ - String8 unit_type_string = dwarf_string_from_unit_type((DWARF_UnitType)unit->unit_type); - - str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off); - str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off); - str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off); - str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces, - unit->offset_size); - str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version); - str8_list_pushf(arena, out, "%.*sunit_type=%.*s\n", indent, dwarf_spaces, - str8_varg(unit_type_string)); - str8_list_pushf(arena, out, "%.*saddress_size=%u\n", indent, dwarf_spaces, - unit->address_size); - str8_list_pushf(arena, out, "%.*sabbrev_off=0x%llx\n", indent, dwarf_spaces, - unit->abbrev_off); - - switch (unit->unit_type){ - case DWARF_UnitType_skeleton: case DWARF_UnitType_split_compile: - { - str8_list_pushf(arena, out, "%.*sdwo_id=%llu\n", indent, dwarf_spaces, unit->dwo_id); - }break; - - case DWARF_UnitType_type: case DWARF_UnitType_split_type: - { - str8_list_pushf(arena, out, "%.*stype_signature=%llu\n", indent, dwarf_spaces, - unit->type_signature); - str8_list_pushf(arena, out, "%.*stype_offset=%llu\n", indent, dwarf_spaces, - unit->type_offset); - }break; - } -} - -static void -dwarf_stringize_pubnames(Arena *arena, String8List *out, DWARF_PubNamesUnit *unit, - U32 indent){ - str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off); - str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off); - str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off); - str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces, unit->offset_size); - str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version); - str8_list_pushf(arena, out, "%.*sinfo_off=0x%llx\n", indent, dwarf_spaces, unit->info_off); - str8_list_pushf(arena, out, "%.*sinfo_length=0x%llx\n", indent, dwarf_spaces, - unit->info_length); -} - -static void -dwarf_stringize_names(Arena *arena, String8List *out, DWARF_NamesUnit *unit, U32 indent){ - str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off); - str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off); - str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off); - str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version); - str8_list_pushf(arena, out, "%.*scomp_unit_count=%u\n", indent, dwarf_spaces, - unit->comp_unit_count); - str8_list_pushf(arena, out, "%.*slocal_type_unit_count=%u\n", indent, dwarf_spaces, - unit->local_type_unit_count); - str8_list_pushf(arena, out, "%.*sforeign_type_unit_count=%u\n", indent, dwarf_spaces, - unit->foreign_type_unit_count); - str8_list_pushf(arena, out, "%.*sbucket_count=%u\n", indent, dwarf_spaces, - unit->bucket_count); - str8_list_pushf(arena, out, "%.*sname_count=%u\n", indent, dwarf_spaces, unit->name_count); - str8_list_pushf(arena, out, "%.*sabbrev_table_size=%u\n", indent, dwarf_spaces, - unit->abbrev_table_size); - str8_list_pushf(arena, out, "%.*saugmentation_string=%.*s\n", indent, dwarf_spaces, - str8_varg(unit->augmentation_string)); -} - -static void -dwarf_stringize_aranges(Arena *arena, String8List *out, DWARF_ArangesUnit *unit, U32 indent){ - str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off); - str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off); - str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off); - str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version); - str8_list_pushf(arena, out, "%.*saddress_size=%u\n", indent, dwarf_spaces, - unit->address_size); - str8_list_pushf(arena, out, "%.*ssegment_selector_size=%u\n", indent, dwarf_spaces, - unit->segment_selector_size); - str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces, unit->offset_size); - str8_list_pushf(arena, out, "%.*sinfo_off=0x%llx\n", indent, dwarf_spaces, unit->info_off); -} - -static void -dwarf_stringize_addr(Arena *arena, String8List *out, DWARF_AddrUnit *unit, U32 indent){ - str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off); - str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off); - str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off); - str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces, - unit->offset_size); - str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->dwarf_version); - str8_list_pushf(arena, out, "%.*saddress_size=%u\n", indent, dwarf_spaces, - unit->address_size); - str8_list_pushf(arena, out, "%.*ssegment_selector_size=%u\n", indent, dwarf_spaces, - unit->segment_selector_size); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ DWARF Stringize Functions + +static char dwarf_spaces[] = " "; + +static void +dwarf_stringize_info(Arena *arena, String8List *out, DWARF_InfoUnit *unit, U32 indent){ + String8 unit_type_string = dwarf_string_from_unit_type((DWARF_UnitType)unit->unit_type); + + str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off); + str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off); + str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off); + str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces, + unit->offset_size); + str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version); + str8_list_pushf(arena, out, "%.*sunit_type=%.*s\n", indent, dwarf_spaces, + str8_varg(unit_type_string)); + str8_list_pushf(arena, out, "%.*saddress_size=%u\n", indent, dwarf_spaces, + unit->address_size); + str8_list_pushf(arena, out, "%.*sabbrev_off=0x%llx\n", indent, dwarf_spaces, + unit->abbrev_off); + + switch (unit->unit_type){ + case DWARF_UnitType_skeleton: case DWARF_UnitType_split_compile: + { + str8_list_pushf(arena, out, "%.*sdwo_id=%llu\n", indent, dwarf_spaces, unit->dwo_id); + }break; + + case DWARF_UnitType_type: case DWARF_UnitType_split_type: + { + str8_list_pushf(arena, out, "%.*stype_signature=%llu\n", indent, dwarf_spaces, + unit->type_signature); + str8_list_pushf(arena, out, "%.*stype_offset=%llu\n", indent, dwarf_spaces, + unit->type_offset); + }break; + } +} + +static void +dwarf_stringize_pubnames(Arena *arena, String8List *out, DWARF_PubNamesUnit *unit, + U32 indent){ + str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off); + str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off); + str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off); + str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces, unit->offset_size); + str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version); + str8_list_pushf(arena, out, "%.*sinfo_off=0x%llx\n", indent, dwarf_spaces, unit->info_off); + str8_list_pushf(arena, out, "%.*sinfo_length=0x%llx\n", indent, dwarf_spaces, + unit->info_length); +} + +static void +dwarf_stringize_names(Arena *arena, String8List *out, DWARF_NamesUnit *unit, U32 indent){ + str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off); + str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off); + str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off); + str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version); + str8_list_pushf(arena, out, "%.*scomp_unit_count=%u\n", indent, dwarf_spaces, + unit->comp_unit_count); + str8_list_pushf(arena, out, "%.*slocal_type_unit_count=%u\n", indent, dwarf_spaces, + unit->local_type_unit_count); + str8_list_pushf(arena, out, "%.*sforeign_type_unit_count=%u\n", indent, dwarf_spaces, + unit->foreign_type_unit_count); + str8_list_pushf(arena, out, "%.*sbucket_count=%u\n", indent, dwarf_spaces, + unit->bucket_count); + str8_list_pushf(arena, out, "%.*sname_count=%u\n", indent, dwarf_spaces, unit->name_count); + str8_list_pushf(arena, out, "%.*sabbrev_table_size=%u\n", indent, dwarf_spaces, + unit->abbrev_table_size); + str8_list_pushf(arena, out, "%.*saugmentation_string=%.*s\n", indent, dwarf_spaces, + str8_varg(unit->augmentation_string)); +} + +static void +dwarf_stringize_aranges(Arena *arena, String8List *out, DWARF_ArangesUnit *unit, U32 indent){ + str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off); + str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off); + str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off); + str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->version); + str8_list_pushf(arena, out, "%.*saddress_size=%u\n", indent, dwarf_spaces, + unit->address_size); + str8_list_pushf(arena, out, "%.*ssegment_selector_size=%u\n", indent, dwarf_spaces, + unit->segment_selector_size); + str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces, unit->offset_size); + str8_list_pushf(arena, out, "%.*sinfo_off=0x%llx\n", indent, dwarf_spaces, unit->info_off); +} + +static void +dwarf_stringize_addr(Arena *arena, String8List *out, DWARF_AddrUnit *unit, U32 indent){ + str8_list_pushf(arena, out, "%.*shdr_off=0x%llx\n", indent, dwarf_spaces, unit->hdr_off); + str8_list_pushf(arena, out, "%.*sbase_off=0x%llx\n", indent, dwarf_spaces, unit->base_off); + str8_list_pushf(arena, out, "%.*sopl_off=0x%llx\n", indent, dwarf_spaces, unit->opl_off); + str8_list_pushf(arena, out, "%.*soffset_size=%u\n", indent, dwarf_spaces, + unit->offset_size); + str8_list_pushf(arena, out, "%.*sversion=%u\n", indent, dwarf_spaces, unit->dwarf_version); + str8_list_pushf(arena, out, "%.*saddress_size=%u\n", indent, dwarf_spaces, + unit->address_size); + str8_list_pushf(arena, out, "%.*ssegment_selector_size=%u\n", indent, dwarf_spaces, + unit->segment_selector_size); +} diff --git a/src/rdi_from_dwarf/rdi_dwarf_stringize.h b/src/rdi_from_dwarf/rdi_dwarf_stringize.h index 0fa77da1..30a13254 100644 --- a/src/rdi_from_dwarf/rdi_dwarf_stringize.h +++ b/src/rdi_from_dwarf/rdi_dwarf_stringize.h @@ -1,28 +1,28 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RDI_DWARF_STRINGIZE_H -#define RDI_DWARF_STRINGIZE_H - -//////////////////////////////// -//~ DWARF Stringize Functions - -static void -dwarf_stringize_info(Arena *arena, String8List *out, DWARF_InfoUnit *unit, U32 indent); - -static void -dwarf_stringize_pubnames(Arena *arena, String8List *out, DWARF_PubNamesUnit *unit, - U32 indent); - -static void -dwarf_stringize_names(Arena *arena, String8List *out, DWARF_NamesUnit *unit, U32 indent); - -static void -dwarf_stringize_aranges(Arena *arena, String8List *out, DWARF_ArangesUnit *unit, U32 indent); - -static void -dwarf_stringize_addr(Arena *arena, String8List *out, DWARF_AddrUnit *unit, U32 indent); - - - -#endif //RDI_DWARF_STRINGIZE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RDI_DWARF_STRINGIZE_H +#define RDI_DWARF_STRINGIZE_H + +//////////////////////////////// +//~ DWARF Stringize Functions + +static void +dwarf_stringize_info(Arena *arena, String8List *out, DWARF_InfoUnit *unit, U32 indent); + +static void +dwarf_stringize_pubnames(Arena *arena, String8List *out, DWARF_PubNamesUnit *unit, + U32 indent); + +static void +dwarf_stringize_names(Arena *arena, String8List *out, DWARF_NamesUnit *unit, U32 indent); + +static void +dwarf_stringize_aranges(Arena *arena, String8List *out, DWARF_ArangesUnit *unit, U32 indent); + +static void +dwarf_stringize_addr(Arena *arena, String8List *out, DWARF_AddrUnit *unit, U32 indent); + + + +#endif //RDI_DWARF_STRINGIZE_H diff --git a/src/rdi_from_dwarf/rdi_elf.c b/src/rdi_from_dwarf/rdi_elf.c index 50e0f9be..a99fcccf 100644 --- a/src/rdi_from_dwarf/rdi_elf.c +++ b/src/rdi_from_dwarf/rdi_elf.c @@ -1,555 +1,555 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ ELF Parser Functions - -static ELF_Parsed* -elf_parsed_from_data(Arena *arena, String8 elf_data){ - //- test magic number - B32 has_good_magic_number = 0; - if (elf_data.size >= sizeof(ELF_NIDENT) && - MemoryMatch(elf_data.str, elf_magic, sizeof(elf_magic))){ - has_good_magic_number = 1; - } - - //- determine elf class - U8 elf_class = ELF_Class_NONE; - if (has_good_magic_number){ - elf_class = elf_data.str[ELF_Identification_CLASS]; - } - - //- extract header information - B32 decoded_header = 0; - - U8 e_data_encoding = ELF_DataEncoding_NONE; - U16 e_machine = ELF_Machine_NONE; - - U64 e_entry = 0; - - U64 e_shoff = 0; - U16 e_shentsize = 0; - U16 e_shnum = 0; - - U64 e_phoff = 0; - U16 e_phentsize = 0; - U16 e_phnum = 0; - - U16 e_shstrndx = 0; - - switch (elf_class){ - case ELF_Class_NONE: /* not good */ break; - - case ELF_Class_32: - { - if (elf_data.size >= sizeof(ELF_Ehdr32)){ - ELF_Ehdr32 *hdr = (ELF_Ehdr32*)elf_data.str; - - decoded_header = 1; - e_data_encoding = hdr->e_ident[ELF_Identification_DATA]; - e_machine = hdr->e_machine; - e_entry = hdr->e_entry; - e_phoff = hdr->e_phoff; - e_shoff = hdr->e_shoff; - e_phentsize = hdr->e_phentsize; - e_phnum = hdr->e_phnum; - e_shentsize = hdr->e_shentsize; - e_shnum = hdr->e_shnum; - e_shstrndx = hdr->e_shstrndx; - } - }break; - - case ELF_Class_64: - { - if (elf_data.size >= sizeof(ELF_Ehdr64)){ - ELF_Ehdr64 *hdr = (ELF_Ehdr64*)elf_data.str; - - decoded_header = 1; - e_data_encoding = hdr->e_ident[ELF_Identification_DATA]; - e_machine = hdr->e_machine; - e_entry = hdr->e_entry; - e_phoff = hdr->e_phoff; - e_shoff = hdr->e_shoff; - e_phentsize = hdr->e_phentsize; - e_phnum = hdr->e_phnum; - e_shentsize = hdr->e_shentsize; - e_shnum = hdr->e_shnum; - e_shstrndx = hdr->e_shstrndx; - } - }break; - } - - //- validate & translate header values - B32 header_is_good = 0; - Architecture arch = Architecture_Null; - if (decoded_header){ - header_is_good = 1; - - // only supporting little-endian versions right now - if (header_is_good){ - if (e_data_encoding != ELF_DataEncoding_2LSB){ - header_is_good = 0; - } - } - - // make sure this is a supported machine type - if (header_is_good){ - switch (e_machine){ - default: header_is_good = 0; - case ELF_Machine_386: arch = Architecture_x86; break; - case ELF_Machine_X86_64: arch = Architecture_x64; break; - } - } - - // make sure section & segment sizes are correct - if (header_is_good){ - switch (elf_class){ - case ELF_Class_32: - { - if (e_shentsize != sizeof(ELF_Shdr32) || - e_phentsize != sizeof(ELF_Phdr32)){ - header_is_good = 0; - } - }break; - case ELF_Class_64: - { - if (e_shentsize != sizeof(ELF_Shdr64) || - e_phentsize != sizeof(ELF_Phdr64)){ - header_is_good = 0; - } - }break; - } - } - } - - //- extract extra information from the special first section - U64 section_count_raw = e_shnum; - U32 section_header_string_table_index = e_shstrndx; - if (header_is_good){ - if (e_shoff <= elf_data.size && e_shentsize <= elf_data.size && - e_shoff + e_shentsize <= elf_data.size){ - U64 size = 0; - U32 link = 0; - switch (elf_class){ - case ELF_Class_32: - { - ELF_Shdr32 *shdr = (ELF_Shdr32*)(elf_data.str + e_shoff); - size = shdr->sh_size; - link = shdr->sh_link; - }break; - case ELF_Class_64: - { - ELF_Shdr64 *shdr = (ELF_Shdr64*)(elf_data.str + e_shoff); - size = shdr->sh_size; - link = shdr->sh_link; - }break; - } - - // extended section count - if (size != 0){ - section_count_raw = size; - } - - // extended section header string table index - if (link != 0){ - section_header_string_table_index = link; - } - } - } - - //- clamp section & program arrays to size - U64 section_foff = 0; - U64 section_size = 0; - U64 section_count = 0; - - U64 segment_foff = 0; - U64 segment_size = 0; - U64 segment_count = 0; - - if (header_is_good){ - if (e_shentsize > 0){ - U64 section_opl_raw = e_shoff + e_shentsize*section_count_raw; - U64 section_opl = ClampTop(section_opl_raw, elf_data.size); - if (section_opl > e_shoff){ - section_foff = e_shoff; - section_size = e_shentsize; - section_count = (section_opl - e_shoff)/e_shentsize; - } - } - - if (e_phentsize > 0){ - U64 segment_opl_raw = e_phoff + e_phentsize*e_phnum; - U64 segment_opl = ClampTop(segment_opl_raw, elf_data.size); - if (segment_opl > e_phoff){ - segment_foff = e_phoff; - segment_size = e_phentsize; - segment_count = (segment_opl - e_phoff)/e_phentsize; - } - } - } - - //- determine the vbase for this file - U64 vbase = 0; - if (header_is_good){ - // find the first LOAD segment - U64 load_segment_off = 0; - { - U64 segment_cursor = segment_foff; - U64 segment_opl = segment_foff + segment_size*segment_count; - for (;segment_cursor < segment_opl; segment_cursor += segment_size){ - U32 p_type = *(U32*)(elf_data.str + segment_cursor); - if (p_type == ELF_SegmentType_LOAD){ - load_segment_off = segment_cursor; - break; - } - } - } - - // use the segment's p_vaddr to determine vbase - if (load_segment_off != 0){ - switch (elf_class){ - case ELF_Class_32: - { - ELF_Phdr32 *phdr = (ELF_Phdr32*)(elf_data.str + load_segment_off); - vbase = phdr->p_vaddr; - }break; - case ELF_Class_64: - { - ELF_Phdr64 *phdr = (ELF_Phdr64*)(elf_data.str + load_segment_off); - vbase = phdr->p_vaddr; - }break; - } - } - } - - //- locate the section header string table - U64 section_name_table_foff = 0; - U64 section_name_table_opl = 0; - if (header_is_good){ - if (section_header_string_table_index < section_count){ - U64 sec_foff = section_foff + section_header_string_table_index*section_size; - switch (elf_class){ - case ELF_Class_32: - { - ELF_Shdr32 *shdr = (ELF_Shdr32*)(elf_data.str + sec_foff); - section_name_table_foff = shdr->sh_offset; - section_name_table_opl = shdr->sh_offset + shdr->sh_size; - }break; - case ELF_Class_64: - { - ELF_Shdr64 *shdr = (ELF_Shdr64*)(elf_data.str + sec_foff); - section_name_table_foff = shdr->sh_offset; - section_name_table_opl = shdr->sh_offset + shdr->sh_size; - }break; - } - } - } - - //- format sections data - ELF_Shdr64 *sections = 0; - if (header_is_good && section_count > 0){ - switch (elf_class){ - case ELF_Class_32: - { - sections = push_array(arena, ELF_Shdr64, section_count); - { - ELF_Shdr32 *shdr32 = (ELF_Shdr32*)(elf_data.str + section_foff); - ELF_Shdr64 *shdr64 = sections; - for (U64 i = 0; i < section_count; i += 1, shdr32 += 1, shdr64 += 1){ - shdr64->sh_name = shdr32->sh_name; - shdr64->sh_type = shdr32->sh_type; - shdr64->sh_flags = shdr32->sh_flags; - shdr64->sh_addr = shdr32->sh_addr; - shdr64->sh_offset = shdr32->sh_offset; - shdr64->sh_size = shdr32->sh_size; - shdr64->sh_link = shdr32->sh_link; - shdr64->sh_info = shdr32->sh_info; - shdr64->sh_addralign = shdr32->sh_addralign; - shdr64->sh_entsize = shdr32->sh_entsize; - } - } - }break; - case ELF_Class_64: - { - sections = (ELF_Shdr64*)(elf_data.str + section_foff); - }break; - } - } - - //- extract section names - String8 *section_names = 0; - if (sections != 0 && section_count > 0){ - U8 *string_table_opl = elf_data.str + section_name_table_opl; - - section_names = push_array(arena, String8, section_count); - String8 *sec_name = section_names; - ELF_Shdr64 *sec = sections; - for (U64 i = 0; - i < section_count; - i += 1, sec += 1, sec_name += 1){ - U64 name_foff = section_name_table_foff + sec->sh_name; - if (section_name_table_foff <= name_foff && name_foff < section_name_table_opl){ - U8 *base = elf_data.str + name_foff; - U8 *opl = base; - for (;opl < string_table_opl && *opl != 0; opl += 1); - sec_name->str = base; - sec_name->size = (U64)(opl - base); - } - } - } - - //- format segments data - ELF_Phdr64 *segments = 0; - if (header_is_good && segment_count > 0){ - switch (elf_class){ - case ELF_Class_32: - { - segments = push_array(arena, ELF_Phdr64, segment_count); - { - ELF_Phdr32 *phdr32 = (ELF_Phdr32*)(elf_data.str + segment_foff); - ELF_Phdr64 *phdr64 = segments; - for (U64 i = 0; i < segment_count; i += 1, phdr32 += 1, phdr64 += 1){ - phdr64->p_type = phdr32->p_type; - phdr64->p_flags = phdr32->p_flags; - phdr64->p_offset = phdr32->p_offset; - phdr64->p_vaddr = phdr32->p_vaddr; - phdr64->p_paddr = phdr32->p_paddr; - phdr64->p_filesz = phdr32->p_filesz; - phdr64->p_memsz = phdr32->p_memsz; - phdr64->p_align = phdr32->p_align; - } - } - }break; - case ELF_Class_64: - { - segments = (ELF_Phdr64*)(elf_data.str + segment_foff); - }break; - } - } - - //- find special sections - U64 strtab_idx = 0; - U64 symtab_idx = 0; - U64 dynsym_idx = 0; - if (section_names != 0){ - for (U64 i = 0; i < section_count; i += 1){ - String8 name = section_names[i]; - if (str8_match(name, str8_lit(".strtab"), 0)){ - strtab_idx = i; - } - else if (str8_match(name, str8_lit(".symtab"), 0)){ - symtab_idx = i; - } - else if (str8_match(name, str8_lit(".dynsym"), 0)){ - dynsym_idx = i; - } - } - } - - - //- fill result - ELF_Parsed *result = 0; - if (header_is_good){ - result = push_array(arena, ELF_Parsed, 1); - result->data = elf_data; - result->elf_class = elf_class; - result->arch = arch; - result->sections = sections; - result->section_names = section_names; - result->section_foff = section_foff; - result->section_count = section_count; - result->segments = segments; - result->segment_foff = segment_foff; - result->segment_count = segment_count; - result->vbase = vbase; - result->entry_vaddr = e_entry; - result->section_name_table_foff = section_name_table_foff; - result->section_name_table_opl = section_name_table_opl; - result->strtab_idx = strtab_idx; - result->symtab_idx = symtab_idx; - result->dynsym_idx = dynsym_idx; - } - - return(result); -} - -static ELF_SectionArray -elf_section_array_from_elf(ELF_Parsed *elf){ - ELF_SectionArray result = {0}; - if (elf != 0){ - result.sections = elf->sections; - result.count = elf->section_count; - } - return(result); -} - -static String8Array -elf_section_name_array_from_elf(ELF_Parsed *elf){ - String8Array result = {0}; - if (elf != 0){ - result.v = elf->section_names; - result.count = elf->section_count; - } - return(result); -} - -static ELF_SegmentArray -elf_segment_array_from_elf(ELF_Parsed *elf){ - ELF_SegmentArray result = {0}; - if (elf != 0){ - result.segments = elf->segments; - result.count = elf->segment_count; - } - return(result); -} - -static String8 -elf_section_name_from_name_offset(ELF_Parsed *elf, U64 offset){ - String8 result = {0}; - if (elf != 0){ - if (offset > 0){ - U64 foff = elf->section_name_table_foff + offset; - if (elf->section_name_table_foff <= foff && foff < elf->section_name_table_opl){ - U8 *base = elf->data.str + foff; - U8 *section_opl = elf->data.str + elf->section_name_table_opl; - U8 *opl = base; - for (;opl < section_opl && *opl != 0; opl += 1); - result.str = base; - result.size = opl - base; - } - } - } - return(result); -} - -static String8 -elf_section_name_from_idx(ELF_Parsed *elf, U32 idx){ - String8 result = {0}; - if (elf != 0){ - if (idx < elf->section_count){ - result = elf->section_names[idx]; - } - } - return(result); -} - -static U32 -elf_section_idx_from_name(ELF_Parsed *elf, String8 name){ - U32 result = 0; - if (elf != 0){ - String8 *sec_name = elf->section_names; - U64 count = elf->section_count; - for (U64 i = 0; i < count; i += 1, sec_name += 1){ - if (str8_match(*sec_name, name, 0)){ - result = i; - break; - } - } - } - return(result); -} - -static String8 -elf_section_data_from_idx(ELF_Parsed *elf, U32 idx){ - String8 result = {0}; - if (elf != 0){ - if (idx < elf->section_count){ - ELF_Shdr64 *shdr = elf->sections + idx; - U64 off_raw = shdr->sh_offset; - U64 size = shdr->sh_size; - if (shdr->sh_flags & ELF_SectionType_NOBITS){ - size = 0; - } - U64 opl_raw = off_raw + size; - U64 opl = ClampTop(opl_raw, elf->data.size); - U64 off = ClampTop(off_raw, opl); - result.str = elf->data.str + off; - result.size = opl - off; - } - } - return(result); -} - -static ELF_SymArray -elf_sym_array_from_data(Arena *arena, ELF_Class elf_class, String8 data){ - // converge to sym64 layout - ELF_Sym64 *symbols = 0; - U64 count = 0; - switch (elf_class){ - case ELF_Class_32: - { - count = data.size/sizeof(ELF_Sym32); - symbols = push_array(arena, ELF_Sym64, count); - { - ELF_Sym32 *sym32 = (ELF_Sym32*)(data.str); - ELF_Sym64 *sym64 = symbols; - for (U64 i = 0; i < count; i += 1, sym32 += 1, sym64 += 1){ - sym64->st_name = sym32->st_name; - sym64->st_value = sym32->st_value; - sym64->st_size = sym32->st_size; - sym64->st_info = sym32->st_info; - sym64->st_other = sym32->st_other; - sym64->st_shndx = sym32->st_shndx; - } - } - }break; - - case ELF_Class_64: - { - count = data.size/sizeof(ELF_Sym64); - symbols = (ELF_Sym64*)(data.str); - }break; - } - - // fill result - ELF_SymArray result = {0}; - result.symbols = symbols; - result.count = count; - return(result); -} - -// string functions - -static String8 -elf_string_from_section_type(ELF_SectionType section_type){ - String8 result = str8_lit("INVALID_SECTION_TYPE"); - switch (section_type){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_SectionTypeXList(X) -#undef X - } - return(result); -} - -static String8 -elf_string_from_symbol_binding(ELF_SymbolBinding binding){ - String8 result = str8_lit("INVALID_SYMBOL_BINDING"); - switch (binding){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_SymbolBindingXList(X) -#undef X - } - return(result); -} - -static String8 -elf_string_from_symbol_type(ELF_SymbolType type){ - String8 result = str8_lit("INVALID_SYMBOL_TYPE"); - switch (type){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_SymbolTypeXList(X) -#undef X - } - return(result); -} - -static String8 -elf_string_from_symbol_visibility(ELF_SymbolVisibility visibility){ - String8 result = str8_lit("INVALID_SYMBOL_VISIBILITY"); - switch (visibility){ -#define X(N,C) case C: result = str8_lit(#N); break; - ELF_SymbolVisibilityXList(X) -#undef X - } - return(result); -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ ELF Parser Functions + +static ELF_Parsed* +elf_parsed_from_data(Arena *arena, String8 elf_data){ + //- test magic number + B32 has_good_magic_number = 0; + if (elf_data.size >= sizeof(ELF_NIDENT) && + MemoryMatch(elf_data.str, elf_magic, sizeof(elf_magic))){ + has_good_magic_number = 1; + } + + //- determine elf class + U8 elf_class = ELF_Class_NONE; + if (has_good_magic_number){ + elf_class = elf_data.str[ELF_Identification_CLASS]; + } + + //- extract header information + B32 decoded_header = 0; + + U8 e_data_encoding = ELF_DataEncoding_NONE; + U16 e_machine = ELF_Machine_NONE; + + U64 e_entry = 0; + + U64 e_shoff = 0; + U16 e_shentsize = 0; + U16 e_shnum = 0; + + U64 e_phoff = 0; + U16 e_phentsize = 0; + U16 e_phnum = 0; + + U16 e_shstrndx = 0; + + switch (elf_class){ + case ELF_Class_NONE: /* not good */ break; + + case ELF_Class_32: + { + if (elf_data.size >= sizeof(ELF_Ehdr32)){ + ELF_Ehdr32 *hdr = (ELF_Ehdr32*)elf_data.str; + + decoded_header = 1; + e_data_encoding = hdr->e_ident[ELF_Identification_DATA]; + e_machine = hdr->e_machine; + e_entry = hdr->e_entry; + e_phoff = hdr->e_phoff; + e_shoff = hdr->e_shoff; + e_phentsize = hdr->e_phentsize; + e_phnum = hdr->e_phnum; + e_shentsize = hdr->e_shentsize; + e_shnum = hdr->e_shnum; + e_shstrndx = hdr->e_shstrndx; + } + }break; + + case ELF_Class_64: + { + if (elf_data.size >= sizeof(ELF_Ehdr64)){ + ELF_Ehdr64 *hdr = (ELF_Ehdr64*)elf_data.str; + + decoded_header = 1; + e_data_encoding = hdr->e_ident[ELF_Identification_DATA]; + e_machine = hdr->e_machine; + e_entry = hdr->e_entry; + e_phoff = hdr->e_phoff; + e_shoff = hdr->e_shoff; + e_phentsize = hdr->e_phentsize; + e_phnum = hdr->e_phnum; + e_shentsize = hdr->e_shentsize; + e_shnum = hdr->e_shnum; + e_shstrndx = hdr->e_shstrndx; + } + }break; + } + + //- validate & translate header values + B32 header_is_good = 0; + Architecture arch = Architecture_Null; + if (decoded_header){ + header_is_good = 1; + + // only supporting little-endian versions right now + if (header_is_good){ + if (e_data_encoding != ELF_DataEncoding_2LSB){ + header_is_good = 0; + } + } + + // make sure this is a supported machine type + if (header_is_good){ + switch (e_machine){ + default: header_is_good = 0; + case ELF_Machine_386: arch = Architecture_x86; break; + case ELF_Machine_X86_64: arch = Architecture_x64; break; + } + } + + // make sure section & segment sizes are correct + if (header_is_good){ + switch (elf_class){ + case ELF_Class_32: + { + if (e_shentsize != sizeof(ELF_Shdr32) || + e_phentsize != sizeof(ELF_Phdr32)){ + header_is_good = 0; + } + }break; + case ELF_Class_64: + { + if (e_shentsize != sizeof(ELF_Shdr64) || + e_phentsize != sizeof(ELF_Phdr64)){ + header_is_good = 0; + } + }break; + } + } + } + + //- extract extra information from the special first section + U64 section_count_raw = e_shnum; + U32 section_header_string_table_index = e_shstrndx; + if (header_is_good){ + if (e_shoff <= elf_data.size && e_shentsize <= elf_data.size && + e_shoff + e_shentsize <= elf_data.size){ + U64 size = 0; + U32 link = 0; + switch (elf_class){ + case ELF_Class_32: + { + ELF_Shdr32 *shdr = (ELF_Shdr32*)(elf_data.str + e_shoff); + size = shdr->sh_size; + link = shdr->sh_link; + }break; + case ELF_Class_64: + { + ELF_Shdr64 *shdr = (ELF_Shdr64*)(elf_data.str + e_shoff); + size = shdr->sh_size; + link = shdr->sh_link; + }break; + } + + // extended section count + if (size != 0){ + section_count_raw = size; + } + + // extended section header string table index + if (link != 0){ + section_header_string_table_index = link; + } + } + } + + //- clamp section & program arrays to size + U64 section_foff = 0; + U64 section_size = 0; + U64 section_count = 0; + + U64 segment_foff = 0; + U64 segment_size = 0; + U64 segment_count = 0; + + if (header_is_good){ + if (e_shentsize > 0){ + U64 section_opl_raw = e_shoff + e_shentsize*section_count_raw; + U64 section_opl = ClampTop(section_opl_raw, elf_data.size); + if (section_opl > e_shoff){ + section_foff = e_shoff; + section_size = e_shentsize; + section_count = (section_opl - e_shoff)/e_shentsize; + } + } + + if (e_phentsize > 0){ + U64 segment_opl_raw = e_phoff + e_phentsize*e_phnum; + U64 segment_opl = ClampTop(segment_opl_raw, elf_data.size); + if (segment_opl > e_phoff){ + segment_foff = e_phoff; + segment_size = e_phentsize; + segment_count = (segment_opl - e_phoff)/e_phentsize; + } + } + } + + //- determine the vbase for this file + U64 vbase = 0; + if (header_is_good){ + // find the first LOAD segment + U64 load_segment_off = 0; + { + U64 segment_cursor = segment_foff; + U64 segment_opl = segment_foff + segment_size*segment_count; + for (;segment_cursor < segment_opl; segment_cursor += segment_size){ + U32 p_type = *(U32*)(elf_data.str + segment_cursor); + if (p_type == ELF_SegmentType_LOAD){ + load_segment_off = segment_cursor; + break; + } + } + } + + // use the segment's p_vaddr to determine vbase + if (load_segment_off != 0){ + switch (elf_class){ + case ELF_Class_32: + { + ELF_Phdr32 *phdr = (ELF_Phdr32*)(elf_data.str + load_segment_off); + vbase = phdr->p_vaddr; + }break; + case ELF_Class_64: + { + ELF_Phdr64 *phdr = (ELF_Phdr64*)(elf_data.str + load_segment_off); + vbase = phdr->p_vaddr; + }break; + } + } + } + + //- locate the section header string table + U64 section_name_table_foff = 0; + U64 section_name_table_opl = 0; + if (header_is_good){ + if (section_header_string_table_index < section_count){ + U64 sec_foff = section_foff + section_header_string_table_index*section_size; + switch (elf_class){ + case ELF_Class_32: + { + ELF_Shdr32 *shdr = (ELF_Shdr32*)(elf_data.str + sec_foff); + section_name_table_foff = shdr->sh_offset; + section_name_table_opl = shdr->sh_offset + shdr->sh_size; + }break; + case ELF_Class_64: + { + ELF_Shdr64 *shdr = (ELF_Shdr64*)(elf_data.str + sec_foff); + section_name_table_foff = shdr->sh_offset; + section_name_table_opl = shdr->sh_offset + shdr->sh_size; + }break; + } + } + } + + //- format sections data + ELF_Shdr64 *sections = 0; + if (header_is_good && section_count > 0){ + switch (elf_class){ + case ELF_Class_32: + { + sections = push_array(arena, ELF_Shdr64, section_count); + { + ELF_Shdr32 *shdr32 = (ELF_Shdr32*)(elf_data.str + section_foff); + ELF_Shdr64 *shdr64 = sections; + for (U64 i = 0; i < section_count; i += 1, shdr32 += 1, shdr64 += 1){ + shdr64->sh_name = shdr32->sh_name; + shdr64->sh_type = shdr32->sh_type; + shdr64->sh_flags = shdr32->sh_flags; + shdr64->sh_addr = shdr32->sh_addr; + shdr64->sh_offset = shdr32->sh_offset; + shdr64->sh_size = shdr32->sh_size; + shdr64->sh_link = shdr32->sh_link; + shdr64->sh_info = shdr32->sh_info; + shdr64->sh_addralign = shdr32->sh_addralign; + shdr64->sh_entsize = shdr32->sh_entsize; + } + } + }break; + case ELF_Class_64: + { + sections = (ELF_Shdr64*)(elf_data.str + section_foff); + }break; + } + } + + //- extract section names + String8 *section_names = 0; + if (sections != 0 && section_count > 0){ + U8 *string_table_opl = elf_data.str + section_name_table_opl; + + section_names = push_array(arena, String8, section_count); + String8 *sec_name = section_names; + ELF_Shdr64 *sec = sections; + for (U64 i = 0; + i < section_count; + i += 1, sec += 1, sec_name += 1){ + U64 name_foff = section_name_table_foff + sec->sh_name; + if (section_name_table_foff <= name_foff && name_foff < section_name_table_opl){ + U8 *base = elf_data.str + name_foff; + U8 *opl = base; + for (;opl < string_table_opl && *opl != 0; opl += 1); + sec_name->str = base; + sec_name->size = (U64)(opl - base); + } + } + } + + //- format segments data + ELF_Phdr64 *segments = 0; + if (header_is_good && segment_count > 0){ + switch (elf_class){ + case ELF_Class_32: + { + segments = push_array(arena, ELF_Phdr64, segment_count); + { + ELF_Phdr32 *phdr32 = (ELF_Phdr32*)(elf_data.str + segment_foff); + ELF_Phdr64 *phdr64 = segments; + for (U64 i = 0; i < segment_count; i += 1, phdr32 += 1, phdr64 += 1){ + phdr64->p_type = phdr32->p_type; + phdr64->p_flags = phdr32->p_flags; + phdr64->p_offset = phdr32->p_offset; + phdr64->p_vaddr = phdr32->p_vaddr; + phdr64->p_paddr = phdr32->p_paddr; + phdr64->p_filesz = phdr32->p_filesz; + phdr64->p_memsz = phdr32->p_memsz; + phdr64->p_align = phdr32->p_align; + } + } + }break; + case ELF_Class_64: + { + segments = (ELF_Phdr64*)(elf_data.str + segment_foff); + }break; + } + } + + //- find special sections + U64 strtab_idx = 0; + U64 symtab_idx = 0; + U64 dynsym_idx = 0; + if (section_names != 0){ + for (U64 i = 0; i < section_count; i += 1){ + String8 name = section_names[i]; + if (str8_match(name, str8_lit(".strtab"), 0)){ + strtab_idx = i; + } + else if (str8_match(name, str8_lit(".symtab"), 0)){ + symtab_idx = i; + } + else if (str8_match(name, str8_lit(".dynsym"), 0)){ + dynsym_idx = i; + } + } + } + + + //- fill result + ELF_Parsed *result = 0; + if (header_is_good){ + result = push_array(arena, ELF_Parsed, 1); + result->data = elf_data; + result->elf_class = elf_class; + result->arch = arch; + result->sections = sections; + result->section_names = section_names; + result->section_foff = section_foff; + result->section_count = section_count; + result->segments = segments; + result->segment_foff = segment_foff; + result->segment_count = segment_count; + result->vbase = vbase; + result->entry_vaddr = e_entry; + result->section_name_table_foff = section_name_table_foff; + result->section_name_table_opl = section_name_table_opl; + result->strtab_idx = strtab_idx; + result->symtab_idx = symtab_idx; + result->dynsym_idx = dynsym_idx; + } + + return(result); +} + +static ELF_SectionArray +elf_section_array_from_elf(ELF_Parsed *elf){ + ELF_SectionArray result = {0}; + if (elf != 0){ + result.sections = elf->sections; + result.count = elf->section_count; + } + return(result); +} + +static String8Array +elf_section_name_array_from_elf(ELF_Parsed *elf){ + String8Array result = {0}; + if (elf != 0){ + result.v = elf->section_names; + result.count = elf->section_count; + } + return(result); +} + +static ELF_SegmentArray +elf_segment_array_from_elf(ELF_Parsed *elf){ + ELF_SegmentArray result = {0}; + if (elf != 0){ + result.segments = elf->segments; + result.count = elf->segment_count; + } + return(result); +} + +static String8 +elf_section_name_from_name_offset(ELF_Parsed *elf, U64 offset){ + String8 result = {0}; + if (elf != 0){ + if (offset > 0){ + U64 foff = elf->section_name_table_foff + offset; + if (elf->section_name_table_foff <= foff && foff < elf->section_name_table_opl){ + U8 *base = elf->data.str + foff; + U8 *section_opl = elf->data.str + elf->section_name_table_opl; + U8 *opl = base; + for (;opl < section_opl && *opl != 0; opl += 1); + result.str = base; + result.size = opl - base; + } + } + } + return(result); +} + +static String8 +elf_section_name_from_idx(ELF_Parsed *elf, U32 idx){ + String8 result = {0}; + if (elf != 0){ + if (idx < elf->section_count){ + result = elf->section_names[idx]; + } + } + return(result); +} + +static U32 +elf_section_idx_from_name(ELF_Parsed *elf, String8 name){ + U32 result = 0; + if (elf != 0){ + String8 *sec_name = elf->section_names; + U64 count = elf->section_count; + for (U64 i = 0; i < count; i += 1, sec_name += 1){ + if (str8_match(*sec_name, name, 0)){ + result = i; + break; + } + } + } + return(result); +} + +static String8 +elf_section_data_from_idx(ELF_Parsed *elf, U32 idx){ + String8 result = {0}; + if (elf != 0){ + if (idx < elf->section_count){ + ELF_Shdr64 *shdr = elf->sections + idx; + U64 off_raw = shdr->sh_offset; + U64 size = shdr->sh_size; + if (shdr->sh_flags & ELF_SectionType_NOBITS){ + size = 0; + } + U64 opl_raw = off_raw + size; + U64 opl = ClampTop(opl_raw, elf->data.size); + U64 off = ClampTop(off_raw, opl); + result.str = elf->data.str + off; + result.size = opl - off; + } + } + return(result); +} + +static ELF_SymArray +elf_sym_array_from_data(Arena *arena, ELF_Class elf_class, String8 data){ + // converge to sym64 layout + ELF_Sym64 *symbols = 0; + U64 count = 0; + switch (elf_class){ + case ELF_Class_32: + { + count = data.size/sizeof(ELF_Sym32); + symbols = push_array(arena, ELF_Sym64, count); + { + ELF_Sym32 *sym32 = (ELF_Sym32*)(data.str); + ELF_Sym64 *sym64 = symbols; + for (U64 i = 0; i < count; i += 1, sym32 += 1, sym64 += 1){ + sym64->st_name = sym32->st_name; + sym64->st_value = sym32->st_value; + sym64->st_size = sym32->st_size; + sym64->st_info = sym32->st_info; + sym64->st_other = sym32->st_other; + sym64->st_shndx = sym32->st_shndx; + } + } + }break; + + case ELF_Class_64: + { + count = data.size/sizeof(ELF_Sym64); + symbols = (ELF_Sym64*)(data.str); + }break; + } + + // fill result + ELF_SymArray result = {0}; + result.symbols = symbols; + result.count = count; + return(result); +} + +// string functions + +static String8 +elf_string_from_section_type(ELF_SectionType section_type){ + String8 result = str8_lit("INVALID_SECTION_TYPE"); + switch (section_type){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_SectionTypeXList(X) +#undef X + } + return(result); +} + +static String8 +elf_string_from_symbol_binding(ELF_SymbolBinding binding){ + String8 result = str8_lit("INVALID_SYMBOL_BINDING"); + switch (binding){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_SymbolBindingXList(X) +#undef X + } + return(result); +} + +static String8 +elf_string_from_symbol_type(ELF_SymbolType type){ + String8 result = str8_lit("INVALID_SYMBOL_TYPE"); + switch (type){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_SymbolTypeXList(X) +#undef X + } + return(result); +} + +static String8 +elf_string_from_symbol_visibility(ELF_SymbolVisibility visibility){ + String8 result = str8_lit("INVALID_SYMBOL_VISIBILITY"); + switch (visibility){ +#define X(N,C) case C: result = str8_lit(#N); break; + ELF_SymbolVisibilityXList(X) +#undef X + } + return(result); +} diff --git a/src/rdi_from_dwarf/rdi_elf.h b/src/rdi_from_dwarf/rdi_elf.h index ae0bf781..873d340d 100644 --- a/src/rdi_from_dwarf/rdi_elf.h +++ b/src/rdi_from_dwarf/rdi_elf.h @@ -1,517 +1,517 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RDI_ELF_H -#define RDI_ELF_H - -// https://refspecs.linuxfoundation.org/elf/elf.pdf - -//////////////////////////////// -//~ Elf Format Types - -// elf header - -#define ELF_NIDENT 16 - -typedef struct ELF_Ehdr32{ - U8 e_ident[ELF_NIDENT]; - U16 e_type; - U16 e_machine; - U32 e_version; - U32 e_entry; - U32 e_phoff; - U32 e_shoff; - U32 e_flags; - U16 e_ehsize; - U16 e_phentsize; - U16 e_phnum; - U16 e_shentsize; - U16 e_shnum; - U16 e_shstrndx; -} ELF_Ehdr32; - -typedef struct ELF_Ehdr64{ - U8 e_ident[ELF_NIDENT]; - U16 e_type; - U16 e_machine; - U32 e_version; - U64 e_entry; - U64 e_phoff; - U64 e_shoff; - U32 e_flags; - U16 e_ehsize; - U16 e_phentsize; - U16 e_phnum; - U16 e_shentsize; - U16 e_shnum; - U16 e_shstrndx; -} ELF_Ehdr64; - -typedef enum ELF_Type{ - ELF_Type_NONE = 0, - ELF_Type_REL = 1, - ELF_Type_EXEC = 2, - ELF_Type_DYN = 3, - ELF_Type_CORE = 4, - ELF_Type_LOOS = 0xfe00, - ELF_Type_HIOS = 0xfeff, - ELF_Type_LOPROC = 0xff00, - ELF_Type_HIPROC = 0xffff, -} ELF_Type; - -typedef enum ELF_Machine{ - ELF_Machine_NONE = 0, - ELF_Machine_M32 = 1, - ELF_Machine_SPARC = 2, - ELF_Machine_386 = 3, - ELF_Machine_68K = 4, - ELF_Machine_88K = 5, - ELF_Machine_860 = 7, - ELF_Machine_MIPS = 8, - ELF_Machine_S370 = 9, - ELF_Machine_MIPS_RS3_LE = 10, - ELF_Machine_PARISC = 15, - ELF_Machine_VPP500 = 17, - ELF_Machine_SPARC32PLUS = 18, - ELF_Machine_960 = 19, - ELF_Machine_PPC = 20, - ELF_Machine_PPC64 = 21, - ELF_Machine_S390 = 22, - ELF_Machine_V800 = 36, - ELF_Machine_FR20 = 37, - ELF_Machine_RH32 = 38, - ELF_Machine_RCE = 39, - ELF_Machine_ARM = 40, - ELF_Machine_ALPHA = 41, - ELF_Machine_SH = 42, - ELF_Machine_SPARCV9 = 43, - ELF_Machine_TRICORE = 44, - ELF_Machine_ARC = 45, - ELF_Machine_H8_300 = 46, - ELF_Machine_H8_300H = 47, - ELF_Machine_H8S = 48, - ELF_Machine_H8_500 = 49, - ELF_Machine_IA_64 = 50, - ELF_Machine_MIPS_X = 51, - ELF_Machine_COLDFIRE = 52, - ELF_Machine_68HC12 = 53, - ELF_Machine_MMA = 54, - ELF_Machine_PCP = 55, - ELF_Machine_NCPU = 56, - ELF_Machine_NDR1 = 57, - ELF_Machine_STARCORE = 58, - ELF_Machine_ME16 = 59, - ELF_Machine_ST100 = 60, - ELF_Machine_TINYJ = 61, - ELF_Machine_X86_64 = 62, - ELF_Machine_PDSP = 63, - ELF_Machine_PDP10 = 64, - ELF_Machine_PDP11 = 65, - ELF_Machine_FX66 = 66, - ELF_Machine_ST9PLUS = 67, - ELF_Machine_ST7 = 68, - ELF_Machine_68HC16 = 69, - ELF_Machine_68HC11 = 70, - ELF_Machine_68HC08 = 71, - ELF_Machine_68HC05 = 72, - ELF_Machine_SVX = 73, - ELF_Machine_ST19 = 74, - ELF_Machine_VAX = 75, - ELF_Machine_CRIS = 76, - ELF_Machine_JAVELIN = 77, - ELF_Machine_FIREPATH = 78, - ELF_Machine_ZSP = 79, - ELF_Machine_MMIX = 80, - ELF_Machine_HUANY = 81, - ELF_Machine_PRISM = 82, - ELF_Machine_AVR = 83, - ELF_Machine_FR30 = 84, - ELF_Machine_D10V = 85, - ELF_Machine_D30V = 86, - ELF_Machine_V850 = 87, - ELF_Machine_M32R = 88, - ELF_Machine_MN10300 = 89, - ELF_Machine_MN10200 = 90, - ELF_Machine_PJ = 91, - ELF_Machine_OPENRISC = 92, - ELF_Machine_ARC_A5 = 93, - ELF_Machine_XTENSA = 94, - ELF_Machine_VIDEOCORE = 95, - ELF_Machine_TMM_GPP = 96, - ELF_Machine_NS32K = 97, - ELF_Machine_TPC = 98, - ELF_Machine_SNP1K = 99, - ELF_Machine_ST200 = 100, -} ELF_Machine; - -typedef enum ELF_Version{ - ELF_Version_NONE = 0, - ELF_Version_CURRENT = 1, -} ELF_Version; - -typedef enum ELF_Identification{ - ELF_Identification_MAG0 = 0, - ELF_Identification_MAG1 = 1, - ELF_Identification_MAG2 = 2, - ELF_Identification_MAG3 = 3, - ELF_Identification_CLASS = 4, - ELF_Identification_DATA = 5, - ELF_Identification_VERSION = 6, - ELF_Identification_OSABI = 7, - ELF_Identification_ABIVERSION = 8, - ELF_Identification_PAD = 9, -} ELF_Identification; - -read_only global U8 elf_magic[] = {0x7F, 'E', 'L', 'F'}; - -typedef enum ELF_Class{ - ELF_Class_NONE = 0, - ELF_Class_32 = 1, - ELF_Class_64 = 2, -} ELF_Class; - -typedef enum ELF_DataEncoding{ - ELF_DataEncoding_NONE = 0, - ELF_DataEncoding_2LSB = 1, - ELF_DataEncoding_2MSB = 2, -} ELF_DataEncoding; - -typedef enum ELF_OsAbi{ - ELF_OsAbi_NONE = 0, - ELF_OsAbi_HPUX = 1, - ELF_OsAbi_NETBSD = 2, - ELF_OsAbi_LINUX = 3, - ELF_OsAbi_SOLARIS = 6, - ELF_OsAbi_AIX = 7, - ELF_OsAbi_IRIX = 8, - ELF_OsAbi_FREEBSD = 9, - ELF_OsAbi_TRU64 = 10, - ELF_OsAbi_MODESTO = 11, - ELF_OsAbi_OPENBSD = 12, - ELF_OsAbi_OPENVMS = 13, - ELF_OsAbi_NSK = 14, -} ELF_OsAbi; - -// sections - -typedef enum ELF_ReservedSectionIndex{ - ELF_ReservedSectionIndex_UNDEF = 0, - ELF_ReservedSectionIndex_LORESERVE = 0xFF00, - ELF_ReservedSectionIndex_LOPROC = 0xFF00, - ELF_ReservedSectionIndex_HIPROC = 0xFF1F, - ELF_ReservedSectionIndex_LOOS = 0xFF20, - ELF_ReservedSectionIndex_HIOS = 0xFF3F, - ELF_ReservedSectionIndex_ABS = 0xFFF1, - ELF_ReservedSectionIndex_COMMON = 0xFFF2, - ELF_ReservedSectionIndex_XINDEX = 0xFFFF, - ELF_ReservedSectionIndex_HIRESERVE = 0xFFFF, -} ELF_ReservedSectionIndex; - -typedef struct ELF_Shdr32{ - U32 sh_name; - U32 sh_type; - U32 sh_flags; - U32 sh_addr; - U32 sh_offset; - U32 sh_size; - U32 sh_link; - U32 sh_info; - U32 sh_addralign; - U32 sh_entsize; -} ELF_Shdr32; - -typedef struct ELF_Shdr64{ - U32 sh_name; - U32 sh_type; - U64 sh_flags; - U64 sh_addr; - U64 sh_offset; - U64 sh_size; - U32 sh_link; - U32 sh_info; - U64 sh_addralign; - U64 sh_entsize; -} ELF_Shdr64; - -// X(name, code) -#define ELF_SectionTypeXList(X)\ -X(NULL, 0)\ -X(PROGBITS, 1)\ -X(SYMTAB, 2)\ -X(STRTAB, 3)\ -X(RELA, 4)\ -X(HASH, 5)\ -X(DYNAMIC, 6)\ -X(NOTE, 7)\ -X(NOBITS, 8)\ -X(REL, 9)\ -X(SHLIB, 10)\ -X(DYNSYM, 11)\ -X(INIT_ARRAY, 14)\ -X(FINI_ARRAY, 15)\ -X(PREINIT_ARRAY, 16)\ -X(GROUP, 17)\ -X(SYMTAB_SHNDX, 18)\ -X(LOOS, 0x60000000)\ -X(HIOS, 0x6FFFFFFF)\ -X(LOPROC, 0x70000000)\ -X(HIPROC, 0x7FFFFFFF)\ -X(LOUSER, 0x80000000)\ -X(HIUSER, 0x8FFFFFFF) - -typedef enum ELF_SectionType{ -#define X(N,C) ELF_SectionType_##N = C, - ELF_SectionTypeXList(X) -#undef X -} ELF_SectionType; - -typedef enum ELF_SectionAttributeFlags{ - ELF_SectionAttributeFlag_WRITE = 0x001, - ELF_SectionAttributeFlag_ALLOC = 0x002, - ELF_SectionAttributeFlag_EXECINSTR = 0x004, - ELF_SectionAttributeFlag_MERGE = 0x010, - ELF_SectionAttributeFlag_STRINGS = 0x020, - ELF_SectionAttributeFlag_INFO_LINK = 0x040, - ELF_SectionAttributeFlag_LINK_ORDER = 0x080, - ELF_SectionAttributeFlag_OS_NONCONFORMING = 0x100, - ELF_SectionAttributeFlag_GROUP = 0x200, - ELF_SectionAttributeFlag_TLS = 0x400, - ELF_SectionAttributeFlag_MASKOS = 0x0FF00000, - ELF_SectionAttributeFlag_MASKPROC = 0xF0000000, -} ELF_SectionAttributeFlags; - -typedef enum ELF_SectionGroupFlags{ - ELF_SectionGroupFlag_COMDAT = 0x1, - ELF_SectionGroupFlag_MASKOS = 0x0FF00000, - ELF_SectionGroupFlag_MASKPROC = 0xF0000000, -} ELF_SectionGroupFlags; - -typedef enum ELF_ReservedSymbolTableIndex{ - ELF_ReservedSymbolTableIndex_UNDEF = 0, -} ELF_ReservedSymbolTableIndex; - -// symbol table - -typedef struct ELF_Sym32{ - U32 st_name; - U32 st_value; - U32 st_size; - U8 st_info; - U8 st_other; - U16 st_shndx; -} ELF_Sym32; - -typedef struct ELF_Sym64{ - U32 st_name; - U8 st_info; - U8 st_other; - U16 st_shndx; - U64 st_value; - U64 st_size; -} ELF_Sym64; - -#define ELF_SymBindingFromInfo(x) (ELF_SymbolBinding)((x)>>4) -#define ELF_SymTypeFromInfo(x) (ELF_SymbolType)((x)&0xF) -#define ELF_SymInfoFromBindingType(b,t) ((((b)<<4)&0xF)|((t)&0xF)) - -#define ELF_SymVisibilityFromOther(x) ((x)&0x3) -#define ELF_SymOtherFromVisibility(x) ((x)&0x3) - -#define ELF_SymbolBindingXList(X)\ -X(LOCAL, 0)\ -X(GLOBAL, 1)\ -X(WEAK, 2)\ -X(LOOS, 10)\ -X(HIOS, 12)\ -X(LOPROC, 13)\ -X(HIPROC, 15)\ - -typedef enum ELF_SymbolBinding{ -#define X(N,C) ELF_SymbolBinding_##N = C, - ELF_SymbolBindingXList(X) -#undef X -} ELF_SymbolBinding; - -#define ELF_SymbolTypeXList(X)\ -X(NOTYPE, 0)\ -X(OBJECT, 1)\ -X(FUNC, 2)\ -X(SECTION, 3)\ -X(FILE, 4)\ -X(COMMON, 5)\ -X(TLS, 6)\ -X(LOOS, 10)\ -X(HIOS, 12)\ -X(LOPROC, 13)\ -X(HIPROC, 15) - -typedef enum ELF_SymbolType{ -#define X(N,C) ELF_SymbolType_##N = C, - ELF_SymbolTypeXList(X) -#undef X -} ELF_SymbolType; - -#define ELF_SymbolVisibilityXList(X)\ -X(DEFAULT, 0)\ -X(INTERNAL, 1)\ -X(HIDDEN, 2)\ -X(PROTECTED, 3) - -typedef enum ELF_SymbolVisibility{ -#define X(N,C) ELF_SymbolVisibility_##N = C, - ELF_SymbolVisibilityXList(X) -#undef X -} ELF_SymbolVisibility; - -// relocation - -typedef struct ELF_Rel32{ - U32 r_offset; - U32 r_info; -} ELF_Rel32; - -typedef struct ELF_Rela32{ - U32 r_offset; - U32 r_info; - S32 r_addend; -} ELF_Rela32; - -typedef struct ELF_Rel64{ - U64 r_offset; - U64 r_info; -} ELF_Rel64; - -typedef struct ELF_Rela64{ - U64 r_offset; - U64 r_info; - S64 r_addend; -} ELF_Rela64; - -#define ELF_RelSymIndexFromInfo32(x) ((x)>>8) -#define ELF_RelTypeFromInfo32(x) ((x)&0xF) -#define ELF_RelInfoFromSymIndexType32(n,t) (((n)<<8)|((t)&0xF)) - -#define ELF_RelSymIndexFromInfo64(x) ((x)>>32) -#define ELF_RelTypeFromInfo64(x) ((x)&0xFFFFFFFFL) -#define ELF_RelInfoFromSymIndexType64(n,t) (((n)<<8)|((t)&0xFFFFFFFFL)) - - - -// program header - -typedef struct ELF_Phdr32{ - U32 p_type; - U32 p_offset; - U32 p_vaddr; - U32 p_paddr; - U32 p_filesz; - U32 p_memsz; - U32 p_flags; - U32 p_align; -} ELF_Phdr32; - -typedef struct ELF_Phdr64{ - U32 p_type; - U32 p_flags; - U64 p_offset; - U64 p_vaddr; - U64 p_paddr; - U64 p_filesz; - U64 p_memsz; - U64 p_align; -} ELF_Phdr64; - -typedef enum ELF_SegmentType{ - ELF_SegmentType_NULL = 0, - ELF_SegmentType_LOAD = 1, - ELF_SegmentType_DYNAMIC = 2, - ELF_SegmentType_INTERP = 3, - ELF_SegmentType_NOTE = 4, - ELF_SegmentType_SHLIB = 5, - ELF_SegmentType_PHDR = 6, - ELF_SegmentType_TLS = 7, - ELF_SegmentType_LOOS = 0x60000000, - ELF_SegmentType_HIOS = 0x6fffffff, - ELF_SegmentType_LOPROC = 0x70000000, - ELF_SegmentType_HIPROC = 0x7fffffff, -} ELF_SegmentType; - -typedef enum ELF_SegmentFlags{ - ELF_SegmentFlag_X = 0x1, - ELF_SegmentFlag_W = 0x2, - ELF_SegmentFlag_R = 0x4, - ELF_SegmentFlag_MASKOS = 0x0FF00000, - ELF_SegmentFlag_MASKPROC = 0xF0000000, -} ELF_SegmentFlags; - -//////////////////////////////// -//~ ELF Parser Types - -// elf top level - -typedef struct ELF_SectionArray{ - ELF_Shdr64 *sections; - U64 count; -} ELF_SectionArray; - -typedef struct ELF_SegmentArray{ - ELF_Phdr64 *segments; - U64 count; -} ELF_SegmentArray; - -typedef struct ELF_Parsed{ - String8 data; - ELF_Class elf_class; - Architecture arch; - - ELF_Shdr64 *sections; - String8 *section_names; - U64 section_foff; - U64 section_count; - - ELF_Phdr64 *segments; - U64 segment_foff; - U64 segment_count; - - U64 vbase; - U64 entry_vaddr; - U64 section_name_table_foff; - U64 section_name_table_opl; - - U64 strtab_idx; - U64 symtab_idx; - U64 dynsym_idx; -} ELF_Parsed; - -// elf symtab - -typedef struct ELF_SymArray{ - ELF_Sym64 *symbols; - U64 count; -} ELF_SymArray; - -//////////////////////////////// -//~ ELF Parser Functions - -static ELF_Parsed* elf_parsed_from_data(Arena *arena, String8 elf_data); - -static ELF_SectionArray elf_section_array_from_elf(ELF_Parsed *elf); -static String8Array elf_section_name_array_from_elf(ELF_Parsed *elf); -static ELF_SegmentArray elf_segment_array_from_elf(ELF_Parsed *elf); - -static String8 elf_section_name_from_name_offset(ELF_Parsed *elf, U64 offset); -static String8 elf_section_name_from_idx(ELF_Parsed *elf, U32 idx); -static U32 elf_section_idx_from_name(ELF_Parsed *elf, String8 name); - -static String8 elf_section_data_from_idx(ELF_Parsed *elf, U32 idx); - -static ELF_SymArray elf_sym_array_from_data(Arena *arena, ELF_Class elf_class, String8 data); - -// string functions - -static String8 elf_string_from_section_type(ELF_SectionType section_type); -static String8 elf_string_from_symbol_binding(ELF_SymbolBinding binding); -static String8 elf_string_from_symbol_type(ELF_SymbolType type); -static String8 elf_string_from_symbol_visibility(ELF_SymbolVisibility visibility); - -#endif //RDI_ELF_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RDI_ELF_H +#define RDI_ELF_H + +// https://refspecs.linuxfoundation.org/elf/elf.pdf + +//////////////////////////////// +//~ Elf Format Types + +// elf header + +#define ELF_NIDENT 16 + +typedef struct ELF_Ehdr32{ + U8 e_ident[ELF_NIDENT]; + U16 e_type; + U16 e_machine; + U32 e_version; + U32 e_entry; + U32 e_phoff; + U32 e_shoff; + U32 e_flags; + U16 e_ehsize; + U16 e_phentsize; + U16 e_phnum; + U16 e_shentsize; + U16 e_shnum; + U16 e_shstrndx; +} ELF_Ehdr32; + +typedef struct ELF_Ehdr64{ + U8 e_ident[ELF_NIDENT]; + U16 e_type; + U16 e_machine; + U32 e_version; + U64 e_entry; + U64 e_phoff; + U64 e_shoff; + U32 e_flags; + U16 e_ehsize; + U16 e_phentsize; + U16 e_phnum; + U16 e_shentsize; + U16 e_shnum; + U16 e_shstrndx; +} ELF_Ehdr64; + +typedef enum ELF_Type{ + ELF_Type_NONE = 0, + ELF_Type_REL = 1, + ELF_Type_EXEC = 2, + ELF_Type_DYN = 3, + ELF_Type_CORE = 4, + ELF_Type_LOOS = 0xfe00, + ELF_Type_HIOS = 0xfeff, + ELF_Type_LOPROC = 0xff00, + ELF_Type_HIPROC = 0xffff, +} ELF_Type; + +typedef enum ELF_Machine{ + ELF_Machine_NONE = 0, + ELF_Machine_M32 = 1, + ELF_Machine_SPARC = 2, + ELF_Machine_386 = 3, + ELF_Machine_68K = 4, + ELF_Machine_88K = 5, + ELF_Machine_860 = 7, + ELF_Machine_MIPS = 8, + ELF_Machine_S370 = 9, + ELF_Machine_MIPS_RS3_LE = 10, + ELF_Machine_PARISC = 15, + ELF_Machine_VPP500 = 17, + ELF_Machine_SPARC32PLUS = 18, + ELF_Machine_960 = 19, + ELF_Machine_PPC = 20, + ELF_Machine_PPC64 = 21, + ELF_Machine_S390 = 22, + ELF_Machine_V800 = 36, + ELF_Machine_FR20 = 37, + ELF_Machine_RH32 = 38, + ELF_Machine_RCE = 39, + ELF_Machine_ARM = 40, + ELF_Machine_ALPHA = 41, + ELF_Machine_SH = 42, + ELF_Machine_SPARCV9 = 43, + ELF_Machine_TRICORE = 44, + ELF_Machine_ARC = 45, + ELF_Machine_H8_300 = 46, + ELF_Machine_H8_300H = 47, + ELF_Machine_H8S = 48, + ELF_Machine_H8_500 = 49, + ELF_Machine_IA_64 = 50, + ELF_Machine_MIPS_X = 51, + ELF_Machine_COLDFIRE = 52, + ELF_Machine_68HC12 = 53, + ELF_Machine_MMA = 54, + ELF_Machine_PCP = 55, + ELF_Machine_NCPU = 56, + ELF_Machine_NDR1 = 57, + ELF_Machine_STARCORE = 58, + ELF_Machine_ME16 = 59, + ELF_Machine_ST100 = 60, + ELF_Machine_TINYJ = 61, + ELF_Machine_X86_64 = 62, + ELF_Machine_PDSP = 63, + ELF_Machine_PDP10 = 64, + ELF_Machine_PDP11 = 65, + ELF_Machine_FX66 = 66, + ELF_Machine_ST9PLUS = 67, + ELF_Machine_ST7 = 68, + ELF_Machine_68HC16 = 69, + ELF_Machine_68HC11 = 70, + ELF_Machine_68HC08 = 71, + ELF_Machine_68HC05 = 72, + ELF_Machine_SVX = 73, + ELF_Machine_ST19 = 74, + ELF_Machine_VAX = 75, + ELF_Machine_CRIS = 76, + ELF_Machine_JAVELIN = 77, + ELF_Machine_FIREPATH = 78, + ELF_Machine_ZSP = 79, + ELF_Machine_MMIX = 80, + ELF_Machine_HUANY = 81, + ELF_Machine_PRISM = 82, + ELF_Machine_AVR = 83, + ELF_Machine_FR30 = 84, + ELF_Machine_D10V = 85, + ELF_Machine_D30V = 86, + ELF_Machine_V850 = 87, + ELF_Machine_M32R = 88, + ELF_Machine_MN10300 = 89, + ELF_Machine_MN10200 = 90, + ELF_Machine_PJ = 91, + ELF_Machine_OPENRISC = 92, + ELF_Machine_ARC_A5 = 93, + ELF_Machine_XTENSA = 94, + ELF_Machine_VIDEOCORE = 95, + ELF_Machine_TMM_GPP = 96, + ELF_Machine_NS32K = 97, + ELF_Machine_TPC = 98, + ELF_Machine_SNP1K = 99, + ELF_Machine_ST200 = 100, +} ELF_Machine; + +typedef enum ELF_Version{ + ELF_Version_NONE = 0, + ELF_Version_CURRENT = 1, +} ELF_Version; + +typedef enum ELF_Identification{ + ELF_Identification_MAG0 = 0, + ELF_Identification_MAG1 = 1, + ELF_Identification_MAG2 = 2, + ELF_Identification_MAG3 = 3, + ELF_Identification_CLASS = 4, + ELF_Identification_DATA = 5, + ELF_Identification_VERSION = 6, + ELF_Identification_OSABI = 7, + ELF_Identification_ABIVERSION = 8, + ELF_Identification_PAD = 9, +} ELF_Identification; + +read_only global U8 elf_magic[] = {0x7F, 'E', 'L', 'F'}; + +typedef enum ELF_Class{ + ELF_Class_NONE = 0, + ELF_Class_32 = 1, + ELF_Class_64 = 2, +} ELF_Class; + +typedef enum ELF_DataEncoding{ + ELF_DataEncoding_NONE = 0, + ELF_DataEncoding_2LSB = 1, + ELF_DataEncoding_2MSB = 2, +} ELF_DataEncoding; + +typedef enum ELF_OsAbi{ + ELF_OsAbi_NONE = 0, + ELF_OsAbi_HPUX = 1, + ELF_OsAbi_NETBSD = 2, + ELF_OsAbi_LINUX = 3, + ELF_OsAbi_SOLARIS = 6, + ELF_OsAbi_AIX = 7, + ELF_OsAbi_IRIX = 8, + ELF_OsAbi_FREEBSD = 9, + ELF_OsAbi_TRU64 = 10, + ELF_OsAbi_MODESTO = 11, + ELF_OsAbi_OPENBSD = 12, + ELF_OsAbi_OPENVMS = 13, + ELF_OsAbi_NSK = 14, +} ELF_OsAbi; + +// sections + +typedef enum ELF_ReservedSectionIndex{ + ELF_ReservedSectionIndex_UNDEF = 0, + ELF_ReservedSectionIndex_LORESERVE = 0xFF00, + ELF_ReservedSectionIndex_LOPROC = 0xFF00, + ELF_ReservedSectionIndex_HIPROC = 0xFF1F, + ELF_ReservedSectionIndex_LOOS = 0xFF20, + ELF_ReservedSectionIndex_HIOS = 0xFF3F, + ELF_ReservedSectionIndex_ABS = 0xFFF1, + ELF_ReservedSectionIndex_COMMON = 0xFFF2, + ELF_ReservedSectionIndex_XINDEX = 0xFFFF, + ELF_ReservedSectionIndex_HIRESERVE = 0xFFFF, +} ELF_ReservedSectionIndex; + +typedef struct ELF_Shdr32{ + U32 sh_name; + U32 sh_type; + U32 sh_flags; + U32 sh_addr; + U32 sh_offset; + U32 sh_size; + U32 sh_link; + U32 sh_info; + U32 sh_addralign; + U32 sh_entsize; +} ELF_Shdr32; + +typedef struct ELF_Shdr64{ + U32 sh_name; + U32 sh_type; + U64 sh_flags; + U64 sh_addr; + U64 sh_offset; + U64 sh_size; + U32 sh_link; + U32 sh_info; + U64 sh_addralign; + U64 sh_entsize; +} ELF_Shdr64; + +// X(name, code) +#define ELF_SectionTypeXList(X)\ +X(NULL, 0)\ +X(PROGBITS, 1)\ +X(SYMTAB, 2)\ +X(STRTAB, 3)\ +X(RELA, 4)\ +X(HASH, 5)\ +X(DYNAMIC, 6)\ +X(NOTE, 7)\ +X(NOBITS, 8)\ +X(REL, 9)\ +X(SHLIB, 10)\ +X(DYNSYM, 11)\ +X(INIT_ARRAY, 14)\ +X(FINI_ARRAY, 15)\ +X(PREINIT_ARRAY, 16)\ +X(GROUP, 17)\ +X(SYMTAB_SHNDX, 18)\ +X(LOOS, 0x60000000)\ +X(HIOS, 0x6FFFFFFF)\ +X(LOPROC, 0x70000000)\ +X(HIPROC, 0x7FFFFFFF)\ +X(LOUSER, 0x80000000)\ +X(HIUSER, 0x8FFFFFFF) + +typedef enum ELF_SectionType{ +#define X(N,C) ELF_SectionType_##N = C, + ELF_SectionTypeXList(X) +#undef X +} ELF_SectionType; + +typedef enum ELF_SectionAttributeFlags{ + ELF_SectionAttributeFlag_WRITE = 0x001, + ELF_SectionAttributeFlag_ALLOC = 0x002, + ELF_SectionAttributeFlag_EXECINSTR = 0x004, + ELF_SectionAttributeFlag_MERGE = 0x010, + ELF_SectionAttributeFlag_STRINGS = 0x020, + ELF_SectionAttributeFlag_INFO_LINK = 0x040, + ELF_SectionAttributeFlag_LINK_ORDER = 0x080, + ELF_SectionAttributeFlag_OS_NONCONFORMING = 0x100, + ELF_SectionAttributeFlag_GROUP = 0x200, + ELF_SectionAttributeFlag_TLS = 0x400, + ELF_SectionAttributeFlag_MASKOS = 0x0FF00000, + ELF_SectionAttributeFlag_MASKPROC = 0xF0000000, +} ELF_SectionAttributeFlags; + +typedef enum ELF_SectionGroupFlags{ + ELF_SectionGroupFlag_COMDAT = 0x1, + ELF_SectionGroupFlag_MASKOS = 0x0FF00000, + ELF_SectionGroupFlag_MASKPROC = 0xF0000000, +} ELF_SectionGroupFlags; + +typedef enum ELF_ReservedSymbolTableIndex{ + ELF_ReservedSymbolTableIndex_UNDEF = 0, +} ELF_ReservedSymbolTableIndex; + +// symbol table + +typedef struct ELF_Sym32{ + U32 st_name; + U32 st_value; + U32 st_size; + U8 st_info; + U8 st_other; + U16 st_shndx; +} ELF_Sym32; + +typedef struct ELF_Sym64{ + U32 st_name; + U8 st_info; + U8 st_other; + U16 st_shndx; + U64 st_value; + U64 st_size; +} ELF_Sym64; + +#define ELF_SymBindingFromInfo(x) (ELF_SymbolBinding)((x)>>4) +#define ELF_SymTypeFromInfo(x) (ELF_SymbolType)((x)&0xF) +#define ELF_SymInfoFromBindingType(b,t) ((((b)<<4)&0xF)|((t)&0xF)) + +#define ELF_SymVisibilityFromOther(x) ((x)&0x3) +#define ELF_SymOtherFromVisibility(x) ((x)&0x3) + +#define ELF_SymbolBindingXList(X)\ +X(LOCAL, 0)\ +X(GLOBAL, 1)\ +X(WEAK, 2)\ +X(LOOS, 10)\ +X(HIOS, 12)\ +X(LOPROC, 13)\ +X(HIPROC, 15)\ + +typedef enum ELF_SymbolBinding{ +#define X(N,C) ELF_SymbolBinding_##N = C, + ELF_SymbolBindingXList(X) +#undef X +} ELF_SymbolBinding; + +#define ELF_SymbolTypeXList(X)\ +X(NOTYPE, 0)\ +X(OBJECT, 1)\ +X(FUNC, 2)\ +X(SECTION, 3)\ +X(FILE, 4)\ +X(COMMON, 5)\ +X(TLS, 6)\ +X(LOOS, 10)\ +X(HIOS, 12)\ +X(LOPROC, 13)\ +X(HIPROC, 15) + +typedef enum ELF_SymbolType{ +#define X(N,C) ELF_SymbolType_##N = C, + ELF_SymbolTypeXList(X) +#undef X +} ELF_SymbolType; + +#define ELF_SymbolVisibilityXList(X)\ +X(DEFAULT, 0)\ +X(INTERNAL, 1)\ +X(HIDDEN, 2)\ +X(PROTECTED, 3) + +typedef enum ELF_SymbolVisibility{ +#define X(N,C) ELF_SymbolVisibility_##N = C, + ELF_SymbolVisibilityXList(X) +#undef X +} ELF_SymbolVisibility; + +// relocation + +typedef struct ELF_Rel32{ + U32 r_offset; + U32 r_info; +} ELF_Rel32; + +typedef struct ELF_Rela32{ + U32 r_offset; + U32 r_info; + S32 r_addend; +} ELF_Rela32; + +typedef struct ELF_Rel64{ + U64 r_offset; + U64 r_info; +} ELF_Rel64; + +typedef struct ELF_Rela64{ + U64 r_offset; + U64 r_info; + S64 r_addend; +} ELF_Rela64; + +#define ELF_RelSymIndexFromInfo32(x) ((x)>>8) +#define ELF_RelTypeFromInfo32(x) ((x)&0xF) +#define ELF_RelInfoFromSymIndexType32(n,t) (((n)<<8)|((t)&0xF)) + +#define ELF_RelSymIndexFromInfo64(x) ((x)>>32) +#define ELF_RelTypeFromInfo64(x) ((x)&0xFFFFFFFFL) +#define ELF_RelInfoFromSymIndexType64(n,t) (((n)<<8)|((t)&0xFFFFFFFFL)) + + + +// program header + +typedef struct ELF_Phdr32{ + U32 p_type; + U32 p_offset; + U32 p_vaddr; + U32 p_paddr; + U32 p_filesz; + U32 p_memsz; + U32 p_flags; + U32 p_align; +} ELF_Phdr32; + +typedef struct ELF_Phdr64{ + U32 p_type; + U32 p_flags; + U64 p_offset; + U64 p_vaddr; + U64 p_paddr; + U64 p_filesz; + U64 p_memsz; + U64 p_align; +} ELF_Phdr64; + +typedef enum ELF_SegmentType{ + ELF_SegmentType_NULL = 0, + ELF_SegmentType_LOAD = 1, + ELF_SegmentType_DYNAMIC = 2, + ELF_SegmentType_INTERP = 3, + ELF_SegmentType_NOTE = 4, + ELF_SegmentType_SHLIB = 5, + ELF_SegmentType_PHDR = 6, + ELF_SegmentType_TLS = 7, + ELF_SegmentType_LOOS = 0x60000000, + ELF_SegmentType_HIOS = 0x6fffffff, + ELF_SegmentType_LOPROC = 0x70000000, + ELF_SegmentType_HIPROC = 0x7fffffff, +} ELF_SegmentType; + +typedef enum ELF_SegmentFlags{ + ELF_SegmentFlag_X = 0x1, + ELF_SegmentFlag_W = 0x2, + ELF_SegmentFlag_R = 0x4, + ELF_SegmentFlag_MASKOS = 0x0FF00000, + ELF_SegmentFlag_MASKPROC = 0xF0000000, +} ELF_SegmentFlags; + +//////////////////////////////// +//~ ELF Parser Types + +// elf top level + +typedef struct ELF_SectionArray{ + ELF_Shdr64 *sections; + U64 count; +} ELF_SectionArray; + +typedef struct ELF_SegmentArray{ + ELF_Phdr64 *segments; + U64 count; +} ELF_SegmentArray; + +typedef struct ELF_Parsed{ + String8 data; + ELF_Class elf_class; + Architecture arch; + + ELF_Shdr64 *sections; + String8 *section_names; + U64 section_foff; + U64 section_count; + + ELF_Phdr64 *segments; + U64 segment_foff; + U64 segment_count; + + U64 vbase; + U64 entry_vaddr; + U64 section_name_table_foff; + U64 section_name_table_opl; + + U64 strtab_idx; + U64 symtab_idx; + U64 dynsym_idx; +} ELF_Parsed; + +// elf symtab + +typedef struct ELF_SymArray{ + ELF_Sym64 *symbols; + U64 count; +} ELF_SymArray; + +//////////////////////////////// +//~ ELF Parser Functions + +static ELF_Parsed* elf_parsed_from_data(Arena *arena, String8 elf_data); + +static ELF_SectionArray elf_section_array_from_elf(ELF_Parsed *elf); +static String8Array elf_section_name_array_from_elf(ELF_Parsed *elf); +static ELF_SegmentArray elf_segment_array_from_elf(ELF_Parsed *elf); + +static String8 elf_section_name_from_name_offset(ELF_Parsed *elf, U64 offset); +static String8 elf_section_name_from_idx(ELF_Parsed *elf, U32 idx); +static U32 elf_section_idx_from_name(ELF_Parsed *elf, String8 name); + +static String8 elf_section_data_from_idx(ELF_Parsed *elf, U32 idx); + +static ELF_SymArray elf_sym_array_from_data(Arena *arena, ELF_Class elf_class, String8 data); + +// string functions + +static String8 elf_string_from_section_type(ELF_SectionType section_type); +static String8 elf_string_from_symbol_binding(ELF_SymbolBinding binding); +static String8 elf_string_from_symbol_type(ELF_SymbolType type); +static String8 elf_string_from_symbol_visibility(ELF_SymbolVisibility visibility); + +#endif //RDI_ELF_H diff --git a/src/rdi_from_dwarf/rdi_from_dwarf.c b/src/rdi_from_dwarf/rdi_from_dwarf.c index 473313d2..1b62e666 100644 --- a/src/rdi_from_dwarf/rdi_from_dwarf.c +++ b/src/rdi_from_dwarf/rdi_from_dwarf.c @@ -1,898 +1,898 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Build Options - -#define BUILD_VERSION_MAJOR 0 -#define BUILD_VERSION_MINOR 9 -#define BUILD_VERSION_PATCH 11 -#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA" -#define BUILD_TITLE "rdi_from_dwarf" -#define BUILD_CONSOLE_INTERFACE 1 - -//////////////////////////////// -//~ rjf: Includes - -//- rjf: [lib] -#include "lib_rdi_format/rdi_format.h" -#include "lib_rdi_format/rdi_format.c" -#include "third_party/rad_lzb_simple/rad_lzb_simple.h" -#include "third_party/rad_lzb_simple/rad_lzb_simple.c" - -//- rjf: [h] -#include "base/base_inc.h" -#include "os/os_inc.h" -#include "rdi_make/rdi_make_local.h" -#include "rdi_elf.h" -#include "rdi_dwarf.h" -#include "rdi_dwarf_stringize.h" -#include "rdi_from_dwarf.h" - -//- rjf: [c] -#include "base/base_inc.c" -#include "os/os_inc.c" -#include "rdi_make/rdi_make_local.c" -#include "rdi_elf.c" -#include "rdi_dwarf.c" -#include "rdi_dwarf_stringize.c" - -// TODO(allen): -// [ ] need sample data for .debug_names - -//////////////////////////////// -//~ Program Parameters Parser - -static DWARFCONV_Params* -dwarf_convert_params_from_cmd_line(Arena *arena, CmdLine *cmdline){ - DWARFCONV_Params *result = push_array(arena, DWARFCONV_Params, 1); - result->unit_idx_max = ~0ull; - - // get input pdb - { - String8 input_name = cmd_line_string(cmdline, str8_lit("elf")); - if (input_name.size == 0){ - str8_list_push(arena, &result->errors, - str8_lit("missing required parameter '--elf:'")); - } - - if (input_name.size > 0){ - String8 input_data = os_data_from_file_path(arena, input_name); - - if (input_data.size == 0){ - str8_list_pushf(arena, &result->errors, - "could not load input file '%.*s'", str8_varg(input_name)); - } - - if (input_data.size != 0){ - result->input_elf_name = input_name; - result->input_elf_data = input_data; - } - } - } - - // get output name - { - result->output_name = cmd_line_string(cmdline, str8_lit("out")); - } - - // error options - if (cmd_line_has_flag(cmdline, str8_lit("hide_errors"))){ - String8List vals = cmd_line_strings(cmdline, str8_lit("hide_errors")); - - // if no values - set all to hidden - if (vals.node_count == 0){ - B8 *ptr = (B8*)&result->hide_errors; - B8 *opl = ptr + sizeof(result->hide_errors); - for (;ptr < opl; ptr += 1){ - *ptr = 1; - } - } - - // for each explicit value set the corresponding flag to hidden - for (String8Node *node = vals.first; - node != 0; - node = node->next){ - if (str8_match(node->string, str8_lit("input"), 0)){ - result->hide_errors.input = 1; - } - } - - } - - // unit idx selector - if (cmd_line_has_flag(cmdline, str8_lit("unit_idx"))){ - String8List vals = cmd_line_strings(cmdline, str8_lit("unit_idx")); - - // single value unit index - if (vals.node_count == 1){ - U64 idx = u64_from_str8(vals.first->string, 10); - result->unit_idx_min = idx; - result->unit_idx_max = idx; - } - - // range value unit index - else if (vals.node_count >= 2){ - U64 idx_a = u64_from_str8(vals.first->string, 10); - U64 idx_b = u64_from_str8(vals.first->next->string, 10); - result->unit_idx_min = Min(idx_a, idx_b); - result->unit_idx_max = Max(idx_a, idx_b); - } - } - - // dump options - if (cmd_line_has_flag(cmdline, str8_lit("dump"))){ - result->dump = 1; - - String8List vals = cmd_line_strings(cmdline, str8_lit("dump")); - if (vals.first == 0){ - B8 *ptr = &result->dump__first; - for (; ptr < &result->dump__last; ptr += 1){ - *ptr = 1; - } - } - else{ - for (String8Node *node = vals.first; - node != 0; - node = node->next){ - if (str8_match(node->string, str8_lit("header"), 0)){ - result->dump_header = 1; - } - else if (str8_match(node->string, str8_lit("sections"), 0)){ - result->dump_sections = 1; - } - else if (str8_match(node->string, str8_lit("segments"), 0)){ - result->dump_segments = 1; - } - else if (str8_match(node->string, str8_lit("symtab"), 0)){ - result->dump_symtab = 1; - } - else if (str8_match(node->string, str8_lit("dynsym"), 0)){ - result->dump_dynsym = 1; - } - else if (str8_match(node->string, str8_lit("debug_sections"), 0)){ - result->dump_debug_sections = 1; - } - else if (str8_match(node->string, str8_lit("debug_info"), 0)){ - result->dump_debug_info = 1; - } - else if (str8_match(node->string, str8_lit("debug_abbrev"), 0)){ - result->dump_debug_abbrev = 1; - } - else if (str8_match(node->string, str8_lit("debug_pubnames"), 0)){ - result->dump_debug_pubnames = 1; - } - else if (str8_match(node->string, str8_lit("debug_pubtypes"), 0)){ - result->dump_debug_pubtypes = 1; - } - else if (str8_match(node->string, str8_lit("debug_names"), 0)){ - result->dump_debug_names = 1; - } - else if (str8_match(node->string, str8_lit("debug_aranges"), 0)){ - result->dump_debug_aranges = 1; - } - else if (str8_match(node->string, str8_lit("debug_addr"), 0)){ - result->dump_debug_addr = 1; - } - } - } - } - - return(result); -} - -//////////////////////////////// -//~ Entry Point - -static void -dump_symtab(Arena *arena, String8List *out, ELF_SymArray *symbols, String8 strtab, - U32 indent){ - static char spaces[] = " "; - - U8 *str_first = strtab.str; - U8 *str_opl = strtab.str + strtab.size; - - ELF_Sym64 *symbol = symbols->symbols; - U64 count = symbols->count; - for (U64 i = 0; i < count; i += 1, symbol += 1){ - U8 *name_first = str_first + symbol->st_name; - U8 *name_opl = name_first; - for (;name_opl < str_opl && *name_opl != 0;) name_opl += 1; - String8 name = str8_range(name_first, name_opl); - - ELF_SymbolBinding binding = ELF_SymBindingFromInfo(symbol->st_info); - String8 binding_string = elf_string_from_symbol_binding(binding); - - ELF_SymbolType type = ELF_SymTypeFromInfo(symbol->st_info); - String8 type_string = elf_string_from_symbol_type(type); - - ELF_SymbolVisibility vis = ELF_SymVisibilityFromOther(symbol->st_other); - String8 vis_string = elf_string_from_symbol_visibility(vis); - - str8_list_pushf(arena, out, - "%.*ssymbol[%5llu] %6.*s %7.*s %9.*s 0x%08llx size=%-5llu sec=%-5u " - "%.*s\n", - indent, spaces, i, - str8_varg(binding_string), str8_varg(type_string), - str8_varg(vis_string), - symbol->st_value, symbol->st_size, - symbol->st_shndx, str8_varg(name)); - } -} - -#if 0 -static void -dump_entry_tree(Arena *arena, String8List *out, - DWARF_Parsed *dwarf, DWARF_InfoUnit *unit, - DWARF_InfoEntry *entry, U32 indent){ - static char spaces[] = " "; - - DWARF_AbbrevDecl *abbrev_decl = entry->abbrev_decl; - - // tag - DWARF_Tag tag = abbrev_decl->tag; - String8 tag_string = dwarf_string_from_tag(tag); - str8_list_pushf(arena, out, "%.*sentry(@%llx) TAG %.*s\n", - indent, spaces, entry->info_offset, str8_varg(tag_string)); - - // attributes - U32 attrib_count = abbrev_decl->attrib_count; - DWARF_AbbrevAttribSpec *attrib_spec = abbrev_decl->attrib_specs; - DWARF_InfoAttribVal *attrib_val = entry->attrib_vals; - for (U32 i = 0; i < attrib_count; i += 1, attrib_spec += 1, attrib_val += 1){ - // attribute name - DWARF_AttributeName name = attrib_spec->name; - String8 name_string = dwarf_string_from_attribute_name(name); - str8_list_pushf(arena, out, "%.*sATTR %.*s ", indent + 4, spaces, str8_varg(name_string)); - - // attribute value - switch (attrib_spec->form){ - default: - { - String8 form_string = dwarf_string_from_attribute_form(attrib_spec->form); - str8_list_pushf(arena, out, " {%llu, 0x%p}\n", - str8_varg(form_string), attrib_val->val, attrib_val->dataptr); - }break; - - case DWARF_AttributeForm_strp: - { - String8 str = {0}; - - String8 data = dwarf->debug_data[DWARF_SectionCode_Str]; - U64 off = attrib_val->val; - if (off < data.size){ - U8 *start = data.str + off; - U8 *opl = data.str + data.size; - U8 *ptr = start; - for (;ptr < opl && *ptr != 0;) ptr += 1; - str = str8_range(start, ptr); - } - - str8_list_pushf(arena, out, "'%.*s'\n", str8_varg(str)); - }break; - - case DWARF_AttributeForm_sec_offset: - { - DWARF_AttributeClassFlags attr_classes1 = dwarf_attribute_class_from_name(name); - DWARF_AttributeClassFlags attr_classes2 = DWARF_AttributeClassFlag_sec_offset_classes; - DWARF_AttributeClassFlags attr_classes = attr_classes1&attr_classes2; - - DWARF_SectionCode sec_code = DWARF_SectionCode_Null; - if (unit->dwarf_version == 5){ - switch (attr_classes){ - case DWARF_AttributeClassFlag_addrptr: sec_code = DWARF_SectionCode_Addr; break; - case DWARF_AttributeClassFlag_lineptr: sec_code = DWARF_SectionCode_Line; break; - case DWARF_AttributeClassFlag_loclist: sec_code = DWARF_SectionCode_LocLists; break; - case DWARF_AttributeClassFlag_loclistsptr: sec_code = DWARF_SectionCode_LocLists; break; - case DWARF_AttributeClassFlag_macptr: sec_code = DWARF_SectionCode_Macro; break; - case DWARF_AttributeClassFlag_rnglist: sec_code = DWARF_SectionCode_RngLists; break; - case DWARF_AttributeClassFlag_rnglistsptr: sec_code = DWARF_SectionCode_RngLists; break; - case DWARF_AttributeClassFlag_stroffsetsptr: sec_code = DWARF_SectionCode_StrOffsets; break; - } - } - else if (unit->dwarf_version == 4){ - switch (attr_classes){ - case DWARF_AttributeClassFlag_lineptr: sec_code = DWARF_SectionCode_Line; break; - case DWARF_AttributeClassFlag_loclist: sec_code = DWARF_SectionCode_Loc; break; - case DWARF_AttributeClassFlag_macptr: sec_code = DWARF_SectionCode_MacInfo; break; - case DWARF_AttributeClassFlag_rnglist: sec_code = DWARF_SectionCode_Ranges; break; - } - } - - String8 sec_name = dwarf_name_from_debug_section(dwarf, sec_code); - str8_list_pushf(arena, out, "sec(%.*s) + %llu\n", str8_varg(sec_name), attrib_val->val); - }break; - - case DWARF_AttributeForm_ref1: - case DWARF_AttributeForm_ref2: - case DWARF_AttributeForm_ref4: - case DWARF_AttributeForm_ref8: - case DWARF_AttributeForm_ref_udata: - { - str8_list_pushf(arena, out, "entry(@%llx)\n", attrib_val->val); - }break; - - case DWARF_AttributeForm_addr: - { - str8_list_pushf(arena, out, "0x%llx\n", attrib_val->val); - }break; - - case DWARF_AttributeForm_exprloc: - { - str8_list_pushf(arena, out, "expression\n"); - // TODO(allen): dwarf expression dumping - }break; - - case DWARF_AttributeForm_strx1: - case DWARF_AttributeForm_strx2: - case DWARF_AttributeForm_strx3: - case DWARF_AttributeForm_strx4: - { - String8 str = {0}; - - U32 idx = attrib_val->val; - U64 str_offsets_off = unit->str_offsets_base + idx*unit->offset_size; - - String8 str_offsets = dwarf->debug_data[DWARF_SectionCode_StrOffsets]; - if (str_offsets_off + unit->offset_size < str_offsets.size){ - U64 off = 0; - MemoryCopy(&off, str_offsets.str + str_offsets_off, unit->offset_size); - - String8 data = dwarf->debug_data[DWARF_SectionCode_Str]; - if (off < data.size){ - U8 *start = data.str + off; - U8 *opl = data.str + data.size; - U8 *ptr = start; - for (;ptr < opl && *ptr != 0;) ptr += 1; - str = str8_range(start, ptr); - } - } - - str8_list_pushf(arena, out, "'%.*s'\n", str8_varg(str)); - }break; - - case DWARF_AttributeForm_addrx: - case DWARF_AttributeForm_addrx1: - case DWARF_AttributeForm_addrx2: - case DWARF_AttributeForm_addrx3: - case DWARF_AttributeForm_addrx4: - { - U64 address = 0; - - U32 idx = attrib_val->val; - U64 address_off = unit->addr_base + idx*unit->address_size; - - String8 data = dwarf->debug_data[DWARF_SectionCode_Addr]; - if (address_off + unit->address_size < data.size){ - MemoryCopy(&address, data.str + address_off, unit->address_size); - } - - str8_list_pushf(arena, out, "0x%x\n", address); - }break; - - case DWARF_AttributeForm_rnglistx: - { - U64 rnglist_off = unit->rnglists_base + attrib_val->val; - int x = 0; - }break; - - case DWARF_AttributeForm_data1: - case DWARF_AttributeForm_data2: - case DWARF_AttributeForm_data4: - case DWARF_AttributeForm_data8: - case DWARF_AttributeForm_data16: - case DWARF_AttributeForm_udata: - case DWARF_AttributeForm_implicit_const: - case DWARF_AttributeForm_flag: - case DWARF_AttributeForm_flag_present: - { - str8_list_pushf(arena, out, "%llu\n", attrib_val->val); - }break; - - case DWARF_AttributeForm_sdata: - { - str8_list_pushf(arena, out, "%lld\n", (S64)attrib_val->val); - }break; - - case DWARF_AttributeForm_string: - { - str8_list_pushf(arena, out, "'%.*s'\n", (int)attrib_val->val, attrib_val->dataptr); - }break; - } - } - - // dump children - for (DWARF_InfoEntry *child = entry->first_child; - child != 0; - child = child->next_sibling){ - dump_entry_tree(arena, out, dwarf, unit, child, indent + 1); - } -} -#endif - -internal void -entry_point(CmdLine *cmd_line) -{ - Arena *arena = arena_alloc(); - - // parse arguments - DWARFCONV_Params *params = dwarf_convert_params_from_cmd_line(arena, cmd_line); - - // show input errors - if (params->errors.node_count > 0 && - !params->hide_errors.input){ - for (String8Node *node = params->errors.first; - node != 0; - node = node->next){ - fprintf(stdout, "error(input): %.*s\n", str8_varg(node->string)); - } - } - - // will we try to parse an input file? - B32 try_parse_input = (params->errors.node_count == 0); - - // track parse success - B32 successful_parse = 1; - -#define PARSE_CHECK_ERROR(p,fmt,...) do{ if ((p) == 0){ \ -successful_parse = 0; \ -fprintf(stdout, "error(parsing): " fmt "\n", __VA_ARGS__); \ -} }while(0) - - // parse elf - ELF_Parsed *elf = 0; - if (try_parse_input) ProfScope("parse elf"){ - elf = elf_parsed_from_data(arena, params->input_elf_data); - PARSE_CHECK_ERROR(elf, "ELF"); - } - - // parse strtab - String8 strtab = {0}; - if (elf != 0) ProfScope("parse strtab"){ - strtab = elf_section_data_from_idx(elf, elf->strtab_idx); - } - - // parse symtab - ELF_SymArray symtab = {0}; - if (elf != 0) ProfScope("parse symtab"){ - String8 data = elf_section_data_from_idx(elf, elf->symtab_idx); - symtab = elf_sym_array_from_data(arena, elf->elf_class, data); - } - - // parse dynsym - ELF_SymArray dynsym = {0}; - if (elf != 0) ProfScope("parse dynsym"){ - String8 data = elf_section_data_from_idx(elf, elf->dynsym_idx); - dynsym = elf_sym_array_from_data(arena, elf->elf_class, data); - } - - // parse dwarf - DWARF_Parsed *dwarf = 0; - if (elf != 0) ProfScope("parse dwarf"){ - dwarf = dwarf_parsed_from_elf(arena, elf); - PARSE_CHECK_ERROR(dwarf, "DWARF"); - } - - // parse info - DWARF_InfoParsed *info = 0; - if (dwarf != 0){ - String8 data = dwarf->debug_data[DWARF_SectionCode_Info]; - if (data.size > 0) ProfScope("parse .debug_info"){ - info = dwarf_info_from_data(arena, data); - PARSE_CHECK_ERROR(info, "DEBUG INFO"); - } - } - - // parse pubnames - DWARF_PubNamesParsed *pubnames = 0; - if (dwarf != 0){ - String8 data = dwarf->debug_data[DWARF_SectionCode_PubNames]; - if (data.size) ProfScope("parse .debug_pubnames"){ - pubnames = dwarf_pubnames_from_data(arena, data); - PARSE_CHECK_ERROR(pubnames, "DEBUG PUBNAMES"); - } - } - - // parse pubtypes - DWARF_PubNamesParsed *pubtypes = 0; - if (dwarf != 0){ - String8 data = dwarf->debug_data[DWARF_SectionCode_PubTypes]; - if (data.size) ProfScope("parse .debug_pubtypes"){ - pubtypes = dwarf_pubnames_from_data(arena, data); - PARSE_CHECK_ERROR(pubtypes, "DEBUG PUBTYPES"); - } - } - - // parse names - DWARF_NamesParsed *names = 0; - if (dwarf != 0){ - String8 data = dwarf->debug_data[DWARF_SectionCode_Names]; - if (data.size) ProfScope("parse .debug_names"){ - names = dwarf_names_from_data(arena, data); - PARSE_CHECK_ERROR(names, "DEBUG NAMES"); - } - } - - // parse aranges - DWARF_ArangesParsed *aranges = 0; - if (dwarf != 0){ - String8 data = dwarf->debug_data[DWARF_SectionCode_Aranges]; - if (data.size) ProfScope("parse .debug_aranges"){ - aranges = dwarf_aranges_from_data(arena, data); - PARSE_CHECK_ERROR(aranges, "DEBUG ARANGES"); - } - } - - // parse addr - DWARF_AddrParsed *addr = 0; - if (dwarf != 0){ - String8 data = dwarf->debug_data[DWARF_SectionCode_Addr]; - if (data.size) ProfScope("parse .debug_addr"){ - addr = dwarf_addr_from_data(arena, data); - PARSE_CHECK_ERROR(addr, "DEBUG ADDR"); - } - } - -#if 0 - // parse abbrev - DWARF_AbbrevParsed *abbrev = 0; - if (dwarf != 0){ - String8 data = dwarf->debug_data[DWARF_SectionCode_Abbrev]; - if (data.size > 0) ProfScope("parse .debug_abbrev"){ - DWARF_AbbrevParams abbrev_params = {0}; - abbrev_params.unit_idx_min = params->unit_idx_min; - abbrev_params.unit_idx_max = params->unit_idx_max; - abbrev = dwarf_abbrev_from_data(arena, data, &abbrev_params); - PARSE_CHECK_ERROR(abbrev, "DEBUG ABBREV"); - } - } - - // parse info - DWARF_InfoParsed *info = 0; - if (abbrev != 0){ - String8 data = dwarf->debug_data[DWARF_SectionCode_Info]; - if (data.size > 0) ProfScope("parse .debug_info"){ - DWARF_InfoParams info_params = {0}; - info_params.unit_idx_min = params->unit_idx_min; - info_params.unit_idx_max = params->unit_idx_max; - info = dwarf_info_from_data(arena, data, &info_params, abbrev); - PARSE_CHECK_ERROR(info, "DEBUG INFO"); - } - } -#endif - - // dump - if (params->dump) ProfScope("dump"){ - String8List dump = {0}; - - // ELF - if (params->dump_header){ - if (elf != 0){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "ELF:\n")); - - // TODO: better stringizers for fields here - str8_list_pushf(arena, &dump, " elf_class=%u\n", elf->elf_class); - str8_list_pushf(arena, &dump, " arch=%u\n", elf->arch); - str8_list_pushf(arena, &dump, " section_count=%llu\n", elf->section_count); - str8_list_pushf(arena, &dump, " segment_count=%llu\n", elf->segment_count); - str8_list_pushf(arena, &dump, " vbase=0x%llx\n", elf->vbase); - str8_list_pushf(arena, &dump, " entry_vaddr=0x%llx\n", elf->vbase); - - str8_list_push(arena, &dump, str8_lit("\n")); - } - } - - // SECTIONS - if (params->dump_sections){ - if (elf != 0){ - ELF_SectionArray section_array = elf_section_array_from_elf(elf); - String8Array section_name_array = elf_section_name_array_from_elf(elf); - - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "SECTIONS:\n")); - - ELF_Shdr64 *sec = section_array.sections; - String8 *sec_name = section_name_array.v; - U64 count = section_array.count; - for (U64 i = 0 ; i < count; i += 1, sec += 1, sec_name += 1){ - String8 type_string = elf_string_from_section_type(sec->sh_type); - - // TODO: better stringizers for fields here - str8_list_pushf(arena, &dump, " section[%llu]:\n", i); - str8_list_pushf(arena, &dump, " name='%.*s'\n", str8_varg(*sec_name)); - str8_list_pushf(arena, &dump, " type=%.*s\n", str8_varg(type_string)); - str8_list_pushf(arena, &dump, " flags=0x%llx\n", sec->sh_flags); - str8_list_pushf(arena, &dump, " addr=0x%llx\n", sec->sh_addr); - str8_list_pushf(arena, &dump, " offset=0x%llx\n", sec->sh_offset); - str8_list_pushf(arena, &dump, " size=%llu\n", sec->sh_size); - str8_list_pushf(arena, &dump, " link=%u\n", sec->sh_link); - str8_list_pushf(arena, &dump, " info=%u\n", sec->sh_info); - str8_list_pushf(arena, &dump, " addralign=0x%llx\n", sec->sh_addralign); - str8_list_pushf(arena, &dump, " entsize=%llu\n", sec->sh_entsize); - str8_list_push(arena, &dump, str8_lit("\n")); - } - } - } - - // SYMTAB - if (symtab.count > 0 && params->dump_symtab){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "SYMTAB:\n")); - str8_list_pushf(arena, &dump, " section: %llu\n", elf->symtab_idx); - dump_symtab(arena, &dump, &symtab, strtab, 1); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - // DYNSYM - if (dynsym.count > 0 && params->dump_dynsym){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "DYNSYM:\n")); - str8_list_pushf(arena, &dump, " section: %llu\n", elf->dynsym_idx); - dump_symtab(arena, &dump, &dynsym, strtab, 1); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - // SEGMENTS - if (params->dump_segments){ - if (elf != 0){ - ELF_SegmentArray segment_array = elf_segment_array_from_elf(elf); - - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "SEGMENTS:\n")); - - ELF_Phdr64 *segments = segment_array.segments; - U64 count = segment_array.count; - for (U64 i = 0 ; i < count; i += 1){ - ELF_Phdr64 *seg = segments + i; - - // TODO: better stringizers for fields here - str8_list_pushf(arena, &dump, " segment[%llu]:\n", i); - str8_list_pushf(arena, &dump, " p_type=%u\n", seg->p_type); - str8_list_pushf(arena, &dump, " p_flags=0x%x\n", seg->p_flags); - str8_list_pushf(arena, &dump, " p_offset=0x%llx\n", seg->p_offset); - str8_list_pushf(arena, &dump, " p_vaddr=0x%llx\n", seg->p_vaddr); - str8_list_pushf(arena, &dump, " p_paddr=0x%llx\n", seg->p_paddr); - str8_list_pushf(arena, &dump, " p_filesz=%llu\n", seg->p_filesz); - str8_list_pushf(arena, &dump, " p_memsz=%llu\n", seg->p_memsz); - str8_list_pushf(arena, &dump, " p_align=%llu\n", seg->p_align); - str8_list_push(arena, &dump, str8_lit("\n")); - } - } - } - - // DEBUG SECTIONS - if (params->dump_debug_sections){ - if (dwarf != 0){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "DEBUG SECTIONS:\n")); - - U32 *debug_section_idx = dwarf->debug_section_idx; - String8 *debug_data = dwarf->debug_data; - for (U32 i = 1; i < DWARF_SectionCode_COUNT; i += 1, debug_data += 1){ - U32 idx = debug_section_idx[i]; - String8 name = dwarf_string_from_section_code(i); - str8_list_pushf(arena, &dump, " %-10.*s section_idx=%u\n", str8_varg(name), idx); - } - str8_list_push(arena, &dump, str8_lit("\n")); - } - } - - // DEBUG INFO - if (params->dump_debug_info){ - if (info != 0){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "DEBUG INFO:\n")); - - U32 i = 0; - for (DWARF_InfoUnit *unit = info->unit_first; - unit != 0; - unit = unit->next, i += 1){ - str8_list_pushf(arena, &dump, " unit[%u]:\n", i); - dwarf_stringize_info(arena, &dump, unit, 2); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - } - } - - // DEBUG PUBNAMES - if (params->dump_debug_pubnames){ - if (pubnames != 0){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "DEBUG PUBNAMES:\n")); - - U32 i = 0; - for (DWARF_PubNamesUnit *unit = pubnames->unit_first; - unit != 0; - unit = unit->next, i += 1){ - str8_list_pushf(arena, &dump, " unit[%u]:\n", i); - dwarf_stringize_pubnames(arena, &dump, unit, 2); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - } - } - - // DEBUG PUBTYPES - if (params->dump_debug_pubtypes){ - if (pubtypes != 0){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "DEBUG PUBTYPES:\n")); - - U32 i = 0; - for (DWARF_PubNamesUnit *unit = pubtypes->unit_first; - unit != 0; - unit = unit->next, i += 1){ - str8_list_pushf(arena, &dump, " unit[%u]:\n", i); - dwarf_stringize_pubnames(arena, &dump, unit, 2); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - } - } - - // DEBUG NAMES - if (params->dump_debug_names){ - if (names != 0){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "DEBUG NAMES:\n")); - - U32 i = 0; - for (DWARF_NamesUnit *unit = names->unit_first; - unit != 0; - unit = unit->next, i += 1){ - str8_list_pushf(arena, &dump, " unit[%u]:\n", i); - dwarf_stringize_names(arena, &dump, unit, 2); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - } - } - - // DEBUG ARANGES - if (params->dump_debug_aranges){ - if (aranges != 0){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "DEBUG ARANGES:\n")); - - U32 i = 0; - for (DWARF_ArangesUnit *unit = aranges->unit_first; - unit != 0; - unit = unit->next, i += 1){ - str8_list_pushf(arena, &dump, " unit[%u]:\n", i); - dwarf_stringize_aranges(arena, &dump, unit, 2); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - } - } - - // DEBUG ADDR - if (params->dump_debug_addr){ - if (addr != 0){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "DEBUG ADDR:\n")); - - U32 i = 0; - for (DWARF_AddrUnit *unit = addr->unit_first; - unit != 0; - unit = unit->next, i += 1){ - str8_list_pushf(arena, &dump, " unit[%u]:\n", i); - dwarf_stringize_addr(arena, &dump, unit, 2); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - } - } - -#if 0 - // DEBUG ABBREV - if (params->dump_debug_abbrev){ - if (abbrev != 0){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "DEBUG ABBREV:\n")); - - U32 i = 0; - for (DWARF_AbbrevUnit *unit = abbrev->unit_first; - unit != 0; - unit = unit->next, i += 1){ - U32 j = 0; - for (DWARF_AbbrevDecl *abbrev_decl = unit->first; - abbrev_decl != 0; - abbrev_decl = abbrev_decl->next, j += 1){ - String8 tag_string = dwarf_string_from_tag(abbrev_decl->tag); - - str8_list_pushf(arena, &dump, " unit[%u],abbrev[%u]:\n", i, j); - str8_list_pushf(arena, &dump, " code=%llu\n", abbrev_decl->abbrev_code); - str8_list_pushf(arena, &dump, " tag=%.*s\n", str8_varg(tag_string)); - str8_list_pushf(arena, &dump, " has_children=%u\n", abbrev_decl->has_children); - str8_list_pushf(arena, &dump, " attrib_count=%u\n", abbrev_decl->attrib_count); - str8_list_pushf(arena, &dump, " attribs:\n", abbrev_decl->attrib_count); - - U32 attrib_count = abbrev_decl->attrib_count; - DWARF_AbbrevAttribSpec *attrib_spec = abbrev_decl->attrib_specs; - for (U32 k = 0; k < attrib_count; k += 1, attrib_spec += 1){ - String8 name_string = dwarf_string_from_attribute_name(attrib_spec->name); - String8 form_string = dwarf_string_from_attribute_form(attrib_spec->form); - - str8_list_pushf(arena, &dump, " [%-14.*s %-10.*s]\n", - str8_varg(name_string), str8_varg(form_string)); - } - - str8_list_push(arena, &dump, str8_lit("\n")); - } - } - - } - } -#endif - -#if 0 - // DEBUG INFO - if (params->dump_debug_info){ - if (info != 0){ - str8_list_push(arena, &dump, - str8_lit("################################" - "################################\n" - "DEBUG INFO:\n")); - - U32 i = 0; - for (DWARF_InfoUnit *unit = info->unit_first; - unit != 0; - unit = unit->next, i += 1){ - str8_list_pushf(arena, &dump, " unit[%u]:\n", i); - str8_list_pushf(arena, &dump, " [header]\n"); - str8_list_pushf(arena, &dump, " version=%u\n", unit->dwarf_version); - str8_list_pushf(arena, &dump, " offset_size=%u\n", unit->offset_size); - str8_list_pushf(arena, &dump, " address_size=%u\n", unit->address_size); - str8_list_pushf(arena, &dump, " [extracted attributes]\n"); - str8_list_pushf(arena, &dump, " langauge=%u\n", (U32)unit->language); - str8_list_pushf(arena, &dump, " line_info_offset=%llu\n", unit->line_info_offset); - str8_list_pushf(arena, &dump, " vbase=0x%llx\n", unit->vbase); - str8_list_pushf(arena, &dump, " str_offsets_base=%llu\n", unit->str_offsets_base); - str8_list_pushf(arena, &dump, " addr_base=%llu\n", unit->addr_base); - str8_list_pushf(arena, &dump, " rnglists_base=%llu\n", unit->rnglists_base); - str8_list_pushf(arena, &dump, " loclists_base=%llu\n", unit->loclists_base); - dump_entry_tree(arena, &dump, dwarf, unit, unit->entry_root, 2); - str8_list_push(arena, &dump, str8_lit("\n")); - } - - } - } -#endif - - // print dump - for (String8Node *node = dump.first; - node != 0; - node = node->next){ - fwrite(node->string.str, 1, node->string.size, stdout); - } - } -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Build Options + +#define BUILD_VERSION_MAJOR 0 +#define BUILD_VERSION_MINOR 9 +#define BUILD_VERSION_PATCH 11 +#define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA" +#define BUILD_TITLE "rdi_from_dwarf" +#define BUILD_CONSOLE_INTERFACE 1 + +//////////////////////////////// +//~ rjf: Includes + +//- rjf: [lib] +#include "lib_rdi_format/rdi_format.h" +#include "lib_rdi_format/rdi_format.c" +#include "third_party/rad_lzb_simple/rad_lzb_simple.h" +#include "third_party/rad_lzb_simple/rad_lzb_simple.c" + +//- rjf: [h] +#include "base/base_inc.h" +#include "os/os_inc.h" +#include "rdi_make/rdi_make_local.h" +#include "rdi_elf.h" +#include "rdi_dwarf.h" +#include "rdi_dwarf_stringize.h" +#include "rdi_from_dwarf.h" + +//- rjf: [c] +#include "base/base_inc.c" +#include "os/os_inc.c" +#include "rdi_make/rdi_make_local.c" +#include "rdi_elf.c" +#include "rdi_dwarf.c" +#include "rdi_dwarf_stringize.c" + +// TODO(allen): +// [ ] need sample data for .debug_names + +//////////////////////////////// +//~ Program Parameters Parser + +static DWARFCONV_Params* +dwarf_convert_params_from_cmd_line(Arena *arena, CmdLine *cmdline){ + DWARFCONV_Params *result = push_array(arena, DWARFCONV_Params, 1); + result->unit_idx_max = ~0ull; + + // get input pdb + { + String8 input_name = cmd_line_string(cmdline, str8_lit("elf")); + if (input_name.size == 0){ + str8_list_push(arena, &result->errors, + str8_lit("missing required parameter '--elf:'")); + } + + if (input_name.size > 0){ + String8 input_data = os_data_from_file_path(arena, input_name); + + if (input_data.size == 0){ + str8_list_pushf(arena, &result->errors, + "could not load input file '%.*s'", str8_varg(input_name)); + } + + if (input_data.size != 0){ + result->input_elf_name = input_name; + result->input_elf_data = input_data; + } + } + } + + // get output name + { + result->output_name = cmd_line_string(cmdline, str8_lit("out")); + } + + // error options + if (cmd_line_has_flag(cmdline, str8_lit("hide_errors"))){ + String8List vals = cmd_line_strings(cmdline, str8_lit("hide_errors")); + + // if no values - set all to hidden + if (vals.node_count == 0){ + B8 *ptr = (B8*)&result->hide_errors; + B8 *opl = ptr + sizeof(result->hide_errors); + for (;ptr < opl; ptr += 1){ + *ptr = 1; + } + } + + // for each explicit value set the corresponding flag to hidden + for (String8Node *node = vals.first; + node != 0; + node = node->next){ + if (str8_match(node->string, str8_lit("input"), 0)){ + result->hide_errors.input = 1; + } + } + + } + + // unit idx selector + if (cmd_line_has_flag(cmdline, str8_lit("unit_idx"))){ + String8List vals = cmd_line_strings(cmdline, str8_lit("unit_idx")); + + // single value unit index + if (vals.node_count == 1){ + U64 idx = u64_from_str8(vals.first->string, 10); + result->unit_idx_min = idx; + result->unit_idx_max = idx; + } + + // range value unit index + else if (vals.node_count >= 2){ + U64 idx_a = u64_from_str8(vals.first->string, 10); + U64 idx_b = u64_from_str8(vals.first->next->string, 10); + result->unit_idx_min = Min(idx_a, idx_b); + result->unit_idx_max = Max(idx_a, idx_b); + } + } + + // dump options + if (cmd_line_has_flag(cmdline, str8_lit("dump"))){ + result->dump = 1; + + String8List vals = cmd_line_strings(cmdline, str8_lit("dump")); + if (vals.first == 0){ + B8 *ptr = &result->dump__first; + for (; ptr < &result->dump__last; ptr += 1){ + *ptr = 1; + } + } + else{ + for (String8Node *node = vals.first; + node != 0; + node = node->next){ + if (str8_match(node->string, str8_lit("header"), 0)){ + result->dump_header = 1; + } + else if (str8_match(node->string, str8_lit("sections"), 0)){ + result->dump_sections = 1; + } + else if (str8_match(node->string, str8_lit("segments"), 0)){ + result->dump_segments = 1; + } + else if (str8_match(node->string, str8_lit("symtab"), 0)){ + result->dump_symtab = 1; + } + else if (str8_match(node->string, str8_lit("dynsym"), 0)){ + result->dump_dynsym = 1; + } + else if (str8_match(node->string, str8_lit("debug_sections"), 0)){ + result->dump_debug_sections = 1; + } + else if (str8_match(node->string, str8_lit("debug_info"), 0)){ + result->dump_debug_info = 1; + } + else if (str8_match(node->string, str8_lit("debug_abbrev"), 0)){ + result->dump_debug_abbrev = 1; + } + else if (str8_match(node->string, str8_lit("debug_pubnames"), 0)){ + result->dump_debug_pubnames = 1; + } + else if (str8_match(node->string, str8_lit("debug_pubtypes"), 0)){ + result->dump_debug_pubtypes = 1; + } + else if (str8_match(node->string, str8_lit("debug_names"), 0)){ + result->dump_debug_names = 1; + } + else if (str8_match(node->string, str8_lit("debug_aranges"), 0)){ + result->dump_debug_aranges = 1; + } + else if (str8_match(node->string, str8_lit("debug_addr"), 0)){ + result->dump_debug_addr = 1; + } + } + } + } + + return(result); +} + +//////////////////////////////// +//~ Entry Point + +static void +dump_symtab(Arena *arena, String8List *out, ELF_SymArray *symbols, String8 strtab, + U32 indent){ + static char spaces[] = " "; + + U8 *str_first = strtab.str; + U8 *str_opl = strtab.str + strtab.size; + + ELF_Sym64 *symbol = symbols->symbols; + U64 count = symbols->count; + for (U64 i = 0; i < count; i += 1, symbol += 1){ + U8 *name_first = str_first + symbol->st_name; + U8 *name_opl = name_first; + for (;name_opl < str_opl && *name_opl != 0;) name_opl += 1; + String8 name = str8_range(name_first, name_opl); + + ELF_SymbolBinding binding = ELF_SymBindingFromInfo(symbol->st_info); + String8 binding_string = elf_string_from_symbol_binding(binding); + + ELF_SymbolType type = ELF_SymTypeFromInfo(symbol->st_info); + String8 type_string = elf_string_from_symbol_type(type); + + ELF_SymbolVisibility vis = ELF_SymVisibilityFromOther(symbol->st_other); + String8 vis_string = elf_string_from_symbol_visibility(vis); + + str8_list_pushf(arena, out, + "%.*ssymbol[%5llu] %6.*s %7.*s %9.*s 0x%08llx size=%-5llu sec=%-5u " + "%.*s\n", + indent, spaces, i, + str8_varg(binding_string), str8_varg(type_string), + str8_varg(vis_string), + symbol->st_value, symbol->st_size, + symbol->st_shndx, str8_varg(name)); + } +} + +#if 0 +static void +dump_entry_tree(Arena *arena, String8List *out, + DWARF_Parsed *dwarf, DWARF_InfoUnit *unit, + DWARF_InfoEntry *entry, U32 indent){ + static char spaces[] = " "; + + DWARF_AbbrevDecl *abbrev_decl = entry->abbrev_decl; + + // tag + DWARF_Tag tag = abbrev_decl->tag; + String8 tag_string = dwarf_string_from_tag(tag); + str8_list_pushf(arena, out, "%.*sentry(@%llx) TAG %.*s\n", + indent, spaces, entry->info_offset, str8_varg(tag_string)); + + // attributes + U32 attrib_count = abbrev_decl->attrib_count; + DWARF_AbbrevAttribSpec *attrib_spec = abbrev_decl->attrib_specs; + DWARF_InfoAttribVal *attrib_val = entry->attrib_vals; + for (U32 i = 0; i < attrib_count; i += 1, attrib_spec += 1, attrib_val += 1){ + // attribute name + DWARF_AttributeName name = attrib_spec->name; + String8 name_string = dwarf_string_from_attribute_name(name); + str8_list_pushf(arena, out, "%.*sATTR %.*s ", indent + 4, spaces, str8_varg(name_string)); + + // attribute value + switch (attrib_spec->form){ + default: + { + String8 form_string = dwarf_string_from_attribute_form(attrib_spec->form); + str8_list_pushf(arena, out, " {%llu, 0x%p}\n", + str8_varg(form_string), attrib_val->val, attrib_val->dataptr); + }break; + + case DWARF_AttributeForm_strp: + { + String8 str = {0}; + + String8 data = dwarf->debug_data[DWARF_SectionCode_Str]; + U64 off = attrib_val->val; + if (off < data.size){ + U8 *start = data.str + off; + U8 *opl = data.str + data.size; + U8 *ptr = start; + for (;ptr < opl && *ptr != 0;) ptr += 1; + str = str8_range(start, ptr); + } + + str8_list_pushf(arena, out, "'%.*s'\n", str8_varg(str)); + }break; + + case DWARF_AttributeForm_sec_offset: + { + DWARF_AttributeClassFlags attr_classes1 = dwarf_attribute_class_from_name(name); + DWARF_AttributeClassFlags attr_classes2 = DWARF_AttributeClassFlag_sec_offset_classes; + DWARF_AttributeClassFlags attr_classes = attr_classes1&attr_classes2; + + DWARF_SectionCode sec_code = DWARF_SectionCode_Null; + if (unit->dwarf_version == 5){ + switch (attr_classes){ + case DWARF_AttributeClassFlag_addrptr: sec_code = DWARF_SectionCode_Addr; break; + case DWARF_AttributeClassFlag_lineptr: sec_code = DWARF_SectionCode_Line; break; + case DWARF_AttributeClassFlag_loclist: sec_code = DWARF_SectionCode_LocLists; break; + case DWARF_AttributeClassFlag_loclistsptr: sec_code = DWARF_SectionCode_LocLists; break; + case DWARF_AttributeClassFlag_macptr: sec_code = DWARF_SectionCode_Macro; break; + case DWARF_AttributeClassFlag_rnglist: sec_code = DWARF_SectionCode_RngLists; break; + case DWARF_AttributeClassFlag_rnglistsptr: sec_code = DWARF_SectionCode_RngLists; break; + case DWARF_AttributeClassFlag_stroffsetsptr: sec_code = DWARF_SectionCode_StrOffsets; break; + } + } + else if (unit->dwarf_version == 4){ + switch (attr_classes){ + case DWARF_AttributeClassFlag_lineptr: sec_code = DWARF_SectionCode_Line; break; + case DWARF_AttributeClassFlag_loclist: sec_code = DWARF_SectionCode_Loc; break; + case DWARF_AttributeClassFlag_macptr: sec_code = DWARF_SectionCode_MacInfo; break; + case DWARF_AttributeClassFlag_rnglist: sec_code = DWARF_SectionCode_Ranges; break; + } + } + + String8 sec_name = dwarf_name_from_debug_section(dwarf, sec_code); + str8_list_pushf(arena, out, "sec(%.*s) + %llu\n", str8_varg(sec_name), attrib_val->val); + }break; + + case DWARF_AttributeForm_ref1: + case DWARF_AttributeForm_ref2: + case DWARF_AttributeForm_ref4: + case DWARF_AttributeForm_ref8: + case DWARF_AttributeForm_ref_udata: + { + str8_list_pushf(arena, out, "entry(@%llx)\n", attrib_val->val); + }break; + + case DWARF_AttributeForm_addr: + { + str8_list_pushf(arena, out, "0x%llx\n", attrib_val->val); + }break; + + case DWARF_AttributeForm_exprloc: + { + str8_list_pushf(arena, out, "expression\n"); + // TODO(allen): dwarf expression dumping + }break; + + case DWARF_AttributeForm_strx1: + case DWARF_AttributeForm_strx2: + case DWARF_AttributeForm_strx3: + case DWARF_AttributeForm_strx4: + { + String8 str = {0}; + + U32 idx = attrib_val->val; + U64 str_offsets_off = unit->str_offsets_base + idx*unit->offset_size; + + String8 str_offsets = dwarf->debug_data[DWARF_SectionCode_StrOffsets]; + if (str_offsets_off + unit->offset_size < str_offsets.size){ + U64 off = 0; + MemoryCopy(&off, str_offsets.str + str_offsets_off, unit->offset_size); + + String8 data = dwarf->debug_data[DWARF_SectionCode_Str]; + if (off < data.size){ + U8 *start = data.str + off; + U8 *opl = data.str + data.size; + U8 *ptr = start; + for (;ptr < opl && *ptr != 0;) ptr += 1; + str = str8_range(start, ptr); + } + } + + str8_list_pushf(arena, out, "'%.*s'\n", str8_varg(str)); + }break; + + case DWARF_AttributeForm_addrx: + case DWARF_AttributeForm_addrx1: + case DWARF_AttributeForm_addrx2: + case DWARF_AttributeForm_addrx3: + case DWARF_AttributeForm_addrx4: + { + U64 address = 0; + + U32 idx = attrib_val->val; + U64 address_off = unit->addr_base + idx*unit->address_size; + + String8 data = dwarf->debug_data[DWARF_SectionCode_Addr]; + if (address_off + unit->address_size < data.size){ + MemoryCopy(&address, data.str + address_off, unit->address_size); + } + + str8_list_pushf(arena, out, "0x%x\n", address); + }break; + + case DWARF_AttributeForm_rnglistx: + { + U64 rnglist_off = unit->rnglists_base + attrib_val->val; + int x = 0; + }break; + + case DWARF_AttributeForm_data1: + case DWARF_AttributeForm_data2: + case DWARF_AttributeForm_data4: + case DWARF_AttributeForm_data8: + case DWARF_AttributeForm_data16: + case DWARF_AttributeForm_udata: + case DWARF_AttributeForm_implicit_const: + case DWARF_AttributeForm_flag: + case DWARF_AttributeForm_flag_present: + { + str8_list_pushf(arena, out, "%llu\n", attrib_val->val); + }break; + + case DWARF_AttributeForm_sdata: + { + str8_list_pushf(arena, out, "%lld\n", (S64)attrib_val->val); + }break; + + case DWARF_AttributeForm_string: + { + str8_list_pushf(arena, out, "'%.*s'\n", (int)attrib_val->val, attrib_val->dataptr); + }break; + } + } + + // dump children + for (DWARF_InfoEntry *child = entry->first_child; + child != 0; + child = child->next_sibling){ + dump_entry_tree(arena, out, dwarf, unit, child, indent + 1); + } +} +#endif + +internal void +entry_point(CmdLine *cmd_line) +{ + Arena *arena = arena_alloc(); + + // parse arguments + DWARFCONV_Params *params = dwarf_convert_params_from_cmd_line(arena, cmd_line); + + // show input errors + if (params->errors.node_count > 0 && + !params->hide_errors.input){ + for (String8Node *node = params->errors.first; + node != 0; + node = node->next){ + fprintf(stdout, "error(input): %.*s\n", str8_varg(node->string)); + } + } + + // will we try to parse an input file? + B32 try_parse_input = (params->errors.node_count == 0); + + // track parse success + B32 successful_parse = 1; + +#define PARSE_CHECK_ERROR(p,fmt,...) do{ if ((p) == 0){ \ +successful_parse = 0; \ +fprintf(stdout, "error(parsing): " fmt "\n", __VA_ARGS__); \ +} }while(0) + + // parse elf + ELF_Parsed *elf = 0; + if (try_parse_input) ProfScope("parse elf"){ + elf = elf_parsed_from_data(arena, params->input_elf_data); + PARSE_CHECK_ERROR(elf, "ELF"); + } + + // parse strtab + String8 strtab = {0}; + if (elf != 0) ProfScope("parse strtab"){ + strtab = elf_section_data_from_idx(elf, elf->strtab_idx); + } + + // parse symtab + ELF_SymArray symtab = {0}; + if (elf != 0) ProfScope("parse symtab"){ + String8 data = elf_section_data_from_idx(elf, elf->symtab_idx); + symtab = elf_sym_array_from_data(arena, elf->elf_class, data); + } + + // parse dynsym + ELF_SymArray dynsym = {0}; + if (elf != 0) ProfScope("parse dynsym"){ + String8 data = elf_section_data_from_idx(elf, elf->dynsym_idx); + dynsym = elf_sym_array_from_data(arena, elf->elf_class, data); + } + + // parse dwarf + DWARF_Parsed *dwarf = 0; + if (elf != 0) ProfScope("parse dwarf"){ + dwarf = dwarf_parsed_from_elf(arena, elf); + PARSE_CHECK_ERROR(dwarf, "DWARF"); + } + + // parse info + DWARF_InfoParsed *info = 0; + if (dwarf != 0){ + String8 data = dwarf->debug_data[DWARF_SectionCode_Info]; + if (data.size > 0) ProfScope("parse .debug_info"){ + info = dwarf_info_from_data(arena, data); + PARSE_CHECK_ERROR(info, "DEBUG INFO"); + } + } + + // parse pubnames + DWARF_PubNamesParsed *pubnames = 0; + if (dwarf != 0){ + String8 data = dwarf->debug_data[DWARF_SectionCode_PubNames]; + if (data.size) ProfScope("parse .debug_pubnames"){ + pubnames = dwarf_pubnames_from_data(arena, data); + PARSE_CHECK_ERROR(pubnames, "DEBUG PUBNAMES"); + } + } + + // parse pubtypes + DWARF_PubNamesParsed *pubtypes = 0; + if (dwarf != 0){ + String8 data = dwarf->debug_data[DWARF_SectionCode_PubTypes]; + if (data.size) ProfScope("parse .debug_pubtypes"){ + pubtypes = dwarf_pubnames_from_data(arena, data); + PARSE_CHECK_ERROR(pubtypes, "DEBUG PUBTYPES"); + } + } + + // parse names + DWARF_NamesParsed *names = 0; + if (dwarf != 0){ + String8 data = dwarf->debug_data[DWARF_SectionCode_Names]; + if (data.size) ProfScope("parse .debug_names"){ + names = dwarf_names_from_data(arena, data); + PARSE_CHECK_ERROR(names, "DEBUG NAMES"); + } + } + + // parse aranges + DWARF_ArangesParsed *aranges = 0; + if (dwarf != 0){ + String8 data = dwarf->debug_data[DWARF_SectionCode_Aranges]; + if (data.size) ProfScope("parse .debug_aranges"){ + aranges = dwarf_aranges_from_data(arena, data); + PARSE_CHECK_ERROR(aranges, "DEBUG ARANGES"); + } + } + + // parse addr + DWARF_AddrParsed *addr = 0; + if (dwarf != 0){ + String8 data = dwarf->debug_data[DWARF_SectionCode_Addr]; + if (data.size) ProfScope("parse .debug_addr"){ + addr = dwarf_addr_from_data(arena, data); + PARSE_CHECK_ERROR(addr, "DEBUG ADDR"); + } + } + +#if 0 + // parse abbrev + DWARF_AbbrevParsed *abbrev = 0; + if (dwarf != 0){ + String8 data = dwarf->debug_data[DWARF_SectionCode_Abbrev]; + if (data.size > 0) ProfScope("parse .debug_abbrev"){ + DWARF_AbbrevParams abbrev_params = {0}; + abbrev_params.unit_idx_min = params->unit_idx_min; + abbrev_params.unit_idx_max = params->unit_idx_max; + abbrev = dwarf_abbrev_from_data(arena, data, &abbrev_params); + PARSE_CHECK_ERROR(abbrev, "DEBUG ABBREV"); + } + } + + // parse info + DWARF_InfoParsed *info = 0; + if (abbrev != 0){ + String8 data = dwarf->debug_data[DWARF_SectionCode_Info]; + if (data.size > 0) ProfScope("parse .debug_info"){ + DWARF_InfoParams info_params = {0}; + info_params.unit_idx_min = params->unit_idx_min; + info_params.unit_idx_max = params->unit_idx_max; + info = dwarf_info_from_data(arena, data, &info_params, abbrev); + PARSE_CHECK_ERROR(info, "DEBUG INFO"); + } + } +#endif + + // dump + if (params->dump) ProfScope("dump"){ + String8List dump = {0}; + + // ELF + if (params->dump_header){ + if (elf != 0){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "ELF:\n")); + + // TODO: better stringizers for fields here + str8_list_pushf(arena, &dump, " elf_class=%u\n", elf->elf_class); + str8_list_pushf(arena, &dump, " arch=%u\n", elf->arch); + str8_list_pushf(arena, &dump, " section_count=%llu\n", elf->section_count); + str8_list_pushf(arena, &dump, " segment_count=%llu\n", elf->segment_count); + str8_list_pushf(arena, &dump, " vbase=0x%llx\n", elf->vbase); + str8_list_pushf(arena, &dump, " entry_vaddr=0x%llx\n", elf->vbase); + + str8_list_push(arena, &dump, str8_lit("\n")); + } + } + + // SECTIONS + if (params->dump_sections){ + if (elf != 0){ + ELF_SectionArray section_array = elf_section_array_from_elf(elf); + String8Array section_name_array = elf_section_name_array_from_elf(elf); + + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "SECTIONS:\n")); + + ELF_Shdr64 *sec = section_array.sections; + String8 *sec_name = section_name_array.v; + U64 count = section_array.count; + for (U64 i = 0 ; i < count; i += 1, sec += 1, sec_name += 1){ + String8 type_string = elf_string_from_section_type(sec->sh_type); + + // TODO: better stringizers for fields here + str8_list_pushf(arena, &dump, " section[%llu]:\n", i); + str8_list_pushf(arena, &dump, " name='%.*s'\n", str8_varg(*sec_name)); + str8_list_pushf(arena, &dump, " type=%.*s\n", str8_varg(type_string)); + str8_list_pushf(arena, &dump, " flags=0x%llx\n", sec->sh_flags); + str8_list_pushf(arena, &dump, " addr=0x%llx\n", sec->sh_addr); + str8_list_pushf(arena, &dump, " offset=0x%llx\n", sec->sh_offset); + str8_list_pushf(arena, &dump, " size=%llu\n", sec->sh_size); + str8_list_pushf(arena, &dump, " link=%u\n", sec->sh_link); + str8_list_pushf(arena, &dump, " info=%u\n", sec->sh_info); + str8_list_pushf(arena, &dump, " addralign=0x%llx\n", sec->sh_addralign); + str8_list_pushf(arena, &dump, " entsize=%llu\n", sec->sh_entsize); + str8_list_push(arena, &dump, str8_lit("\n")); + } + } + } + + // SYMTAB + if (symtab.count > 0 && params->dump_symtab){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "SYMTAB:\n")); + str8_list_pushf(arena, &dump, " section: %llu\n", elf->symtab_idx); + dump_symtab(arena, &dump, &symtab, strtab, 1); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + // DYNSYM + if (dynsym.count > 0 && params->dump_dynsym){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "DYNSYM:\n")); + str8_list_pushf(arena, &dump, " section: %llu\n", elf->dynsym_idx); + dump_symtab(arena, &dump, &dynsym, strtab, 1); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + // SEGMENTS + if (params->dump_segments){ + if (elf != 0){ + ELF_SegmentArray segment_array = elf_segment_array_from_elf(elf); + + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "SEGMENTS:\n")); + + ELF_Phdr64 *segments = segment_array.segments; + U64 count = segment_array.count; + for (U64 i = 0 ; i < count; i += 1){ + ELF_Phdr64 *seg = segments + i; + + // TODO: better stringizers for fields here + str8_list_pushf(arena, &dump, " segment[%llu]:\n", i); + str8_list_pushf(arena, &dump, " p_type=%u\n", seg->p_type); + str8_list_pushf(arena, &dump, " p_flags=0x%x\n", seg->p_flags); + str8_list_pushf(arena, &dump, " p_offset=0x%llx\n", seg->p_offset); + str8_list_pushf(arena, &dump, " p_vaddr=0x%llx\n", seg->p_vaddr); + str8_list_pushf(arena, &dump, " p_paddr=0x%llx\n", seg->p_paddr); + str8_list_pushf(arena, &dump, " p_filesz=%llu\n", seg->p_filesz); + str8_list_pushf(arena, &dump, " p_memsz=%llu\n", seg->p_memsz); + str8_list_pushf(arena, &dump, " p_align=%llu\n", seg->p_align); + str8_list_push(arena, &dump, str8_lit("\n")); + } + } + } + + // DEBUG SECTIONS + if (params->dump_debug_sections){ + if (dwarf != 0){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "DEBUG SECTIONS:\n")); + + U32 *debug_section_idx = dwarf->debug_section_idx; + String8 *debug_data = dwarf->debug_data; + for (U32 i = 1; i < DWARF_SectionCode_COUNT; i += 1, debug_data += 1){ + U32 idx = debug_section_idx[i]; + String8 name = dwarf_string_from_section_code(i); + str8_list_pushf(arena, &dump, " %-10.*s section_idx=%u\n", str8_varg(name), idx); + } + str8_list_push(arena, &dump, str8_lit("\n")); + } + } + + // DEBUG INFO + if (params->dump_debug_info){ + if (info != 0){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "DEBUG INFO:\n")); + + U32 i = 0; + for (DWARF_InfoUnit *unit = info->unit_first; + unit != 0; + unit = unit->next, i += 1){ + str8_list_pushf(arena, &dump, " unit[%u]:\n", i); + dwarf_stringize_info(arena, &dump, unit, 2); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + } + } + + // DEBUG PUBNAMES + if (params->dump_debug_pubnames){ + if (pubnames != 0){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "DEBUG PUBNAMES:\n")); + + U32 i = 0; + for (DWARF_PubNamesUnit *unit = pubnames->unit_first; + unit != 0; + unit = unit->next, i += 1){ + str8_list_pushf(arena, &dump, " unit[%u]:\n", i); + dwarf_stringize_pubnames(arena, &dump, unit, 2); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + } + } + + // DEBUG PUBTYPES + if (params->dump_debug_pubtypes){ + if (pubtypes != 0){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "DEBUG PUBTYPES:\n")); + + U32 i = 0; + for (DWARF_PubNamesUnit *unit = pubtypes->unit_first; + unit != 0; + unit = unit->next, i += 1){ + str8_list_pushf(arena, &dump, " unit[%u]:\n", i); + dwarf_stringize_pubnames(arena, &dump, unit, 2); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + } + } + + // DEBUG NAMES + if (params->dump_debug_names){ + if (names != 0){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "DEBUG NAMES:\n")); + + U32 i = 0; + for (DWARF_NamesUnit *unit = names->unit_first; + unit != 0; + unit = unit->next, i += 1){ + str8_list_pushf(arena, &dump, " unit[%u]:\n", i); + dwarf_stringize_names(arena, &dump, unit, 2); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + } + } + + // DEBUG ARANGES + if (params->dump_debug_aranges){ + if (aranges != 0){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "DEBUG ARANGES:\n")); + + U32 i = 0; + for (DWARF_ArangesUnit *unit = aranges->unit_first; + unit != 0; + unit = unit->next, i += 1){ + str8_list_pushf(arena, &dump, " unit[%u]:\n", i); + dwarf_stringize_aranges(arena, &dump, unit, 2); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + } + } + + // DEBUG ADDR + if (params->dump_debug_addr){ + if (addr != 0){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "DEBUG ADDR:\n")); + + U32 i = 0; + for (DWARF_AddrUnit *unit = addr->unit_first; + unit != 0; + unit = unit->next, i += 1){ + str8_list_pushf(arena, &dump, " unit[%u]:\n", i); + dwarf_stringize_addr(arena, &dump, unit, 2); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + } + } + +#if 0 + // DEBUG ABBREV + if (params->dump_debug_abbrev){ + if (abbrev != 0){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "DEBUG ABBREV:\n")); + + U32 i = 0; + for (DWARF_AbbrevUnit *unit = abbrev->unit_first; + unit != 0; + unit = unit->next, i += 1){ + U32 j = 0; + for (DWARF_AbbrevDecl *abbrev_decl = unit->first; + abbrev_decl != 0; + abbrev_decl = abbrev_decl->next, j += 1){ + String8 tag_string = dwarf_string_from_tag(abbrev_decl->tag); + + str8_list_pushf(arena, &dump, " unit[%u],abbrev[%u]:\n", i, j); + str8_list_pushf(arena, &dump, " code=%llu\n", abbrev_decl->abbrev_code); + str8_list_pushf(arena, &dump, " tag=%.*s\n", str8_varg(tag_string)); + str8_list_pushf(arena, &dump, " has_children=%u\n", abbrev_decl->has_children); + str8_list_pushf(arena, &dump, " attrib_count=%u\n", abbrev_decl->attrib_count); + str8_list_pushf(arena, &dump, " attribs:\n", abbrev_decl->attrib_count); + + U32 attrib_count = abbrev_decl->attrib_count; + DWARF_AbbrevAttribSpec *attrib_spec = abbrev_decl->attrib_specs; + for (U32 k = 0; k < attrib_count; k += 1, attrib_spec += 1){ + String8 name_string = dwarf_string_from_attribute_name(attrib_spec->name); + String8 form_string = dwarf_string_from_attribute_form(attrib_spec->form); + + str8_list_pushf(arena, &dump, " [%-14.*s %-10.*s]\n", + str8_varg(name_string), str8_varg(form_string)); + } + + str8_list_push(arena, &dump, str8_lit("\n")); + } + } + + } + } +#endif + +#if 0 + // DEBUG INFO + if (params->dump_debug_info){ + if (info != 0){ + str8_list_push(arena, &dump, + str8_lit("################################" + "################################\n" + "DEBUG INFO:\n")); + + U32 i = 0; + for (DWARF_InfoUnit *unit = info->unit_first; + unit != 0; + unit = unit->next, i += 1){ + str8_list_pushf(arena, &dump, " unit[%u]:\n", i); + str8_list_pushf(arena, &dump, " [header]\n"); + str8_list_pushf(arena, &dump, " version=%u\n", unit->dwarf_version); + str8_list_pushf(arena, &dump, " offset_size=%u\n", unit->offset_size); + str8_list_pushf(arena, &dump, " address_size=%u\n", unit->address_size); + str8_list_pushf(arena, &dump, " [extracted attributes]\n"); + str8_list_pushf(arena, &dump, " langauge=%u\n", (U32)unit->language); + str8_list_pushf(arena, &dump, " line_info_offset=%llu\n", unit->line_info_offset); + str8_list_pushf(arena, &dump, " vbase=0x%llx\n", unit->vbase); + str8_list_pushf(arena, &dump, " str_offsets_base=%llu\n", unit->str_offsets_base); + str8_list_pushf(arena, &dump, " addr_base=%llu\n", unit->addr_base); + str8_list_pushf(arena, &dump, " rnglists_base=%llu\n", unit->rnglists_base); + str8_list_pushf(arena, &dump, " loclists_base=%llu\n", unit->loclists_base); + dump_entry_tree(arena, &dump, dwarf, unit, unit->entry_root, 2); + str8_list_push(arena, &dump, str8_lit("\n")); + } + + } + } +#endif + + // print dump + for (String8Node *node = dump.first; + node != 0; + node = node->next){ + fwrite(node->string.str, 1, node->string.size, stdout); + } + } +} diff --git a/src/rdi_from_dwarf/rdi_from_dwarf.h b/src/rdi_from_dwarf/rdi_from_dwarf.h index bf9689c9..c5ea8251 100644 --- a/src/rdi_from_dwarf/rdi_from_dwarf.h +++ b/src/rdi_from_dwarf/rdi_from_dwarf.h @@ -1,50 +1,50 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RDI_FROM_DWARF_H -#define RDI_FROM_DWARF_H - -//////////////////////////////// -//~ Program Parameters Type - -typedef struct DWARFCONV_Params{ - String8 input_elf_name; - String8 input_elf_data; - - String8 output_name; - - U64 unit_idx_min; - U64 unit_idx_max; - - struct{ - B8 input; - } hide_errors; - - B8 dump; - B8 dump__first; - B8 dump_header; - B8 dump_sections; - B8 dump_segments; - B8 dump_symtab; - B8 dump_dynsym; - B8 dump_debug_sections; - B8 dump_debug_info; - B8 dump_debug_abbrev; - B8 dump_debug_pubnames; - B8 dump_debug_pubtypes; - B8 dump_debug_names; - B8 dump_debug_aranges; - B8 dump_debug_addr; - B8 dump__last; - - String8List errors; -} DWARFCONV_Params; - -//////////////////////////////// -//~ Program Parameters Parser - -static DWARFCONV_Params *dwarf_convert_params_from_cmd_line(Arena *arena, CmdLine *cmdline); - - - -#endif //RDI_FROM_DWARF_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RDI_FROM_DWARF_H +#define RDI_FROM_DWARF_H + +//////////////////////////////// +//~ Program Parameters Type + +typedef struct DWARFCONV_Params{ + String8 input_elf_name; + String8 input_elf_data; + + String8 output_name; + + U64 unit_idx_min; + U64 unit_idx_max; + + struct{ + B8 input; + } hide_errors; + + B8 dump; + B8 dump__first; + B8 dump_header; + B8 dump_sections; + B8 dump_segments; + B8 dump_symtab; + B8 dump_dynsym; + B8 dump_debug_sections; + B8 dump_debug_info; + B8 dump_debug_abbrev; + B8 dump_debug_pubnames; + B8 dump_debug_pubtypes; + B8 dump_debug_names; + B8 dump_debug_aranges; + B8 dump_debug_addr; + B8 dump__last; + + String8List errors; +} DWARFCONV_Params; + +//////////////////////////////// +//~ Program Parameters Parser + +static DWARFCONV_Params *dwarf_convert_params_from_cmd_line(Arena *arena, CmdLine *cmdline); + + + +#endif //RDI_FROM_DWARF_H diff --git a/src/rdi_from_pdb/rdi_from_pdb.h b/src/rdi_from_pdb/rdi_from_pdb.h index a0a78db5..d94500b8 100644 --- a/src/rdi_from_pdb/rdi_from_pdb.h +++ b/src/rdi_from_pdb/rdi_from_pdb.h @@ -1,650 +1,650 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RDI_FROM_PDB_H -#define RDI_FROM_PDB_H - -//////////////////////////////// -//~ rjf: Export Artifact Flags - -typedef U32 P2R_ConvertFlags; -enum -{ - P2R_ConvertFlag_Strings = (1<<0), - P2R_ConvertFlag_IndexRuns = (1<<1), - P2R_ConvertFlag_BinarySections = (1<<2), - P2R_ConvertFlag_Units = (1<<3), - P2R_ConvertFlag_Procedures = (1<<4), - P2R_ConvertFlag_GlobalVariables = (1<<5), - P2R_ConvertFlag_ThreadVariables = (1<<6), - P2R_ConvertFlag_Scopes = (1<<7), - P2R_ConvertFlag_Locals = (1<<8), - P2R_ConvertFlag_Types = (1<<9), - P2R_ConvertFlag_UDTs = (1<<10), - P2R_ConvertFlag_LineInfo = (1<<11), - P2R_ConvertFlag_GlobalVariableNameMap = (1<<12), - P2R_ConvertFlag_ThreadVariableNameMap = (1<<13), - P2R_ConvertFlag_ProcedureNameMap = (1<<14), - P2R_ConvertFlag_TypeNameMap = (1<<15), - P2R_ConvertFlag_LinkNameProcedureNameMap= (1<<16), - P2R_ConvertFlag_NormalSourcePathNameMap = (1<<17), - P2R_ConvertFlag_All = 0xffffffff, -}; - -//////////////////////////////// -//~ rjf: Conversion Stage Inputs/Outputs - -typedef struct P2R_User2Convert P2R_User2Convert; -struct P2R_User2Convert -{ - String8 input_pdb_name; - String8 input_pdb_data; - String8 input_exe_name; - String8 input_exe_data; - String8 output_name; - P2R_ConvertFlags flags; - String8List errors; -}; - -typedef struct P2R_Convert2Bake P2R_Convert2Bake; -struct P2R_Convert2Bake -{ - RDIM_BakeParams bake_params; -}; - -typedef struct P2R_Bake2Serialize P2R_Bake2Serialize; -struct P2R_Bake2Serialize -{ - RDIM_BakeResults bake_results; -}; - -typedef struct P2R_Serialize2File P2R_Serialize2File; -struct P2R_Serialize2File -{ - RDIM_SerializedSectionBundle bundle; -}; - -//////////////////////////////// -//~ rjf: Initial PDB Information Extraction & Conversion Preparation Task Types - -//- rjf: tpi hash parsing - -typedef struct P2R_TPIHashParseIn P2R_TPIHashParseIn; -struct P2R_TPIHashParseIn -{ - PDB_Strtbl *strtbl; - PDB_TpiParsed *tpi; - String8 hash_data; - String8 aux_data; -}; - -//- rjf: tpi leaves parsing - -typedef struct P2R_TPILeafParseIn P2R_TPILeafParseIn; -struct P2R_TPILeafParseIn -{ - String8 leaf_data; - CV_TypeId itype_first; -}; - -//- rjf: exe hashing - -typedef struct P2R_EXEHashIn P2R_EXEHashIn; -struct P2R_EXEHashIn -{ - String8 exe_data; -}; - -//- rjf: symbol stream parsing - -typedef struct P2R_SymbolStreamParseIn P2R_SymbolStreamParseIn; -struct P2R_SymbolStreamParseIn -{ - String8 data; -}; - -//- rjf: c13 line info stream parsing - -typedef struct P2R_C13StreamParseIn P2R_C13StreamParseIn; -struct P2R_C13StreamParseIn -{ - String8 data; - PDB_Strtbl *strtbl; - PDB_CoffSectionArray *coff_sections; -}; - -//- rjf: comp unit parsing - -typedef struct P2R_CompUnitParseIn P2R_CompUnitParseIn; -struct P2R_CompUnitParseIn -{ - String8 data; -}; - -//- rjf: comp unit contribution table parsing - -typedef struct P2R_CompUnitContributionsParseIn P2R_CompUnitContributionsParseIn; -struct P2R_CompUnitContributionsParseIn -{ - String8 data; - PDB_CoffSectionArray *coff_sections; -}; - -//////////////////////////////// -//~ rjf: Conversion Data Structure & Task Types - -//- rjf: link name map (voff -> string) - -typedef struct P2R_LinkNameNode P2R_LinkNameNode; -struct P2R_LinkNameNode -{ - P2R_LinkNameNode *next; - U64 voff; - String8 name; -}; - -typedef struct P2R_LinkNameMap P2R_LinkNameMap; -struct P2R_LinkNameMap -{ - P2R_LinkNameNode **buckets; - U64 buckets_count; - U64 bucket_collision_count; - U64 link_name_count; -}; - -//- rjf: normalized file path -> source file map - -typedef struct P2R_SrcFileNode P2R_SrcFileNode; -struct P2R_SrcFileNode -{ - P2R_SrcFileNode *next; - RDIM_SrcFile *src_file; -}; - -typedef struct P2R_SrcFileMap P2R_SrcFileMap; -struct P2R_SrcFileMap -{ - P2R_SrcFileNode **slots; - U64 slots_count; -}; - -//- rjf: unit conversion tasks - -typedef struct P2R_UnitConvertIn P2R_UnitConvertIn; -struct P2R_UnitConvertIn -{ - PDB_Strtbl *pdb_strtbl; - PDB_CoffSectionArray *coff_sections; - PDB_CompUnitArray *comp_units; - PDB_CompUnitContributionArray *comp_unit_contributions; - CV_SymParsed **comp_unit_syms; - CV_C13Parsed **comp_unit_c13s; -}; - -typedef struct P2R_UnitConvertOut P2R_UnitConvertOut; -struct P2R_UnitConvertOut -{ - RDIM_UnitChunkList units; - RDIM_SrcFileChunkList src_files; - RDIM_LineTableChunkList line_tables; - RDIM_LineTable **units_first_inline_site_line_tables; -}; - -//- rjf: link name map building tasks - -typedef struct P2R_LinkNameMapBuildIn P2R_LinkNameMapBuildIn; -struct P2R_LinkNameMapBuildIn -{ - CV_SymParsed *sym; - PDB_CoffSectionArray *coff_sections; - P2R_LinkNameMap *link_name_map; -}; - -//- rjf: type forward resolution map build - -typedef struct P2R_ITypeFwdMapFillIn P2R_ITypeFwdMapFillIn; -struct P2R_ITypeFwdMapFillIn -{ - PDB_TpiHashParsed *tpi_hash; - CV_LeafParsed *tpi_leaf; - CV_TypeId itype_first; - CV_TypeId itype_opl; - CV_TypeId *itype_fwd_map; -}; - -//- rjf: itype chain build - -typedef struct P2R_TypeIdChain P2R_TypeIdChain; -struct P2R_TypeIdChain -{ - P2R_TypeIdChain *next; - CV_TypeId itype; -}; - -typedef struct P2R_ITypeChainBuildIn P2R_ITypeChainBuildIn; -struct P2R_ITypeChainBuildIn -{ - CV_LeafParsed *tpi_leaf; - CV_TypeId itype_first; - CV_TypeId itype_opl; - CV_TypeId *itype_fwd_map; - P2R_TypeIdChain **itype_chains; -}; - -//- rjf: udt conversion - -typedef struct P2R_UDTConvertIn P2R_UDTConvertIn; -struct P2R_UDTConvertIn -{ - CV_LeafParsed *tpi_leaf; - CV_TypeId itype_first; - CV_TypeId itype_opl; - CV_TypeId *itype_fwd_map; - RDIM_Type **itype_type_ptrs; -}; - -//- rjf: symbol stream conversion - -typedef struct P2R_SymbolStreamConvertIn P2R_SymbolStreamConvertIn; -struct P2R_SymbolStreamConvertIn -{ - RDI_Arch arch; - PDB_CoffSectionArray *coff_sections; - PDB_TpiHashParsed *tpi_hash; - CV_LeafParsed *tpi_leaf; - CV_LeafParsed *ipi_leaf; - CV_SymParsed *sym; - U64 sym_ranges_first; - U64 sym_ranges_opl; - CV_TypeId *itype_fwd_map; - RDIM_Type **itype_type_ptrs; - P2R_LinkNameMap *link_name_map; - RDIM_LineTable *first_inline_site_line_table; -}; - -typedef struct P2R_SymbolStreamConvertOut P2R_SymbolStreamConvertOut; -struct P2R_SymbolStreamConvertOut -{ - RDIM_SymbolChunkList procedures; - RDIM_SymbolChunkList global_variables; - RDIM_SymbolChunkList thread_variables; - RDIM_ScopeChunkList scopes; - RDIM_InlineSiteChunkList inline_sites; -}; - -//////////////////////////////// -//~ rjf: Baking Task Types - -//- rjf: line table baking task types - -typedef struct P2R_BakeLineTablesIn P2R_BakeLineTablesIn; -struct P2R_BakeLineTablesIn -{ - RDIM_LineTableChunkList *line_tables; -}; - -//- rjf: string map baking task types - -typedef struct P2R_BakeSrcFilesStringsIn P2R_BakeSrcFilesStringsIn; -struct P2R_BakeSrcFilesStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - RDIM_SrcFileChunkList *list; -}; - -typedef struct P2R_BakeUnitsStringsIn P2R_BakeUnitsStringsIn; -struct P2R_BakeUnitsStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - RDIM_UnitChunkList *list; -}; - -typedef struct P2R_BakeTypesStringsInNode P2R_BakeTypesStringsInNode; -struct P2R_BakeTypesStringsInNode -{ - P2R_BakeTypesStringsInNode *next; - RDIM_Type *v; - RDI_U64 count; -}; - -typedef struct P2R_BakeTypesStringsIn P2R_BakeTypesStringsIn; -struct P2R_BakeTypesStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - P2R_BakeTypesStringsInNode *first; - P2R_BakeTypesStringsInNode *last; -}; - -typedef struct P2R_BakeUDTsStringsInNode P2R_BakeUDTsStringsInNode; -struct P2R_BakeUDTsStringsInNode -{ - P2R_BakeUDTsStringsInNode *next; - RDIM_UDT *v; - RDI_U64 count; -}; - -typedef struct P2R_BakeUDTsStringsIn P2R_BakeUDTsStringsIn; -struct P2R_BakeUDTsStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - P2R_BakeUDTsStringsInNode *first; - P2R_BakeUDTsStringsInNode *last; -}; - -typedef struct P2R_BakeSymbolsStringsInNode P2R_BakeSymbolsStringsInNode; -struct P2R_BakeSymbolsStringsInNode -{ - P2R_BakeSymbolsStringsInNode *next; - RDIM_Symbol *v; - RDI_U64 count; -}; - -typedef struct P2R_BakeSymbolsStringsIn P2R_BakeSymbolsStringsIn; -struct P2R_BakeSymbolsStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - P2R_BakeSymbolsStringsInNode *first; - P2R_BakeSymbolsStringsInNode *last; -}; - -typedef struct P2R_BakeScopesStringsInNode P2R_BakeScopesStringsInNode; -struct P2R_BakeScopesStringsInNode -{ - P2R_BakeScopesStringsInNode *next; - RDIM_Scope *v; - RDI_U64 count; -}; - -typedef struct P2R_BakeScopesStringsIn P2R_BakeScopesStringsIn; -struct P2R_BakeScopesStringsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **maps; - P2R_BakeScopesStringsInNode *first; - P2R_BakeScopesStringsInNode *last; -}; - -//- rjf: string map joining task types - -typedef struct P2R_JoinBakeStringMapSlotsIn P2R_JoinBakeStringMapSlotsIn; -struct P2R_JoinBakeStringMapSlotsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose **src_maps; - U64 src_maps_count; - RDIM_BakeStringMapLoose *dst_map; - Rng1U64 slot_idx_range; -}; - -//- rjf: string map sorting task types - -typedef struct P2R_SortBakeStringMapSlotsIn P2R_SortBakeStringMapSlotsIn; -struct P2R_SortBakeStringMapSlotsIn -{ - RDIM_BakeStringMapTopology *top; - RDIM_BakeStringMapLoose *src_map; - RDIM_BakeStringMapLoose *dst_map; - U64 slot_idx; - U64 slot_count; -}; - -//- rjf: OLD string map baking types - -typedef struct P2R_BuildBakeStringMapIn P2R_BuildBakeStringMapIn; -struct P2R_BuildBakeStringMapIn -{ - RDIM_BakePathTree *path_tree; - RDIM_BakeParams *params; -}; - -typedef struct P2R_BuildBakeNameMapIn P2R_BuildBakeNameMapIn; -struct P2R_BuildBakeNameMapIn -{ - RDI_NameMapKind k; - RDIM_BakeParams *params; -}; - -//- rjf: debug info baking task types - -typedef struct P2R_BakeUnitsIn P2R_BakeUnitsIn; -struct P2R_BakeUnitsIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_BakePathTree *path_tree; - RDIM_UnitChunkList *units; -}; - -typedef struct P2R_BakeUnitVMapIn P2R_BakeUnitVMapIn; -struct P2R_BakeUnitVMapIn -{ - RDIM_UnitChunkList *units; -}; - -typedef struct P2R_BakeSrcFilesIn P2R_BakeSrcFilesIn; -struct P2R_BakeSrcFilesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_BakePathTree *path_tree; - RDIM_SrcFileChunkList *src_files; -}; - -typedef struct P2R_BakeUDTsIn P2R_BakeUDTsIn; -struct P2R_BakeUDTsIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_UDTChunkList *udts; -}; - -typedef struct P2R_BakeGlobalVariablesIn P2R_BakeGlobalVariablesIn; -struct P2R_BakeGlobalVariablesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_SymbolChunkList *global_variables; -}; - -typedef struct P2R_BakeGlobalVMapIn P2R_BakeGlobalVMapIn; -struct P2R_BakeGlobalVMapIn -{ - RDIM_SymbolChunkList *global_variables; -}; - -typedef struct P2R_BakeThreadVariablesIn P2R_BakeThreadVariablesIn; -struct P2R_BakeThreadVariablesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_SymbolChunkList *thread_variables; -}; - -typedef struct P2R_BakeProceduresIn P2R_BakeProceduresIn; -struct P2R_BakeProceduresIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_SymbolChunkList *procedures; -}; - -typedef struct P2R_BakeScopesIn P2R_BakeScopesIn; -struct P2R_BakeScopesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_ScopeChunkList *scopes; -}; - -typedef struct P2R_BakeScopeVMapIn P2R_BakeScopeVMapIn; -struct P2R_BakeScopeVMapIn -{ - RDIM_ScopeChunkList *scopes; -}; - -typedef struct P2R_BakeInlineSitesIn P2R_BakeInlineSitesIn; -struct P2R_BakeInlineSitesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_InlineSiteChunkList *inline_sites; -}; - -typedef struct P2R_BakeFilePathsIn P2R_BakeFilePathsIn; -struct P2R_BakeFilePathsIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_BakePathTree *path_tree; -}; - -typedef struct P2R_BakeStringsIn P2R_BakeStringsIn; -struct P2R_BakeStringsIn -{ - RDIM_BakeStringMapTight *strings; -}; - -typedef struct P2R_BakeTypeNodesIn P2R_BakeTypeNodesIn; -struct P2R_BakeTypeNodesIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_BakeIdxRunMap *idx_runs; - RDIM_TypeChunkList *types; -}; - -typedef struct P2R_BakeNameMapIn P2R_BakeNameMapIn; -struct P2R_BakeNameMapIn -{ - RDIM_BakeStringMapTight *strings; - RDIM_BakeIdxRunMap *idx_runs; - RDIM_BakeNameMap *map; - RDI_NameMapKind kind; -}; - -typedef struct P2R_BakeIdxRunsIn P2R_BakeIdxRunsIn; -struct P2R_BakeIdxRunsIn -{ - RDIM_BakeIdxRunMap *idx_runs; -}; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal U64 p2r_end_of_cplusplus_container_name(String8 str); -internal U64 p2r_hash_from_voff(U64 voff); - -//////////////////////////////// -//~ rjf: Command Line -> Conversion Inputs - -internal P2R_User2Convert *p2r_user2convert_from_cmdln(Arena *arena, CmdLine *cmdline); - -//////////////////////////////// -//~ rjf: COFF => RDI Canonical Conversions - -internal RDI_BinarySectionFlags p2r_rdi_binary_section_flags_from_coff_section_flags(COFF_SectionFlags flags); - -//////////////////////////////// -//~ rjf: CodeView => RDI Canonical Conversions - -internal RDI_Arch p2r_rdi_arch_from_cv_arch(CV_Arch arch); -internal RDI_RegCode p2r_rdi_reg_code_from_cv_reg_code(RDI_Arch arch, CV_Reg reg_code); -internal RDI_Language p2r_rdi_language_from_cv_language(CV_Language language); -internal RDI_TypeKind p2r_rdi_type_kind_from_cv_basic_type(CV_BasicType basic_type); - -//////////////////////////////// -//~ rjf: Location Info Building Helpers - -internal RDIM_Location *p2r_location_from_addr_reg_off(Arena *arena, RDI_Arch arch, RDI_RegCode reg_code, U32 reg_byte_size, U32 reg_byte_pos, S64 offset, B32 extra_indirection); -internal CV_EncodedFramePtrReg p2r_cv_encoded_fp_reg_from_frameproc(CV_SymFrameproc *frameproc, B32 param_base); -internal RDI_RegCode p2r_reg_code_from_arch_encoded_fp_reg(RDI_Arch arch, CV_EncodedFramePtrReg encoded_reg); -internal void p2r_location_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_LocationSet *locset, RDIM_Location *location, CV_LvarAddrRange *range, COFF_SectionHeader *section, CV_LvarAddrGap *gaps, U64 gap_count); - -//////////////////////////////// -//~ rjf: Initial Parsing & Preparation Pass Tasks - -internal TS_TASK_FUNCTION_DEF(p2r_exe_hash_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_tpi_hash_parse_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_tpi_leaf_parse_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_symbol_stream_parse_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_c13_stream_parse_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_comp_unit_parse_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_comp_unit_contributions_parse_task__entry_point); - -//////////////////////////////// -//~ rjf: Unit Conversion Tasks - -internal TS_TASK_FUNCTION_DEF(p2r_units_convert_task__entry_point); - -//////////////////////////////// -//~ rjf: Link Name Map Building Tasks - -internal TS_TASK_FUNCTION_DEF(p2r_link_name_map_build_task__entry_point); - -//////////////////////////////// -//~ rjf: Type Parsing/Conversion Tasks - -internal TS_TASK_FUNCTION_DEF(p2r_itype_fwd_map_fill_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_itype_chain_build_task__entry_point); - -//////////////////////////////// -//~ rjf: UDT Conversion Tasks - -internal TS_TASK_FUNCTION_DEF(p2r_udt_convert_task__entry_point); - -//////////////////////////////// -//~ rjf: Symbol Stream Conversion Tasks - -internal TS_TASK_FUNCTION_DEF(p2r_symbol_stream_convert_task__entry_point); - -//////////////////////////////// -//~ rjf: Top-Level Conversion Entry Point - -internal P2R_Convert2Bake *p2r_convert(Arena *arena, P2R_User2Convert *in); - -//////////////////////////////// -//~ rjf: Baking Stage Tasks - -//- rjf: unsorted bake string map building -internal TS_TASK_FUNCTION_DEF(p2r_bake_src_files_strings_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_units_strings_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_types_strings_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_udts_strings_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_symbols_strings_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_scopes_strings_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_line_tables_task__entry_point); - -//- rjf: bake string map joining -internal TS_TASK_FUNCTION_DEF(p2r_bake_string_map_join_task__entry_point); - -//- rjf: bake string map sorting -internal TS_TASK_FUNCTION_DEF(p2r_bake_string_map_sort_task__entry_point); - -//- rjf: pass 1: interner/deduper map builds -internal TS_TASK_FUNCTION_DEF(p2r_build_bake_name_map_task__entry_point); - -//- rjf: pass 2: string-map-dependent debug info stream builds -internal TS_TASK_FUNCTION_DEF(p2r_bake_units_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_unit_vmap_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_src_files_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_udts_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_global_variables_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_global_vmap_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_thread_variables_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_procedures_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_scopes_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_scope_vmap_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_file_paths_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_strings_task__entry_point); - -//- rjf: pass 3: idx-run-map-dependent debug info stream builds -internal TS_TASK_FUNCTION_DEF(p2r_bake_type_nodes_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_name_map_task__entry_point); -internal TS_TASK_FUNCTION_DEF(p2r_bake_idx_runs_task__entry_point); - -//////////////////////////////// -//~ rjf: Top-Level Baking Entry Point - -internal P2R_Bake2Serialize *p2r_bake(Arena *arena, P2R_Convert2Bake *in); - -//////////////////////////////// -//~ rjf: Top-Level Compression Entry Point - -internal P2R_Serialize2File *p2r_compress(Arena *arena, P2R_Serialize2File *in); - -#endif // RDI_FROM_PDB_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RDI_FROM_PDB_H +#define RDI_FROM_PDB_H + +//////////////////////////////// +//~ rjf: Export Artifact Flags + +typedef U32 P2R_ConvertFlags; +enum +{ + P2R_ConvertFlag_Strings = (1<<0), + P2R_ConvertFlag_IndexRuns = (1<<1), + P2R_ConvertFlag_BinarySections = (1<<2), + P2R_ConvertFlag_Units = (1<<3), + P2R_ConvertFlag_Procedures = (1<<4), + P2R_ConvertFlag_GlobalVariables = (1<<5), + P2R_ConvertFlag_ThreadVariables = (1<<6), + P2R_ConvertFlag_Scopes = (1<<7), + P2R_ConvertFlag_Locals = (1<<8), + P2R_ConvertFlag_Types = (1<<9), + P2R_ConvertFlag_UDTs = (1<<10), + P2R_ConvertFlag_LineInfo = (1<<11), + P2R_ConvertFlag_GlobalVariableNameMap = (1<<12), + P2R_ConvertFlag_ThreadVariableNameMap = (1<<13), + P2R_ConvertFlag_ProcedureNameMap = (1<<14), + P2R_ConvertFlag_TypeNameMap = (1<<15), + P2R_ConvertFlag_LinkNameProcedureNameMap= (1<<16), + P2R_ConvertFlag_NormalSourcePathNameMap = (1<<17), + P2R_ConvertFlag_All = 0xffffffff, +}; + +//////////////////////////////// +//~ rjf: Conversion Stage Inputs/Outputs + +typedef struct P2R_User2Convert P2R_User2Convert; +struct P2R_User2Convert +{ + String8 input_pdb_name; + String8 input_pdb_data; + String8 input_exe_name; + String8 input_exe_data; + String8 output_name; + P2R_ConvertFlags flags; + String8List errors; +}; + +typedef struct P2R_Convert2Bake P2R_Convert2Bake; +struct P2R_Convert2Bake +{ + RDIM_BakeParams bake_params; +}; + +typedef struct P2R_Bake2Serialize P2R_Bake2Serialize; +struct P2R_Bake2Serialize +{ + RDIM_BakeResults bake_results; +}; + +typedef struct P2R_Serialize2File P2R_Serialize2File; +struct P2R_Serialize2File +{ + RDIM_SerializedSectionBundle bundle; +}; + +//////////////////////////////// +//~ rjf: Initial PDB Information Extraction & Conversion Preparation Task Types + +//- rjf: tpi hash parsing + +typedef struct P2R_TPIHashParseIn P2R_TPIHashParseIn; +struct P2R_TPIHashParseIn +{ + PDB_Strtbl *strtbl; + PDB_TpiParsed *tpi; + String8 hash_data; + String8 aux_data; +}; + +//- rjf: tpi leaves parsing + +typedef struct P2R_TPILeafParseIn P2R_TPILeafParseIn; +struct P2R_TPILeafParseIn +{ + String8 leaf_data; + CV_TypeId itype_first; +}; + +//- rjf: exe hashing + +typedef struct P2R_EXEHashIn P2R_EXEHashIn; +struct P2R_EXEHashIn +{ + String8 exe_data; +}; + +//- rjf: symbol stream parsing + +typedef struct P2R_SymbolStreamParseIn P2R_SymbolStreamParseIn; +struct P2R_SymbolStreamParseIn +{ + String8 data; +}; + +//- rjf: c13 line info stream parsing + +typedef struct P2R_C13StreamParseIn P2R_C13StreamParseIn; +struct P2R_C13StreamParseIn +{ + String8 data; + PDB_Strtbl *strtbl; + PDB_CoffSectionArray *coff_sections; +}; + +//- rjf: comp unit parsing + +typedef struct P2R_CompUnitParseIn P2R_CompUnitParseIn; +struct P2R_CompUnitParseIn +{ + String8 data; +}; + +//- rjf: comp unit contribution table parsing + +typedef struct P2R_CompUnitContributionsParseIn P2R_CompUnitContributionsParseIn; +struct P2R_CompUnitContributionsParseIn +{ + String8 data; + PDB_CoffSectionArray *coff_sections; +}; + +//////////////////////////////// +//~ rjf: Conversion Data Structure & Task Types + +//- rjf: link name map (voff -> string) + +typedef struct P2R_LinkNameNode P2R_LinkNameNode; +struct P2R_LinkNameNode +{ + P2R_LinkNameNode *next; + U64 voff; + String8 name; +}; + +typedef struct P2R_LinkNameMap P2R_LinkNameMap; +struct P2R_LinkNameMap +{ + P2R_LinkNameNode **buckets; + U64 buckets_count; + U64 bucket_collision_count; + U64 link_name_count; +}; + +//- rjf: normalized file path -> source file map + +typedef struct P2R_SrcFileNode P2R_SrcFileNode; +struct P2R_SrcFileNode +{ + P2R_SrcFileNode *next; + RDIM_SrcFile *src_file; +}; + +typedef struct P2R_SrcFileMap P2R_SrcFileMap; +struct P2R_SrcFileMap +{ + P2R_SrcFileNode **slots; + U64 slots_count; +}; + +//- rjf: unit conversion tasks + +typedef struct P2R_UnitConvertIn P2R_UnitConvertIn; +struct P2R_UnitConvertIn +{ + PDB_Strtbl *pdb_strtbl; + PDB_CoffSectionArray *coff_sections; + PDB_CompUnitArray *comp_units; + PDB_CompUnitContributionArray *comp_unit_contributions; + CV_SymParsed **comp_unit_syms; + CV_C13Parsed **comp_unit_c13s; +}; + +typedef struct P2R_UnitConvertOut P2R_UnitConvertOut; +struct P2R_UnitConvertOut +{ + RDIM_UnitChunkList units; + RDIM_SrcFileChunkList src_files; + RDIM_LineTableChunkList line_tables; + RDIM_LineTable **units_first_inline_site_line_tables; +}; + +//- rjf: link name map building tasks + +typedef struct P2R_LinkNameMapBuildIn P2R_LinkNameMapBuildIn; +struct P2R_LinkNameMapBuildIn +{ + CV_SymParsed *sym; + PDB_CoffSectionArray *coff_sections; + P2R_LinkNameMap *link_name_map; +}; + +//- rjf: type forward resolution map build + +typedef struct P2R_ITypeFwdMapFillIn P2R_ITypeFwdMapFillIn; +struct P2R_ITypeFwdMapFillIn +{ + PDB_TpiHashParsed *tpi_hash; + CV_LeafParsed *tpi_leaf; + CV_TypeId itype_first; + CV_TypeId itype_opl; + CV_TypeId *itype_fwd_map; +}; + +//- rjf: itype chain build + +typedef struct P2R_TypeIdChain P2R_TypeIdChain; +struct P2R_TypeIdChain +{ + P2R_TypeIdChain *next; + CV_TypeId itype; +}; + +typedef struct P2R_ITypeChainBuildIn P2R_ITypeChainBuildIn; +struct P2R_ITypeChainBuildIn +{ + CV_LeafParsed *tpi_leaf; + CV_TypeId itype_first; + CV_TypeId itype_opl; + CV_TypeId *itype_fwd_map; + P2R_TypeIdChain **itype_chains; +}; + +//- rjf: udt conversion + +typedef struct P2R_UDTConvertIn P2R_UDTConvertIn; +struct P2R_UDTConvertIn +{ + CV_LeafParsed *tpi_leaf; + CV_TypeId itype_first; + CV_TypeId itype_opl; + CV_TypeId *itype_fwd_map; + RDIM_Type **itype_type_ptrs; +}; + +//- rjf: symbol stream conversion + +typedef struct P2R_SymbolStreamConvertIn P2R_SymbolStreamConvertIn; +struct P2R_SymbolStreamConvertIn +{ + RDI_Arch arch; + PDB_CoffSectionArray *coff_sections; + PDB_TpiHashParsed *tpi_hash; + CV_LeafParsed *tpi_leaf; + CV_LeafParsed *ipi_leaf; + CV_SymParsed *sym; + U64 sym_ranges_first; + U64 sym_ranges_opl; + CV_TypeId *itype_fwd_map; + RDIM_Type **itype_type_ptrs; + P2R_LinkNameMap *link_name_map; + RDIM_LineTable *first_inline_site_line_table; +}; + +typedef struct P2R_SymbolStreamConvertOut P2R_SymbolStreamConvertOut; +struct P2R_SymbolStreamConvertOut +{ + RDIM_SymbolChunkList procedures; + RDIM_SymbolChunkList global_variables; + RDIM_SymbolChunkList thread_variables; + RDIM_ScopeChunkList scopes; + RDIM_InlineSiteChunkList inline_sites; +}; + +//////////////////////////////// +//~ rjf: Baking Task Types + +//- rjf: line table baking task types + +typedef struct P2R_BakeLineTablesIn P2R_BakeLineTablesIn; +struct P2R_BakeLineTablesIn +{ + RDIM_LineTableChunkList *line_tables; +}; + +//- rjf: string map baking task types + +typedef struct P2R_BakeSrcFilesStringsIn P2R_BakeSrcFilesStringsIn; +struct P2R_BakeSrcFilesStringsIn +{ + RDIM_BakeStringMapTopology *top; + RDIM_BakeStringMapLoose **maps; + RDIM_SrcFileChunkList *list; +}; + +typedef struct P2R_BakeUnitsStringsIn P2R_BakeUnitsStringsIn; +struct P2R_BakeUnitsStringsIn +{ + RDIM_BakeStringMapTopology *top; + RDIM_BakeStringMapLoose **maps; + RDIM_UnitChunkList *list; +}; + +typedef struct P2R_BakeTypesStringsInNode P2R_BakeTypesStringsInNode; +struct P2R_BakeTypesStringsInNode +{ + P2R_BakeTypesStringsInNode *next; + RDIM_Type *v; + RDI_U64 count; +}; + +typedef struct P2R_BakeTypesStringsIn P2R_BakeTypesStringsIn; +struct P2R_BakeTypesStringsIn +{ + RDIM_BakeStringMapTopology *top; + RDIM_BakeStringMapLoose **maps; + P2R_BakeTypesStringsInNode *first; + P2R_BakeTypesStringsInNode *last; +}; + +typedef struct P2R_BakeUDTsStringsInNode P2R_BakeUDTsStringsInNode; +struct P2R_BakeUDTsStringsInNode +{ + P2R_BakeUDTsStringsInNode *next; + RDIM_UDT *v; + RDI_U64 count; +}; + +typedef struct P2R_BakeUDTsStringsIn P2R_BakeUDTsStringsIn; +struct P2R_BakeUDTsStringsIn +{ + RDIM_BakeStringMapTopology *top; + RDIM_BakeStringMapLoose **maps; + P2R_BakeUDTsStringsInNode *first; + P2R_BakeUDTsStringsInNode *last; +}; + +typedef struct P2R_BakeSymbolsStringsInNode P2R_BakeSymbolsStringsInNode; +struct P2R_BakeSymbolsStringsInNode +{ + P2R_BakeSymbolsStringsInNode *next; + RDIM_Symbol *v; + RDI_U64 count; +}; + +typedef struct P2R_BakeSymbolsStringsIn P2R_BakeSymbolsStringsIn; +struct P2R_BakeSymbolsStringsIn +{ + RDIM_BakeStringMapTopology *top; + RDIM_BakeStringMapLoose **maps; + P2R_BakeSymbolsStringsInNode *first; + P2R_BakeSymbolsStringsInNode *last; +}; + +typedef struct P2R_BakeScopesStringsInNode P2R_BakeScopesStringsInNode; +struct P2R_BakeScopesStringsInNode +{ + P2R_BakeScopesStringsInNode *next; + RDIM_Scope *v; + RDI_U64 count; +}; + +typedef struct P2R_BakeScopesStringsIn P2R_BakeScopesStringsIn; +struct P2R_BakeScopesStringsIn +{ + RDIM_BakeStringMapTopology *top; + RDIM_BakeStringMapLoose **maps; + P2R_BakeScopesStringsInNode *first; + P2R_BakeScopesStringsInNode *last; +}; + +//- rjf: string map joining task types + +typedef struct P2R_JoinBakeStringMapSlotsIn P2R_JoinBakeStringMapSlotsIn; +struct P2R_JoinBakeStringMapSlotsIn +{ + RDIM_BakeStringMapTopology *top; + RDIM_BakeStringMapLoose **src_maps; + U64 src_maps_count; + RDIM_BakeStringMapLoose *dst_map; + Rng1U64 slot_idx_range; +}; + +//- rjf: string map sorting task types + +typedef struct P2R_SortBakeStringMapSlotsIn P2R_SortBakeStringMapSlotsIn; +struct P2R_SortBakeStringMapSlotsIn +{ + RDIM_BakeStringMapTopology *top; + RDIM_BakeStringMapLoose *src_map; + RDIM_BakeStringMapLoose *dst_map; + U64 slot_idx; + U64 slot_count; +}; + +//- rjf: OLD string map baking types + +typedef struct P2R_BuildBakeStringMapIn P2R_BuildBakeStringMapIn; +struct P2R_BuildBakeStringMapIn +{ + RDIM_BakePathTree *path_tree; + RDIM_BakeParams *params; +}; + +typedef struct P2R_BuildBakeNameMapIn P2R_BuildBakeNameMapIn; +struct P2R_BuildBakeNameMapIn +{ + RDI_NameMapKind k; + RDIM_BakeParams *params; +}; + +//- rjf: debug info baking task types + +typedef struct P2R_BakeUnitsIn P2R_BakeUnitsIn; +struct P2R_BakeUnitsIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_BakePathTree *path_tree; + RDIM_UnitChunkList *units; +}; + +typedef struct P2R_BakeUnitVMapIn P2R_BakeUnitVMapIn; +struct P2R_BakeUnitVMapIn +{ + RDIM_UnitChunkList *units; +}; + +typedef struct P2R_BakeSrcFilesIn P2R_BakeSrcFilesIn; +struct P2R_BakeSrcFilesIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_BakePathTree *path_tree; + RDIM_SrcFileChunkList *src_files; +}; + +typedef struct P2R_BakeUDTsIn P2R_BakeUDTsIn; +struct P2R_BakeUDTsIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_UDTChunkList *udts; +}; + +typedef struct P2R_BakeGlobalVariablesIn P2R_BakeGlobalVariablesIn; +struct P2R_BakeGlobalVariablesIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_SymbolChunkList *global_variables; +}; + +typedef struct P2R_BakeGlobalVMapIn P2R_BakeGlobalVMapIn; +struct P2R_BakeGlobalVMapIn +{ + RDIM_SymbolChunkList *global_variables; +}; + +typedef struct P2R_BakeThreadVariablesIn P2R_BakeThreadVariablesIn; +struct P2R_BakeThreadVariablesIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_SymbolChunkList *thread_variables; +}; + +typedef struct P2R_BakeProceduresIn P2R_BakeProceduresIn; +struct P2R_BakeProceduresIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_SymbolChunkList *procedures; +}; + +typedef struct P2R_BakeScopesIn P2R_BakeScopesIn; +struct P2R_BakeScopesIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_ScopeChunkList *scopes; +}; + +typedef struct P2R_BakeScopeVMapIn P2R_BakeScopeVMapIn; +struct P2R_BakeScopeVMapIn +{ + RDIM_ScopeChunkList *scopes; +}; + +typedef struct P2R_BakeInlineSitesIn P2R_BakeInlineSitesIn; +struct P2R_BakeInlineSitesIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_InlineSiteChunkList *inline_sites; +}; + +typedef struct P2R_BakeFilePathsIn P2R_BakeFilePathsIn; +struct P2R_BakeFilePathsIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_BakePathTree *path_tree; +}; + +typedef struct P2R_BakeStringsIn P2R_BakeStringsIn; +struct P2R_BakeStringsIn +{ + RDIM_BakeStringMapTight *strings; +}; + +typedef struct P2R_BakeTypeNodesIn P2R_BakeTypeNodesIn; +struct P2R_BakeTypeNodesIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_BakeIdxRunMap *idx_runs; + RDIM_TypeChunkList *types; +}; + +typedef struct P2R_BakeNameMapIn P2R_BakeNameMapIn; +struct P2R_BakeNameMapIn +{ + RDIM_BakeStringMapTight *strings; + RDIM_BakeIdxRunMap *idx_runs; + RDIM_BakeNameMap *map; + RDI_NameMapKind kind; +}; + +typedef struct P2R_BakeIdxRunsIn P2R_BakeIdxRunsIn; +struct P2R_BakeIdxRunsIn +{ + RDIM_BakeIdxRunMap *idx_runs; +}; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal U64 p2r_end_of_cplusplus_container_name(String8 str); +internal U64 p2r_hash_from_voff(U64 voff); + +//////////////////////////////// +//~ rjf: Command Line -> Conversion Inputs + +internal P2R_User2Convert *p2r_user2convert_from_cmdln(Arena *arena, CmdLine *cmdline); + +//////////////////////////////// +//~ rjf: COFF => RDI Canonical Conversions + +internal RDI_BinarySectionFlags p2r_rdi_binary_section_flags_from_coff_section_flags(COFF_SectionFlags flags); + +//////////////////////////////// +//~ rjf: CodeView => RDI Canonical Conversions + +internal RDI_Arch p2r_rdi_arch_from_cv_arch(CV_Arch arch); +internal RDI_RegCode p2r_rdi_reg_code_from_cv_reg_code(RDI_Arch arch, CV_Reg reg_code); +internal RDI_Language p2r_rdi_language_from_cv_language(CV_Language language); +internal RDI_TypeKind p2r_rdi_type_kind_from_cv_basic_type(CV_BasicType basic_type); + +//////////////////////////////// +//~ rjf: Location Info Building Helpers + +internal RDIM_Location *p2r_location_from_addr_reg_off(Arena *arena, RDI_Arch arch, RDI_RegCode reg_code, U32 reg_byte_size, U32 reg_byte_pos, S64 offset, B32 extra_indirection); +internal CV_EncodedFramePtrReg p2r_cv_encoded_fp_reg_from_frameproc(CV_SymFrameproc *frameproc, B32 param_base); +internal RDI_RegCode p2r_reg_code_from_arch_encoded_fp_reg(RDI_Arch arch, CV_EncodedFramePtrReg encoded_reg); +internal void p2r_location_over_lvar_addr_range(Arena *arena, RDIM_ScopeChunkList *scopes, RDIM_LocationSet *locset, RDIM_Location *location, CV_LvarAddrRange *range, COFF_SectionHeader *section, CV_LvarAddrGap *gaps, U64 gap_count); + +//////////////////////////////// +//~ rjf: Initial Parsing & Preparation Pass Tasks + +internal TS_TASK_FUNCTION_DEF(p2r_exe_hash_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_tpi_hash_parse_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_tpi_leaf_parse_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_symbol_stream_parse_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_c13_stream_parse_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_comp_unit_parse_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_comp_unit_contributions_parse_task__entry_point); + +//////////////////////////////// +//~ rjf: Unit Conversion Tasks + +internal TS_TASK_FUNCTION_DEF(p2r_units_convert_task__entry_point); + +//////////////////////////////// +//~ rjf: Link Name Map Building Tasks + +internal TS_TASK_FUNCTION_DEF(p2r_link_name_map_build_task__entry_point); + +//////////////////////////////// +//~ rjf: Type Parsing/Conversion Tasks + +internal TS_TASK_FUNCTION_DEF(p2r_itype_fwd_map_fill_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_itype_chain_build_task__entry_point); + +//////////////////////////////// +//~ rjf: UDT Conversion Tasks + +internal TS_TASK_FUNCTION_DEF(p2r_udt_convert_task__entry_point); + +//////////////////////////////// +//~ rjf: Symbol Stream Conversion Tasks + +internal TS_TASK_FUNCTION_DEF(p2r_symbol_stream_convert_task__entry_point); + +//////////////////////////////// +//~ rjf: Top-Level Conversion Entry Point + +internal P2R_Convert2Bake *p2r_convert(Arena *arena, P2R_User2Convert *in); + +//////////////////////////////// +//~ rjf: Baking Stage Tasks + +//- rjf: unsorted bake string map building +internal TS_TASK_FUNCTION_DEF(p2r_bake_src_files_strings_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_units_strings_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_types_strings_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_udts_strings_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_symbols_strings_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_scopes_strings_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_line_tables_task__entry_point); + +//- rjf: bake string map joining +internal TS_TASK_FUNCTION_DEF(p2r_bake_string_map_join_task__entry_point); + +//- rjf: bake string map sorting +internal TS_TASK_FUNCTION_DEF(p2r_bake_string_map_sort_task__entry_point); + +//- rjf: pass 1: interner/deduper map builds +internal TS_TASK_FUNCTION_DEF(p2r_build_bake_name_map_task__entry_point); + +//- rjf: pass 2: string-map-dependent debug info stream builds +internal TS_TASK_FUNCTION_DEF(p2r_bake_units_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_unit_vmap_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_src_files_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_udts_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_global_variables_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_global_vmap_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_thread_variables_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_procedures_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_scopes_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_scope_vmap_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_file_paths_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_strings_task__entry_point); + +//- rjf: pass 3: idx-run-map-dependent debug info stream builds +internal TS_TASK_FUNCTION_DEF(p2r_bake_type_nodes_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_name_map_task__entry_point); +internal TS_TASK_FUNCTION_DEF(p2r_bake_idx_runs_task__entry_point); + +//////////////////////////////// +//~ rjf: Top-Level Baking Entry Point + +internal P2R_Bake2Serialize *p2r_bake(Arena *arena, P2R_Convert2Bake *in); + +//////////////////////////////// +//~ rjf: Top-Level Compression Entry Point + +internal P2R_Serialize2File *p2r_compress(Arena *arena, P2R_Serialize2File *in); + +#endif // RDI_FROM_PDB_H diff --git a/src/rdi_make/rdi_make_local.c b/src/rdi_make/rdi_make_local.c index 981c819e..c1b5436c 100644 --- a/src/rdi_make/rdi_make_local.c +++ b/src/rdi_make/rdi_make_local.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#include "lib_rdi_make/rdi_make.c" +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "lib_rdi_make/rdi_make.c" diff --git a/src/rdi_make/rdi_make_local.h b/src/rdi_make/rdi_make_local.h index 873cee30..46a11b16 100644 --- a/src/rdi_make/rdi_make_local.h +++ b/src/rdi_make/rdi_make_local.h @@ -1,50 +1,50 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RDI_CONS_LOCAL_H -#define RDI_CONS_LOCAL_H - -// rjf: base layer memory ops -#define RDIM_MEMSET_OVERRIDE -#define RDIM_MEMCPY_OVERRIDE -#define rdim_memset MemorySet -#define rdim_memcpy MemoryCopy - -// rjf: base layer string overrides -#define RDI_STRING8_OVERRIDE -#define RDIM_String8 String8 -#define RDIM_String8_BaseMember str -#define RDIM_String8_SizeMember size -#define RDI_STRING8LIST_OVERRIDE -#define RDIM_String8Node String8Node -#define RDIM_String8Node_NextPtrMember next -#define RDIM_String8Node_StringMember string -#define RDIM_String8List String8List -#define RDIM_String8List_FirstMember first -#define RDIM_String8List_LastMember last -#define RDIM_String8List_NodeCountMember node_count -#define RDIM_String8List_TotalSizeMember total_size - -// rjf: base layer arena overrides -#define RDIM_ARENA_OVERRIDE -#define RDIM_Arena Arena -#define rdim_arena_alloc arena_alloc -#define rdim_arena_release arena_release -#define rdim_arena_pos arena_pos -#define rdim_arena_push arena_push -#define rdim_arena_pop_to arena_pop_to - -// rjf: base layer scratch arena overrides -#define RDIM_SCRATCH_OVERRIDE -#define RDIM_Temp Temp -#define rdim_temp_arena(t) ((t).arena) -#define rdim_scratch_begin scratch_begin -#define rdim_scratch_end scratch_end - -// rjf: base layer profiling markup overrides -#define RDIM_ProfBegin(...) ProfBeginDynamic(__VA_ARGS__) -#define RDIM_ProfEnd(...) ProfEnd() - -#include "lib_rdi_make/rdi_make.h" - -#endif // RDI_CONS_LOCAL_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RDI_CONS_LOCAL_H +#define RDI_CONS_LOCAL_H + +// rjf: base layer memory ops +#define RDIM_MEMSET_OVERRIDE +#define RDIM_MEMCPY_OVERRIDE +#define rdim_memset MemorySet +#define rdim_memcpy MemoryCopy + +// rjf: base layer string overrides +#define RDI_STRING8_OVERRIDE +#define RDIM_String8 String8 +#define RDIM_String8_BaseMember str +#define RDIM_String8_SizeMember size +#define RDI_STRING8LIST_OVERRIDE +#define RDIM_String8Node String8Node +#define RDIM_String8Node_NextPtrMember next +#define RDIM_String8Node_StringMember string +#define RDIM_String8List String8List +#define RDIM_String8List_FirstMember first +#define RDIM_String8List_LastMember last +#define RDIM_String8List_NodeCountMember node_count +#define RDIM_String8List_TotalSizeMember total_size + +// rjf: base layer arena overrides +#define RDIM_ARENA_OVERRIDE +#define RDIM_Arena Arena +#define rdim_arena_alloc arena_alloc +#define rdim_arena_release arena_release +#define rdim_arena_pos arena_pos +#define rdim_arena_push arena_push +#define rdim_arena_pop_to arena_pop_to + +// rjf: base layer scratch arena overrides +#define RDIM_SCRATCH_OVERRIDE +#define RDIM_Temp Temp +#define rdim_temp_arena(t) ((t).arena) +#define rdim_scratch_begin scratch_begin +#define rdim_scratch_end scratch_end + +// rjf: base layer profiling markup overrides +#define RDIM_ProfBegin(...) ProfBeginDynamic(__VA_ARGS__) +#define RDIM_ProfEnd(...) ProfEnd() + +#include "lib_rdi_make/rdi_make.h" + +#endif // RDI_CONS_LOCAL_H diff --git a/src/regs/rdi/regs_rdi.c b/src/regs/rdi/regs_rdi.c index 1e130772..b191f5f8 100644 --- a/src/regs/rdi/regs_rdi.c +++ b/src/regs/rdi/regs_rdi.c @@ -1,4 +1,4 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#include "regs/rdi/generated/regs_rdi.meta.c" +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "regs/rdi/generated/regs_rdi.meta.c" diff --git a/src/regs/rdi/regs_rdi.h b/src/regs/rdi/regs_rdi.h index c75002d0..543b28f1 100644 --- a/src/regs/rdi/regs_rdi.h +++ b/src/regs/rdi/regs_rdi.h @@ -1,10 +1,10 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef REGS_RDI_H -#define REGS_RDI_H - -internal RDI_RegCode regs_rdi_code_from_arch_reg_code(Architecture arch, REGS_RegCode code); -internal REGS_RegCode regs_reg_code_from_arch_rdi_code(Architecture arch, RDI_RegCode reg); - -#endif //REGS_RDI_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef REGS_RDI_H +#define REGS_RDI_H + +internal RDI_RegCode regs_rdi_code_from_arch_reg_code(Architecture arch, REGS_RegCode code); +internal REGS_RegCode regs_reg_code_from_arch_rdi_code(Architecture arch, RDI_RegCode reg); + +#endif //REGS_RDI_H diff --git a/src/regs/rdi/regs_rdi.mdesk b/src/regs/rdi/regs_rdi.mdesk index 9c407eed..fc6fd43c 100644 --- a/src/regs/rdi/regs_rdi.mdesk +++ b/src/regs/rdi/regs_rdi.mdesk @@ -1,63 +1,63 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: RADDBG Converter Helper Implementation Generators - -@gen @c_file -{ - `internal RDI_RegCode regs_rdi_code_from_arch_reg_code(Architecture arch, REGS_RegCode code)`; - `{`; - `RDI_RegCode result = 0;`; - `switch(arch)`; - `{`; - `default:{}break;`; - `case Architecture_x64:`; - `{`; - `switch(code)` - `{`; - `default:{}break;`; - @expand(REGS_RegTableX64 a) `case REGS_RegCodeX64_$(a.name):{result = RDI_RegCodeX64_$(a.name);}break;`; - `}`; - `}break;`; - `case Architecture_x86:`; - `{`; - `switch(code)` - `{`; - `default:{}break;`; - @expand(REGS_RegTableX86 a) `case REGS_RegCodeX86_$(a.name):{result = RDI_RegCodeX86_$(a.name);}break;`; - `}`; - `}break;`; - `}`; - `return result;`; - `}`; -} - -@gen @c_file -{ - `internal REGS_RegCode regs_reg_code_from_arch_rdi_code(Architecture arch, RDI_RegCode code)`; - `{`; - `REGS_RegCode result = 0;`; - `switch(arch)`; - `{`; - `default:{}break;`; - `case Architecture_x64:`; - `{`; - `switch(code)` - `{`; - `default:{}break;`; - @expand(REGS_RegTableX64 a) `case RDI_RegCodeX64_$(a.name):{result = REGS_RegCodeX64_$(a.name);}break;`; - `}`; - `}break;`; - `case Architecture_x86:`; - `{`; - `switch(code)` - `{`; - `default:{}break;`; - @expand(REGS_RegTableX86 a) `case RDI_RegCodeX86_$(a.name):{result = REGS_RegCodeX86_$(a.name);}break;`; - `}`; - `}break;`; - `}`; - `return result;`; - `}`; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: RADDBG Converter Helper Implementation Generators + +@gen @c_file +{ + `internal RDI_RegCode regs_rdi_code_from_arch_reg_code(Architecture arch, REGS_RegCode code)`; + `{`; + `RDI_RegCode result = 0;`; + `switch(arch)`; + `{`; + `default:{}break;`; + `case Architecture_x64:`; + `{`; + `switch(code)` + `{`; + `default:{}break;`; + @expand(REGS_RegTableX64 a) `case REGS_RegCodeX64_$(a.name):{result = RDI_RegCodeX64_$(a.name);}break;`; + `}`; + `}break;`; + `case Architecture_x86:`; + `{`; + `switch(code)` + `{`; + `default:{}break;`; + @expand(REGS_RegTableX86 a) `case REGS_RegCodeX86_$(a.name):{result = RDI_RegCodeX86_$(a.name);}break;`; + `}`; + `}break;`; + `}`; + `return result;`; + `}`; +} + +@gen @c_file +{ + `internal REGS_RegCode regs_reg_code_from_arch_rdi_code(Architecture arch, RDI_RegCode code)`; + `{`; + `REGS_RegCode result = 0;`; + `switch(arch)`; + `{`; + `default:{}break;`; + `case Architecture_x64:`; + `{`; + `switch(code)` + `{`; + `default:{}break;`; + @expand(REGS_RegTableX64 a) `case RDI_RegCodeX64_$(a.name):{result = REGS_RegCodeX64_$(a.name);}break;`; + `}`; + `}break;`; + `case Architecture_x86:`; + `{`; + `switch(code)` + `{`; + `default:{}break;`; + @expand(REGS_RegTableX86 a) `case RDI_RegCodeX86_$(a.name):{result = REGS_RegCodeX86_$(a.name);}break;`; + `}`; + `}break;`; + `}`; + `return result;`; + `}`; +} diff --git a/src/regs/regs.c b/src/regs/regs.c index 6d701480..fd4bb4e4 100644 --- a/src/regs/regs.c +++ b/src/regs/regs.c @@ -1,58 +1,58 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Generated Code - -#include "regs/generated/regs.meta.c" - -//////////////////////////////// -//~ rjf: Helpers - -internal U64 -regs_rip_from_arch_block(Architecture arch, void *block) -{ - U64 result = 0; - if(block != 0) switch(arch) - { - default:{}break; - case Architecture_x64:{result = ((REGS_RegBlockX64 *)block)->rip.u64;}break; - case Architecture_x86:{result = (U64)((REGS_RegBlockX86 *)block)->eip.u32;}break; - } - return result; -} - -internal U64 -regs_rsp_from_arch_block(Architecture arch, void *block) -{ - U64 result = 0; - if(block != 0) switch(arch) - { - default:{}break; - case Architecture_x64:{result = ((REGS_RegBlockX64 *)block)->rsp.u64;}break; - case Architecture_x86:{result = (U64)((REGS_RegBlockX86 *)block)->esp.u32;}break; - } - return result; -} - -internal void -regs_arch_block_write_rip(Architecture arch, void *block, U64 rip) -{ - if(block != 0) switch(arch) - { - default:{}break; - case Architecture_x64:{((REGS_RegBlockX64 *)block)->rip.u64 = rip;}break; - case Architecture_x86:{((REGS_RegBlockX86 *)block)->eip.u32 = (U32)rip;}break; - } -} - -internal void -regs_arch_block_write_rsp(Architecture arch, void *block, U64 rsp) -{ - if(block != 0) switch(arch) - { - default:{}break; - case Architecture_x64:{((REGS_RegBlockX64 *)block)->rsp.u64 = rsp;}break; - case Architecture_x86:{((REGS_RegBlockX86 *)block)->esp.u32 = (U32)rsp;}break; - } -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Generated Code + +#include "regs/generated/regs.meta.c" + +//////////////////////////////// +//~ rjf: Helpers + +internal U64 +regs_rip_from_arch_block(Architecture arch, void *block) +{ + U64 result = 0; + if(block != 0) switch(arch) + { + default:{}break; + case Architecture_x64:{result = ((REGS_RegBlockX64 *)block)->rip.u64;}break; + case Architecture_x86:{result = (U64)((REGS_RegBlockX86 *)block)->eip.u32;}break; + } + return result; +} + +internal U64 +regs_rsp_from_arch_block(Architecture arch, void *block) +{ + U64 result = 0; + if(block != 0) switch(arch) + { + default:{}break; + case Architecture_x64:{result = ((REGS_RegBlockX64 *)block)->rsp.u64;}break; + case Architecture_x86:{result = (U64)((REGS_RegBlockX86 *)block)->esp.u32;}break; + } + return result; +} + +internal void +regs_arch_block_write_rip(Architecture arch, void *block, U64 rip) +{ + if(block != 0) switch(arch) + { + default:{}break; + case Architecture_x64:{((REGS_RegBlockX64 *)block)->rip.u64 = rip;}break; + case Architecture_x86:{((REGS_RegBlockX86 *)block)->eip.u32 = (U32)rip;}break; + } +} + +internal void +regs_arch_block_write_rsp(Architecture arch, void *block, U64 rsp) +{ + if(block != 0) switch(arch) + { + default:{}break; + case Architecture_x64:{((REGS_RegBlockX64 *)block)->rsp.u64 = rsp;}break; + case Architecture_x86:{((REGS_RegBlockX86 *)block)->esp.u32 = (U32)rsp;}break; + } +} diff --git a/src/render/d3d11/render_d3d11.h b/src/render/d3d11/render_d3d11.h index 8fd89503..47b3d4b7 100644 --- a/src/render/d3d11/render_d3d11.h +++ b/src/render/d3d11/render_d3d11.h @@ -1,187 +1,187 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RENDER_D3D11_H -#define RENDER_D3D11_H - -#include -#include -#include - -#pragma comment(lib, "user32") -#pragma comment(lib, "d3d11") -#pragma comment(lib, "d3dcompiler") - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/render_d3d11.meta.h" - -//////////////////////////////// -//~ rjf: C-side Shader Types - -typedef struct R_D3D11_Uniforms_Rect R_D3D11_Uniforms_Rect; -struct R_D3D11_Uniforms_Rect -{ - Vec2F32 viewport_size; - F32 opacity; - F32 _padding0_; - Vec4F32 texture_sample_channel_map[4]; - Vec2F32 texture_t2d_size; - Vec2F32 translate; - Vec4F32 xform[3]; - Vec2F32 xform_scale; -}; - -typedef struct R_D3D11_Uniforms_BlurPass R_D3D11_Uniforms_BlurPass; -struct R_D3D11_Uniforms_BlurPass -{ - Rng2F32 rect; - Vec4F32 corner_radii; - Vec2F32 direction; - Vec2F32 viewport_size; - U32 blur_count; - U8 _padding0_[204]; -}; -StaticAssert(sizeof(R_D3D11_Uniforms_BlurPass) % 256 == 0, NotAligned); // constant count/offset must be aligned to 256 bytes - -typedef struct R_D3D11_Uniforms_Blur R_D3D11_Uniforms_Blur; -struct R_D3D11_Uniforms_Blur -{ - R_D3D11_Uniforms_BlurPass passes[Axis2_COUNT]; - Vec4F32 kernel[32]; -}; - -typedef struct R_D3D11_Uniforms_Mesh R_D3D11_Uniforms_Mesh; -struct R_D3D11_Uniforms_Mesh -{ - Mat4x4F32 xform; -}; - -//////////////////////////////// -//~ rjf: Main State Types - -typedef struct R_D3D11_Tex2D R_D3D11_Tex2D; -struct R_D3D11_Tex2D -{ - R_D3D11_Tex2D *next; - U64 generation; - ID3D11Texture2D *texture; - ID3D11ShaderResourceView *view; - R_ResourceKind kind; - Vec2S32 size; - R_Tex2DFormat format; -}; - -typedef struct R_D3D11_Buffer R_D3D11_Buffer; -struct R_D3D11_Buffer -{ - R_D3D11_Buffer *next; - U64 generation; - ID3D11Buffer *buffer; - R_ResourceKind kind; - U64 size; -}; - -typedef struct R_D3D11_Window R_D3D11_Window; -struct R_D3D11_Window -{ - R_D3D11_Window *next; - U64 generation; - - // rjf: swapchain/framebuffer - IDXGISwapChain1 *swapchain; - ID3D11Texture2D *framebuffer; - ID3D11RenderTargetView *framebuffer_rtv; - - // rjf: staging buffer - ID3D11Texture2D *stage_color; - ID3D11RenderTargetView *stage_color_rtv; - ID3D11ShaderResourceView *stage_color_srv; - ID3D11Texture2D *stage_scratch_color; - ID3D11RenderTargetView *stage_scratch_color_rtv; - ID3D11ShaderResourceView *stage_scratch_color_srv; - - // rjf: geo3d buffer - ID3D11Texture2D *geo3d_color; - ID3D11RenderTargetView *geo3d_color_rtv; - ID3D11ShaderResourceView *geo3d_color_srv; - ID3D11Texture2D *geo3d_depth; - ID3D11DepthStencilView *geo3d_depth_dsv; - ID3D11ShaderResourceView *geo3d_depth_srv; - - // rjf: last state - Vec2S32 last_resolution; -}; - -typedef struct R_D3D11_FlushBuffer R_D3D11_FlushBuffer; -struct R_D3D11_FlushBuffer -{ - R_D3D11_FlushBuffer *next; - ID3D11Buffer *buffer; -}; - -typedef struct R_D3D11_State R_D3D11_State; -struct R_D3D11_State -{ - // rjf: state - Arena *arena; - R_D3D11_Window *first_free_window; - R_D3D11_Tex2D *first_free_tex2d; - R_D3D11_Buffer *first_free_buffer; - R_D3D11_Tex2D *first_to_free_tex2d; - R_D3D11_Buffer *first_to_free_buffer; - OS_Handle device_rw_mutex; - - // rjf: base d3d11 objects - ID3D11Device *base_device; - ID3D11DeviceContext *base_device_ctx; - ID3D11Device1 *device; - ID3D11DeviceContext1 *device_ctx; - IDXGIDevice1 *dxgi_device; - IDXGIAdapter *dxgi_adapter; - IDXGIFactory2 *dxgi_factory; - ID3D11RasterizerState1 *main_rasterizer; - ID3D11BlendState *main_blend_state; - ID3D11BlendState *no_blend_state; - ID3D11SamplerState *samplers[R_Tex2DSampleKind_COUNT]; - ID3D11DepthStencilState *noop_depth_stencil; - ID3D11DepthStencilState *plain_depth_stencil; - ID3D11Buffer *instance_scratch_buffer_64kb; - - // rjf: backups - R_Handle backup_texture; - - // rjf: vertex shaders - ID3D11VertexShader *vshads[R_D3D11_VShadKind_COUNT]; - ID3D11InputLayout *ilays[R_D3D11_VShadKind_COUNT]; - ID3D11PixelShader *pshads[R_D3D11_PShadKind_COUNT]; - ID3D11Buffer *uniform_type_kind_buffers[R_D3D11_UniformTypeKind_COUNT]; - - // rjf: buffers to flush at subsequent frame - Arena *buffer_flush_arena; - R_D3D11_FlushBuffer *first_buffer_to_flush; - R_D3D11_FlushBuffer *last_buffer_to_flush; -}; - -//////////////////////////////// -//~ rjf: Globals - -global R_D3D11_State *r_d3d11_state = 0; -global R_D3D11_Window r_d3d11_window_nil = {&r_d3d11_window_nil}; -global R_D3D11_Tex2D r_d3d11_tex2d_nil = {&r_d3d11_tex2d_nil}; -global R_D3D11_Buffer r_d3d11_buffer_nil = {&r_d3d11_buffer_nil}; - -//////////////////////////////// -//~ rjf: Helpers - -internal R_D3D11_Window *r_d3d11_window_from_handle(R_Handle handle); -internal R_Handle r_d3d11_handle_from_window(R_D3D11_Window *window); -internal R_D3D11_Tex2D *r_d3d11_tex2d_from_handle(R_Handle handle); -internal R_Handle r_d3d11_handle_from_tex2d(R_D3D11_Tex2D *texture); -internal R_D3D11_Buffer *r_d3d11_buffer_from_handle(R_Handle handle); -internal R_Handle r_d3d11_handle_from_buffer(R_D3D11_Buffer *buffer); -internal ID3D11Buffer *r_d3d11_instance_buffer_from_size(U64 size); -internal void r_usage_access_flags_from_resource_kind(R_ResourceKind kind, D3D11_USAGE *out_d3d11_usage, UINT *out_cpu_access_flags); - -#endif // RENDER_D3D11_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RENDER_D3D11_H +#define RENDER_D3D11_H + +#include +#include +#include + +#pragma comment(lib, "user32") +#pragma comment(lib, "d3d11") +#pragma comment(lib, "d3dcompiler") + +//////////////////////////////// +//~ rjf: Generated Code + +#include "generated/render_d3d11.meta.h" + +//////////////////////////////// +//~ rjf: C-side Shader Types + +typedef struct R_D3D11_Uniforms_Rect R_D3D11_Uniforms_Rect; +struct R_D3D11_Uniforms_Rect +{ + Vec2F32 viewport_size; + F32 opacity; + F32 _padding0_; + Vec4F32 texture_sample_channel_map[4]; + Vec2F32 texture_t2d_size; + Vec2F32 translate; + Vec4F32 xform[3]; + Vec2F32 xform_scale; +}; + +typedef struct R_D3D11_Uniforms_BlurPass R_D3D11_Uniforms_BlurPass; +struct R_D3D11_Uniforms_BlurPass +{ + Rng2F32 rect; + Vec4F32 corner_radii; + Vec2F32 direction; + Vec2F32 viewport_size; + U32 blur_count; + U8 _padding0_[204]; +}; +StaticAssert(sizeof(R_D3D11_Uniforms_BlurPass) % 256 == 0, NotAligned); // constant count/offset must be aligned to 256 bytes + +typedef struct R_D3D11_Uniforms_Blur R_D3D11_Uniforms_Blur; +struct R_D3D11_Uniforms_Blur +{ + R_D3D11_Uniforms_BlurPass passes[Axis2_COUNT]; + Vec4F32 kernel[32]; +}; + +typedef struct R_D3D11_Uniforms_Mesh R_D3D11_Uniforms_Mesh; +struct R_D3D11_Uniforms_Mesh +{ + Mat4x4F32 xform; +}; + +//////////////////////////////// +//~ rjf: Main State Types + +typedef struct R_D3D11_Tex2D R_D3D11_Tex2D; +struct R_D3D11_Tex2D +{ + R_D3D11_Tex2D *next; + U64 generation; + ID3D11Texture2D *texture; + ID3D11ShaderResourceView *view; + R_ResourceKind kind; + Vec2S32 size; + R_Tex2DFormat format; +}; + +typedef struct R_D3D11_Buffer R_D3D11_Buffer; +struct R_D3D11_Buffer +{ + R_D3D11_Buffer *next; + U64 generation; + ID3D11Buffer *buffer; + R_ResourceKind kind; + U64 size; +}; + +typedef struct R_D3D11_Window R_D3D11_Window; +struct R_D3D11_Window +{ + R_D3D11_Window *next; + U64 generation; + + // rjf: swapchain/framebuffer + IDXGISwapChain1 *swapchain; + ID3D11Texture2D *framebuffer; + ID3D11RenderTargetView *framebuffer_rtv; + + // rjf: staging buffer + ID3D11Texture2D *stage_color; + ID3D11RenderTargetView *stage_color_rtv; + ID3D11ShaderResourceView *stage_color_srv; + ID3D11Texture2D *stage_scratch_color; + ID3D11RenderTargetView *stage_scratch_color_rtv; + ID3D11ShaderResourceView *stage_scratch_color_srv; + + // rjf: geo3d buffer + ID3D11Texture2D *geo3d_color; + ID3D11RenderTargetView *geo3d_color_rtv; + ID3D11ShaderResourceView *geo3d_color_srv; + ID3D11Texture2D *geo3d_depth; + ID3D11DepthStencilView *geo3d_depth_dsv; + ID3D11ShaderResourceView *geo3d_depth_srv; + + // rjf: last state + Vec2S32 last_resolution; +}; + +typedef struct R_D3D11_FlushBuffer R_D3D11_FlushBuffer; +struct R_D3D11_FlushBuffer +{ + R_D3D11_FlushBuffer *next; + ID3D11Buffer *buffer; +}; + +typedef struct R_D3D11_State R_D3D11_State; +struct R_D3D11_State +{ + // rjf: state + Arena *arena; + R_D3D11_Window *first_free_window; + R_D3D11_Tex2D *first_free_tex2d; + R_D3D11_Buffer *first_free_buffer; + R_D3D11_Tex2D *first_to_free_tex2d; + R_D3D11_Buffer *first_to_free_buffer; + OS_Handle device_rw_mutex; + + // rjf: base d3d11 objects + ID3D11Device *base_device; + ID3D11DeviceContext *base_device_ctx; + ID3D11Device1 *device; + ID3D11DeviceContext1 *device_ctx; + IDXGIDevice1 *dxgi_device; + IDXGIAdapter *dxgi_adapter; + IDXGIFactory2 *dxgi_factory; + ID3D11RasterizerState1 *main_rasterizer; + ID3D11BlendState *main_blend_state; + ID3D11BlendState *no_blend_state; + ID3D11SamplerState *samplers[R_Tex2DSampleKind_COUNT]; + ID3D11DepthStencilState *noop_depth_stencil; + ID3D11DepthStencilState *plain_depth_stencil; + ID3D11Buffer *instance_scratch_buffer_64kb; + + // rjf: backups + R_Handle backup_texture; + + // rjf: vertex shaders + ID3D11VertexShader *vshads[R_D3D11_VShadKind_COUNT]; + ID3D11InputLayout *ilays[R_D3D11_VShadKind_COUNT]; + ID3D11PixelShader *pshads[R_D3D11_PShadKind_COUNT]; + ID3D11Buffer *uniform_type_kind_buffers[R_D3D11_UniformTypeKind_COUNT]; + + // rjf: buffers to flush at subsequent frame + Arena *buffer_flush_arena; + R_D3D11_FlushBuffer *first_buffer_to_flush; + R_D3D11_FlushBuffer *last_buffer_to_flush; +}; + +//////////////////////////////// +//~ rjf: Globals + +global R_D3D11_State *r_d3d11_state = 0; +global R_D3D11_Window r_d3d11_window_nil = {&r_d3d11_window_nil}; +global R_D3D11_Tex2D r_d3d11_tex2d_nil = {&r_d3d11_tex2d_nil}; +global R_D3D11_Buffer r_d3d11_buffer_nil = {&r_d3d11_buffer_nil}; + +//////////////////////////////// +//~ rjf: Helpers + +internal R_D3D11_Window *r_d3d11_window_from_handle(R_Handle handle); +internal R_Handle r_d3d11_handle_from_window(R_D3D11_Window *window); +internal R_D3D11_Tex2D *r_d3d11_tex2d_from_handle(R_Handle handle); +internal R_Handle r_d3d11_handle_from_tex2d(R_D3D11_Tex2D *texture); +internal R_D3D11_Buffer *r_d3d11_buffer_from_handle(R_Handle handle); +internal R_Handle r_d3d11_handle_from_buffer(R_D3D11_Buffer *buffer); +internal ID3D11Buffer *r_d3d11_instance_buffer_from_size(U64 size); +internal void r_usage_access_flags_from_resource_kind(R_ResourceKind kind, D3D11_USAGE *out_d3d11_usage, UINT *out_cpu_access_flags); + +#endif // RENDER_D3D11_H diff --git a/src/render/d3d11/render_d3d11.mdesk b/src/render/d3d11/render_d3d11.mdesk index 133e6b3b..d05aad0f 100644 --- a/src/render/d3d11/render_d3d11.mdesk +++ b/src/render/d3d11/render_d3d11.mdesk @@ -1,523 +1,523 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Pipeline Tables - -@table(name, source, ilay_table) -R_D3D11_VShadTable: -{ - {Rect r_d3d11_g_rect_shader_src r_d3d11_g_rect_ilay_elements } - {Blur r_d3d11_g_blur_shader_src 0 } - {Mesh r_d3d11_g_mesh_shader_src r_d3d11_g_mesh_ilay_elements } - {Geo3DComposite r_d3d11_g_geo3dcomposite_shader_src 0 } - {Finalize r_d3d11_g_finalize_shader_src 0 } -} - -@table(name, source) -R_D3D11_PShadTable: -{ - {Rect r_d3d11_g_rect_shader_src } - {Blur r_d3d11_g_blur_shader_src } - {Mesh r_d3d11_g_mesh_shader_src } - {Geo3DComposite r_d3d11_g_geo3dcomposite_shader_src } - {Finalize r_d3d11_g_finalize_shader_src } -} - -@table(name) -R_D3D11_UniformTypeTable: -{ - {Rect} - {Blur} - {Mesh} -} - -//////////////////////////////// -//~ rjf: UI Rectangle Shaders - -@embed_string r_d3d11_g_rect_shader_src: -""" -cbuffer Globals : register(b0) -{ - float2 viewport_size_px; - float opacity; - row_major float4x4 texture_sample_channel_map; - float2 texture_t2d_size_px; - row_major float3x3 xform; - float2 xform_scale; -} - -struct CPU2Vertex -{ - float4 dst_rect_px : POS; - float4 src_rect_px : TEX; - float4 color00 : COL0; - float4 color01 : COL1; - float4 color10 : COL2; - float4 color11 : COL3; - float4 corner_radii_px : CRAD; - float4 style_params : STY; // x: border_thickness_px, y: softness_px, z: omit_texture, w: unused - uint vertex_id : SV_VertexID; -}; - -struct Vertex2Pixel -{ - float4 position : SV_POSITION; - nointerpolation float2 rect_half_size_px : PSIZE; - float2 texcoord_pct : TEX; - float2 sdf_sample_pos : SDF; - float4 tint : TINT; - float corner_radius_px : CRAD; - nointerpolation float border_thickness_px : BTHC; - nointerpolation float softness_px : SFT; - nointerpolation float omit_texture : OTX; -}; - -Texture2D main_t2d : register(t0); -SamplerState main_sampler : register(s0); - -float rect_sdf(float2 sample_pos, float2 rect_half_size, float r) -{ - return length(max(abs(sample_pos) - rect_half_size + r, 0.0)) - r; -} - -//- rjf: vertex shader - -Vertex2Pixel -vs_main(CPU2Vertex cpu2vertex) -{ - //- rjf: unpack & xform rectangle src/dst vertices - float2 dst_p0_px = cpu2vertex.dst_rect_px.xy; - float2 dst_p1_px = cpu2vertex.dst_rect_px.zw; - float2 src_p0_px = cpu2vertex.src_rect_px.xy; - float2 src_p1_px = cpu2vertex.src_rect_px.zw; - float2 dst_size_px = abs(dst_p1_px - dst_p0_px); - - //- rjf: unpack style params - float border_thickness_px = cpu2vertex.style_params.x; - float softness_px = cpu2vertex.style_params.y; - float omit_texture = cpu2vertex.style_params.z; - - //- rjf: prep per-vertex arrays to sample from (p: position, t: texcoord, c: colorcoord, r: cornerradius) - float2 dst_p_verts_px[] = - { - float2(dst_p0_px.x, dst_p1_px.y), - float2(dst_p0_px.x, dst_p0_px.y), - float2(dst_p1_px.x, dst_p1_px.y), - float2(dst_p1_px.x, dst_p0_px.y), - }; - float2 src_p_verts_px[] = - { - float2(src_p0_px.x, src_p1_px.y), - float2(src_p0_px.x, src_p0_px.y), - float2(src_p1_px.x, src_p1_px.y), - float2(src_p1_px.x, src_p0_px.y), - }; - float dst_r_verts_px[] = - { - cpu2vertex.corner_radii_px.y, - cpu2vertex.corner_radii_px.x, - cpu2vertex.corner_radii_px.w, - cpu2vertex.corner_radii_px.z, - }; - float4 src_color[] = { - cpu2vertex.color01, - cpu2vertex.color00, - cpu2vertex.color11, - cpu2vertex.color10, - }; - float2 dst_verts_pct = float2((cpu2vertex.vertex_id >> 1) ? 1.f : 0.f, - (cpu2vertex.vertex_id & 1) ? 0.f : 1.f); - - // rjf: fill vertex -> pixel data - Vertex2Pixel vertex2pixel; - { - float2 xformed_pos = mul(xform, float3(dst_p_verts_px[cpu2vertex.vertex_id], 1.f)).xy; - xformed_pos.y = viewport_size_px.y - xformed_pos.y; - vertex2pixel.position.xy = 2.f * xformed_pos/viewport_size_px - 1.f; - vertex2pixel.position.z = 0.f; - vertex2pixel.position.w = 1.f; - vertex2pixel.rect_half_size_px = dst_size_px / 2.f * xform_scale; - vertex2pixel.texcoord_pct = src_p_verts_px[cpu2vertex.vertex_id] / texture_t2d_size_px; - vertex2pixel.sdf_sample_pos = (2.f * dst_verts_pct - 1.f) * vertex2pixel.rect_half_size_px; - vertex2pixel.tint = src_color[cpu2vertex.vertex_id]; - vertex2pixel.corner_radius_px = dst_r_verts_px[cpu2vertex.vertex_id]; - vertex2pixel.border_thickness_px = border_thickness_px; - vertex2pixel.softness_px = softness_px; - vertex2pixel.omit_texture = omit_texture; - } - return vertex2pixel; -} - -//- rjf: pixel shader - -float4 -ps_main(Vertex2Pixel vertex2pixel) : SV_TARGET -{ - // rjf: blend corner colors to produce final tint - float4 tint = vertex2pixel.tint; - - // rjf: sample texture - float4 albedo_sample = float4(1, 1, 1, 1); - if(vertex2pixel.omit_texture < 1) - { - albedo_sample = mul(main_t2d.Sample(main_sampler, vertex2pixel.texcoord_pct), texture_sample_channel_map); - } - - // rjf: determine SDF sample position - float2 sdf_sample_pos = vertex2pixel.sdf_sample_pos; - - // rjf: sample for borders - float border_sdf_t = 1; - if(vertex2pixel.border_thickness_px > 0) - { - float border_sdf_s = rect_sdf(sdf_sample_pos, - vertex2pixel.rect_half_size_px - float2(vertex2pixel.softness_px*2.f, vertex2pixel.softness_px*2.f) - vertex2pixel.border_thickness_px, - max(vertex2pixel.corner_radius_px-vertex2pixel.border_thickness_px, 0)); - border_sdf_t = smoothstep(0, 2*vertex2pixel.softness_px, border_sdf_s); - } - if(border_sdf_t < 0.001f) - { - discard; - } - - // rjf: sample for corners - float corner_sdf_t = 1; - if(vertex2pixel.corner_radius_px > 0 || vertex2pixel.softness_px > 0.75f) - { - float corner_sdf_s = rect_sdf(sdf_sample_pos, - vertex2pixel.rect_half_size_px - float2(vertex2pixel.softness_px*2.f, vertex2pixel.softness_px*2.f), - vertex2pixel.corner_radius_px); - corner_sdf_t = 1-smoothstep(0, 2*vertex2pixel.softness_px, corner_sdf_s); - } - - // rjf: form+return final color - float4 final_color = albedo_sample; - final_color *= tint; - final_color.a *= opacity; - final_color.a *= corner_sdf_t; - final_color.a *= border_sdf_t; - return final_color; -} -""" - -//////////////////////////////// -//~ rjf: Blur Shaders - -@embed_string r_d3d11_g_blur_shader_src: -""" -cbuffer Globals : register(b0) -{ - float4 rect; - float4 corner_radii_px; - float2 direction; - float2 viewport_size; - uint blur_count; -} - -cbuffer Kernel : register(b1) -{ - float4 kernel[32]; -} - -struct CPU2Vertex -{ - uint vertex_id : SV_VertexID; -}; - -struct Vertex2Pixel -{ - float4 position : SV_POSITION; - float2 texcoord : TEX; - float2 sdf_sample_pos : SDF; - nointerpolation float2 rect_half_size : RHS; - float corner_radius : RAD; -}; - -Texture2D stage_t2d : register(t0); -SamplerState stage_sampler : register(s0); - -float rect_sdf(float2 sample_pos, float2 rect_half_size, float r) -{ - return length(max(abs(sample_pos) - rect_half_size + r, 0.0)) - r; -} - -//- rjf: vertex shader - -Vertex2Pixel -vs_main(CPU2Vertex c2v) -{ - float2 vertex_positions__scrn[] = - { - rect.xw, - rect.xy, - rect.zw, - rect.zy, - }; - float corner_radii__px[] = - { - corner_radii_px.y, - corner_radii_px.x, - corner_radii_px.w, - corner_radii_px.z, - }; - float2 cornercoords__pct = float2( - (c2v.vertex_id >> 1) ? 1.f : 0.f, - (c2v.vertex_id & 1) ? 0.f : 1.f); - - float2 vertex_position__pct = vertex_positions__scrn[c2v.vertex_id] / viewport_size; - float2 vertex_position__scr = 2.f * vertex_position__pct - 1.f; - - float2 rect_half_size = float2((rect.z-rect.x)/2, (rect.w-rect.y)/2); - - Vertex2Pixel v2p; - { - v2p.position = float4(vertex_position__scr.x, -vertex_position__scr.y, 0.f, 1.f); - v2p.texcoord = vertex_position__pct; - v2p.sdf_sample_pos = (2.f * cornercoords__pct - 1.f) * rect_half_size; - v2p.rect_half_size = rect_half_size - 2.f; - v2p.corner_radius = corner_radii__px[c2v.vertex_id]; - } - return v2p; -} - -//- rjf: pixel shader - -float4 -ps_main(Vertex2Pixel v2p) : SV_TARGET -{ - // rjf: blend weighted texture samples into color - float3 color = kernel[0].x * stage_t2d.Sample(stage_sampler, v2p.texcoord).rgb; - - for(uint i = 1; i < blur_count; i += 1) - { - float weight = kernel[i].x; - float offset = kernel[i].y; - color += weight * stage_t2d.Sample(stage_sampler, v2p.texcoord - offset * direction).rgb; - color += weight * stage_t2d.Sample(stage_sampler, v2p.texcoord + offset * direction).rgb; - } - - // rjf: sample for corners - float corner_sdf_s = rect_sdf(v2p.sdf_sample_pos, v2p.rect_half_size, v2p.corner_radius); - float corner_sdf_t = 1-smoothstep(0, 2, corner_sdf_s); - - // rjf: weight output color by sdf - // this is doing alpha testing, leave blurring only where mostly opaque pixels are - if (corner_sdf_t < 0.9f) - { - discard; - } - - return float4(color, 1.f); -} -""" - -//////////////////////////////// -//~ rjf: Mesh Shaders - -@embed_string r_d3d11_g_mesh_shader_src: -""" -cbuffer Uniforms : register(b0) -{ - row_major float4x4 xform; -} - -struct CPU2Vertex -{ - float3 position : POS; - float3 normal : NOR; - float2 texcoord : TEX; - float3 color : COL; -}; - -struct Vertex2Pixel -{ - float4 position : SV_POSITION; - float2 texcoord : TEX; - float4 color : COL; -}; - -Vertex2Pixel vs_main(CPU2Vertex c2v) -{ - Vertex2Pixel v2p; - v2p.position = mul(float4(c2v.position, 1.f), xform); - v2p.texcoord = c2v.texcoord; - v2p.color = float4(c2v.color, 1.f); - return v2p; -} - -float4 ps_main(Vertex2Pixel v2p) : SV_TARGET -{ - return v2p.color; -} -"""; - -//////////////////////////////// -//~ rjf: Geo3D Composition Shaders - -@embed_string r_d3d11_g_geo3dcomposite_shader_src: -""" -struct CPU2Vertex -{ - uint vertex_id : SV_VertexID; -}; - -struct Vertex2Pixel -{ - float4 position : SV_POSITION; - float2 texcoord : TEX; -}; - -Texture2D stage_t2d : register(t0); -SamplerState stage_sampler : register(s0); - -//- rjf: vertex shader - -Vertex2Pixel -vs_main(CPU2Vertex c2v) -{ - float4 vertex_positions__modl[] = - { - float4(0, 0, 0, 1), - float4(0, 1, 0, 1), - float4(1, 0, 0, 1), - float4(1, 1, 0, 1), - }; - float4 vertex_position__modl = vertex_positions__modl[c2v.vertex_id]; - float4 vertex_position__clip = float4(2*vertex_position__modl.x - 1, 2*vertex_position__modl.y - 1, 0, 1); - float2 texcoord = float2(vertex_position__modl.x, vertex_position__modl.y); - texcoord.y = 1-texcoord.y; - Vertex2Pixel v2p; - { - v2p.position = vertex_position__clip; - v2p.texcoord = texcoord; - } - return v2p; -} - -//- rjf: pixel shader - -float4 -ps_main(Vertex2Pixel v2p) : SV_TARGET -{ - float4 final_color = stage_t2d.Sample(stage_sampler, v2p.texcoord); - return final_color; -} -""" - -//////////////////////////////// -//~ rjf: Finalize Shaders - -@embed_string r_d3d11_g_finalize_shader_src: -""" -struct CPU2Vertex -{ - uint vertex_id : SV_VertexID; -}; - -struct Vertex2Pixel -{ - float4 position : SV_POSITION; - float2 texcoord : TEX; -}; - -Texture2D stage_t2d : register(t0); -SamplerState stage_sampler : register(s0); - -//- rjf: vertex shader - -Vertex2Pixel -vs_main(CPU2Vertex c2v) -{ - float4 vertex_positions__modl[] = - { - float4(0, 0, 0, 1), - float4(0, 1, 0, 1), - float4(1, 0, 0, 1), - float4(1, 1, 0, 1), - }; - float4 vertex_position__modl = vertex_positions__modl[c2v.vertex_id]; - float4 vertex_position__clip = float4(2*vertex_position__modl.x - 1, 2*vertex_position__modl.y - 1, 0, 1); - float2 texcoord = float2(vertex_position__modl.x, vertex_position__modl.y); - texcoord.y = 1-texcoord.y; - Vertex2Pixel v2p; - { - v2p.position = vertex_position__clip; - v2p.texcoord = texcoord; - } - return v2p; -} - -//- rjf: pixel shader - -float4 -ps_main(Vertex2Pixel v2p) : SV_TARGET -{ - float4 final_color = stage_t2d.Sample(stage_sampler, v2p.texcoord); - final_color.a = 1; - return final_color; -} -""" - -//////////////////////////////// -//~ rjf: Table Generators - -@enum R_D3D11_VShadKind: -{ - @expand(R_D3D11_VShadTable a) `$(a.name)`, - COUNT, -} - -@enum R_D3D11_PShadKind: -{ - @expand(R_D3D11_PShadTable a) `$(a.name)`, - COUNT, -} - -@enum R_D3D11_UniformTypeKind: -{ - @expand(R_D3D11_UniformTypeTable a) `$(a.name)`, - COUNT, -} - -@c_file @data(`String8*`) -r_d3d11_g_vshad_kind_source_table: -{ - @expand(R_D3D11_VShadTable a) `&$(a.source)`; -} - -@c_file @data(String8) -r_d3d11_g_vshad_kind_source_name_table: -{ - @expand(R_D3D11_VShadTable a) `str8_lit_comp("$(a.source)")`; -} - -@c_file @data(`D3D11_INPUT_ELEMENT_DESC *`) -r_d3d11_g_vshad_kind_elements_ptr_table: -{ - @expand(R_D3D11_VShadTable a) `$(a.ilay_table)`; -} - -@c_file @data(U64) -r_d3d11_g_vshad_kind_elements_count_table: -{ - @expand(R_D3D11_VShadTable a) `$(a.ilay_table != 0 -> "ArrayCount("..a.ilay_table..")") $(a.ilay_table == 0 -> "0")`; -} - -@c_file @data(`String8*`) -r_d3d11_g_pshad_kind_source_table: -{ - @expand(R_D3D11_PShadTable a) `&$(a.source)`; -} - -@c_file @data(String8) -r_d3d11_g_pshad_kind_source_name_table: -{ - @expand(R_D3D11_PShadTable a) `str8_lit_comp("$(a.source)")`; -} - -@c_file @data(U64) -r_d3d11_g_uniform_type_kind_size_table: -{ - @expand(R_D3D11_UniformTypeTable a) `sizeof(R_D3D11_Uniforms_$(a.name))`; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Pipeline Tables + +@table(name, source, ilay_table) +R_D3D11_VShadTable: +{ + {Rect r_d3d11_g_rect_shader_src r_d3d11_g_rect_ilay_elements } + {Blur r_d3d11_g_blur_shader_src 0 } + {Mesh r_d3d11_g_mesh_shader_src r_d3d11_g_mesh_ilay_elements } + {Geo3DComposite r_d3d11_g_geo3dcomposite_shader_src 0 } + {Finalize r_d3d11_g_finalize_shader_src 0 } +} + +@table(name, source) +R_D3D11_PShadTable: +{ + {Rect r_d3d11_g_rect_shader_src } + {Blur r_d3d11_g_blur_shader_src } + {Mesh r_d3d11_g_mesh_shader_src } + {Geo3DComposite r_d3d11_g_geo3dcomposite_shader_src } + {Finalize r_d3d11_g_finalize_shader_src } +} + +@table(name) +R_D3D11_UniformTypeTable: +{ + {Rect} + {Blur} + {Mesh} +} + +//////////////////////////////// +//~ rjf: UI Rectangle Shaders + +@embed_string r_d3d11_g_rect_shader_src: +""" +cbuffer Globals : register(b0) +{ + float2 viewport_size_px; + float opacity; + row_major float4x4 texture_sample_channel_map; + float2 texture_t2d_size_px; + row_major float3x3 xform; + float2 xform_scale; +} + +struct CPU2Vertex +{ + float4 dst_rect_px : POS; + float4 src_rect_px : TEX; + float4 color00 : COL0; + float4 color01 : COL1; + float4 color10 : COL2; + float4 color11 : COL3; + float4 corner_radii_px : CRAD; + float4 style_params : STY; // x: border_thickness_px, y: softness_px, z: omit_texture, w: unused + uint vertex_id : SV_VertexID; +}; + +struct Vertex2Pixel +{ + float4 position : SV_POSITION; + nointerpolation float2 rect_half_size_px : PSIZE; + float2 texcoord_pct : TEX; + float2 sdf_sample_pos : SDF; + float4 tint : TINT; + float corner_radius_px : CRAD; + nointerpolation float border_thickness_px : BTHC; + nointerpolation float softness_px : SFT; + nointerpolation float omit_texture : OTX; +}; + +Texture2D main_t2d : register(t0); +SamplerState main_sampler : register(s0); + +float rect_sdf(float2 sample_pos, float2 rect_half_size, float r) +{ + return length(max(abs(sample_pos) - rect_half_size + r, 0.0)) - r; +} + +//- rjf: vertex shader + +Vertex2Pixel +vs_main(CPU2Vertex cpu2vertex) +{ + //- rjf: unpack & xform rectangle src/dst vertices + float2 dst_p0_px = cpu2vertex.dst_rect_px.xy; + float2 dst_p1_px = cpu2vertex.dst_rect_px.zw; + float2 src_p0_px = cpu2vertex.src_rect_px.xy; + float2 src_p1_px = cpu2vertex.src_rect_px.zw; + float2 dst_size_px = abs(dst_p1_px - dst_p0_px); + + //- rjf: unpack style params + float border_thickness_px = cpu2vertex.style_params.x; + float softness_px = cpu2vertex.style_params.y; + float omit_texture = cpu2vertex.style_params.z; + + //- rjf: prep per-vertex arrays to sample from (p: position, t: texcoord, c: colorcoord, r: cornerradius) + float2 dst_p_verts_px[] = + { + float2(dst_p0_px.x, dst_p1_px.y), + float2(dst_p0_px.x, dst_p0_px.y), + float2(dst_p1_px.x, dst_p1_px.y), + float2(dst_p1_px.x, dst_p0_px.y), + }; + float2 src_p_verts_px[] = + { + float2(src_p0_px.x, src_p1_px.y), + float2(src_p0_px.x, src_p0_px.y), + float2(src_p1_px.x, src_p1_px.y), + float2(src_p1_px.x, src_p0_px.y), + }; + float dst_r_verts_px[] = + { + cpu2vertex.corner_radii_px.y, + cpu2vertex.corner_radii_px.x, + cpu2vertex.corner_radii_px.w, + cpu2vertex.corner_radii_px.z, + }; + float4 src_color[] = { + cpu2vertex.color01, + cpu2vertex.color00, + cpu2vertex.color11, + cpu2vertex.color10, + }; + float2 dst_verts_pct = float2((cpu2vertex.vertex_id >> 1) ? 1.f : 0.f, + (cpu2vertex.vertex_id & 1) ? 0.f : 1.f); + + // rjf: fill vertex -> pixel data + Vertex2Pixel vertex2pixel; + { + float2 xformed_pos = mul(xform, float3(dst_p_verts_px[cpu2vertex.vertex_id], 1.f)).xy; + xformed_pos.y = viewport_size_px.y - xformed_pos.y; + vertex2pixel.position.xy = 2.f * xformed_pos/viewport_size_px - 1.f; + vertex2pixel.position.z = 0.f; + vertex2pixel.position.w = 1.f; + vertex2pixel.rect_half_size_px = dst_size_px / 2.f * xform_scale; + vertex2pixel.texcoord_pct = src_p_verts_px[cpu2vertex.vertex_id] / texture_t2d_size_px; + vertex2pixel.sdf_sample_pos = (2.f * dst_verts_pct - 1.f) * vertex2pixel.rect_half_size_px; + vertex2pixel.tint = src_color[cpu2vertex.vertex_id]; + vertex2pixel.corner_radius_px = dst_r_verts_px[cpu2vertex.vertex_id]; + vertex2pixel.border_thickness_px = border_thickness_px; + vertex2pixel.softness_px = softness_px; + vertex2pixel.omit_texture = omit_texture; + } + return vertex2pixel; +} + +//- rjf: pixel shader + +float4 +ps_main(Vertex2Pixel vertex2pixel) : SV_TARGET +{ + // rjf: blend corner colors to produce final tint + float4 tint = vertex2pixel.tint; + + // rjf: sample texture + float4 albedo_sample = float4(1, 1, 1, 1); + if(vertex2pixel.omit_texture < 1) + { + albedo_sample = mul(main_t2d.Sample(main_sampler, vertex2pixel.texcoord_pct), texture_sample_channel_map); + } + + // rjf: determine SDF sample position + float2 sdf_sample_pos = vertex2pixel.sdf_sample_pos; + + // rjf: sample for borders + float border_sdf_t = 1; + if(vertex2pixel.border_thickness_px > 0) + { + float border_sdf_s = rect_sdf(sdf_sample_pos, + vertex2pixel.rect_half_size_px - float2(vertex2pixel.softness_px*2.f, vertex2pixel.softness_px*2.f) - vertex2pixel.border_thickness_px, + max(vertex2pixel.corner_radius_px-vertex2pixel.border_thickness_px, 0)); + border_sdf_t = smoothstep(0, 2*vertex2pixel.softness_px, border_sdf_s); + } + if(border_sdf_t < 0.001f) + { + discard; + } + + // rjf: sample for corners + float corner_sdf_t = 1; + if(vertex2pixel.corner_radius_px > 0 || vertex2pixel.softness_px > 0.75f) + { + float corner_sdf_s = rect_sdf(sdf_sample_pos, + vertex2pixel.rect_half_size_px - float2(vertex2pixel.softness_px*2.f, vertex2pixel.softness_px*2.f), + vertex2pixel.corner_radius_px); + corner_sdf_t = 1-smoothstep(0, 2*vertex2pixel.softness_px, corner_sdf_s); + } + + // rjf: form+return final color + float4 final_color = albedo_sample; + final_color *= tint; + final_color.a *= opacity; + final_color.a *= corner_sdf_t; + final_color.a *= border_sdf_t; + return final_color; +} +""" + +//////////////////////////////// +//~ rjf: Blur Shaders + +@embed_string r_d3d11_g_blur_shader_src: +""" +cbuffer Globals : register(b0) +{ + float4 rect; + float4 corner_radii_px; + float2 direction; + float2 viewport_size; + uint blur_count; +} + +cbuffer Kernel : register(b1) +{ + float4 kernel[32]; +} + +struct CPU2Vertex +{ + uint vertex_id : SV_VertexID; +}; + +struct Vertex2Pixel +{ + float4 position : SV_POSITION; + float2 texcoord : TEX; + float2 sdf_sample_pos : SDF; + nointerpolation float2 rect_half_size : RHS; + float corner_radius : RAD; +}; + +Texture2D stage_t2d : register(t0); +SamplerState stage_sampler : register(s0); + +float rect_sdf(float2 sample_pos, float2 rect_half_size, float r) +{ + return length(max(abs(sample_pos) - rect_half_size + r, 0.0)) - r; +} + +//- rjf: vertex shader + +Vertex2Pixel +vs_main(CPU2Vertex c2v) +{ + float2 vertex_positions__scrn[] = + { + rect.xw, + rect.xy, + rect.zw, + rect.zy, + }; + float corner_radii__px[] = + { + corner_radii_px.y, + corner_radii_px.x, + corner_radii_px.w, + corner_radii_px.z, + }; + float2 cornercoords__pct = float2( + (c2v.vertex_id >> 1) ? 1.f : 0.f, + (c2v.vertex_id & 1) ? 0.f : 1.f); + + float2 vertex_position__pct = vertex_positions__scrn[c2v.vertex_id] / viewport_size; + float2 vertex_position__scr = 2.f * vertex_position__pct - 1.f; + + float2 rect_half_size = float2((rect.z-rect.x)/2, (rect.w-rect.y)/2); + + Vertex2Pixel v2p; + { + v2p.position = float4(vertex_position__scr.x, -vertex_position__scr.y, 0.f, 1.f); + v2p.texcoord = vertex_position__pct; + v2p.sdf_sample_pos = (2.f * cornercoords__pct - 1.f) * rect_half_size; + v2p.rect_half_size = rect_half_size - 2.f; + v2p.corner_radius = corner_radii__px[c2v.vertex_id]; + } + return v2p; +} + +//- rjf: pixel shader + +float4 +ps_main(Vertex2Pixel v2p) : SV_TARGET +{ + // rjf: blend weighted texture samples into color + float3 color = kernel[0].x * stage_t2d.Sample(stage_sampler, v2p.texcoord).rgb; + + for(uint i = 1; i < blur_count; i += 1) + { + float weight = kernel[i].x; + float offset = kernel[i].y; + color += weight * stage_t2d.Sample(stage_sampler, v2p.texcoord - offset * direction).rgb; + color += weight * stage_t2d.Sample(stage_sampler, v2p.texcoord + offset * direction).rgb; + } + + // rjf: sample for corners + float corner_sdf_s = rect_sdf(v2p.sdf_sample_pos, v2p.rect_half_size, v2p.corner_radius); + float corner_sdf_t = 1-smoothstep(0, 2, corner_sdf_s); + + // rjf: weight output color by sdf + // this is doing alpha testing, leave blurring only where mostly opaque pixels are + if (corner_sdf_t < 0.9f) + { + discard; + } + + return float4(color, 1.f); +} +""" + +//////////////////////////////// +//~ rjf: Mesh Shaders + +@embed_string r_d3d11_g_mesh_shader_src: +""" +cbuffer Uniforms : register(b0) +{ + row_major float4x4 xform; +} + +struct CPU2Vertex +{ + float3 position : POS; + float3 normal : NOR; + float2 texcoord : TEX; + float3 color : COL; +}; + +struct Vertex2Pixel +{ + float4 position : SV_POSITION; + float2 texcoord : TEX; + float4 color : COL; +}; + +Vertex2Pixel vs_main(CPU2Vertex c2v) +{ + Vertex2Pixel v2p; + v2p.position = mul(float4(c2v.position, 1.f), xform); + v2p.texcoord = c2v.texcoord; + v2p.color = float4(c2v.color, 1.f); + return v2p; +} + +float4 ps_main(Vertex2Pixel v2p) : SV_TARGET +{ + return v2p.color; +} +"""; + +//////////////////////////////// +//~ rjf: Geo3D Composition Shaders + +@embed_string r_d3d11_g_geo3dcomposite_shader_src: +""" +struct CPU2Vertex +{ + uint vertex_id : SV_VertexID; +}; + +struct Vertex2Pixel +{ + float4 position : SV_POSITION; + float2 texcoord : TEX; +}; + +Texture2D stage_t2d : register(t0); +SamplerState stage_sampler : register(s0); + +//- rjf: vertex shader + +Vertex2Pixel +vs_main(CPU2Vertex c2v) +{ + float4 vertex_positions__modl[] = + { + float4(0, 0, 0, 1), + float4(0, 1, 0, 1), + float4(1, 0, 0, 1), + float4(1, 1, 0, 1), + }; + float4 vertex_position__modl = vertex_positions__modl[c2v.vertex_id]; + float4 vertex_position__clip = float4(2*vertex_position__modl.x - 1, 2*vertex_position__modl.y - 1, 0, 1); + float2 texcoord = float2(vertex_position__modl.x, vertex_position__modl.y); + texcoord.y = 1-texcoord.y; + Vertex2Pixel v2p; + { + v2p.position = vertex_position__clip; + v2p.texcoord = texcoord; + } + return v2p; +} + +//- rjf: pixel shader + +float4 +ps_main(Vertex2Pixel v2p) : SV_TARGET +{ + float4 final_color = stage_t2d.Sample(stage_sampler, v2p.texcoord); + return final_color; +} +""" + +//////////////////////////////// +//~ rjf: Finalize Shaders + +@embed_string r_d3d11_g_finalize_shader_src: +""" +struct CPU2Vertex +{ + uint vertex_id : SV_VertexID; +}; + +struct Vertex2Pixel +{ + float4 position : SV_POSITION; + float2 texcoord : TEX; +}; + +Texture2D stage_t2d : register(t0); +SamplerState stage_sampler : register(s0); + +//- rjf: vertex shader + +Vertex2Pixel +vs_main(CPU2Vertex c2v) +{ + float4 vertex_positions__modl[] = + { + float4(0, 0, 0, 1), + float4(0, 1, 0, 1), + float4(1, 0, 0, 1), + float4(1, 1, 0, 1), + }; + float4 vertex_position__modl = vertex_positions__modl[c2v.vertex_id]; + float4 vertex_position__clip = float4(2*vertex_position__modl.x - 1, 2*vertex_position__modl.y - 1, 0, 1); + float2 texcoord = float2(vertex_position__modl.x, vertex_position__modl.y); + texcoord.y = 1-texcoord.y; + Vertex2Pixel v2p; + { + v2p.position = vertex_position__clip; + v2p.texcoord = texcoord; + } + return v2p; +} + +//- rjf: pixel shader + +float4 +ps_main(Vertex2Pixel v2p) : SV_TARGET +{ + float4 final_color = stage_t2d.Sample(stage_sampler, v2p.texcoord); + final_color.a = 1; + return final_color; +} +""" + +//////////////////////////////// +//~ rjf: Table Generators + +@enum R_D3D11_VShadKind: +{ + @expand(R_D3D11_VShadTable a) `$(a.name)`, + COUNT, +} + +@enum R_D3D11_PShadKind: +{ + @expand(R_D3D11_PShadTable a) `$(a.name)`, + COUNT, +} + +@enum R_D3D11_UniformTypeKind: +{ + @expand(R_D3D11_UniformTypeTable a) `$(a.name)`, + COUNT, +} + +@c_file @data(`String8*`) +r_d3d11_g_vshad_kind_source_table: +{ + @expand(R_D3D11_VShadTable a) `&$(a.source)`; +} + +@c_file @data(String8) +r_d3d11_g_vshad_kind_source_name_table: +{ + @expand(R_D3D11_VShadTable a) `str8_lit_comp("$(a.source)")`; +} + +@c_file @data(`D3D11_INPUT_ELEMENT_DESC *`) +r_d3d11_g_vshad_kind_elements_ptr_table: +{ + @expand(R_D3D11_VShadTable a) `$(a.ilay_table)`; +} + +@c_file @data(U64) +r_d3d11_g_vshad_kind_elements_count_table: +{ + @expand(R_D3D11_VShadTable a) `$(a.ilay_table != 0 -> "ArrayCount("..a.ilay_table..")") $(a.ilay_table == 0 -> "0")`; +} + +@c_file @data(`String8*`) +r_d3d11_g_pshad_kind_source_table: +{ + @expand(R_D3D11_PShadTable a) `&$(a.source)`; +} + +@c_file @data(String8) +r_d3d11_g_pshad_kind_source_name_table: +{ + @expand(R_D3D11_PShadTable a) `str8_lit_comp("$(a.source)")`; +} + +@c_file @data(U64) +r_d3d11_g_uniform_type_kind_size_table: +{ + @expand(R_D3D11_UniformTypeTable a) `sizeof(R_D3D11_Uniforms_$(a.name))`; +} diff --git a/src/render/render_core.c b/src/render/render_core.c index 40e0f537..34a60319 100644 --- a/src/render/render_core.c +++ b/src/render/render_core.c @@ -1,77 +1,77 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/render.meta.c" - -//////////////////////////////// -//~ rjf: Basic Type Functions - -internal R_Handle -r_handle_zero(void) -{ - R_Handle handle = {0}; - return handle; -} - -internal B32 -r_handle_match(R_Handle a, R_Handle b) -{ - return a.u64[0] == b.u64[0] && a.u64[1] == b.u64[1]; -} - -//////////////////////////////// -//~ rjf: Batch Type Functions - -internal R_BatchList -r_batch_list_make(U64 instance_size) -{ - R_BatchList list = {0}; - list.bytes_per_inst = instance_size; - return list; -} - -internal void * -r_batch_list_push_inst(Arena *arena, R_BatchList *list, U64 batch_inst_cap) -{ - void *inst = 0; - { - R_BatchNode *n = list->last; - if(n == 0 || n->v.byte_count+list->bytes_per_inst > n->v.byte_cap) - { - n = push_array(arena, R_BatchNode, 1); - n->v.byte_cap = batch_inst_cap*list->bytes_per_inst; - n->v.v = push_array_no_zero(arena, U8, n->v.byte_cap); - SLLQueuePush(list->first, list->last, n); - list->batch_count += 1; - } - inst = n->v.v + n->v.byte_count; - n->v.byte_count += list->bytes_per_inst; - list->byte_count += list->bytes_per_inst; - } - return inst; -} - -//////////////////////////////// -//~ rjf: Pass Type Functions - -internal R_Pass * -r_pass_from_kind(Arena *arena, R_PassList *list, R_PassKind kind) -{ - R_PassNode *n = list->last; - if(!r_pass_kind_batch_table[kind]) - { - n = 0; - } - if(n == 0 || n->v.kind != kind) - { - n = push_array(arena, R_PassNode, 1); - SLLQueuePush(list->first, list->last, n); - list->count += 1; - n->v.kind = kind; - n->v.params = push_array(arena, U8, r_pass_kind_params_size_table[kind]); - } - return &n->v; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Generated Code + +#include "generated/render.meta.c" + +//////////////////////////////// +//~ rjf: Basic Type Functions + +internal R_Handle +r_handle_zero(void) +{ + R_Handle handle = {0}; + return handle; +} + +internal B32 +r_handle_match(R_Handle a, R_Handle b) +{ + return a.u64[0] == b.u64[0] && a.u64[1] == b.u64[1]; +} + +//////////////////////////////// +//~ rjf: Batch Type Functions + +internal R_BatchList +r_batch_list_make(U64 instance_size) +{ + R_BatchList list = {0}; + list.bytes_per_inst = instance_size; + return list; +} + +internal void * +r_batch_list_push_inst(Arena *arena, R_BatchList *list, U64 batch_inst_cap) +{ + void *inst = 0; + { + R_BatchNode *n = list->last; + if(n == 0 || n->v.byte_count+list->bytes_per_inst > n->v.byte_cap) + { + n = push_array(arena, R_BatchNode, 1); + n->v.byte_cap = batch_inst_cap*list->bytes_per_inst; + n->v.v = push_array_no_zero(arena, U8, n->v.byte_cap); + SLLQueuePush(list->first, list->last, n); + list->batch_count += 1; + } + inst = n->v.v + n->v.byte_count; + n->v.byte_count += list->bytes_per_inst; + list->byte_count += list->bytes_per_inst; + } + return inst; +} + +//////////////////////////////// +//~ rjf: Pass Type Functions + +internal R_Pass * +r_pass_from_kind(Arena *arena, R_PassList *list, R_PassKind kind) +{ + R_PassNode *n = list->last; + if(!r_pass_kind_batch_table[kind]) + { + n = 0; + } + if(n == 0 || n->v.kind != kind) + { + n = push_array(arena, R_PassNode, 1); + SLLQueuePush(list->first, list->last, n); + list->count += 1; + n->v.kind = kind; + n->v.params = push_array(arena, U8, r_pass_kind_params_size_table[kind]); + } + return &n->v; +} diff --git a/src/render/render_core.h b/src/render/render_core.h index 89cda8e0..b17b4ab1 100644 --- a/src/render/render_core.h +++ b/src/render/render_core.h @@ -1,245 +1,245 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RENDER_CORE_H -#define RENDER_CORE_H - -#define r_hook C_LINKAGE - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/render.meta.h" - -//////////////////////////////// -//~ rjf: Enums - -typedef U32 R_GeoVertexFlags; -enum -{ - R_GeoVertexFlag_TexCoord = (1<<0), - R_GeoVertexFlag_Normals = (1<<1), - R_GeoVertexFlag_RGB = (1<<2), - R_GeoVertexFlag_RGBA = (1<<3), -}; - -//////////////////////////////// -//~ rjf: Handle Type - -typedef union R_Handle R_Handle; -union R_Handle -{ - U64 u64[2]; - U32 u32[4]; - U16 u16[8]; -}; - -//////////////////////////////// -//~ rjf: Instance Types - -typedef struct R_Rect2DInst R_Rect2DInst; -struct R_Rect2DInst -{ - Rng2F32 dst; - Rng2F32 src; - Vec4F32 colors[Corner_COUNT]; - F32 corner_radii[Corner_COUNT]; - F32 border_thickness; - F32 edge_softness; - F32 white_texture_override; - F32 _unused_[1]; -}; - -typedef struct R_Mesh3DInst R_Mesh3DInst; -struct R_Mesh3DInst -{ - Mat4x4F32 xform; -}; - -//////////////////////////////// -//~ rjf: Batch Types - -typedef struct R_Batch R_Batch; -struct R_Batch -{ - U8 *v; - U64 byte_count; - U64 byte_cap; -}; - -typedef struct R_BatchNode R_BatchNode; -struct R_BatchNode -{ - R_BatchNode *next; - R_Batch v; -}; - -typedef struct R_BatchList R_BatchList; -struct R_BatchList -{ - R_BatchNode *first; - R_BatchNode *last; - U64 batch_count; - U64 byte_count; - U64 bytes_per_inst; -}; - -typedef struct R_BatchGroup2DParams R_BatchGroup2DParams; -struct R_BatchGroup2DParams -{ - R_Handle tex; - R_Tex2DSampleKind tex_sample_kind; - Mat3x3F32 xform; - Rng2F32 clip; - F32 transparency; -}; - -typedef struct R_BatchGroup2DNode R_BatchGroup2DNode; -struct R_BatchGroup2DNode -{ - R_BatchGroup2DNode *next; - R_BatchList batches; - R_BatchGroup2DParams params; -}; - -typedef struct R_BatchGroup2DList R_BatchGroup2DList; -struct R_BatchGroup2DList -{ - R_BatchGroup2DNode *first; - R_BatchGroup2DNode *last; - U64 count; -}; - -typedef struct R_BatchGroup3DParams R_BatchGroup3DParams; -struct R_BatchGroup3DParams -{ - R_Handle mesh_vertices; - R_Handle mesh_indices; - R_GeoTopologyKind mesh_geo_topology; - R_GeoVertexFlags mesh_geo_vertex_flags; - R_Handle albedo_tex; - R_Tex2DSampleKind albedo_tex_sample_kind; - Mat4x4F32 xform; -}; - -typedef struct R_BatchGroup3DMapNode R_BatchGroup3DMapNode; -struct R_BatchGroup3DMapNode -{ - R_BatchGroup3DMapNode *next; - U64 hash; - R_BatchList batches; - R_BatchGroup3DParams params; -}; - -typedef struct R_BatchGroup3DMap R_BatchGroup3DMap; -struct R_BatchGroup3DMap -{ - R_BatchGroup3DMapNode **slots; - U64 slots_count; -}; - -//////////////////////////////// -//~ rjf: Pass Types - -typedef struct R_PassParams_UI R_PassParams_UI; -struct R_PassParams_UI -{ - R_BatchGroup2DList rects; -}; - -typedef struct R_PassParams_Blur R_PassParams_Blur; -struct R_PassParams_Blur -{ - Rng2F32 rect; - Rng2F32 clip; - F32 blur_size; - F32 corner_radii[Corner_COUNT]; -}; - -typedef struct R_PassParams_Geo3D R_PassParams_Geo3D; -struct R_PassParams_Geo3D -{ - Rng2F32 viewport; - Rng2F32 clip; - Mat4x4F32 view; - Mat4x4F32 projection; - R_BatchGroup3DMap mesh_batches; -}; - -typedef struct R_Pass R_Pass; -struct R_Pass -{ - R_PassKind kind; - union - { - void *params; - R_PassParams_UI *params_ui; - R_PassParams_Blur *params_blur; - R_PassParams_Geo3D *params_geo3d; - }; -}; - -typedef struct R_PassNode R_PassNode; -struct R_PassNode -{ - R_PassNode *next; - R_Pass v; -}; - -typedef struct R_PassList R_PassList; -struct R_PassList -{ - R_PassNode *first; - R_PassNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Handle Type Functions - -internal R_Handle r_handle_zero(void); -internal B32 r_handle_match(R_Handle a, R_Handle b); - -//////////////////////////////// -//~ rjf: Batch Type Functions - -internal R_BatchList r_batch_list_make(U64 instance_size); -internal void *r_batch_list_push_inst(Arena *arena, R_BatchList *list, U64 batch_inst_cap); - -//////////////////////////////// -//~ rjf: Pass Type Functions - -internal R_Pass *r_pass_from_kind(Arena *arena, R_PassList *list, R_PassKind kind); - -//////////////////////////////// -//~ rjf: Backend Hooks - -//- rjf: top-level layer initialization -r_hook void r_init(CmdLine *cmdln); - -//- rjf: window setup/teardown -r_hook R_Handle r_window_equip(OS_Handle window); -r_hook void r_window_unequip(OS_Handle window, R_Handle window_equip); - -//- rjf: textures -r_hook R_Handle r_tex2d_alloc(R_ResourceKind kind, Vec2S32 size, R_Tex2DFormat format, void *data); -r_hook void r_tex2d_release(R_Handle texture); -r_hook R_ResourceKind r_kind_from_tex2d(R_Handle texture); -r_hook Vec2S32 r_size_from_tex2d(R_Handle texture); -r_hook R_Tex2DFormat r_format_from_tex2d(R_Handle texture); -r_hook void r_fill_tex2d_region(R_Handle texture, Rng2S32 subrect, void *data); - -//- rjf: buffers -r_hook R_Handle r_buffer_alloc(R_ResourceKind kind, U64 size, void *data); -r_hook void r_buffer_release(R_Handle buffer); - -//- rjf: frame markers -r_hook void r_begin_frame(void); -r_hook void r_end_frame(void); -r_hook void r_window_begin_frame(OS_Handle window, R_Handle window_equip); -r_hook void r_window_end_frame(OS_Handle window, R_Handle window_equip); - -//- rjf: render pass submission -r_hook void r_window_submit(OS_Handle window, R_Handle window_equip, R_PassList *passes); - -#endif // RENDER_CORE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RENDER_CORE_H +#define RENDER_CORE_H + +#define r_hook C_LINKAGE + +//////////////////////////////// +//~ rjf: Generated Code + +#include "generated/render.meta.h" + +//////////////////////////////// +//~ rjf: Enums + +typedef U32 R_GeoVertexFlags; +enum +{ + R_GeoVertexFlag_TexCoord = (1<<0), + R_GeoVertexFlag_Normals = (1<<1), + R_GeoVertexFlag_RGB = (1<<2), + R_GeoVertexFlag_RGBA = (1<<3), +}; + +//////////////////////////////// +//~ rjf: Handle Type + +typedef union R_Handle R_Handle; +union R_Handle +{ + U64 u64[2]; + U32 u32[4]; + U16 u16[8]; +}; + +//////////////////////////////// +//~ rjf: Instance Types + +typedef struct R_Rect2DInst R_Rect2DInst; +struct R_Rect2DInst +{ + Rng2F32 dst; + Rng2F32 src; + Vec4F32 colors[Corner_COUNT]; + F32 corner_radii[Corner_COUNT]; + F32 border_thickness; + F32 edge_softness; + F32 white_texture_override; + F32 _unused_[1]; +}; + +typedef struct R_Mesh3DInst R_Mesh3DInst; +struct R_Mesh3DInst +{ + Mat4x4F32 xform; +}; + +//////////////////////////////// +//~ rjf: Batch Types + +typedef struct R_Batch R_Batch; +struct R_Batch +{ + U8 *v; + U64 byte_count; + U64 byte_cap; +}; + +typedef struct R_BatchNode R_BatchNode; +struct R_BatchNode +{ + R_BatchNode *next; + R_Batch v; +}; + +typedef struct R_BatchList R_BatchList; +struct R_BatchList +{ + R_BatchNode *first; + R_BatchNode *last; + U64 batch_count; + U64 byte_count; + U64 bytes_per_inst; +}; + +typedef struct R_BatchGroup2DParams R_BatchGroup2DParams; +struct R_BatchGroup2DParams +{ + R_Handle tex; + R_Tex2DSampleKind tex_sample_kind; + Mat3x3F32 xform; + Rng2F32 clip; + F32 transparency; +}; + +typedef struct R_BatchGroup2DNode R_BatchGroup2DNode; +struct R_BatchGroup2DNode +{ + R_BatchGroup2DNode *next; + R_BatchList batches; + R_BatchGroup2DParams params; +}; + +typedef struct R_BatchGroup2DList R_BatchGroup2DList; +struct R_BatchGroup2DList +{ + R_BatchGroup2DNode *first; + R_BatchGroup2DNode *last; + U64 count; +}; + +typedef struct R_BatchGroup3DParams R_BatchGroup3DParams; +struct R_BatchGroup3DParams +{ + R_Handle mesh_vertices; + R_Handle mesh_indices; + R_GeoTopologyKind mesh_geo_topology; + R_GeoVertexFlags mesh_geo_vertex_flags; + R_Handle albedo_tex; + R_Tex2DSampleKind albedo_tex_sample_kind; + Mat4x4F32 xform; +}; + +typedef struct R_BatchGroup3DMapNode R_BatchGroup3DMapNode; +struct R_BatchGroup3DMapNode +{ + R_BatchGroup3DMapNode *next; + U64 hash; + R_BatchList batches; + R_BatchGroup3DParams params; +}; + +typedef struct R_BatchGroup3DMap R_BatchGroup3DMap; +struct R_BatchGroup3DMap +{ + R_BatchGroup3DMapNode **slots; + U64 slots_count; +}; + +//////////////////////////////// +//~ rjf: Pass Types + +typedef struct R_PassParams_UI R_PassParams_UI; +struct R_PassParams_UI +{ + R_BatchGroup2DList rects; +}; + +typedef struct R_PassParams_Blur R_PassParams_Blur; +struct R_PassParams_Blur +{ + Rng2F32 rect; + Rng2F32 clip; + F32 blur_size; + F32 corner_radii[Corner_COUNT]; +}; + +typedef struct R_PassParams_Geo3D R_PassParams_Geo3D; +struct R_PassParams_Geo3D +{ + Rng2F32 viewport; + Rng2F32 clip; + Mat4x4F32 view; + Mat4x4F32 projection; + R_BatchGroup3DMap mesh_batches; +}; + +typedef struct R_Pass R_Pass; +struct R_Pass +{ + R_PassKind kind; + union + { + void *params; + R_PassParams_UI *params_ui; + R_PassParams_Blur *params_blur; + R_PassParams_Geo3D *params_geo3d; + }; +}; + +typedef struct R_PassNode R_PassNode; +struct R_PassNode +{ + R_PassNode *next; + R_Pass v; +}; + +typedef struct R_PassList R_PassList; +struct R_PassList +{ + R_PassNode *first; + R_PassNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Handle Type Functions + +internal R_Handle r_handle_zero(void); +internal B32 r_handle_match(R_Handle a, R_Handle b); + +//////////////////////////////// +//~ rjf: Batch Type Functions + +internal R_BatchList r_batch_list_make(U64 instance_size); +internal void *r_batch_list_push_inst(Arena *arena, R_BatchList *list, U64 batch_inst_cap); + +//////////////////////////////// +//~ rjf: Pass Type Functions + +internal R_Pass *r_pass_from_kind(Arena *arena, R_PassList *list, R_PassKind kind); + +//////////////////////////////// +//~ rjf: Backend Hooks + +//- rjf: top-level layer initialization +r_hook void r_init(CmdLine *cmdln); + +//- rjf: window setup/teardown +r_hook R_Handle r_window_equip(OS_Handle window); +r_hook void r_window_unequip(OS_Handle window, R_Handle window_equip); + +//- rjf: textures +r_hook R_Handle r_tex2d_alloc(R_ResourceKind kind, Vec2S32 size, R_Tex2DFormat format, void *data); +r_hook void r_tex2d_release(R_Handle texture); +r_hook R_ResourceKind r_kind_from_tex2d(R_Handle texture); +r_hook Vec2S32 r_size_from_tex2d(R_Handle texture); +r_hook R_Tex2DFormat r_format_from_tex2d(R_Handle texture); +r_hook void r_fill_tex2d_region(R_Handle texture, Rng2S32 subrect, void *data); + +//- rjf: buffers +r_hook R_Handle r_buffer_alloc(R_ResourceKind kind, U64 size, void *data); +r_hook void r_buffer_release(R_Handle buffer); + +//- rjf: frame markers +r_hook void r_begin_frame(void); +r_hook void r_end_frame(void); +r_hook void r_window_begin_frame(OS_Handle window, R_Handle window_equip); +r_hook void r_window_end_frame(OS_Handle window, R_Handle window_equip); + +//- rjf: render pass submission +r_hook void r_window_submit(OS_Handle window, R_Handle window_equip, R_PassList *passes); + +#endif // RENDER_CORE_H diff --git a/src/render/render_core.mdesk b/src/render/render_core.mdesk index 2aea3963..1dac3c65 100644 --- a/src/render/render_core.mdesk +++ b/src/render/render_core.mdesk @@ -1,131 +1,131 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Tables - -@table(name, display_string, bytes_per_pixel) -R_Tex2DFormatTable: -{ - {R8 "R8" 1} - {RG8 "RG8" 2} - {RGBA8 "RGBA8" 4} - {BGRA8 "BGRA8" 4} - {R16 "R16" 2} - {RGBA16 "RGBA16" 8} - {R32 "R32" 4} - {RG32 "RG32" 8} - {RGBA32 "RGBA32" 16} -} - -@table(name, display_string) -R_ResourceKindTable: -{ - // static resource is immutable - // initial data must be provided at creation time - // GPU can read the resource - // CPU is not allowed to read or write - {Static "Static" } - - // dynamic resource allows resource to be modified - // GPU can read & write to it - // CPU can write to it using UpdateSubresource - {Dynamic "Dynamic"} - - // stream resource will be often updated fully overwriting previous data - // GPU can only read it - // CPU can update via Map (with WRITE_DISCARD flag) + Unmap - {Stream "Stream "} -} - -@table(name, display_string) -R_Tex2DSampleKindTable: -{ - {Nearest "Nearest" } - {Linear "Linear" } -} - -@table(name, display_string) -R_GeoTopologyKindTable: -{ - {Lines "Lines" } - {LineStrip "Line Strip" } - {Triangles "Triangles" } - {TriangleStrip "Triangle Strip" } -} - -@table(name, batch, display_string) -R_PassKindTable: -{ - {UI 1 "UI" } - {Blur 0 "Blur" } - {Geo3D 1 "Geo3D" } -} - -//////////////////////////////// -//~ rjf: Generators - -@enum R_Tex2DFormat: -{ - @expand(R_Tex2DFormatTable a) `$(a.name)`, - COUNT, -} - -@enum R_ResourceKind: -{ - @expand(R_ResourceKindTable a) `$(a.name)`, - COUNT, -} - -@enum R_Tex2DSampleKind: -{ - @expand(R_Tex2DSampleKindTable a) `$(a.name)`, - COUNT, -} - -@enum R_GeoTopologyKind: -{ - @expand(R_GeoTopologyKindTable a) `$(a.name)`, - COUNT, -} - -@enum R_PassKind: -{ - @expand(R_PassKindTable a) `$(a.name)`, - COUNT, -} - -@data(String8) r_tex2d_format_display_string_table: -{ - @expand(R_Tex2DFormatTable a) `str8_lit_comp("$(a.display_string)")`; -} - -@data(U8) r_tex2d_format_bytes_per_pixel_table: -{ - @expand(R_Tex2DFormatTable a) `$(a.bytes_per_pixel)`; -} - -@data(String8) r_tex2d_kind_display_string_table: -{ - @expand(R_Tex2DKindTable a) `str8_lit_comp("$(a.display_string)")`; -} - -@data(String8) r_tex2d_sample_kind_display_string_table: -{ - @expand(R_Tex2DSampleKindTable a) `str8_lit_comp("$(a.display_string)")`; -} - -@data(String8) r_pass_kind_display_string_table: -{ - @expand(R_PassKindTable a) `str8_lit_comp("$(a.display_string)")`; -} - -@data(U8) r_pass_kind_batch_table: -{ - @expand(R_PassKindTable a) `$(a.batch)`; -} - -@data(U64) @c_file r_pass_kind_params_size_table: -{ - @expand(R_PassKindTable a) `sizeof(R_PassParams_$(a.name))`; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Tables + +@table(name, display_string, bytes_per_pixel) +R_Tex2DFormatTable: +{ + {R8 "R8" 1} + {RG8 "RG8" 2} + {RGBA8 "RGBA8" 4} + {BGRA8 "BGRA8" 4} + {R16 "R16" 2} + {RGBA16 "RGBA16" 8} + {R32 "R32" 4} + {RG32 "RG32" 8} + {RGBA32 "RGBA32" 16} +} + +@table(name, display_string) +R_ResourceKindTable: +{ + // static resource is immutable + // initial data must be provided at creation time + // GPU can read the resource + // CPU is not allowed to read or write + {Static "Static" } + + // dynamic resource allows resource to be modified + // GPU can read & write to it + // CPU can write to it using UpdateSubresource + {Dynamic "Dynamic"} + + // stream resource will be often updated fully overwriting previous data + // GPU can only read it + // CPU can update via Map (with WRITE_DISCARD flag) + Unmap + {Stream "Stream "} +} + +@table(name, display_string) +R_Tex2DSampleKindTable: +{ + {Nearest "Nearest" } + {Linear "Linear" } +} + +@table(name, display_string) +R_GeoTopologyKindTable: +{ + {Lines "Lines" } + {LineStrip "Line Strip" } + {Triangles "Triangles" } + {TriangleStrip "Triangle Strip" } +} + +@table(name, batch, display_string) +R_PassKindTable: +{ + {UI 1 "UI" } + {Blur 0 "Blur" } + {Geo3D 1 "Geo3D" } +} + +//////////////////////////////// +//~ rjf: Generators + +@enum R_Tex2DFormat: +{ + @expand(R_Tex2DFormatTable a) `$(a.name)`, + COUNT, +} + +@enum R_ResourceKind: +{ + @expand(R_ResourceKindTable a) `$(a.name)`, + COUNT, +} + +@enum R_Tex2DSampleKind: +{ + @expand(R_Tex2DSampleKindTable a) `$(a.name)`, + COUNT, +} + +@enum R_GeoTopologyKind: +{ + @expand(R_GeoTopologyKindTable a) `$(a.name)`, + COUNT, +} + +@enum R_PassKind: +{ + @expand(R_PassKindTable a) `$(a.name)`, + COUNT, +} + +@data(String8) r_tex2d_format_display_string_table: +{ + @expand(R_Tex2DFormatTable a) `str8_lit_comp("$(a.display_string)")`; +} + +@data(U8) r_tex2d_format_bytes_per_pixel_table: +{ + @expand(R_Tex2DFormatTable a) `$(a.bytes_per_pixel)`; +} + +@data(String8) r_tex2d_kind_display_string_table: +{ + @expand(R_Tex2DKindTable a) `str8_lit_comp("$(a.display_string)")`; +} + +@data(String8) r_tex2d_sample_kind_display_string_table: +{ + @expand(R_Tex2DSampleKindTable a) `str8_lit_comp("$(a.display_string)")`; +} + +@data(String8) r_pass_kind_display_string_table: +{ + @expand(R_PassKindTable a) `str8_lit_comp("$(a.display_string)")`; +} + +@data(U8) r_pass_kind_batch_table: +{ + @expand(R_PassKindTable a) `$(a.batch)`; +} + +@data(U64) @c_file r_pass_kind_params_size_table: +{ + @expand(R_PassKindTable a) `sizeof(R_PassParams_$(a.name))`; +} diff --git a/src/render/render_inc.c b/src/render/render_inc.c index f85761b5..84612fa8 100644 --- a/src/render/render_inc.c +++ b/src/render/render_inc.c @@ -1,12 +1,12 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#include "render_core.c" - -#if R_BACKEND == R_BACKEND_STUB -# include "stub/render_stub.c" -#elif R_BACKEND == R_BACKEND_D3D11 -# include "d3d11/render_d3d11.c" -#else -# error Renderer backend not specified. -#endif +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#include "render_core.c" + +#if R_BACKEND == R_BACKEND_STUB +# include "stub/render_stub.c" +#elif R_BACKEND == R_BACKEND_D3D11 +# include "d3d11/render_d3d11.c" +#else +# error Renderer backend not specified. +#endif diff --git a/src/render/render_inc.h b/src/render/render_inc.h index 95ab1a2f..64802e5c 100644 --- a/src/render/render_inc.h +++ b/src/render/render_inc.h @@ -1,36 +1,36 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RENDER_INC_H -#define RENDER_INC_H - -//////////////////////////////// -//~ rjf: Backend Constants - -#define R_BACKEND_STUB 0 -#define R_BACKEND_D3D11 1 - -//////////////////////////////// -//~ rjf: Decide On Backend - -#if !defined(R_BACKEND) && OS_WINDOWS -# define R_BACKEND R_BACKEND_D3D11 -#endif - -//////////////////////////////// -//~ rjf: Main Includes - -#include "render_core.h" - -//////////////////////////////// -//~ rjf: Backend Includes - -#if R_BACKEND == R_BACKEND_STUB -# include "stub/render_stub.h" -#elif R_BACKEND == R_BACKEND_D3D11 -# include "d3d11/render_d3d11.h" -#else -# error Renderer backend not specified. -#endif - -#endif // RENDER_INC_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RENDER_INC_H +#define RENDER_INC_H + +//////////////////////////////// +//~ rjf: Backend Constants + +#define R_BACKEND_STUB 0 +#define R_BACKEND_D3D11 1 + +//////////////////////////////// +//~ rjf: Decide On Backend + +#if !defined(R_BACKEND) && OS_WINDOWS +# define R_BACKEND R_BACKEND_D3D11 +#endif + +//////////////////////////////// +//~ rjf: Main Includes + +#include "render_core.h" + +//////////////////////////////// +//~ rjf: Backend Includes + +#if R_BACKEND == R_BACKEND_STUB +# include "stub/render_stub.h" +#elif R_BACKEND == R_BACKEND_D3D11 +# include "d3d11/render_d3d11.h" +#else +# error Renderer backend not specified. +#endif + +#endif // RENDER_INC_H diff --git a/src/render/stub/render_stub.c b/src/render/stub/render_stub.c index b64dfa83..1c8ffbca 100644 --- a/src/render/stub/render_stub.c +++ b/src/render/stub/render_stub.c @@ -1,106 +1,106 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//- rjf: top-level layer initialization - -r_hook void -r_init(CmdLine *cmdln) -{ -} - -//- rjf: window setup/teardown - -r_hook R_Handle -r_window_equip(OS_Handle window) -{ - R_Handle handle = {0}; - handle.u64[0] = 1; - return handle; -} - -r_hook void -r_window_unequip(OS_Handle window, R_Handle window_equip) -{ -} - -//- rjf: textures - -r_hook R_Handle -r_tex2d_alloc(R_ResourceKind kind, Vec2S32 size, R_Tex2DFormat format, void *data) -{ - R_Handle handle = {0}; - handle.u64[0] = 1; - return handle; -} - -r_hook void -r_tex2d_release(R_Handle texture) -{ -} - -r_hook R_ResourceKind -r_kind_from_tex2d(R_Handle texture) -{ - return R_ResourceStatic; -} - -r_hook Vec2S32 -r_size_from_tex2d(R_Handle texture) -{ - return v2s32(1, 1); -} - -r_hook R_Tex2DFormat -r_format_from_tex2d(R_Handle texture) -{ - return R_Tex2DFormat_RGBA8; -} - -r_hook void -r_fill_tex2d_region(R_Handle texture, Rng2S32 subrect, void *data) -{ -} - -//- rjf: buffers - -r_hook R_Handle -r_buffer_alloc(R_ResourceKind kind, U64 size, void *data) -{ - R_Handle handle = {0}; - handle.u64[0] = 1; - return handle; -} - -r_hook void -r_buffer_release(R_Handle buffer) -{ -} - -//- rjf: frame markers - -r_hook void -r_begin_frame(void) -{ -} - -r_hook void -r_end_frame(void) -{ -} - -r_hook void -r_window_begin_frame(OS_Handle window, R_Handle window_equip) -{ -} - -r_hook void -r_window_end_frame(OS_Handle window, R_Handle window_equip) -{ -} - -//- rjf: render pass submission - -r_hook void -r_window_submit(OS_Handle window, R_Handle window_equip, R_PassList *passes) -{ -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//- rjf: top-level layer initialization + +r_hook void +r_init(CmdLine *cmdln) +{ +} + +//- rjf: window setup/teardown + +r_hook R_Handle +r_window_equip(OS_Handle window) +{ + R_Handle handle = {0}; + handle.u64[0] = 1; + return handle; +} + +r_hook void +r_window_unequip(OS_Handle window, R_Handle window_equip) +{ +} + +//- rjf: textures + +r_hook R_Handle +r_tex2d_alloc(R_ResourceKind kind, Vec2S32 size, R_Tex2DFormat format, void *data) +{ + R_Handle handle = {0}; + handle.u64[0] = 1; + return handle; +} + +r_hook void +r_tex2d_release(R_Handle texture) +{ +} + +r_hook R_ResourceKind +r_kind_from_tex2d(R_Handle texture) +{ + return R_ResourceStatic; +} + +r_hook Vec2S32 +r_size_from_tex2d(R_Handle texture) +{ + return v2s32(1, 1); +} + +r_hook R_Tex2DFormat +r_format_from_tex2d(R_Handle texture) +{ + return R_Tex2DFormat_RGBA8; +} + +r_hook void +r_fill_tex2d_region(R_Handle texture, Rng2S32 subrect, void *data) +{ +} + +//- rjf: buffers + +r_hook R_Handle +r_buffer_alloc(R_ResourceKind kind, U64 size, void *data) +{ + R_Handle handle = {0}; + handle.u64[0] = 1; + return handle; +} + +r_hook void +r_buffer_release(R_Handle buffer) +{ +} + +//- rjf: frame markers + +r_hook void +r_begin_frame(void) +{ +} + +r_hook void +r_end_frame(void) +{ +} + +r_hook void +r_window_begin_frame(OS_Handle window, R_Handle window_equip) +{ +} + +r_hook void +r_window_end_frame(OS_Handle window, R_Handle window_equip) +{ +} + +//- rjf: render pass submission + +r_hook void +r_window_submit(OS_Handle window, R_Handle window_equip, R_PassList *passes) +{ +} diff --git a/src/render/stub/render_stub.h b/src/render/stub/render_stub.h index 6b1c364e..9142c4f2 100644 --- a/src/render/stub/render_stub.h +++ b/src/render/stub/render_stub.h @@ -1,7 +1,7 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef RENDER_STUB_H -#define RENDER_STUB_H - -#endif // RENDER_STUB_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef RENDER_STUB_H +#define RENDER_STUB_H + +#endif // RENDER_STUB_H diff --git a/src/scratch/i_hate_c_plus_plus.cpp b/src/scratch/i_hate_c_plus_plus.cpp index e0ceee5f..1d294990 100644 --- a/src/scratch/i_hate_c_plus_plus.cpp +++ b/src/scratch/i_hate_c_plus_plus.cpp @@ -1,60 +1,60 @@ - -static int Static = 5; -namespace NS -{ - static int staticDataInNS = 99; - struct A - { - static int staticData; - int a = 20; - int b = 30; - void Foo() - { - Static += 1; - staticDataInNS += 1; - staticData++; - a++; - b++; - } - }; - int A::staticData = 123; -} - -struct Resource -{ - int resourceType; -}; - -struct Stack -{ - Resource *resource; -}; - -struct StackNode -{ - StackNode *next; - Stack v; -}; - -struct Context -{ - StackNode *entry_stack_first; -}; - -int main(void) -{ - Resource r_ = {0}; - Resource *r = &r_; - Stack s = {r}; - StackNode n_ = {0, s}; - StackNode *n = &n_; - Context c_ = {n}; - Context *context = &c_; - - // evaluate `context.entry_stack_first.v.resource.resourceType == 0xd8` - int x = 0; - - NS::A a = {0}; - a.Foo(); - return 0; -} + +static int Static = 5; +namespace NS +{ + static int staticDataInNS = 99; + struct A + { + static int staticData; + int a = 20; + int b = 30; + void Foo() + { + Static += 1; + staticDataInNS += 1; + staticData++; + a++; + b++; + } + }; + int A::staticData = 123; +} + +struct Resource +{ + int resourceType; +}; + +struct Stack +{ + Resource *resource; +}; + +struct StackNode +{ + StackNode *next; + Stack v; +}; + +struct Context +{ + StackNode *entry_stack_first; +}; + +int main(void) +{ + Resource r_ = {0}; + Resource *r = &r_; + Stack s = {r}; + StackNode n_ = {0, s}; + StackNode *n = &n_; + Context c_ = {n}; + Context *context = &c_; + + // evaluate `context.entry_stack_first.v.resource.resourceType == 0xd8` + int x = 0; + + NS::A a = {0}; + a.Foo(); + return 0; +} diff --git a/src/scratch/look_at_raddbg.c b/src/scratch/look_at_raddbg.c index d141a7dd..0010dd77 100644 --- a/src/scratch/look_at_raddbg.c +++ b/src/scratch/look_at_raddbg.c @@ -1,59 +1,59 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -// build with: -// cl /Zi /nologo look_at_raddbg.c - -#include -#include -#include "rdi_format/rdi_format.h" -#include "rdi_format/rdi_format_parse.h" -#include "rdi_format/rdi_format.c" -#include "rdi_format/rdi_format_parse.c" - -int main(int argument_count, char **arguments) -{ - // map raddbg file into address space - HANDLE file = CreateFileA("UnrealEditorFortnite.raddbg", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - DWORD size_hi32 = 0; - DWORD size_lo32 = GetFileSize(file, &size_hi32); - HANDLE map = CreateFileMappingA(file, 0, PAGE_READONLY, 0, 0, 0); - uint64_t data_size = (size_lo32 | ((uint64_t)size_hi32 << 32)); - uint8_t *data = (uint8_t *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, data_size); - - // parse raw data as raddbg - RDI_Parsed rdi = {0}; - RDI_ParseStatus parse_status = rdi_parse(data, data_size, &rdi); - - // usage example: print out all procedure symbol names -#if 1 - for(uint64_t procedure_idx = 0; procedure_idx < rdi.procedure_count; procedure_idx += 1) - { - RDI_Procedure *procedure = &rdi.procedures[procedure_idx]; - uint64_t name_size = 0; - uint8_t *name = rdi_string_from_idx(&rdi, procedure->name_string_idx, &name_size); - printf("[%I64u] %.*s\n", procedure_idx, (int)name_size, name); - } -#endif - - // usage example: print out all user-defined-type names -#if 0 - for(uint64_t udt_idx = 0; udt_idx < rdi.udt_count; udt_idx += 1) - { - RDI_UDT *udt = &rdi.udts[udt_idx]; - RDI_TypeNode *type = &rdi.type_nodes[udt->self_type_idx]; - uint64_t name_size = 0; - uint8_t *name = rdi_string_from_idx(&rdi, type->user_defined.name_string_idx, &name_size); - printf("[%I64u] %.*s\n", udt_idx, (int)name_size, name); - } -#endif - - // for getting more info, look at the `RDI_Parsed` structure. all data is - // represented as a bunch of flat plain-old-data tables. data which must - // reference other data uses indices into that other data's table. for - // example, given a `type_idx`, I will index into `rdi.type_nodes`. given a - // `udt_idx`, I will index into `rdi.udts`. given a `scope_idx`, I will - // index into `rdi.scopes`. and so on. - - return 0; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +// build with: +// cl /Zi /nologo look_at_raddbg.c + +#include +#include +#include "rdi_format/rdi_format.h" +#include "rdi_format/rdi_format_parse.h" +#include "rdi_format/rdi_format.c" +#include "rdi_format/rdi_format_parse.c" + +int main(int argument_count, char **arguments) +{ + // map raddbg file into address space + HANDLE file = CreateFileA("UnrealEditorFortnite.raddbg", GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + DWORD size_hi32 = 0; + DWORD size_lo32 = GetFileSize(file, &size_hi32); + HANDLE map = CreateFileMappingA(file, 0, PAGE_READONLY, 0, 0, 0); + uint64_t data_size = (size_lo32 | ((uint64_t)size_hi32 << 32)); + uint8_t *data = (uint8_t *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, data_size); + + // parse raw data as raddbg + RDI_Parsed rdi = {0}; + RDI_ParseStatus parse_status = rdi_parse(data, data_size, &rdi); + + // usage example: print out all procedure symbol names +#if 1 + for(uint64_t procedure_idx = 0; procedure_idx < rdi.procedure_count; procedure_idx += 1) + { + RDI_Procedure *procedure = &rdi.procedures[procedure_idx]; + uint64_t name_size = 0; + uint8_t *name = rdi_string_from_idx(&rdi, procedure->name_string_idx, &name_size); + printf("[%I64u] %.*s\n", procedure_idx, (int)name_size, name); + } +#endif + + // usage example: print out all user-defined-type names +#if 0 + for(uint64_t udt_idx = 0; udt_idx < rdi.udt_count; udt_idx += 1) + { + RDI_UDT *udt = &rdi.udts[udt_idx]; + RDI_TypeNode *type = &rdi.type_nodes[udt->self_type_idx]; + uint64_t name_size = 0; + uint8_t *name = rdi_string_from_idx(&rdi, type->user_defined.name_string_idx, &name_size); + printf("[%I64u] %.*s\n", udt_idx, (int)name_size, name); + } +#endif + + // for getting more info, look at the `RDI_Parsed` structure. all data is + // represented as a bunch of flat plain-old-data tables. data which must + // reference other data uses indices into that other data's table. for + // example, given a `type_idx`, I will index into `rdi.type_nodes`. given a + // `udt_idx`, I will index into `rdi.udts`. given a `scope_idx`, I will + // index into `rdi.scopes`. and so on. + + return 0; +} diff --git a/src/task_system/task_system.h b/src/task_system/task_system.h index d8a705eb..96483595 100644 --- a/src/task_system/task_system.h +++ b/src/task_system/task_system.h @@ -1,140 +1,140 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef TASK_SYSTEM_H -#define TASK_SYSTEM_H - -//////////////////////////////// -//~ rjf: Task "Ticket" Type -// -// "Tickets" are opaque handles, used to refer to submitted tasks. -// - -typedef struct TS_Ticket TS_Ticket; -struct TS_Ticket -{ - U64 u64[2]; -}; - -typedef struct TS_TicketNode TS_TicketNode; -struct TS_TicketNode -{ - TS_TicketNode *next; - TS_Ticket v; -}; - -typedef struct TS_TicketList TS_TicketList; -struct TS_TicketList -{ - TS_TicketNode *first; - TS_TicketNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Task Request Types - -#define TS_TASK_FUNCTION_DEF(name) void *name(Arena *arena, U64 thread_idx, void *p) -typedef TS_TASK_FUNCTION_DEF(TS_TaskFunctionType); - -//////////////////////////////// -//~ rjf: Task Artifact Cache Types - -typedef struct TS_TaskArtifact TS_TaskArtifact; -struct TS_TaskArtifact -{ - TS_TaskArtifact *next; - U64 num; - B64 task_is_done; - void *result; -}; - -typedef struct TS_TaskArtifactSlot TS_TaskArtifactSlot; -struct TS_TaskArtifactSlot -{ - TS_TaskArtifact *first; - TS_TaskArtifact *last; -}; - -typedef struct TS_TaskArtifactStripe TS_TaskArtifactStripe; -struct TS_TaskArtifactStripe -{ - Arena *arena; - OS_Handle cv; - OS_Handle rw_mutex; - TS_TaskArtifact *free_artifact; -}; - -//////////////////////////////// -//~ rjf: Per-Thread State - -typedef struct TS_TaskThread TS_TaskThread; -struct TS_TaskThread -{ - Arena *arena; - OS_Handle thread; -}; - -//////////////////////////////// -//~ rjf: Main Shared State - -typedef struct TS_Shared TS_Shared; -struct TS_Shared -{ - Arena *arena; - - // rjf: task artifact cache - U64 artifact_num_gen; - U64 artifact_slots_count; - U64 artifact_stripes_count; - TS_TaskArtifactSlot *artifact_slots; - TS_TaskArtifactStripe *artifact_stripes; - - // rjf: task ring buffer - U64 u2t_ring_size; - U8 *u2t_ring_base; - U64 u2t_ring_write_pos; - U64 u2t_ring_read_pos; - OS_Handle u2t_ring_mutex; - OS_Handle u2t_ring_cv; - - // rjf: task threads - TS_TaskThread *task_threads; - U64 task_threads_count; -}; - -//////////////////////////////// -//~ rjf: Globals - -global TS_Shared *ts_shared = 0; - -//////////////////////////////// -//~ rjf: Basic Type Functions - -internal TS_Ticket ts_ticket_zero(void); -internal void ts_ticket_list_push(Arena *arena, TS_TicketList *list, TS_Ticket ticket); - -//////////////////////////////// -//~ rjf: Top-Level Layer Initialization - -internal void ts_init(void); - -//////////////////////////////// -//~ rjf: Top-Level Accessors - -internal U64 ts_thread_count(void); - -//////////////////////////////// -//~ rjf: High-Level Task Kickoff / Joining - -internal TS_Ticket ts_kickoff(TS_TaskFunctionType *entry_point, Arena **optional_arena_ptr, void *p); -internal void *ts_join(TS_Ticket ticket, U64 endt_us); -#define ts_join_struct(ticket, endt_us, type) (type *)ts_join((ticket), (endt_us)) - -//////////////////////////////// -//~ rjf: Task Threads - -internal void ts_u2t_dequeue_task(TS_TaskFunctionType **entry_point_out, Arena **arena_out, void **p_out, TS_Ticket *ticket_out); -internal void ts_task_thread__entry_point(void *p); - -#endif // TASK_SYSTEM_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef TASK_SYSTEM_H +#define TASK_SYSTEM_H + +//////////////////////////////// +//~ rjf: Task "Ticket" Type +// +// "Tickets" are opaque handles, used to refer to submitted tasks. +// + +typedef struct TS_Ticket TS_Ticket; +struct TS_Ticket +{ + U64 u64[2]; +}; + +typedef struct TS_TicketNode TS_TicketNode; +struct TS_TicketNode +{ + TS_TicketNode *next; + TS_Ticket v; +}; + +typedef struct TS_TicketList TS_TicketList; +struct TS_TicketList +{ + TS_TicketNode *first; + TS_TicketNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Task Request Types + +#define TS_TASK_FUNCTION_DEF(name) void *name(Arena *arena, U64 thread_idx, void *p) +typedef TS_TASK_FUNCTION_DEF(TS_TaskFunctionType); + +//////////////////////////////// +//~ rjf: Task Artifact Cache Types + +typedef struct TS_TaskArtifact TS_TaskArtifact; +struct TS_TaskArtifact +{ + TS_TaskArtifact *next; + U64 num; + B64 task_is_done; + void *result; +}; + +typedef struct TS_TaskArtifactSlot TS_TaskArtifactSlot; +struct TS_TaskArtifactSlot +{ + TS_TaskArtifact *first; + TS_TaskArtifact *last; +}; + +typedef struct TS_TaskArtifactStripe TS_TaskArtifactStripe; +struct TS_TaskArtifactStripe +{ + Arena *arena; + OS_Handle cv; + OS_Handle rw_mutex; + TS_TaskArtifact *free_artifact; +}; + +//////////////////////////////// +//~ rjf: Per-Thread State + +typedef struct TS_TaskThread TS_TaskThread; +struct TS_TaskThread +{ + Arena *arena; + OS_Handle thread; +}; + +//////////////////////////////// +//~ rjf: Main Shared State + +typedef struct TS_Shared TS_Shared; +struct TS_Shared +{ + Arena *arena; + + // rjf: task artifact cache + U64 artifact_num_gen; + U64 artifact_slots_count; + U64 artifact_stripes_count; + TS_TaskArtifactSlot *artifact_slots; + TS_TaskArtifactStripe *artifact_stripes; + + // rjf: task ring buffer + U64 u2t_ring_size; + U8 *u2t_ring_base; + U64 u2t_ring_write_pos; + U64 u2t_ring_read_pos; + OS_Handle u2t_ring_mutex; + OS_Handle u2t_ring_cv; + + // rjf: task threads + TS_TaskThread *task_threads; + U64 task_threads_count; +}; + +//////////////////////////////// +//~ rjf: Globals + +global TS_Shared *ts_shared = 0; + +//////////////////////////////// +//~ rjf: Basic Type Functions + +internal TS_Ticket ts_ticket_zero(void); +internal void ts_ticket_list_push(Arena *arena, TS_TicketList *list, TS_Ticket ticket); + +//////////////////////////////// +//~ rjf: Top-Level Layer Initialization + +internal void ts_init(void); + +//////////////////////////////// +//~ rjf: Top-Level Accessors + +internal U64 ts_thread_count(void); + +//////////////////////////////// +//~ rjf: High-Level Task Kickoff / Joining + +internal TS_Ticket ts_kickoff(TS_TaskFunctionType *entry_point, Arena **optional_arena_ptr, void *p); +internal void *ts_join(TS_Ticket ticket, U64 endt_us); +#define ts_join_struct(ticket, endt_us, type) (type *)ts_join((ticket), (endt_us)) + +//////////////////////////////// +//~ rjf: Task Threads + +internal void ts_u2t_dequeue_task(TS_TaskFunctionType **entry_point_out, Arena **arena_out, void **p_out, TS_Ticket *ticket_out); +internal void ts_task_thread__entry_point(void *p); + +#endif // TASK_SYSTEM_H diff --git a/src/text_cache/text_cache.h b/src/text_cache/text_cache.h index 529310bb..cfbfe61c 100644 --- a/src/text_cache/text_cache.h +++ b/src/text_cache/text_cache.h @@ -1,313 +1,313 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef TEXT_CACHE_H -#define TEXT_CACHE_H - -//////////////////////////////// -//~ rjf: Value Types - -typedef enum TXT_LineEndKind -{ - TXT_LineEndKind_Null, - TXT_LineEndKind_LF, - TXT_LineEndKind_CRLF, - TXT_LineEndKind_COUNT -} -TXT_LineEndKind; - -typedef enum TXT_TokenKind -{ - TXT_TokenKind_Null, - TXT_TokenKind_Error, - TXT_TokenKind_Whitespace, - TXT_TokenKind_Keyword, - TXT_TokenKind_Identifier, - TXT_TokenKind_Numeric, - TXT_TokenKind_String, - TXT_TokenKind_Symbol, - TXT_TokenKind_Comment, - TXT_TokenKind_Meta, // preprocessor, etc. - TXT_TokenKind_COUNT -} -TXT_TokenKind; - -typedef struct TXT_Token TXT_Token; -struct TXT_Token -{ - TXT_TokenKind kind; - Rng1U64 range; -}; - -typedef struct TXT_TokenChunkNode TXT_TokenChunkNode; -struct TXT_TokenChunkNode -{ - TXT_TokenChunkNode *next; - U64 count; - U64 cap; - TXT_Token *v; -}; - -typedef struct TXT_TokenChunkList TXT_TokenChunkList; -struct TXT_TokenChunkList -{ - TXT_TokenChunkNode *first; - TXT_TokenChunkNode *last; - U64 chunk_count; - U64 token_count; -}; - -typedef struct TXT_TokenNode TXT_TokenNode; -struct TXT_TokenNode -{ - TXT_TokenNode *next; - TXT_Token v; -}; - -typedef struct TXT_TokenList TXT_TokenList; -struct TXT_TokenList -{ - TXT_TokenNode *first; - TXT_TokenNode *last; - U64 count; -}; - -typedef struct TXT_TokenArray TXT_TokenArray; -struct TXT_TokenArray -{ - U64 count; - TXT_Token *v; -}; - -typedef struct TXT_TokenArrayArray TXT_TokenArrayArray; -struct TXT_TokenArrayArray -{ - U64 count; - TXT_TokenArray *v; -}; - -typedef struct TXT_TextInfo TXT_TextInfo; -struct TXT_TextInfo -{ - U64 lines_count; - Rng1U64 *lines_ranges; - U64 lines_max_size; - TXT_LineEndKind line_end_kind; - TXT_TokenArray tokens; - U64 bytes_processed; - U64 bytes_to_process; -}; - -typedef struct TXT_LineTokensSlice TXT_LineTokensSlice; -struct TXT_LineTokensSlice -{ - TXT_TokenArray *line_tokens; -}; - -//////////////////////////////// -//~ rjf: Language Kind Types - -typedef enum TXT_LangKind -{ - TXT_LangKind_Null, - TXT_LangKind_C, - TXT_LangKind_CPlusPlus, - TXT_LangKind_Odin, - TXT_LangKind_Jai, - TXT_LangKind_Zig, - TXT_LangKind_DisasmX64Intel, - TXT_LangKind_COUNT -} -TXT_LangKind; - -typedef TXT_TokenArray TXT_LangLexFunctionType(Arena *arena, U64 *bytes_processed_counter, String8 string); - -//////////////////////////////// -//~ rjf: Cache Types - -typedef struct TXT_Node TXT_Node; -struct TXT_Node -{ - // rjf: links - TXT_Node *next; - TXT_Node *prev; - - // rjf: key - U128 hash; - TXT_LangKind lang; - - // rjf: artifacts - Arena *arena; - TXT_TextInfo info; - - // rjf: metadata - B32 is_working; - U64 scope_ref_count; - U64 last_time_touched_us; - U64 last_user_clock_idx_touched; - U64 load_count; -}; - -typedef struct TXT_Slot TXT_Slot; -struct TXT_Slot -{ - TXT_Node *first; - TXT_Node *last; -}; - -typedef struct TXT_Stripe TXT_Stripe; -struct TXT_Stripe -{ - Arena *arena; - OS_Handle rw_mutex; - OS_Handle cv; -}; - -//////////////////////////////// -//~ rjf: Scoped Access - -typedef struct TXT_Touch TXT_Touch; -struct TXT_Touch -{ - TXT_Touch *next; - U128 hash; - TXT_LangKind lang; -}; - -typedef struct TXT_Scope TXT_Scope; -struct TXT_Scope -{ - TXT_Scope *next; - TXT_Touch *top_touch; -}; - -//////////////////////////////// -//~ rjf: Thread Context - -typedef struct TXT_TCTX TXT_TCTX; -struct TXT_TCTX -{ - Arena *arena; - TXT_Scope *free_scope; - TXT_Touch *free_touch; -}; - -//////////////////////////////// -//~ rjf: Shared State - -typedef struct TXT_Shared TXT_Shared; -struct TXT_Shared -{ - Arena *arena; - - // rjf: user clock - U64 user_clock_idx; - - // rjf: cache - U64 slots_count; - U64 stripes_count; - TXT_Slot *slots; - TXT_Stripe *stripes; - TXT_Node **stripes_free_nodes; - - // rjf: user -> parse thread - U64 u2p_ring_size; - U8 *u2p_ring_base; - U64 u2p_ring_write_pos; - U64 u2p_ring_read_pos; - OS_Handle u2p_ring_cv; - OS_Handle u2p_ring_mutex; - - // rjf: parse threads - U64 parse_thread_count; - OS_Handle *parse_threads; - - // rjf: evictor thread - OS_Handle evictor_thread; -}; - -//////////////////////////////// -//~ rjf: Globals - -thread_static TXT_TCTX *txt_tctx = 0; -global TXT_Shared *txt_shared = 0; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal TXT_LangKind txt_lang_kind_from_extension(String8 extension); -internal String8 txt_extension_from_lang_kind(TXT_LangKind kind); -internal TXT_LangKind txt_lang_kind_from_architecture(Architecture arch); -internal TXT_LangLexFunctionType *txt_lex_function_from_lang_kind(TXT_LangKind kind); - -//////////////////////////////// -//~ rjf: Token Type Functions - -internal void txt_token_chunk_list_push(Arena *arena, TXT_TokenChunkList *list, U64 cap, TXT_Token *token); -internal void txt_token_list_push(Arena *arena, TXT_TokenList *list, TXT_Token *token); -internal TXT_TokenArray txt_token_array_from_chunk_list(Arena *arena, TXT_TokenChunkList *list); -internal TXT_TokenArray txt_token_array_from_list(Arena *arena, TXT_TokenList *list); - -//////////////////////////////// -//~ rjf: Lexing Functions - -internal TXT_TokenArray txt_token_array_from_string__c_cpp(Arena *arena, U64 *bytes_processed_counter, String8 string); -internal TXT_TokenArray txt_token_array_from_string__odin(Arena *arena, U64 *bytes_processed_counter, String8 string); -internal TXT_TokenArray txt_token_array_from_string__jai(Arena *arena, U64 *bytes_processed_counter, String8 string); -internal TXT_TokenArray txt_token_array_from_string__zig(Arena *arena, U64 *bytes_processed_counter, String8 string); -internal TXT_TokenArray txt_token_array_from_string__disasm_x64_intel(Arena *arena, U64 *bytes_processed_counter, String8 string); - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void txt_init(void); - -//////////////////////////////// -//~ rjf: Thread Context Initialization - -internal void txt_tctx_ensure_inited(void); - -//////////////////////////////// -//~ rjf: User Clock - -internal void txt_user_clock_tick(void); -internal U64 txt_user_clock_idx(void); - -//////////////////////////////// -//~ rjf: Scoped Access - -internal TXT_Scope *txt_scope_open(void); -internal void txt_scope_close(TXT_Scope *scope); -internal void txt_scope_touch_node__stripe_r_guarded(TXT_Scope *scope, TXT_Node *node); - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal TXT_TextInfo txt_text_info_from_hash_lang(TXT_Scope *scope, U128 hash, TXT_LangKind lang); -internal TXT_TextInfo txt_text_info_from_key_lang(TXT_Scope *scope, U128 key, TXT_LangKind lang, U128 *hash_out); - -//////////////////////////////// -//~ rjf: Text Info Extractor Helpers - -internal U64 txt_off_from_info_pt(TXT_TextInfo *info, TxtPt pt); -internal TxtPt txt_pt_from_info_off__linear_scan(TXT_TextInfo *info, U64 off); -internal TXT_TokenArray txt_token_array_from_info_line_num__linear_scan(TXT_TextInfo *info, S64 line_num); -internal Rng1U64 txt_expr_off_range_from_line_off_range_string_tokens(U64 off, Rng1U64 line_range, String8 line_text, TXT_TokenArray *line_tokens); -internal Rng1U64 txt_expr_off_range_from_info_data_pt(TXT_TextInfo *info, String8 data, TxtPt pt); -internal String8 txt_string_from_info_data_txt_rng(TXT_TextInfo *info, String8 data, TxtRng rng); -internal String8 txt_string_from_info_data_line_num(TXT_TextInfo *info, String8 data, S64 line_num); -internal TXT_LineTokensSlice txt_line_tokens_slice_from_info_data_line_range(Arena *arena, TXT_TextInfo *info, String8 data, Rng1S64 line_range); - -//////////////////////////////// -//~ rjf: Parse Threads - -internal B32 txt_u2p_enqueue_req(U128 hash, TXT_LangKind lang, U64 endt_us); -internal void txt_u2p_dequeue_req(U128 *hash_out, TXT_LangKind *lang_out); -internal void txt_parse_thread__entry_point(void *p); - -//////////////////////////////// -//~ rjf: Evictor Threads - -internal void txt_evictor_thread__entry_point(void *p); - -#endif // TEXT_CACHE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef TEXT_CACHE_H +#define TEXT_CACHE_H + +//////////////////////////////// +//~ rjf: Value Types + +typedef enum TXT_LineEndKind +{ + TXT_LineEndKind_Null, + TXT_LineEndKind_LF, + TXT_LineEndKind_CRLF, + TXT_LineEndKind_COUNT +} +TXT_LineEndKind; + +typedef enum TXT_TokenKind +{ + TXT_TokenKind_Null, + TXT_TokenKind_Error, + TXT_TokenKind_Whitespace, + TXT_TokenKind_Keyword, + TXT_TokenKind_Identifier, + TXT_TokenKind_Numeric, + TXT_TokenKind_String, + TXT_TokenKind_Symbol, + TXT_TokenKind_Comment, + TXT_TokenKind_Meta, // preprocessor, etc. + TXT_TokenKind_COUNT +} +TXT_TokenKind; + +typedef struct TXT_Token TXT_Token; +struct TXT_Token +{ + TXT_TokenKind kind; + Rng1U64 range; +}; + +typedef struct TXT_TokenChunkNode TXT_TokenChunkNode; +struct TXT_TokenChunkNode +{ + TXT_TokenChunkNode *next; + U64 count; + U64 cap; + TXT_Token *v; +}; + +typedef struct TXT_TokenChunkList TXT_TokenChunkList; +struct TXT_TokenChunkList +{ + TXT_TokenChunkNode *first; + TXT_TokenChunkNode *last; + U64 chunk_count; + U64 token_count; +}; + +typedef struct TXT_TokenNode TXT_TokenNode; +struct TXT_TokenNode +{ + TXT_TokenNode *next; + TXT_Token v; +}; + +typedef struct TXT_TokenList TXT_TokenList; +struct TXT_TokenList +{ + TXT_TokenNode *first; + TXT_TokenNode *last; + U64 count; +}; + +typedef struct TXT_TokenArray TXT_TokenArray; +struct TXT_TokenArray +{ + U64 count; + TXT_Token *v; +}; + +typedef struct TXT_TokenArrayArray TXT_TokenArrayArray; +struct TXT_TokenArrayArray +{ + U64 count; + TXT_TokenArray *v; +}; + +typedef struct TXT_TextInfo TXT_TextInfo; +struct TXT_TextInfo +{ + U64 lines_count; + Rng1U64 *lines_ranges; + U64 lines_max_size; + TXT_LineEndKind line_end_kind; + TXT_TokenArray tokens; + U64 bytes_processed; + U64 bytes_to_process; +}; + +typedef struct TXT_LineTokensSlice TXT_LineTokensSlice; +struct TXT_LineTokensSlice +{ + TXT_TokenArray *line_tokens; +}; + +//////////////////////////////// +//~ rjf: Language Kind Types + +typedef enum TXT_LangKind +{ + TXT_LangKind_Null, + TXT_LangKind_C, + TXT_LangKind_CPlusPlus, + TXT_LangKind_Odin, + TXT_LangKind_Jai, + TXT_LangKind_Zig, + TXT_LangKind_DisasmX64Intel, + TXT_LangKind_COUNT +} +TXT_LangKind; + +typedef TXT_TokenArray TXT_LangLexFunctionType(Arena *arena, U64 *bytes_processed_counter, String8 string); + +//////////////////////////////// +//~ rjf: Cache Types + +typedef struct TXT_Node TXT_Node; +struct TXT_Node +{ + // rjf: links + TXT_Node *next; + TXT_Node *prev; + + // rjf: key + U128 hash; + TXT_LangKind lang; + + // rjf: artifacts + Arena *arena; + TXT_TextInfo info; + + // rjf: metadata + B32 is_working; + U64 scope_ref_count; + U64 last_time_touched_us; + U64 last_user_clock_idx_touched; + U64 load_count; +}; + +typedef struct TXT_Slot TXT_Slot; +struct TXT_Slot +{ + TXT_Node *first; + TXT_Node *last; +}; + +typedef struct TXT_Stripe TXT_Stripe; +struct TXT_Stripe +{ + Arena *arena; + OS_Handle rw_mutex; + OS_Handle cv; +}; + +//////////////////////////////// +//~ rjf: Scoped Access + +typedef struct TXT_Touch TXT_Touch; +struct TXT_Touch +{ + TXT_Touch *next; + U128 hash; + TXT_LangKind lang; +}; + +typedef struct TXT_Scope TXT_Scope; +struct TXT_Scope +{ + TXT_Scope *next; + TXT_Touch *top_touch; +}; + +//////////////////////////////// +//~ rjf: Thread Context + +typedef struct TXT_TCTX TXT_TCTX; +struct TXT_TCTX +{ + Arena *arena; + TXT_Scope *free_scope; + TXT_Touch *free_touch; +}; + +//////////////////////////////// +//~ rjf: Shared State + +typedef struct TXT_Shared TXT_Shared; +struct TXT_Shared +{ + Arena *arena; + + // rjf: user clock + U64 user_clock_idx; + + // rjf: cache + U64 slots_count; + U64 stripes_count; + TXT_Slot *slots; + TXT_Stripe *stripes; + TXT_Node **stripes_free_nodes; + + // rjf: user -> parse thread + U64 u2p_ring_size; + U8 *u2p_ring_base; + U64 u2p_ring_write_pos; + U64 u2p_ring_read_pos; + OS_Handle u2p_ring_cv; + OS_Handle u2p_ring_mutex; + + // rjf: parse threads + U64 parse_thread_count; + OS_Handle *parse_threads; + + // rjf: evictor thread + OS_Handle evictor_thread; +}; + +//////////////////////////////// +//~ rjf: Globals + +thread_static TXT_TCTX *txt_tctx = 0; +global TXT_Shared *txt_shared = 0; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal TXT_LangKind txt_lang_kind_from_extension(String8 extension); +internal String8 txt_extension_from_lang_kind(TXT_LangKind kind); +internal TXT_LangKind txt_lang_kind_from_architecture(Architecture arch); +internal TXT_LangLexFunctionType *txt_lex_function_from_lang_kind(TXT_LangKind kind); + +//////////////////////////////// +//~ rjf: Token Type Functions + +internal void txt_token_chunk_list_push(Arena *arena, TXT_TokenChunkList *list, U64 cap, TXT_Token *token); +internal void txt_token_list_push(Arena *arena, TXT_TokenList *list, TXT_Token *token); +internal TXT_TokenArray txt_token_array_from_chunk_list(Arena *arena, TXT_TokenChunkList *list); +internal TXT_TokenArray txt_token_array_from_list(Arena *arena, TXT_TokenList *list); + +//////////////////////////////// +//~ rjf: Lexing Functions + +internal TXT_TokenArray txt_token_array_from_string__c_cpp(Arena *arena, U64 *bytes_processed_counter, String8 string); +internal TXT_TokenArray txt_token_array_from_string__odin(Arena *arena, U64 *bytes_processed_counter, String8 string); +internal TXT_TokenArray txt_token_array_from_string__jai(Arena *arena, U64 *bytes_processed_counter, String8 string); +internal TXT_TokenArray txt_token_array_from_string__zig(Arena *arena, U64 *bytes_processed_counter, String8 string); +internal TXT_TokenArray txt_token_array_from_string__disasm_x64_intel(Arena *arena, U64 *bytes_processed_counter, String8 string); + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void txt_init(void); + +//////////////////////////////// +//~ rjf: Thread Context Initialization + +internal void txt_tctx_ensure_inited(void); + +//////////////////////////////// +//~ rjf: User Clock + +internal void txt_user_clock_tick(void); +internal U64 txt_user_clock_idx(void); + +//////////////////////////////// +//~ rjf: Scoped Access + +internal TXT_Scope *txt_scope_open(void); +internal void txt_scope_close(TXT_Scope *scope); +internal void txt_scope_touch_node__stripe_r_guarded(TXT_Scope *scope, TXT_Node *node); + +//////////////////////////////// +//~ rjf: Cache Lookups + +internal TXT_TextInfo txt_text_info_from_hash_lang(TXT_Scope *scope, U128 hash, TXT_LangKind lang); +internal TXT_TextInfo txt_text_info_from_key_lang(TXT_Scope *scope, U128 key, TXT_LangKind lang, U128 *hash_out); + +//////////////////////////////// +//~ rjf: Text Info Extractor Helpers + +internal U64 txt_off_from_info_pt(TXT_TextInfo *info, TxtPt pt); +internal TxtPt txt_pt_from_info_off__linear_scan(TXT_TextInfo *info, U64 off); +internal TXT_TokenArray txt_token_array_from_info_line_num__linear_scan(TXT_TextInfo *info, S64 line_num); +internal Rng1U64 txt_expr_off_range_from_line_off_range_string_tokens(U64 off, Rng1U64 line_range, String8 line_text, TXT_TokenArray *line_tokens); +internal Rng1U64 txt_expr_off_range_from_info_data_pt(TXT_TextInfo *info, String8 data, TxtPt pt); +internal String8 txt_string_from_info_data_txt_rng(TXT_TextInfo *info, String8 data, TxtRng rng); +internal String8 txt_string_from_info_data_line_num(TXT_TextInfo *info, String8 data, S64 line_num); +internal TXT_LineTokensSlice txt_line_tokens_slice_from_info_data_line_range(Arena *arena, TXT_TextInfo *info, String8 data, Rng1S64 line_range); + +//////////////////////////////// +//~ rjf: Parse Threads + +internal B32 txt_u2p_enqueue_req(U128 hash, TXT_LangKind lang, U64 endt_us); +internal void txt_u2p_dequeue_req(U128 *hash_out, TXT_LangKind *lang_out); +internal void txt_parse_thread__entry_point(void *p); + +//////////////////////////////// +//~ rjf: Evictor Threads + +internal void txt_evictor_thread__entry_point(void *p); + +#endif // TEXT_CACHE_H diff --git a/src/texture_cache/texture_cache.h b/src/texture_cache/texture_cache.h index 1a638c06..b44664c6 100644 --- a/src/texture_cache/texture_cache.h +++ b/src/texture_cache/texture_cache.h @@ -1,165 +1,165 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef TEXTURE_CACHE_H -#define TEXTURE_CACHE_H - -//////////////////////////////// -//~ rjf: Texture Topology - -typedef struct TEX_Topology TEX_Topology; -struct TEX_Topology -{ - Vec2S16 dim; - R_Tex2DFormat fmt; -}; - -//////////////////////////////// -//~ rjf: Cache Types - -typedef struct TEX_Node TEX_Node; -struct TEX_Node -{ - TEX_Node *next; - TEX_Node *prev; - U128 hash; - TEX_Topology topology; - R_Handle texture; - B32 is_working; - U64 scope_ref_count; - U64 last_time_touched_us; - U64 last_user_clock_idx_touched; - U64 load_count; -}; - -typedef struct TEX_Slot TEX_Slot; -struct TEX_Slot -{ - TEX_Node *first; - TEX_Node *last; -}; - -typedef struct TEX_Stripe TEX_Stripe; -struct TEX_Stripe -{ - Arena *arena; - OS_Handle rw_mutex; - OS_Handle cv; -}; - -//////////////////////////////// -//~ rjf: Scoped Access - -typedef struct TEX_Touch TEX_Touch; -struct TEX_Touch -{ - TEX_Touch *next; - U128 hash; - TEX_Topology topology; -}; - -typedef struct TEX_Scope TEX_Scope; -struct TEX_Scope -{ - TEX_Scope *next; - TEX_Touch *top_touch; -}; - -//////////////////////////////// -//~ rjf: Thread Context - -typedef struct TEX_TCTX TEX_TCTX; -struct TEX_TCTX -{ - Arena *arena; - TEX_Scope *free_scope; - TEX_Touch *free_touch; -}; - -//////////////////////////////// -//~ rjf: Shared State - -typedef struct TEX_Shared TEX_Shared; -struct TEX_Shared -{ - Arena *arena; - - // rjf: user clock - U64 user_clock_idx; - - // rjf: cache - U64 slots_count; - U64 stripes_count; - TEX_Slot *slots; - TEX_Stripe *stripes; - TEX_Node **stripes_free_nodes; - - // rjf: user -> xfer thread - U64 u2x_ring_size; - U8 *u2x_ring_base; - U64 u2x_ring_write_pos; - U64 u2x_ring_read_pos; - OS_Handle u2x_ring_cv; - OS_Handle u2x_ring_mutex; - - // rjf: transfer threads - U64 xfer_thread_count; - OS_Handle *xfer_threads; - - // rjf: evictor thread - OS_Handle evictor_thread; -}; - -//////////////////////////////// -//~ rjf: Globals - -thread_static TEX_TCTX *tex_tctx = 0; -global TEX_Shared *tex_shared = 0; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal TEX_Topology tex_topology_make(Vec2S32 dim, R_Tex2DFormat fmt); - -//////////////////////////////// -//~ rjf: Main Layer Initialization - -internal void tex_init(void); - -//////////////////////////////// -//~ rjf: Thread Context Initialization - -internal void tex_tctx_ensure_inited(void); - -//////////////////////////////// -//~ rjf: User Clock - -internal void tex_user_clock_tick(void); -internal U64 tex_user_clock_idx(void); - -//////////////////////////////// -//~ rjf: Scoped Access - -internal TEX_Scope *tex_scope_open(void); -internal void tex_scope_close(TEX_Scope *scope); -internal void tex_scope_touch_node__stripe_r_guarded(TEX_Scope *scope, TEX_Node *node); - -//////////////////////////////// -//~ rjf: Cache Lookups - -internal R_Handle tex_texture_from_hash_topology(TEX_Scope *scope, U128 hash, TEX_Topology topology); -internal R_Handle tex_texture_from_key_topology(TEX_Scope *scope, U128 key, TEX_Topology topology, U128 *hash_out); - -//////////////////////////////// -//~ rjf: Transfer Threads - -internal B32 tex_u2x_enqueue_req(U128 hash, TEX_Topology top, U64 endt_us); -internal void tex_u2x_dequeue_req(U128 *hash_out, TEX_Topology *top_out); -internal void tex_xfer_thread__entry_point(void *p); - -//////////////////////////////// -//~ rjf: Evictor Threads - -internal void tex_evictor_thread__entry_point(void *p); - -#endif //TEXTURE_CACHE_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef TEXTURE_CACHE_H +#define TEXTURE_CACHE_H + +//////////////////////////////// +//~ rjf: Texture Topology + +typedef struct TEX_Topology TEX_Topology; +struct TEX_Topology +{ + Vec2S16 dim; + R_Tex2DFormat fmt; +}; + +//////////////////////////////// +//~ rjf: Cache Types + +typedef struct TEX_Node TEX_Node; +struct TEX_Node +{ + TEX_Node *next; + TEX_Node *prev; + U128 hash; + TEX_Topology topology; + R_Handle texture; + B32 is_working; + U64 scope_ref_count; + U64 last_time_touched_us; + U64 last_user_clock_idx_touched; + U64 load_count; +}; + +typedef struct TEX_Slot TEX_Slot; +struct TEX_Slot +{ + TEX_Node *first; + TEX_Node *last; +}; + +typedef struct TEX_Stripe TEX_Stripe; +struct TEX_Stripe +{ + Arena *arena; + OS_Handle rw_mutex; + OS_Handle cv; +}; + +//////////////////////////////// +//~ rjf: Scoped Access + +typedef struct TEX_Touch TEX_Touch; +struct TEX_Touch +{ + TEX_Touch *next; + U128 hash; + TEX_Topology topology; +}; + +typedef struct TEX_Scope TEX_Scope; +struct TEX_Scope +{ + TEX_Scope *next; + TEX_Touch *top_touch; +}; + +//////////////////////////////// +//~ rjf: Thread Context + +typedef struct TEX_TCTX TEX_TCTX; +struct TEX_TCTX +{ + Arena *arena; + TEX_Scope *free_scope; + TEX_Touch *free_touch; +}; + +//////////////////////////////// +//~ rjf: Shared State + +typedef struct TEX_Shared TEX_Shared; +struct TEX_Shared +{ + Arena *arena; + + // rjf: user clock + U64 user_clock_idx; + + // rjf: cache + U64 slots_count; + U64 stripes_count; + TEX_Slot *slots; + TEX_Stripe *stripes; + TEX_Node **stripes_free_nodes; + + // rjf: user -> xfer thread + U64 u2x_ring_size; + U8 *u2x_ring_base; + U64 u2x_ring_write_pos; + U64 u2x_ring_read_pos; + OS_Handle u2x_ring_cv; + OS_Handle u2x_ring_mutex; + + // rjf: transfer threads + U64 xfer_thread_count; + OS_Handle *xfer_threads; + + // rjf: evictor thread + OS_Handle evictor_thread; +}; + +//////////////////////////////// +//~ rjf: Globals + +thread_static TEX_TCTX *tex_tctx = 0; +global TEX_Shared *tex_shared = 0; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal TEX_Topology tex_topology_make(Vec2S32 dim, R_Tex2DFormat fmt); + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void tex_init(void); + +//////////////////////////////// +//~ rjf: Thread Context Initialization + +internal void tex_tctx_ensure_inited(void); + +//////////////////////////////// +//~ rjf: User Clock + +internal void tex_user_clock_tick(void); +internal U64 tex_user_clock_idx(void); + +//////////////////////////////// +//~ rjf: Scoped Access + +internal TEX_Scope *tex_scope_open(void); +internal void tex_scope_close(TEX_Scope *scope); +internal void tex_scope_touch_node__stripe_r_guarded(TEX_Scope *scope, TEX_Node *node); + +//////////////////////////////// +//~ rjf: Cache Lookups + +internal R_Handle tex_texture_from_hash_topology(TEX_Scope *scope, U128 hash, TEX_Topology topology); +internal R_Handle tex_texture_from_key_topology(TEX_Scope *scope, U128 key, TEX_Topology topology, U128 *hash_out); + +//////////////////////////////// +//~ rjf: Transfer Threads + +internal B32 tex_u2x_enqueue_req(U128 hash, TEX_Topology top, U64 endt_us); +internal void tex_u2x_dequeue_req(U128 *hash_out, TEX_Topology *top_out); +internal void tex_xfer_thread__entry_point(void *p); + +//////////////////////////////// +//~ rjf: Evictor Threads + +internal void tex_evictor_thread__entry_point(void *p); + +#endif //TEXTURE_CACHE_H diff --git a/src/third_party/blake2/blake2-config.h b/src/third_party/blake2/blake2-config.h index a524aa95..ef1dd8b4 100644 --- a/src/third_party/blake2/blake2-config.h +++ b/src/third_party/blake2/blake2-config.h @@ -1,72 +1,72 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2_CONFIG_H -#define BLAKE2_CONFIG_H - -/* These don't work everywhere */ -#if defined(__SSE2__) || defined(__x86_64__) || defined(__amd64__) -#define HAVE_SSE2 -#endif - -#if defined(__SSSE3__) -#define HAVE_SSSE3 -#endif - -#if defined(__SSE4_1__) -#define HAVE_SSE41 -#endif - -#if defined(__AVX__) -#define HAVE_AVX -#endif - -#if defined(__XOP__) -#define HAVE_XOP -#endif - - -#ifdef HAVE_AVX2 -#ifndef HAVE_AVX -#define HAVE_AVX -#endif -#endif - -#ifdef HAVE_XOP -#ifndef HAVE_AVX -#define HAVE_AVX -#endif -#endif - -#ifdef HAVE_AVX -#ifndef HAVE_SSE41 -#define HAVE_SSE41 -#endif -#endif - -#ifdef HAVE_SSE41 -#ifndef HAVE_SSSE3 -#define HAVE_SSSE3 -#endif -#endif - -#ifdef HAVE_SSSE3 -#define HAVE_SSE2 -#endif - -#if !defined(HAVE_SSE2) -#error "This code requires at least SSE2." -#endif - -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2_CONFIG_H +#define BLAKE2_CONFIG_H + +/* These don't work everywhere */ +#if defined(__SSE2__) || defined(__x86_64__) || defined(__amd64__) +#define HAVE_SSE2 +#endif + +#if defined(__SSSE3__) +#define HAVE_SSSE3 +#endif + +#if defined(__SSE4_1__) +#define HAVE_SSE41 +#endif + +#if defined(__AVX__) +#define HAVE_AVX +#endif + +#if defined(__XOP__) +#define HAVE_XOP +#endif + + +#ifdef HAVE_AVX2 +#ifndef HAVE_AVX +#define HAVE_AVX +#endif +#endif + +#ifdef HAVE_XOP +#ifndef HAVE_AVX +#define HAVE_AVX +#endif +#endif + +#ifdef HAVE_AVX +#ifndef HAVE_SSE41 +#define HAVE_SSE41 +#endif +#endif + +#ifdef HAVE_SSE41 +#ifndef HAVE_SSSE3 +#define HAVE_SSSE3 +#endif +#endif + +#ifdef HAVE_SSSE3 +#define HAVE_SSE2 +#endif + +#if !defined(HAVE_SSE2) +#error "This code requires at least SSE2." +#endif + +#endif diff --git a/src/third_party/blake2/blake2-impl.h b/src/third_party/blake2/blake2-impl.h index c1df82e0..a63682dc 100644 --- a/src/third_party/blake2/blake2-impl.h +++ b/src/third_party/blake2/blake2-impl.h @@ -1,160 +1,160 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2_IMPL_H -#define BLAKE2_IMPL_H - -#include -#include - -#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) - #if defined(_MSC_VER) - #define BLAKE2_INLINE __inline - #elif defined(__GNUC__) - #define BLAKE2_INLINE __inline__ - #else - #define BLAKE2_INLINE - #endif -#else - #define BLAKE2_INLINE inline -#endif - -static BLAKE2_INLINE uint32_t load32( const void *src ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - uint32_t w; - memcpy(&w, src, sizeof w); - return w; -#else - const uint8_t *p = ( const uint8_t * )src; - return (( uint32_t )( p[0] ) << 0) | - (( uint32_t )( p[1] ) << 8) | - (( uint32_t )( p[2] ) << 16) | - (( uint32_t )( p[3] ) << 24) ; -#endif -} - -static BLAKE2_INLINE uint64_t load64( const void *src ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - uint64_t w; - memcpy(&w, src, sizeof w); - return w; -#else - const uint8_t *p = ( const uint8_t * )src; - return (( uint64_t )( p[0] ) << 0) | - (( uint64_t )( p[1] ) << 8) | - (( uint64_t )( p[2] ) << 16) | - (( uint64_t )( p[3] ) << 24) | - (( uint64_t )( p[4] ) << 32) | - (( uint64_t )( p[5] ) << 40) | - (( uint64_t )( p[6] ) << 48) | - (( uint64_t )( p[7] ) << 56) ; -#endif -} - -static BLAKE2_INLINE uint16_t load16( const void *src ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - uint16_t w; - memcpy(&w, src, sizeof w); - return w; -#else - const uint8_t *p = ( const uint8_t * )src; - return ( uint16_t )((( uint32_t )( p[0] ) << 0) | - (( uint32_t )( p[1] ) << 8)); -#endif -} - -static BLAKE2_INLINE void store16( void *dst, uint16_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - *p++ = ( uint8_t )w; w >>= 8; - *p++ = ( uint8_t )w; -#endif -} - -static BLAKE2_INLINE void store32( void *dst, uint32_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); -#endif -} - -static BLAKE2_INLINE void store64( void *dst, uint64_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); - p[4] = (uint8_t)(w >> 32); - p[5] = (uint8_t)(w >> 40); - p[6] = (uint8_t)(w >> 48); - p[7] = (uint8_t)(w >> 56); -#endif -} - -static BLAKE2_INLINE uint64_t load48( const void *src ) -{ - const uint8_t *p = ( const uint8_t * )src; - return (( uint64_t )( p[0] ) << 0) | - (( uint64_t )( p[1] ) << 8) | - (( uint64_t )( p[2] ) << 16) | - (( uint64_t )( p[3] ) << 24) | - (( uint64_t )( p[4] ) << 32) | - (( uint64_t )( p[5] ) << 40) ; -} - -static BLAKE2_INLINE void store48( void *dst, uint64_t w ) -{ - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); - p[4] = (uint8_t)(w >> 32); - p[5] = (uint8_t)(w >> 40); -} - -static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) -{ - return ( w >> c ) | ( w << ( 32 - c ) ); -} - -static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) -{ - return ( w >> c ) | ( w << ( 64 - c ) ); -} - -/* prevents compiler optimizing out memset() */ -static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) -{ - static void *(*const volatile memset_v)(void *, int, size_t) = &memset; - memset_v(v, 0, n); -} - -#endif +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2_IMPL_H +#define BLAKE2_IMPL_H + +#include +#include + +#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) + #if defined(_MSC_VER) + #define BLAKE2_INLINE __inline + #elif defined(__GNUC__) + #define BLAKE2_INLINE __inline__ + #else + #define BLAKE2_INLINE + #endif +#else + #define BLAKE2_INLINE inline +#endif + +static BLAKE2_INLINE uint32_t load32( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint32_t )( p[0] ) << 0) | + (( uint32_t )( p[1] ) << 8) | + (( uint32_t )( p[2] ) << 16) | + (( uint32_t )( p[3] ) << 24) ; +#endif +} + +static BLAKE2_INLINE uint64_t load64( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return (( uint64_t )( p[0] ) << 0) | + (( uint64_t )( p[1] ) << 8) | + (( uint64_t )( p[2] ) << 16) | + (( uint64_t )( p[3] ) << 24) | + (( uint64_t )( p[4] ) << 32) | + (( uint64_t )( p[5] ) << 40) | + (( uint64_t )( p[6] ) << 48) | + (( uint64_t )( p[7] ) << 56) ; +#endif +} + +static BLAKE2_INLINE uint16_t load16( const void *src ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + uint16_t w; + memcpy(&w, src, sizeof w); + return w; +#else + const uint8_t *p = ( const uint8_t * )src; + return ( uint16_t )((( uint32_t )( p[0] ) << 0) | + (( uint32_t )( p[1] ) << 8)); +#endif +} + +static BLAKE2_INLINE void store16( void *dst, uint16_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + *p++ = ( uint8_t )w; w >>= 8; + *p++ = ( uint8_t )w; +#endif +} + +static BLAKE2_INLINE void store32( void *dst, uint32_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); +#endif +} + +static BLAKE2_INLINE void store64( void *dst, uint64_t w ) +{ +#if defined(NATIVE_LITTLE_ENDIAN) + memcpy(dst, &w, sizeof w); +#else + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); + p[6] = (uint8_t)(w >> 48); + p[7] = (uint8_t)(w >> 56); +#endif +} + +static BLAKE2_INLINE uint64_t load48( const void *src ) +{ + const uint8_t *p = ( const uint8_t * )src; + return (( uint64_t )( p[0] ) << 0) | + (( uint64_t )( p[1] ) << 8) | + (( uint64_t )( p[2] ) << 16) | + (( uint64_t )( p[3] ) << 24) | + (( uint64_t )( p[4] ) << 32) | + (( uint64_t )( p[5] ) << 40) ; +} + +static BLAKE2_INLINE void store48( void *dst, uint64_t w ) +{ + uint8_t *p = ( uint8_t * )dst; + p[0] = (uint8_t)(w >> 0); + p[1] = (uint8_t)(w >> 8); + p[2] = (uint8_t)(w >> 16); + p[3] = (uint8_t)(w >> 24); + p[4] = (uint8_t)(w >> 32); + p[5] = (uint8_t)(w >> 40); +} + +static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) +{ + return ( w >> c ) | ( w << ( 32 - c ) ); +} + +static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) +{ + return ( w >> c ) | ( w << ( 64 - c ) ); +} + +/* prevents compiler optimizing out memset() */ +static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) +{ + static void *(*const volatile memset_v)(void *, int, size_t) = &memset; + memset_v(v, 0, n); +} + +#endif diff --git a/src/third_party/blake2/blake2.h b/src/third_party/blake2/blake2.h index ca390305..c2f7fafa 100644 --- a/src/third_party/blake2/blake2.h +++ b/src/third_party/blake2/blake2.h @@ -1,195 +1,195 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2_H -#define BLAKE2_H - -#include -#include - -#if defined(_MSC_VER) -#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) -#else -#define BLAKE2_PACKED(x) x __attribute__((packed)) -#endif - -#if defined(__cplusplus) -extern "C" { -#endif - - enum blake2s_constant - { - BLAKE2S_BLOCKBYTES = 64, - BLAKE2S_OUTBYTES = 32, - BLAKE2S_KEYBYTES = 32, - BLAKE2S_SALTBYTES = 8, - BLAKE2S_PERSONALBYTES = 8 - }; - - enum blake2b_constant - { - BLAKE2B_BLOCKBYTES = 128, - BLAKE2B_OUTBYTES = 64, - BLAKE2B_KEYBYTES = 64, - BLAKE2B_SALTBYTES = 16, - BLAKE2B_PERSONALBYTES = 16 - }; - - typedef struct blake2s_state__ - { - uint32_t h[8]; - uint32_t t[2]; - uint32_t f[2]; - uint8_t buf[BLAKE2S_BLOCKBYTES]; - size_t buflen; - size_t outlen; - uint8_t last_node; - } blake2s_state; - - typedef struct blake2b_state__ - { - uint64_t h[8]; - uint64_t t[2]; - uint64_t f[2]; - uint8_t buf[BLAKE2B_BLOCKBYTES]; - size_t buflen; - size_t outlen; - uint8_t last_node; - } blake2b_state; - - typedef struct blake2sp_state__ - { - blake2s_state S[8][1]; - blake2s_state R[1]; - uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; - size_t buflen; - size_t outlen; - } blake2sp_state; - - typedef struct blake2bp_state__ - { - blake2b_state S[4][1]; - blake2b_state R[1]; - uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; - size_t buflen; - size_t outlen; - } blake2bp_state; - - - BLAKE2_PACKED(struct blake2s_param__ - { - uint8_t digest_length; /* 1 */ - uint8_t key_length; /* 2 */ - uint8_t fanout; /* 3 */ - uint8_t depth; /* 4 */ - uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ - uint16_t xof_length; /* 14 */ - uint8_t node_depth; /* 15 */ - uint8_t inner_length; /* 16 */ - /* uint8_t reserved[0]; */ - uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ - uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ - }); - - typedef struct blake2s_param__ blake2s_param; - - BLAKE2_PACKED(struct blake2b_param__ - { - uint8_t digest_length; /* 1 */ - uint8_t key_length; /* 2 */ - uint8_t fanout; /* 3 */ - uint8_t depth; /* 4 */ - uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ - uint32_t xof_length; /* 16 */ - uint8_t node_depth; /* 17 */ - uint8_t inner_length; /* 18 */ - uint8_t reserved[14]; /* 32 */ - uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ - uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ - }); - - typedef struct blake2b_param__ blake2b_param; - - typedef struct blake2xs_state__ - { - blake2s_state S[1]; - blake2s_param P[1]; - } blake2xs_state; - - typedef struct blake2xb_state__ - { - blake2b_state S[1]; - blake2b_param P[1]; - } blake2xb_state; - - /* Padded structs result in a compile-time error */ - enum { - BLAKE2_DUMMY_1 = 1/(int)(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), - BLAKE2_DUMMY_2 = 1/(int)(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) - }; - - /* Streaming API */ - int blake2s_init( blake2s_state *S, size_t outlen ); - int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); - int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); - int blake2s_final( blake2s_state *S, void *out, size_t outlen ); - - int blake2b_init( blake2b_state *S, size_t outlen ); - int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); - int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); - int blake2b_final( blake2b_state *S, void *out, size_t outlen ); - - int blake2sp_init( blake2sp_state *S, size_t outlen ); - int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); - int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); - - int blake2bp_init( blake2bp_state *S, size_t outlen ); - int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); - int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); - - /* Variable output length API */ - int blake2xs_init( blake2xs_state *S, const size_t outlen ); - int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); - int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); - int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); - - int blake2xb_init( blake2xb_state *S, const size_t outlen ); - int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); - int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); - int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); - - /* Simple API */ - int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - /* This is simply an alias for blake2b */ - int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - -#if defined(__cplusplus) -} -#endif - -#endif +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2_H +#define BLAKE2_H + +#include +#include + +#if defined(_MSC_VER) +#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) +#else +#define BLAKE2_PACKED(x) x __attribute__((packed)) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + + enum blake2s_constant + { + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32, + BLAKE2S_KEYBYTES = 32, + BLAKE2S_SALTBYTES = 8, + BLAKE2S_PERSONALBYTES = 8 + }; + + enum blake2b_constant + { + BLAKE2B_BLOCKBYTES = 128, + BLAKE2B_OUTBYTES = 64, + BLAKE2B_KEYBYTES = 64, + BLAKE2B_SALTBYTES = 16, + BLAKE2B_PERSONALBYTES = 16 + }; + + typedef struct blake2s_state__ + { + uint32_t h[8]; + uint32_t t[2]; + uint32_t f[2]; + uint8_t buf[BLAKE2S_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; + } blake2s_state; + + typedef struct blake2b_state__ + { + uint64_t h[8]; + uint64_t t[2]; + uint64_t f[2]; + uint8_t buf[BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + uint8_t last_node; + } blake2b_state; + + typedef struct blake2sp_state__ + { + blake2s_state S[8][1]; + blake2s_state R[1]; + uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; + size_t buflen; + size_t outlen; + } blake2sp_state; + + typedef struct blake2bp_state__ + { + blake2b_state S[4][1]; + blake2b_state R[1]; + uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; + size_t buflen; + size_t outlen; + } blake2bp_state; + + + BLAKE2_PACKED(struct blake2s_param__ + { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint16_t xof_length; /* 14 */ + uint8_t node_depth; /* 15 */ + uint8_t inner_length; /* 16 */ + /* uint8_t reserved[0]; */ + uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ + uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ + }); + + typedef struct blake2s_param__ blake2s_param; + + BLAKE2_PACKED(struct blake2b_param__ + { + uint8_t digest_length; /* 1 */ + uint8_t key_length; /* 2 */ + uint8_t fanout; /* 3 */ + uint8_t depth; /* 4 */ + uint32_t leaf_length; /* 8 */ + uint32_t node_offset; /* 12 */ + uint32_t xof_length; /* 16 */ + uint8_t node_depth; /* 17 */ + uint8_t inner_length; /* 18 */ + uint8_t reserved[14]; /* 32 */ + uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ + uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ + }); + + typedef struct blake2b_param__ blake2b_param; + + typedef struct blake2xs_state__ + { + blake2s_state S[1]; + blake2s_param P[1]; + } blake2xs_state; + + typedef struct blake2xb_state__ + { + blake2b_state S[1]; + blake2b_param P[1]; + } blake2xb_state; + + /* Padded structs result in a compile-time error */ + enum { + BLAKE2_DUMMY_1 = 1/(int)(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), + BLAKE2_DUMMY_2 = 1/(int)(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) + }; + + /* Streaming API */ + int blake2s_init( blake2s_state *S, size_t outlen ); + int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); + int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); + int blake2s_final( blake2s_state *S, void *out, size_t outlen ); + + int blake2b_init( blake2b_state *S, size_t outlen ); + int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); + int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); + int blake2b_final( blake2b_state *S, void *out, size_t outlen ); + + int blake2sp_init( blake2sp_state *S, size_t outlen ); + int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); + int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); + + int blake2bp_init( blake2bp_state *S, size_t outlen ); + int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); + int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); + int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); + + /* Variable output length API */ + int blake2xs_init( blake2xs_state *S, const size_t outlen ); + int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); + int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); + int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); + + int blake2xb_init( blake2xb_state *S, const size_t outlen ); + int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); + int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); + int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); + + /* Simple API */ + int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + + /* This is simply an alias for blake2b */ + int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/src/third_party/blake2/blake2b-load-sse2.h b/src/third_party/blake2/blake2b-load-sse2.h index 23a8d40b..75ce9c9c 100644 --- a/src/third_party/blake2/blake2b-load-sse2.h +++ b/src/third_party/blake2/blake2b-load-sse2.h @@ -1,68 +1,68 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2B_LOAD_SSE2_H -#define BLAKE2B_LOAD_SSE2_H - -#define LOAD_MSG_0_1(b0, b1) b0 = _mm_set_epi64x(m2, m0); b1 = _mm_set_epi64x(m6, m4) -#define LOAD_MSG_0_2(b0, b1) b0 = _mm_set_epi64x(m3, m1); b1 = _mm_set_epi64x(m7, m5) -#define LOAD_MSG_0_3(b0, b1) b0 = _mm_set_epi64x(m10, m8); b1 = _mm_set_epi64x(m14, m12) -#define LOAD_MSG_0_4(b0, b1) b0 = _mm_set_epi64x(m11, m9); b1 = _mm_set_epi64x(m15, m13) -#define LOAD_MSG_1_1(b0, b1) b0 = _mm_set_epi64x(m4, m14); b1 = _mm_set_epi64x(m13, m9) -#define LOAD_MSG_1_2(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m6, m15) -#define LOAD_MSG_1_3(b0, b1) b0 = _mm_set_epi64x(m0, m1); b1 = _mm_set_epi64x(m5, m11) -#define LOAD_MSG_1_4(b0, b1) b0 = _mm_set_epi64x(m2, m12); b1 = _mm_set_epi64x(m3, m7) -#define LOAD_MSG_2_1(b0, b1) b0 = _mm_set_epi64x(m12, m11); b1 = _mm_set_epi64x(m15, m5) -#define LOAD_MSG_2_2(b0, b1) b0 = _mm_set_epi64x(m0, m8); b1 = _mm_set_epi64x(m13, m2) -#define LOAD_MSG_2_3(b0, b1) b0 = _mm_set_epi64x(m3, m10); b1 = _mm_set_epi64x(m9, m7) -#define LOAD_MSG_2_4(b0, b1) b0 = _mm_set_epi64x(m6, m14); b1 = _mm_set_epi64x(m4, m1) -#define LOAD_MSG_3_1(b0, b1) b0 = _mm_set_epi64x(m3, m7); b1 = _mm_set_epi64x(m11, m13) -#define LOAD_MSG_3_2(b0, b1) b0 = _mm_set_epi64x(m1, m9); b1 = _mm_set_epi64x(m14, m12) -#define LOAD_MSG_3_3(b0, b1) b0 = _mm_set_epi64x(m5, m2); b1 = _mm_set_epi64x(m15, m4) -#define LOAD_MSG_3_4(b0, b1) b0 = _mm_set_epi64x(m10, m6); b1 = _mm_set_epi64x(m8, m0) -#define LOAD_MSG_4_1(b0, b1) b0 = _mm_set_epi64x(m5, m9); b1 = _mm_set_epi64x(m10, m2) -#define LOAD_MSG_4_2(b0, b1) b0 = _mm_set_epi64x(m7, m0); b1 = _mm_set_epi64x(m15, m4) -#define LOAD_MSG_4_3(b0, b1) b0 = _mm_set_epi64x(m11, m14); b1 = _mm_set_epi64x(m3, m6) -#define LOAD_MSG_4_4(b0, b1) b0 = _mm_set_epi64x(m12, m1); b1 = _mm_set_epi64x(m13, m8) -#define LOAD_MSG_5_1(b0, b1) b0 = _mm_set_epi64x(m6, m2); b1 = _mm_set_epi64x(m8, m0) -#define LOAD_MSG_5_2(b0, b1) b0 = _mm_set_epi64x(m10, m12); b1 = _mm_set_epi64x(m3, m11) -#define LOAD_MSG_5_3(b0, b1) b0 = _mm_set_epi64x(m7, m4); b1 = _mm_set_epi64x(m1, m15) -#define LOAD_MSG_5_4(b0, b1) b0 = _mm_set_epi64x(m5, m13); b1 = _mm_set_epi64x(m9, m14) -#define LOAD_MSG_6_1(b0, b1) b0 = _mm_set_epi64x(m1, m12); b1 = _mm_set_epi64x(m4, m14) -#define LOAD_MSG_6_2(b0, b1) b0 = _mm_set_epi64x(m15, m5); b1 = _mm_set_epi64x(m10, m13) -#define LOAD_MSG_6_3(b0, b1) b0 = _mm_set_epi64x(m6, m0); b1 = _mm_set_epi64x(m8, m9) -#define LOAD_MSG_6_4(b0, b1) b0 = _mm_set_epi64x(m3, m7); b1 = _mm_set_epi64x(m11, m2) -#define LOAD_MSG_7_1(b0, b1) b0 = _mm_set_epi64x(m7, m13); b1 = _mm_set_epi64x(m3, m12) -#define LOAD_MSG_7_2(b0, b1) b0 = _mm_set_epi64x(m14, m11); b1 = _mm_set_epi64x(m9, m1) -#define LOAD_MSG_7_3(b0, b1) b0 = _mm_set_epi64x(m15, m5); b1 = _mm_set_epi64x(m2, m8) -#define LOAD_MSG_7_4(b0, b1) b0 = _mm_set_epi64x(m4, m0); b1 = _mm_set_epi64x(m10, m6) -#define LOAD_MSG_8_1(b0, b1) b0 = _mm_set_epi64x(m14, m6); b1 = _mm_set_epi64x(m0, m11) -#define LOAD_MSG_8_2(b0, b1) b0 = _mm_set_epi64x(m9, m15); b1 = _mm_set_epi64x(m8, m3) -#define LOAD_MSG_8_3(b0, b1) b0 = _mm_set_epi64x(m13, m12); b1 = _mm_set_epi64x(m10, m1) -#define LOAD_MSG_8_4(b0, b1) b0 = _mm_set_epi64x(m7, m2); b1 = _mm_set_epi64x(m5, m4) -#define LOAD_MSG_9_1(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m1, m7) -#define LOAD_MSG_9_2(b0, b1) b0 = _mm_set_epi64x(m4, m2); b1 = _mm_set_epi64x(m5, m6) -#define LOAD_MSG_9_3(b0, b1) b0 = _mm_set_epi64x(m9, m15); b1 = _mm_set_epi64x(m13, m3) -#define LOAD_MSG_9_4(b0, b1) b0 = _mm_set_epi64x(m14, m11); b1 = _mm_set_epi64x(m0, m12) -#define LOAD_MSG_10_1(b0, b1) b0 = _mm_set_epi64x(m2, m0); b1 = _mm_set_epi64x(m6, m4) -#define LOAD_MSG_10_2(b0, b1) b0 = _mm_set_epi64x(m3, m1); b1 = _mm_set_epi64x(m7, m5) -#define LOAD_MSG_10_3(b0, b1) b0 = _mm_set_epi64x(m10, m8); b1 = _mm_set_epi64x(m14, m12) -#define LOAD_MSG_10_4(b0, b1) b0 = _mm_set_epi64x(m11, m9); b1 = _mm_set_epi64x(m15, m13) -#define LOAD_MSG_11_1(b0, b1) b0 = _mm_set_epi64x(m4, m14); b1 = _mm_set_epi64x(m13, m9) -#define LOAD_MSG_11_2(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m6, m15) -#define LOAD_MSG_11_3(b0, b1) b0 = _mm_set_epi64x(m0, m1); b1 = _mm_set_epi64x(m5, m11) -#define LOAD_MSG_11_4(b0, b1) b0 = _mm_set_epi64x(m2, m12); b1 = _mm_set_epi64x(m3, m7) - - -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2B_LOAD_SSE2_H +#define BLAKE2B_LOAD_SSE2_H + +#define LOAD_MSG_0_1(b0, b1) b0 = _mm_set_epi64x(m2, m0); b1 = _mm_set_epi64x(m6, m4) +#define LOAD_MSG_0_2(b0, b1) b0 = _mm_set_epi64x(m3, m1); b1 = _mm_set_epi64x(m7, m5) +#define LOAD_MSG_0_3(b0, b1) b0 = _mm_set_epi64x(m10, m8); b1 = _mm_set_epi64x(m14, m12) +#define LOAD_MSG_0_4(b0, b1) b0 = _mm_set_epi64x(m11, m9); b1 = _mm_set_epi64x(m15, m13) +#define LOAD_MSG_1_1(b0, b1) b0 = _mm_set_epi64x(m4, m14); b1 = _mm_set_epi64x(m13, m9) +#define LOAD_MSG_1_2(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m6, m15) +#define LOAD_MSG_1_3(b0, b1) b0 = _mm_set_epi64x(m0, m1); b1 = _mm_set_epi64x(m5, m11) +#define LOAD_MSG_1_4(b0, b1) b0 = _mm_set_epi64x(m2, m12); b1 = _mm_set_epi64x(m3, m7) +#define LOAD_MSG_2_1(b0, b1) b0 = _mm_set_epi64x(m12, m11); b1 = _mm_set_epi64x(m15, m5) +#define LOAD_MSG_2_2(b0, b1) b0 = _mm_set_epi64x(m0, m8); b1 = _mm_set_epi64x(m13, m2) +#define LOAD_MSG_2_3(b0, b1) b0 = _mm_set_epi64x(m3, m10); b1 = _mm_set_epi64x(m9, m7) +#define LOAD_MSG_2_4(b0, b1) b0 = _mm_set_epi64x(m6, m14); b1 = _mm_set_epi64x(m4, m1) +#define LOAD_MSG_3_1(b0, b1) b0 = _mm_set_epi64x(m3, m7); b1 = _mm_set_epi64x(m11, m13) +#define LOAD_MSG_3_2(b0, b1) b0 = _mm_set_epi64x(m1, m9); b1 = _mm_set_epi64x(m14, m12) +#define LOAD_MSG_3_3(b0, b1) b0 = _mm_set_epi64x(m5, m2); b1 = _mm_set_epi64x(m15, m4) +#define LOAD_MSG_3_4(b0, b1) b0 = _mm_set_epi64x(m10, m6); b1 = _mm_set_epi64x(m8, m0) +#define LOAD_MSG_4_1(b0, b1) b0 = _mm_set_epi64x(m5, m9); b1 = _mm_set_epi64x(m10, m2) +#define LOAD_MSG_4_2(b0, b1) b0 = _mm_set_epi64x(m7, m0); b1 = _mm_set_epi64x(m15, m4) +#define LOAD_MSG_4_3(b0, b1) b0 = _mm_set_epi64x(m11, m14); b1 = _mm_set_epi64x(m3, m6) +#define LOAD_MSG_4_4(b0, b1) b0 = _mm_set_epi64x(m12, m1); b1 = _mm_set_epi64x(m13, m8) +#define LOAD_MSG_5_1(b0, b1) b0 = _mm_set_epi64x(m6, m2); b1 = _mm_set_epi64x(m8, m0) +#define LOAD_MSG_5_2(b0, b1) b0 = _mm_set_epi64x(m10, m12); b1 = _mm_set_epi64x(m3, m11) +#define LOAD_MSG_5_3(b0, b1) b0 = _mm_set_epi64x(m7, m4); b1 = _mm_set_epi64x(m1, m15) +#define LOAD_MSG_5_4(b0, b1) b0 = _mm_set_epi64x(m5, m13); b1 = _mm_set_epi64x(m9, m14) +#define LOAD_MSG_6_1(b0, b1) b0 = _mm_set_epi64x(m1, m12); b1 = _mm_set_epi64x(m4, m14) +#define LOAD_MSG_6_2(b0, b1) b0 = _mm_set_epi64x(m15, m5); b1 = _mm_set_epi64x(m10, m13) +#define LOAD_MSG_6_3(b0, b1) b0 = _mm_set_epi64x(m6, m0); b1 = _mm_set_epi64x(m8, m9) +#define LOAD_MSG_6_4(b0, b1) b0 = _mm_set_epi64x(m3, m7); b1 = _mm_set_epi64x(m11, m2) +#define LOAD_MSG_7_1(b0, b1) b0 = _mm_set_epi64x(m7, m13); b1 = _mm_set_epi64x(m3, m12) +#define LOAD_MSG_7_2(b0, b1) b0 = _mm_set_epi64x(m14, m11); b1 = _mm_set_epi64x(m9, m1) +#define LOAD_MSG_7_3(b0, b1) b0 = _mm_set_epi64x(m15, m5); b1 = _mm_set_epi64x(m2, m8) +#define LOAD_MSG_7_4(b0, b1) b0 = _mm_set_epi64x(m4, m0); b1 = _mm_set_epi64x(m10, m6) +#define LOAD_MSG_8_1(b0, b1) b0 = _mm_set_epi64x(m14, m6); b1 = _mm_set_epi64x(m0, m11) +#define LOAD_MSG_8_2(b0, b1) b0 = _mm_set_epi64x(m9, m15); b1 = _mm_set_epi64x(m8, m3) +#define LOAD_MSG_8_3(b0, b1) b0 = _mm_set_epi64x(m13, m12); b1 = _mm_set_epi64x(m10, m1) +#define LOAD_MSG_8_4(b0, b1) b0 = _mm_set_epi64x(m7, m2); b1 = _mm_set_epi64x(m5, m4) +#define LOAD_MSG_9_1(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m1, m7) +#define LOAD_MSG_9_2(b0, b1) b0 = _mm_set_epi64x(m4, m2); b1 = _mm_set_epi64x(m5, m6) +#define LOAD_MSG_9_3(b0, b1) b0 = _mm_set_epi64x(m9, m15); b1 = _mm_set_epi64x(m13, m3) +#define LOAD_MSG_9_4(b0, b1) b0 = _mm_set_epi64x(m14, m11); b1 = _mm_set_epi64x(m0, m12) +#define LOAD_MSG_10_1(b0, b1) b0 = _mm_set_epi64x(m2, m0); b1 = _mm_set_epi64x(m6, m4) +#define LOAD_MSG_10_2(b0, b1) b0 = _mm_set_epi64x(m3, m1); b1 = _mm_set_epi64x(m7, m5) +#define LOAD_MSG_10_3(b0, b1) b0 = _mm_set_epi64x(m10, m8); b1 = _mm_set_epi64x(m14, m12) +#define LOAD_MSG_10_4(b0, b1) b0 = _mm_set_epi64x(m11, m9); b1 = _mm_set_epi64x(m15, m13) +#define LOAD_MSG_11_1(b0, b1) b0 = _mm_set_epi64x(m4, m14); b1 = _mm_set_epi64x(m13, m9) +#define LOAD_MSG_11_2(b0, b1) b0 = _mm_set_epi64x(m8, m10); b1 = _mm_set_epi64x(m6, m15) +#define LOAD_MSG_11_3(b0, b1) b0 = _mm_set_epi64x(m0, m1); b1 = _mm_set_epi64x(m5, m11) +#define LOAD_MSG_11_4(b0, b1) b0 = _mm_set_epi64x(m2, m12); b1 = _mm_set_epi64x(m3, m7) + + +#endif diff --git a/src/third_party/blake2/blake2b-load-sse41.h b/src/third_party/blake2/blake2b-load-sse41.h index 0eca8659..8a9093cf 100644 --- a/src/third_party/blake2/blake2b-load-sse41.h +++ b/src/third_party/blake2/blake2b-load-sse41.h @@ -1,402 +1,402 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2B_LOAD_SSE41_H -#define BLAKE2B_LOAD_SSE41_H - -#define LOAD_MSG_0_1(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m0, m1); \ -b1 = _mm_unpacklo_epi64(m2, m3); \ -} while(0) - - -#define LOAD_MSG_0_2(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m0, m1); \ -b1 = _mm_unpackhi_epi64(m2, m3); \ -} while(0) - - -#define LOAD_MSG_0_3(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m4, m5); \ -b1 = _mm_unpacklo_epi64(m6, m7); \ -} while(0) - - -#define LOAD_MSG_0_4(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m4, m5); \ -b1 = _mm_unpackhi_epi64(m6, m7); \ -} while(0) - - -#define LOAD_MSG_1_1(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m7, m2); \ -b1 = _mm_unpackhi_epi64(m4, m6); \ -} while(0) - - -#define LOAD_MSG_1_2(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m5, m4); \ -b1 = _mm_alignr_epi8(m3, m7, 8); \ -} while(0) - - -#define LOAD_MSG_1_3(b0, b1) \ -do \ -{ \ -b0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1,0,3,2)); \ -b1 = _mm_unpackhi_epi64(m5, m2); \ -} while(0) - - -#define LOAD_MSG_1_4(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m6, m1); \ -b1 = _mm_unpackhi_epi64(m3, m1); \ -} while(0) - - -#define LOAD_MSG_2_1(b0, b1) \ -do \ -{ \ -b0 = _mm_alignr_epi8(m6, m5, 8); \ -b1 = _mm_unpackhi_epi64(m2, m7); \ -} while(0) - - -#define LOAD_MSG_2_2(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m4, m0); \ -b1 = _mm_blend_epi16(m1, m6, 0xF0); \ -} while(0) - - -#define LOAD_MSG_2_3(b0, b1) \ -do \ -{ \ -b0 = _mm_blend_epi16(m5, m1, 0xF0); \ -b1 = _mm_unpackhi_epi64(m3, m4); \ -} while(0) - - -#define LOAD_MSG_2_4(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m7, m3); \ -b1 = _mm_alignr_epi8(m2, m0, 8); \ -} while(0) - - -#define LOAD_MSG_3_1(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m3, m1); \ -b1 = _mm_unpackhi_epi64(m6, m5); \ -} while(0) - - -#define LOAD_MSG_3_2(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m4, m0); \ -b1 = _mm_unpacklo_epi64(m6, m7); \ -} while(0) - - -#define LOAD_MSG_3_3(b0, b1) \ -do \ -{ \ -b0 = _mm_blend_epi16(m1, m2, 0xF0); \ -b1 = _mm_blend_epi16(m2, m7, 0xF0); \ -} while(0) - - -#define LOAD_MSG_3_4(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m3, m5); \ -b1 = _mm_unpacklo_epi64(m0, m4); \ -} while(0) - - -#define LOAD_MSG_4_1(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m4, m2); \ -b1 = _mm_unpacklo_epi64(m1, m5); \ -} while(0) - - -#define LOAD_MSG_4_2(b0, b1) \ -do \ -{ \ -b0 = _mm_blend_epi16(m0, m3, 0xF0); \ -b1 = _mm_blend_epi16(m2, m7, 0xF0); \ -} while(0) - - -#define LOAD_MSG_4_3(b0, b1) \ -do \ -{ \ -b0 = _mm_blend_epi16(m7, m5, 0xF0); \ -b1 = _mm_blend_epi16(m3, m1, 0xF0); \ -} while(0) - - -#define LOAD_MSG_4_4(b0, b1) \ -do \ -{ \ -b0 = _mm_alignr_epi8(m6, m0, 8); \ -b1 = _mm_blend_epi16(m4, m6, 0xF0); \ -} while(0) - - -#define LOAD_MSG_5_1(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m1, m3); \ -b1 = _mm_unpacklo_epi64(m0, m4); \ -} while(0) - - -#define LOAD_MSG_5_2(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m6, m5); \ -b1 = _mm_unpackhi_epi64(m5, m1); \ -} while(0) - - -#define LOAD_MSG_5_3(b0, b1) \ -do \ -{ \ -b0 = _mm_blend_epi16(m2, m3, 0xF0); \ -b1 = _mm_unpackhi_epi64(m7, m0); \ -} while(0) - - -#define LOAD_MSG_5_4(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m6, m2); \ -b1 = _mm_blend_epi16(m7, m4, 0xF0); \ -} while(0) - - -#define LOAD_MSG_6_1(b0, b1) \ -do \ -{ \ -b0 = _mm_blend_epi16(m6, m0, 0xF0); \ -b1 = _mm_unpacklo_epi64(m7, m2); \ -} while(0) - - -#define LOAD_MSG_6_2(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m2, m7); \ -b1 = _mm_alignr_epi8(m5, m6, 8); \ -} while(0) - - -#define LOAD_MSG_6_3(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m0, m3); \ -b1 = _mm_shuffle_epi32(m4, _MM_SHUFFLE(1,0,3,2)); \ -} while(0) - - -#define LOAD_MSG_6_4(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m3, m1); \ -b1 = _mm_blend_epi16(m1, m5, 0xF0); \ -} while(0) - - -#define LOAD_MSG_7_1(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m6, m3); \ -b1 = _mm_blend_epi16(m6, m1, 0xF0); \ -} while(0) - - -#define LOAD_MSG_7_2(b0, b1) \ -do \ -{ \ -b0 = _mm_alignr_epi8(m7, m5, 8); \ -b1 = _mm_unpackhi_epi64(m0, m4); \ -} while(0) - - -#define LOAD_MSG_7_3(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m2, m7); \ -b1 = _mm_unpacklo_epi64(m4, m1); \ -} while(0) - - -#define LOAD_MSG_7_4(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m0, m2); \ -b1 = _mm_unpacklo_epi64(m3, m5); \ -} while(0) - - -#define LOAD_MSG_8_1(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m3, m7); \ -b1 = _mm_alignr_epi8(m0, m5, 8); \ -} while(0) - - -#define LOAD_MSG_8_2(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m7, m4); \ -b1 = _mm_alignr_epi8(m4, m1, 8); \ -} while(0) - - -#define LOAD_MSG_8_3(b0, b1) \ -do \ -{ \ -b0 = m6; \ -b1 = _mm_alignr_epi8(m5, m0, 8); \ -} while(0) - - -#define LOAD_MSG_8_4(b0, b1) \ -do \ -{ \ -b0 = _mm_blend_epi16(m1, m3, 0xF0); \ -b1 = m2; \ -} while(0) - - -#define LOAD_MSG_9_1(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m5, m4); \ -b1 = _mm_unpackhi_epi64(m3, m0); \ -} while(0) - - -#define LOAD_MSG_9_2(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m1, m2); \ -b1 = _mm_blend_epi16(m3, m2, 0xF0); \ -} while(0) - - -#define LOAD_MSG_9_3(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m7, m4); \ -b1 = _mm_unpackhi_epi64(m1, m6); \ -} while(0) - - -#define LOAD_MSG_9_4(b0, b1) \ -do \ -{ \ -b0 = _mm_alignr_epi8(m7, m5, 8); \ -b1 = _mm_unpacklo_epi64(m6, m0); \ -} while(0) - - -#define LOAD_MSG_10_1(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m0, m1); \ -b1 = _mm_unpacklo_epi64(m2, m3); \ -} while(0) - - -#define LOAD_MSG_10_2(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m0, m1); \ -b1 = _mm_unpackhi_epi64(m2, m3); \ -} while(0) - - -#define LOAD_MSG_10_3(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m4, m5); \ -b1 = _mm_unpacklo_epi64(m6, m7); \ -} while(0) - - -#define LOAD_MSG_10_4(b0, b1) \ -do \ -{ \ -b0 = _mm_unpackhi_epi64(m4, m5); \ -b1 = _mm_unpackhi_epi64(m6, m7); \ -} while(0) - - -#define LOAD_MSG_11_1(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m7, m2); \ -b1 = _mm_unpackhi_epi64(m4, m6); \ -} while(0) - - -#define LOAD_MSG_11_2(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m5, m4); \ -b1 = _mm_alignr_epi8(m3, m7, 8); \ -} while(0) - - -#define LOAD_MSG_11_3(b0, b1) \ -do \ -{ \ -b0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1,0,3,2)); \ -b1 = _mm_unpackhi_epi64(m5, m2); \ -} while(0) - - -#define LOAD_MSG_11_4(b0, b1) \ -do \ -{ \ -b0 = _mm_unpacklo_epi64(m6, m1); \ -b1 = _mm_unpackhi_epi64(m3, m1); \ -} while(0) - - -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2B_LOAD_SSE41_H +#define BLAKE2B_LOAD_SSE41_H + +#define LOAD_MSG_0_1(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m0, m1); \ +b1 = _mm_unpacklo_epi64(m2, m3); \ +} while(0) + + +#define LOAD_MSG_0_2(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m0, m1); \ +b1 = _mm_unpackhi_epi64(m2, m3); \ +} while(0) + + +#define LOAD_MSG_0_3(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m4, m5); \ +b1 = _mm_unpacklo_epi64(m6, m7); \ +} while(0) + + +#define LOAD_MSG_0_4(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m4, m5); \ +b1 = _mm_unpackhi_epi64(m6, m7); \ +} while(0) + + +#define LOAD_MSG_1_1(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m7, m2); \ +b1 = _mm_unpackhi_epi64(m4, m6); \ +} while(0) + + +#define LOAD_MSG_1_2(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m5, m4); \ +b1 = _mm_alignr_epi8(m3, m7, 8); \ +} while(0) + + +#define LOAD_MSG_1_3(b0, b1) \ +do \ +{ \ +b0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1,0,3,2)); \ +b1 = _mm_unpackhi_epi64(m5, m2); \ +} while(0) + + +#define LOAD_MSG_1_4(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m6, m1); \ +b1 = _mm_unpackhi_epi64(m3, m1); \ +} while(0) + + +#define LOAD_MSG_2_1(b0, b1) \ +do \ +{ \ +b0 = _mm_alignr_epi8(m6, m5, 8); \ +b1 = _mm_unpackhi_epi64(m2, m7); \ +} while(0) + + +#define LOAD_MSG_2_2(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m4, m0); \ +b1 = _mm_blend_epi16(m1, m6, 0xF0); \ +} while(0) + + +#define LOAD_MSG_2_3(b0, b1) \ +do \ +{ \ +b0 = _mm_blend_epi16(m5, m1, 0xF0); \ +b1 = _mm_unpackhi_epi64(m3, m4); \ +} while(0) + + +#define LOAD_MSG_2_4(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m7, m3); \ +b1 = _mm_alignr_epi8(m2, m0, 8); \ +} while(0) + + +#define LOAD_MSG_3_1(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m3, m1); \ +b1 = _mm_unpackhi_epi64(m6, m5); \ +} while(0) + + +#define LOAD_MSG_3_2(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m4, m0); \ +b1 = _mm_unpacklo_epi64(m6, m7); \ +} while(0) + + +#define LOAD_MSG_3_3(b0, b1) \ +do \ +{ \ +b0 = _mm_blend_epi16(m1, m2, 0xF0); \ +b1 = _mm_blend_epi16(m2, m7, 0xF0); \ +} while(0) + + +#define LOAD_MSG_3_4(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m3, m5); \ +b1 = _mm_unpacklo_epi64(m0, m4); \ +} while(0) + + +#define LOAD_MSG_4_1(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m4, m2); \ +b1 = _mm_unpacklo_epi64(m1, m5); \ +} while(0) + + +#define LOAD_MSG_4_2(b0, b1) \ +do \ +{ \ +b0 = _mm_blend_epi16(m0, m3, 0xF0); \ +b1 = _mm_blend_epi16(m2, m7, 0xF0); \ +} while(0) + + +#define LOAD_MSG_4_3(b0, b1) \ +do \ +{ \ +b0 = _mm_blend_epi16(m7, m5, 0xF0); \ +b1 = _mm_blend_epi16(m3, m1, 0xF0); \ +} while(0) + + +#define LOAD_MSG_4_4(b0, b1) \ +do \ +{ \ +b0 = _mm_alignr_epi8(m6, m0, 8); \ +b1 = _mm_blend_epi16(m4, m6, 0xF0); \ +} while(0) + + +#define LOAD_MSG_5_1(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m1, m3); \ +b1 = _mm_unpacklo_epi64(m0, m4); \ +} while(0) + + +#define LOAD_MSG_5_2(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m6, m5); \ +b1 = _mm_unpackhi_epi64(m5, m1); \ +} while(0) + + +#define LOAD_MSG_5_3(b0, b1) \ +do \ +{ \ +b0 = _mm_blend_epi16(m2, m3, 0xF0); \ +b1 = _mm_unpackhi_epi64(m7, m0); \ +} while(0) + + +#define LOAD_MSG_5_4(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m6, m2); \ +b1 = _mm_blend_epi16(m7, m4, 0xF0); \ +} while(0) + + +#define LOAD_MSG_6_1(b0, b1) \ +do \ +{ \ +b0 = _mm_blend_epi16(m6, m0, 0xF0); \ +b1 = _mm_unpacklo_epi64(m7, m2); \ +} while(0) + + +#define LOAD_MSG_6_2(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m2, m7); \ +b1 = _mm_alignr_epi8(m5, m6, 8); \ +} while(0) + + +#define LOAD_MSG_6_3(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m0, m3); \ +b1 = _mm_shuffle_epi32(m4, _MM_SHUFFLE(1,0,3,2)); \ +} while(0) + + +#define LOAD_MSG_6_4(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m3, m1); \ +b1 = _mm_blend_epi16(m1, m5, 0xF0); \ +} while(0) + + +#define LOAD_MSG_7_1(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m6, m3); \ +b1 = _mm_blend_epi16(m6, m1, 0xF0); \ +} while(0) + + +#define LOAD_MSG_7_2(b0, b1) \ +do \ +{ \ +b0 = _mm_alignr_epi8(m7, m5, 8); \ +b1 = _mm_unpackhi_epi64(m0, m4); \ +} while(0) + + +#define LOAD_MSG_7_3(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m2, m7); \ +b1 = _mm_unpacklo_epi64(m4, m1); \ +} while(0) + + +#define LOAD_MSG_7_4(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m0, m2); \ +b1 = _mm_unpacklo_epi64(m3, m5); \ +} while(0) + + +#define LOAD_MSG_8_1(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m3, m7); \ +b1 = _mm_alignr_epi8(m0, m5, 8); \ +} while(0) + + +#define LOAD_MSG_8_2(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m7, m4); \ +b1 = _mm_alignr_epi8(m4, m1, 8); \ +} while(0) + + +#define LOAD_MSG_8_3(b0, b1) \ +do \ +{ \ +b0 = m6; \ +b1 = _mm_alignr_epi8(m5, m0, 8); \ +} while(0) + + +#define LOAD_MSG_8_4(b0, b1) \ +do \ +{ \ +b0 = _mm_blend_epi16(m1, m3, 0xF0); \ +b1 = m2; \ +} while(0) + + +#define LOAD_MSG_9_1(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m5, m4); \ +b1 = _mm_unpackhi_epi64(m3, m0); \ +} while(0) + + +#define LOAD_MSG_9_2(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m1, m2); \ +b1 = _mm_blend_epi16(m3, m2, 0xF0); \ +} while(0) + + +#define LOAD_MSG_9_3(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m7, m4); \ +b1 = _mm_unpackhi_epi64(m1, m6); \ +} while(0) + + +#define LOAD_MSG_9_4(b0, b1) \ +do \ +{ \ +b0 = _mm_alignr_epi8(m7, m5, 8); \ +b1 = _mm_unpacklo_epi64(m6, m0); \ +} while(0) + + +#define LOAD_MSG_10_1(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m0, m1); \ +b1 = _mm_unpacklo_epi64(m2, m3); \ +} while(0) + + +#define LOAD_MSG_10_2(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m0, m1); \ +b1 = _mm_unpackhi_epi64(m2, m3); \ +} while(0) + + +#define LOAD_MSG_10_3(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m4, m5); \ +b1 = _mm_unpacklo_epi64(m6, m7); \ +} while(0) + + +#define LOAD_MSG_10_4(b0, b1) \ +do \ +{ \ +b0 = _mm_unpackhi_epi64(m4, m5); \ +b1 = _mm_unpackhi_epi64(m6, m7); \ +} while(0) + + +#define LOAD_MSG_11_1(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m7, m2); \ +b1 = _mm_unpackhi_epi64(m4, m6); \ +} while(0) + + +#define LOAD_MSG_11_2(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m5, m4); \ +b1 = _mm_alignr_epi8(m3, m7, 8); \ +} while(0) + + +#define LOAD_MSG_11_3(b0, b1) \ +do \ +{ \ +b0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1,0,3,2)); \ +b1 = _mm_unpackhi_epi64(m5, m2); \ +} while(0) + + +#define LOAD_MSG_11_4(b0, b1) \ +do \ +{ \ +b0 = _mm_unpacklo_epi64(m6, m1); \ +b1 = _mm_unpackhi_epi64(m3, m1); \ +} while(0) + + +#endif diff --git a/src/third_party/blake2/blake2b-ref.c b/src/third_party/blake2/blake2b-ref.c index cd38b1ba..016ec0f7 100644 --- a/src/third_party/blake2/blake2b-ref.c +++ b/src/third_party/blake2/blake2b-ref.c @@ -1,379 +1,379 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#include "blake2.h" -#include "blake2-impl.h" - -static const uint64_t blake2b_IV[8] = -{ - 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, - 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, - 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, - 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL -}; - -static const uint8_t blake2b_sigma[12][16] = -{ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } -}; - - -static void blake2b_set_lastnode( blake2b_state *S ) -{ - S->f[1] = (uint64_t)-1; -} - -/* Some helper functions, not necessarily useful */ -static int blake2b_is_lastblock( const blake2b_state *S ) -{ - return S->f[0] != 0; -} - -static void blake2b_set_lastblock( blake2b_state *S ) -{ - if( S->last_node ) blake2b_set_lastnode( S ); - - S->f[0] = (uint64_t)-1; -} - -static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) -{ - S->t[0] += inc; - S->t[1] += ( S->t[0] < inc ); -} - -static void blake2b_init0( blake2b_state *S ) -{ - size_t i; - memset( S, 0, sizeof( blake2b_state ) ); - - for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; -} - -/* init xors IV with input parameter block */ -int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) -{ - const uint8_t *p = ( const uint8_t * )( P ); - size_t i; - - blake2b_init0( S ); - - /* IV XOR ParamBlock */ - for( i = 0; i < 8; ++i ) - S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); - - S->outlen = P->digest_length; - return 0; -} - - - -int blake2b_init( blake2b_state *S, size_t outlen ) -{ - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = 0; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2b_init_param( S, P ); -} - - -int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) -{ - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - if( blake2b_init_param( S, P ) < 0 ) return -1; - - { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset( block, 0, BLAKE2B_BLOCKBYTES ); - memcpy( block, key, keylen ); - blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - -#define G(r,i,a,b,c,d) \ - do { \ - a = a + b + m[blake2b_sigma[r][2*i+0]]; \ - d = rotr64(d ^ a, 32); \ - c = c + d; \ - b = rotr64(b ^ c, 24); \ - a = a + b + m[blake2b_sigma[r][2*i+1]]; \ - d = rotr64(d ^ a, 16); \ - c = c + d; \ - b = rotr64(b ^ c, 63); \ - } while(0) - -#define ROUND(r) \ - do { \ - G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ - G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ - G(r,2,v[ 2],v[ 6],v[10],v[14]); \ - G(r,3,v[ 3],v[ 7],v[11],v[15]); \ - G(r,4,v[ 0],v[ 5],v[10],v[15]); \ - G(r,5,v[ 1],v[ 6],v[11],v[12]); \ - G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ - G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ - } while(0) - -static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) -{ - uint64_t m[16]; - uint64_t v[16]; - size_t i; - - for( i = 0; i < 16; ++i ) { - m[i] = load64( block + i * sizeof( m[i] ) ); - } - - for( i = 0; i < 8; ++i ) { - v[i] = S->h[i]; - } - - v[ 8] = blake2b_IV[0]; - v[ 9] = blake2b_IV[1]; - v[10] = blake2b_IV[2]; - v[11] = blake2b_IV[3]; - v[12] = blake2b_IV[4] ^ S->t[0]; - v[13] = blake2b_IV[5] ^ S->t[1]; - v[14] = blake2b_IV[6] ^ S->f[0]; - v[15] = blake2b_IV[7] ^ S->f[1]; - - ROUND( 0 ); - ROUND( 1 ); - ROUND( 2 ); - ROUND( 3 ); - ROUND( 4 ); - ROUND( 5 ); - ROUND( 6 ); - ROUND( 7 ); - ROUND( 8 ); - ROUND( 9 ); - ROUND( 10 ); - ROUND( 11 ); - - for( i = 0; i < 8; ++i ) { - S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; - } -} - -#undef G -#undef ROUND - -int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - if( inlen > 0 ) - { - size_t left = S->buflen; - size_t fill = BLAKE2B_BLOCKBYTES - left; - if( inlen > fill ) - { - S->buflen = 0; - memcpy( S->buf + left, in, fill ); /* Fill buffer */ - blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); - blake2b_compress( S, S->buf ); /* Compress */ - in += fill; inlen -= fill; - while(inlen > BLAKE2B_BLOCKBYTES) { - blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); - blake2b_compress( S, in ); - in += BLAKE2B_BLOCKBYTES; - inlen -= BLAKE2B_BLOCKBYTES; - } - } - memcpy( S->buf + S->buflen, in, inlen ); - S->buflen += inlen; - } - return 0; -} - -int blake2b_final( blake2b_state *S, void *out, size_t outlen ) -{ - uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; - size_t i; - - if( out == NULL || outlen < S->outlen ) - return -1; - - if( blake2b_is_lastblock( S ) ) - return -1; - - blake2b_increment_counter( S, S->buflen ); - blake2b_set_lastblock( S ); - memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ - blake2b_compress( S, S->buf ); - - for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ - store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); - - memcpy( out, buffer, S->outlen ); - secure_zero_memory(buffer, sizeof(buffer)); - return 0; -} - -/* inlen, at least, should be uint64_t. Others can be size_t. */ -int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - blake2b_state S[1]; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if( NULL == key && keylen > 0 ) return -1; - - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - if( keylen > BLAKE2B_KEYBYTES ) return -1; - - if( keylen > 0 ) - { - if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; - } - else - { - if( blake2b_init( S, outlen ) < 0 ) return -1; - } - - blake2b_update( S, ( const uint8_t * )in, inlen ); - blake2b_final( S, out, outlen ); - return 0; -} - -int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { - return blake2b(out, outlen, in, inlen, key, keylen); -} - -#if defined(SUPERCOP) -int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) -{ - return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); -} -#endif - -#if defined(BLAKE2B_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2B_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); - - if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2b_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2b_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +static const uint64_t blake2b_IV[8] = +{ + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +}; + +static const uint8_t blake2b_sigma[12][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } +}; + + +static void blake2b_set_lastnode( blake2b_state *S ) +{ + S->f[1] = (uint64_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2b_is_lastblock( const blake2b_state *S ) +{ + return S->f[0] != 0; +} + +static void blake2b_set_lastblock( blake2b_state *S ) +{ + if( S->last_node ) blake2b_set_lastnode( S ); + + S->f[0] = (uint64_t)-1; +} + +static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + +static void blake2b_init0( blake2b_state *S ) +{ + size_t i; + memset( S, 0, sizeof( blake2b_state ) ); + + for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; +} + +/* init xors IV with input parameter block */ +int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) +{ + const uint8_t *p = ( const uint8_t * )( P ); + size_t i; + + blake2b_init0( S ); + + /* IV XOR ParamBlock */ + for( i = 0; i < 8; ++i ) + S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); + + S->outlen = P->digest_length; + return 0; +} + + + +int blake2b_init( blake2b_state *S, size_t outlen ) +{ + blake2b_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2b_init_param( S, P ); +} + + +int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) +{ + blake2b_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + if( blake2b_init_param( S, P ) < 0 ) return -1; + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset( block, 0, BLAKE2B_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); + secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2b_sigma[r][2*i+0]]; \ + d = rotr64(d ^ a, 32); \ + c = c + d; \ + b = rotr64(b ^ c, 24); \ + a = a + b + m[blake2b_sigma[r][2*i+1]]; \ + d = rotr64(d ^ a, 16); \ + c = c + d; \ + b = rotr64(b ^ c, 63); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while(0) + +static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) +{ + uint64_t m[16]; + uint64_t v[16]; + size_t i; + + for( i = 0; i < 16; ++i ) { + m[i] = load64( block + i * sizeof( m[i] ) ); + } + + for( i = 0; i < 8; ++i ) { + v[i] = S->h[i]; + } + + v[ 8] = blake2b_IV[0]; + v[ 9] = blake2b_IV[1]; + v[10] = blake2b_IV[2]; + v[11] = blake2b_IV[3]; + v[12] = blake2b_IV[4] ^ S->t[0]; + v[13] = blake2b_IV[5] ^ S->t[1]; + v[14] = blake2b_IV[6] ^ S->f[0]; + v[15] = blake2b_IV[7] ^ S->f[1]; + + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + ROUND( 10 ); + ROUND( 11 ); + + for( i = 0; i < 8; ++i ) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + if( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + if( inlen > fill ) + { + S->buflen = 0; + memcpy( S->buf + left, in, fill ); /* Fill buffer */ + blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); + blake2b_compress( S, S->buf ); /* Compress */ + in += fill; inlen -= fill; + while(inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress( S, in ); + in += BLAKE2B_BLOCKBYTES; + inlen -= BLAKE2B_BLOCKBYTES; + } + } + memcpy( S->buf + S->buflen, in, inlen ); + S->buflen += inlen; + } + return 0; +} + +int blake2b_final( blake2b_state *S, void *out, size_t outlen ) +{ + uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; + size_t i; + + if( out == NULL || outlen < S->outlen ) + return -1; + + if( blake2b_is_lastblock( S ) ) + return -1; + + blake2b_increment_counter( S, S->buflen ); + blake2b_set_lastblock( S ); + memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ + blake2b_compress( S, S->buf ); + + for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ + store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + + memcpy( out, buffer, S->outlen ); + secure_zero_memory(buffer, sizeof(buffer)); + return 0; +} + +/* inlen, at least, should be uint64_t. Others can be size_t. */ +int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + blake2b_state S[1]; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if( NULL == key && keylen > 0 ) return -1; + + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + if( keylen > BLAKE2B_KEYBYTES ) return -1; + + if( keylen > 0 ) + { + if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; + } + else + { + if( blake2b_init( S, outlen ) < 0 ) return -1; + } + + blake2b_update( S, ( const uint8_t * )in, inlen ); + blake2b_final( S, out, outlen ); + return 0; +} + +int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { + return blake2b(out, outlen, in, inlen, key, keylen); +} + +#if defined(SUPERCOP) +int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) +{ + return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); +} +#endif + +#if defined(BLAKE2B_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2B_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); + + if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2b_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2b_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2b-round.h b/src/third_party/blake2/blake2b-round.h index 6537fff3..bae9ae45 100644 --- a/src/third_party/blake2/blake2b-round.h +++ b/src/third_party/blake2/blake2b-round.h @@ -1,157 +1,157 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2B_ROUND_H -#define BLAKE2B_ROUND_H - -#define LOADU(p) _mm_loadu_si128( (const __m128i *)(p) ) -#define STOREU(p,r) _mm_storeu_si128((__m128i *)(p), r) - -#define TOF(reg) _mm_castsi128_ps((reg)) -#define TOI(reg) _mm_castps_si128((reg)) - -#define LIKELY(x) __builtin_expect((x),1) - - -/* Microarchitecture-specific macros */ -#ifndef HAVE_XOP -#ifdef HAVE_SSSE3 -#define _mm_roti_epi64(x, c) \ - (-(c) == 32) ? _mm_shuffle_epi32((x), _MM_SHUFFLE(2,3,0,1)) \ - : (-(c) == 24) ? _mm_shuffle_epi8((x), r24) \ - : (-(c) == 16) ? _mm_shuffle_epi8((x), r16) \ - : (-(c) == 63) ? _mm_xor_si128(_mm_srli_epi64((x), -(c)), _mm_add_epi64((x), (x))) \ - : _mm_xor_si128(_mm_srli_epi64((x), -(c)), _mm_slli_epi64((x), 64-(-(c)))) -#else -#define _mm_roti_epi64(r, c) _mm_xor_si128(_mm_srli_epi64( (r), -(c) ),_mm_slli_epi64( (r), 64-(-(c)) )) -#endif -#else -/* ... */ -#endif - - - -#define G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \ - row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \ - row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \ - \ - row4l = _mm_xor_si128(row4l, row1l); \ - row4h = _mm_xor_si128(row4h, row1h); \ - \ - row4l = _mm_roti_epi64(row4l, -32); \ - row4h = _mm_roti_epi64(row4h, -32); \ - \ - row3l = _mm_add_epi64(row3l, row4l); \ - row3h = _mm_add_epi64(row3h, row4h); \ - \ - row2l = _mm_xor_si128(row2l, row3l); \ - row2h = _mm_xor_si128(row2h, row3h); \ - \ - row2l = _mm_roti_epi64(row2l, -24); \ - row2h = _mm_roti_epi64(row2h, -24); \ - -#define G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \ - row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \ - row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \ - \ - row4l = _mm_xor_si128(row4l, row1l); \ - row4h = _mm_xor_si128(row4h, row1h); \ - \ - row4l = _mm_roti_epi64(row4l, -16); \ - row4h = _mm_roti_epi64(row4h, -16); \ - \ - row3l = _mm_add_epi64(row3l, row4l); \ - row3h = _mm_add_epi64(row3h, row4h); \ - \ - row2l = _mm_xor_si128(row2l, row3l); \ - row2h = _mm_xor_si128(row2h, row3h); \ - \ - row2l = _mm_roti_epi64(row2l, -63); \ - row2h = _mm_roti_epi64(row2h, -63); \ - -#if defined(HAVE_SSSE3) -#define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \ - t0 = _mm_alignr_epi8(row2h, row2l, 8); \ - t1 = _mm_alignr_epi8(row2l, row2h, 8); \ - row2l = t0; \ - row2h = t1; \ - \ - t0 = row3l; \ - row3l = row3h; \ - row3h = t0; \ - \ - t0 = _mm_alignr_epi8(row4h, row4l, 8); \ - t1 = _mm_alignr_epi8(row4l, row4h, 8); \ - row4l = t1; \ - row4h = t0; - -#define UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \ - t0 = _mm_alignr_epi8(row2l, row2h, 8); \ - t1 = _mm_alignr_epi8(row2h, row2l, 8); \ - row2l = t0; \ - row2h = t1; \ - \ - t0 = row3l; \ - row3l = row3h; \ - row3h = t0; \ - \ - t0 = _mm_alignr_epi8(row4l, row4h, 8); \ - t1 = _mm_alignr_epi8(row4h, row4l, 8); \ - row4l = t1; \ - row4h = t0; -#else - -#define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \ - t0 = row4l;\ - t1 = row2l;\ - row4l = row3l;\ - row3l = row3h;\ - row3h = row4l;\ - row4l = _mm_unpackhi_epi64(row4h, _mm_unpacklo_epi64(t0, t0)); \ - row4h = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(row4h, row4h)); \ - row2l = _mm_unpackhi_epi64(row2l, _mm_unpacklo_epi64(row2h, row2h)); \ - row2h = _mm_unpackhi_epi64(row2h, _mm_unpacklo_epi64(t1, t1)) - -#define UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \ - t0 = row3l;\ - row3l = row3h;\ - row3h = t0;\ - t0 = row2l;\ - t1 = row4l;\ - row2l = _mm_unpackhi_epi64(row2h, _mm_unpacklo_epi64(row2l, row2l)); \ - row2h = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(row2h, row2h)); \ - row4l = _mm_unpackhi_epi64(row4l, _mm_unpacklo_epi64(row4h, row4h)); \ - row4h = _mm_unpackhi_epi64(row4h, _mm_unpacklo_epi64(t1, t1)) - -#endif - -#if defined(HAVE_SSE41) -#include "blake2b-load-sse41.h" -#else -#include "blake2b-load-sse2.h" -#endif - -#define ROUND(r) \ - LOAD_MSG_ ##r ##_1(b0, b1); \ - G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \ - LOAD_MSG_ ##r ##_2(b0, b1); \ - G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \ - DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h); \ - LOAD_MSG_ ##r ##_3(b0, b1); \ - G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \ - LOAD_MSG_ ##r ##_4(b0, b1); \ - G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \ - UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h); - -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2B_ROUND_H +#define BLAKE2B_ROUND_H + +#define LOADU(p) _mm_loadu_si128( (const __m128i *)(p) ) +#define STOREU(p,r) _mm_storeu_si128((__m128i *)(p), r) + +#define TOF(reg) _mm_castsi128_ps((reg)) +#define TOI(reg) _mm_castps_si128((reg)) + +#define LIKELY(x) __builtin_expect((x),1) + + +/* Microarchitecture-specific macros */ +#ifndef HAVE_XOP +#ifdef HAVE_SSSE3 +#define _mm_roti_epi64(x, c) \ + (-(c) == 32) ? _mm_shuffle_epi32((x), _MM_SHUFFLE(2,3,0,1)) \ + : (-(c) == 24) ? _mm_shuffle_epi8((x), r24) \ + : (-(c) == 16) ? _mm_shuffle_epi8((x), r16) \ + : (-(c) == 63) ? _mm_xor_si128(_mm_srli_epi64((x), -(c)), _mm_add_epi64((x), (x))) \ + : _mm_xor_si128(_mm_srli_epi64((x), -(c)), _mm_slli_epi64((x), 64-(-(c)))) +#else +#define _mm_roti_epi64(r, c) _mm_xor_si128(_mm_srli_epi64( (r), -(c) ),_mm_slli_epi64( (r), 64-(-(c)) )) +#endif +#else +/* ... */ +#endif + + + +#define G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \ + row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \ + row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \ + \ + row4l = _mm_xor_si128(row4l, row1l); \ + row4h = _mm_xor_si128(row4h, row1h); \ + \ + row4l = _mm_roti_epi64(row4l, -32); \ + row4h = _mm_roti_epi64(row4h, -32); \ + \ + row3l = _mm_add_epi64(row3l, row4l); \ + row3h = _mm_add_epi64(row3h, row4h); \ + \ + row2l = _mm_xor_si128(row2l, row3l); \ + row2h = _mm_xor_si128(row2h, row3h); \ + \ + row2l = _mm_roti_epi64(row2l, -24); \ + row2h = _mm_roti_epi64(row2h, -24); \ + +#define G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1) \ + row1l = _mm_add_epi64(_mm_add_epi64(row1l, b0), row2l); \ + row1h = _mm_add_epi64(_mm_add_epi64(row1h, b1), row2h); \ + \ + row4l = _mm_xor_si128(row4l, row1l); \ + row4h = _mm_xor_si128(row4h, row1h); \ + \ + row4l = _mm_roti_epi64(row4l, -16); \ + row4h = _mm_roti_epi64(row4h, -16); \ + \ + row3l = _mm_add_epi64(row3l, row4l); \ + row3h = _mm_add_epi64(row3h, row4h); \ + \ + row2l = _mm_xor_si128(row2l, row3l); \ + row2h = _mm_xor_si128(row2h, row3h); \ + \ + row2l = _mm_roti_epi64(row2l, -63); \ + row2h = _mm_roti_epi64(row2h, -63); \ + +#if defined(HAVE_SSSE3) +#define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \ + t0 = _mm_alignr_epi8(row2h, row2l, 8); \ + t1 = _mm_alignr_epi8(row2l, row2h, 8); \ + row2l = t0; \ + row2h = t1; \ + \ + t0 = row3l; \ + row3l = row3h; \ + row3h = t0; \ + \ + t0 = _mm_alignr_epi8(row4h, row4l, 8); \ + t1 = _mm_alignr_epi8(row4l, row4h, 8); \ + row4l = t1; \ + row4h = t0; + +#define UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \ + t0 = _mm_alignr_epi8(row2l, row2h, 8); \ + t1 = _mm_alignr_epi8(row2h, row2l, 8); \ + row2l = t0; \ + row2h = t1; \ + \ + t0 = row3l; \ + row3l = row3h; \ + row3h = t0; \ + \ + t0 = _mm_alignr_epi8(row4l, row4h, 8); \ + t1 = _mm_alignr_epi8(row4h, row4l, 8); \ + row4l = t1; \ + row4h = t0; +#else + +#define DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \ + t0 = row4l;\ + t1 = row2l;\ + row4l = row3l;\ + row3l = row3h;\ + row3h = row4l;\ + row4l = _mm_unpackhi_epi64(row4h, _mm_unpacklo_epi64(t0, t0)); \ + row4h = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(row4h, row4h)); \ + row2l = _mm_unpackhi_epi64(row2l, _mm_unpacklo_epi64(row2h, row2h)); \ + row2h = _mm_unpackhi_epi64(row2h, _mm_unpacklo_epi64(t1, t1)) + +#define UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h) \ + t0 = row3l;\ + row3l = row3h;\ + row3h = t0;\ + t0 = row2l;\ + t1 = row4l;\ + row2l = _mm_unpackhi_epi64(row2h, _mm_unpacklo_epi64(row2l, row2l)); \ + row2h = _mm_unpackhi_epi64(t0, _mm_unpacklo_epi64(row2h, row2h)); \ + row4l = _mm_unpackhi_epi64(row4l, _mm_unpacklo_epi64(row4h, row4h)); \ + row4h = _mm_unpackhi_epi64(row4h, _mm_unpacklo_epi64(t1, t1)) + +#endif + +#if defined(HAVE_SSE41) +#include "blake2b-load-sse41.h" +#else +#include "blake2b-load-sse2.h" +#endif + +#define ROUND(r) \ + LOAD_MSG_ ##r ##_1(b0, b1); \ + G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \ + LOAD_MSG_ ##r ##_2(b0, b1); \ + G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \ + DIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h); \ + LOAD_MSG_ ##r ##_3(b0, b1); \ + G1(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \ + LOAD_MSG_ ##r ##_4(b0, b1); \ + G2(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h,b0,b1); \ + UNDIAGONALIZE(row1l,row2l,row3l,row4l,row1h,row2h,row3h,row4h); + +#endif diff --git a/src/third_party/blake2/blake2b.c b/src/third_party/blake2/blake2b.c index c8c1c5f1..52c61de4 100644 --- a/src/third_party/blake2/blake2b.c +++ b/src/third_party/blake2/blake2b.c @@ -1,373 +1,373 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#include "blake2.h" -#include "blake2-impl.h" - -#include "blake2-config.h" - -#ifdef _MSC_VER -#include /* for _mm_set_epi64x */ -#endif -#include -#if defined(HAVE_SSSE3) -#include -#endif -#if defined(HAVE_SSE41) -#include -#endif -#if defined(HAVE_AVX) -#include -#endif -#if defined(HAVE_XOP) -#include -#endif - -#include "blake2b-round.h" - -static const uint64_t blake2b_IV[8] = -{ - 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, - 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, - 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, - 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL -}; - -/* Some helper functions */ -static void blake2b_set_lastnode( blake2b_state *S ) -{ - S->f[1] = (uint64_t)-1; -} - -static int blake2b_is_lastblock( const blake2b_state *S ) -{ - return S->f[0] != 0; -} - -static void blake2b_set_lastblock( blake2b_state *S ) -{ - if( S->last_node ) blake2b_set_lastnode( S ); - - S->f[0] = (uint64_t)-1; -} - -static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) -{ - S->t[0] += inc; - S->t[1] += ( S->t[0] < inc ); -} - -/* init xors IV with input parameter block */ -int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) -{ - size_t i; - /*blake2b_init0( S ); */ - const unsigned char * v = ( const unsigned char * )( blake2b_IV ); - const unsigned char * p = ( const unsigned char * )( P ); - unsigned char * h = ( unsigned char * )( S->h ); - /* IV XOR ParamBlock */ - memset( S, 0, sizeof( blake2b_state ) ); - - for( i = 0; i < BLAKE2B_OUTBYTES; ++i ) h[i] = v[i] ^ p[i]; - - S->outlen = P->digest_length; - return 0; -} - - -/* Some sort of default parameter block initialization, for sequential blake2b */ -int blake2b_init( blake2b_state *S, size_t outlen ) -{ - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = 0; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - return blake2b_init_param( S, P ); -} - -int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) -{ - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - if ( ( !keylen ) || keylen > BLAKE2B_KEYBYTES ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - if( blake2b_init_param( S, P ) < 0 ) - return 0; - - { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset( block, 0, BLAKE2B_BLOCKBYTES ); - memcpy( block, key, keylen ); - blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - -static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) -{ - __m128i row1l, row1h; - __m128i row2l, row2h; - __m128i row3l, row3h; - __m128i row4l, row4h; - __m128i b0, b1; - __m128i t0, t1; -#if defined(HAVE_SSSE3) && !defined(HAVE_XOP) - const __m128i r16 = _mm_setr_epi8( 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9 ); - const __m128i r24 = _mm_setr_epi8( 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10 ); -#endif -#if defined(HAVE_SSE41) - const __m128i m0 = LOADU( block + 00 ); - const __m128i m1 = LOADU( block + 16 ); - const __m128i m2 = LOADU( block + 32 ); - const __m128i m3 = LOADU( block + 48 ); - const __m128i m4 = LOADU( block + 64 ); - const __m128i m5 = LOADU( block + 80 ); - const __m128i m6 = LOADU( block + 96 ); - const __m128i m7 = LOADU( block + 112 ); -#else - const uint64_t m0 = load64(block + 0 * sizeof(uint64_t)); - const uint64_t m1 = load64(block + 1 * sizeof(uint64_t)); - const uint64_t m2 = load64(block + 2 * sizeof(uint64_t)); - const uint64_t m3 = load64(block + 3 * sizeof(uint64_t)); - const uint64_t m4 = load64(block + 4 * sizeof(uint64_t)); - const uint64_t m5 = load64(block + 5 * sizeof(uint64_t)); - const uint64_t m6 = load64(block + 6 * sizeof(uint64_t)); - const uint64_t m7 = load64(block + 7 * sizeof(uint64_t)); - const uint64_t m8 = load64(block + 8 * sizeof(uint64_t)); - const uint64_t m9 = load64(block + 9 * sizeof(uint64_t)); - const uint64_t m10 = load64(block + 10 * sizeof(uint64_t)); - const uint64_t m11 = load64(block + 11 * sizeof(uint64_t)); - const uint64_t m12 = load64(block + 12 * sizeof(uint64_t)); - const uint64_t m13 = load64(block + 13 * sizeof(uint64_t)); - const uint64_t m14 = load64(block + 14 * sizeof(uint64_t)); - const uint64_t m15 = load64(block + 15 * sizeof(uint64_t)); -#endif - row1l = LOADU( &S->h[0] ); - row1h = LOADU( &S->h[2] ); - row2l = LOADU( &S->h[4] ); - row2h = LOADU( &S->h[6] ); - row3l = LOADU( &blake2b_IV[0] ); - row3h = LOADU( &blake2b_IV[2] ); - row4l = _mm_xor_si128( LOADU( &blake2b_IV[4] ), LOADU( &S->t[0] ) ); - row4h = _mm_xor_si128( LOADU( &blake2b_IV[6] ), LOADU( &S->f[0] ) ); - ROUND( 0 ); - ROUND( 1 ); - ROUND( 2 ); - ROUND( 3 ); - ROUND( 4 ); - ROUND( 5 ); - ROUND( 6 ); - ROUND( 7 ); - ROUND( 8 ); - ROUND( 9 ); - ROUND( 10 ); - ROUND( 11 ); - row1l = _mm_xor_si128( row3l, row1l ); - row1h = _mm_xor_si128( row3h, row1h ); - STOREU( &S->h[0], _mm_xor_si128( LOADU( &S->h[0] ), row1l ) ); - STOREU( &S->h[2], _mm_xor_si128( LOADU( &S->h[2] ), row1h ) ); - row2l = _mm_xor_si128( row4l, row2l ); - row2h = _mm_xor_si128( row4h, row2h ); - STOREU( &S->h[4], _mm_xor_si128( LOADU( &S->h[4] ), row2l ) ); - STOREU( &S->h[6], _mm_xor_si128( LOADU( &S->h[6] ), row2h ) ); -} - - -int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - if( inlen > 0 ) - { - size_t left = S->buflen; - size_t fill = BLAKE2B_BLOCKBYTES - left; - if( inlen > fill ) - { - S->buflen = 0; - memcpy( S->buf + left, in, fill ); /* Fill buffer */ - blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); - blake2b_compress( S, S->buf ); /* Compress */ - in += fill; inlen -= fill; - while(inlen > BLAKE2B_BLOCKBYTES) { - blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); - blake2b_compress( S, in ); - in += BLAKE2B_BLOCKBYTES; - inlen -= BLAKE2B_BLOCKBYTES; - } - } - memcpy( S->buf + S->buflen, in, inlen ); - S->buflen += inlen; - } - return 0; -} - - -int blake2b_final( blake2b_state *S, void *out, size_t outlen ) -{ - if( out == NULL || outlen < S->outlen ) - return -1; - - if( blake2b_is_lastblock( S ) ) - return -1; - - blake2b_increment_counter( S, S->buflen ); - blake2b_set_lastblock( S ); - memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ - blake2b_compress( S, S->buf ); - - memcpy( out, &S->h[0], S->outlen ); - return 0; -} - - -int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - blake2b_state S[1]; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if( NULL == key && keylen > 0 ) return -1; - - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - if( keylen > BLAKE2B_KEYBYTES ) return -1; - - if( keylen ) - { - if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; - } - else - { - if( blake2b_init( S, outlen ) < 0 ) return -1; - } - - blake2b_update( S, ( const uint8_t * )in, inlen ); - blake2b_final( S, out, outlen ); - return 0; -} - -int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { - return blake2b(out, outlen, in, inlen, key, keylen); -} - -#if defined(SUPERCOP) -int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) -{ - return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); -} -#endif - -#if defined(BLAKE2B_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2B_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); - - if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2b_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2b_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +#include "blake2-config.h" + +#ifdef _MSC_VER +#include /* for _mm_set_epi64x */ +#endif +#include +#if defined(HAVE_SSSE3) +#include +#endif +#if defined(HAVE_SSE41) +#include +#endif +#if defined(HAVE_AVX) +#include +#endif +#if defined(HAVE_XOP) +#include +#endif + +#include "blake2b-round.h" + +static const uint64_t blake2b_IV[8] = +{ + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL +}; + +/* Some helper functions */ +static void blake2b_set_lastnode( blake2b_state *S ) +{ + S->f[1] = (uint64_t)-1; +} + +static int blake2b_is_lastblock( const blake2b_state *S ) +{ + return S->f[0] != 0; +} + +static void blake2b_set_lastblock( blake2b_state *S ) +{ + if( S->last_node ) blake2b_set_lastnode( S ); + + S->f[0] = (uint64_t)-1; +} + +static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + +/* init xors IV with input parameter block */ +int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) +{ + size_t i; + /*blake2b_init0( S ); */ + const unsigned char * v = ( const unsigned char * )( blake2b_IV ); + const unsigned char * p = ( const unsigned char * )( P ); + unsigned char * h = ( unsigned char * )( S->h ); + /* IV XOR ParamBlock */ + memset( S, 0, sizeof( blake2b_state ) ); + + for( i = 0; i < BLAKE2B_OUTBYTES; ++i ) h[i] = v[i] ^ p[i]; + + S->outlen = P->digest_length; + return 0; +} + + +/* Some sort of default parameter block initialization, for sequential blake2b */ +int blake2b_init( blake2b_state *S, size_t outlen ) +{ + blake2b_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + return blake2b_init_param( S, P ); +} + +int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) +{ + blake2b_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; + + if ( ( !keylen ) || keylen > BLAKE2B_KEYBYTES ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + if( blake2b_init_param( S, P ) < 0 ) + return 0; + + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset( block, 0, BLAKE2B_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); + secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + +static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) +{ + __m128i row1l, row1h; + __m128i row2l, row2h; + __m128i row3l, row3h; + __m128i row4l, row4h; + __m128i b0, b1; + __m128i t0, t1; +#if defined(HAVE_SSSE3) && !defined(HAVE_XOP) + const __m128i r16 = _mm_setr_epi8( 2, 3, 4, 5, 6, 7, 0, 1, 10, 11, 12, 13, 14, 15, 8, 9 ); + const __m128i r24 = _mm_setr_epi8( 3, 4, 5, 6, 7, 0, 1, 2, 11, 12, 13, 14, 15, 8, 9, 10 ); +#endif +#if defined(HAVE_SSE41) + const __m128i m0 = LOADU( block + 00 ); + const __m128i m1 = LOADU( block + 16 ); + const __m128i m2 = LOADU( block + 32 ); + const __m128i m3 = LOADU( block + 48 ); + const __m128i m4 = LOADU( block + 64 ); + const __m128i m5 = LOADU( block + 80 ); + const __m128i m6 = LOADU( block + 96 ); + const __m128i m7 = LOADU( block + 112 ); +#else + const uint64_t m0 = load64(block + 0 * sizeof(uint64_t)); + const uint64_t m1 = load64(block + 1 * sizeof(uint64_t)); + const uint64_t m2 = load64(block + 2 * sizeof(uint64_t)); + const uint64_t m3 = load64(block + 3 * sizeof(uint64_t)); + const uint64_t m4 = load64(block + 4 * sizeof(uint64_t)); + const uint64_t m5 = load64(block + 5 * sizeof(uint64_t)); + const uint64_t m6 = load64(block + 6 * sizeof(uint64_t)); + const uint64_t m7 = load64(block + 7 * sizeof(uint64_t)); + const uint64_t m8 = load64(block + 8 * sizeof(uint64_t)); + const uint64_t m9 = load64(block + 9 * sizeof(uint64_t)); + const uint64_t m10 = load64(block + 10 * sizeof(uint64_t)); + const uint64_t m11 = load64(block + 11 * sizeof(uint64_t)); + const uint64_t m12 = load64(block + 12 * sizeof(uint64_t)); + const uint64_t m13 = load64(block + 13 * sizeof(uint64_t)); + const uint64_t m14 = load64(block + 14 * sizeof(uint64_t)); + const uint64_t m15 = load64(block + 15 * sizeof(uint64_t)); +#endif + row1l = LOADU( &S->h[0] ); + row1h = LOADU( &S->h[2] ); + row2l = LOADU( &S->h[4] ); + row2h = LOADU( &S->h[6] ); + row3l = LOADU( &blake2b_IV[0] ); + row3h = LOADU( &blake2b_IV[2] ); + row4l = _mm_xor_si128( LOADU( &blake2b_IV[4] ), LOADU( &S->t[0] ) ); + row4h = _mm_xor_si128( LOADU( &blake2b_IV[6] ), LOADU( &S->f[0] ) ); + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + ROUND( 10 ); + ROUND( 11 ); + row1l = _mm_xor_si128( row3l, row1l ); + row1h = _mm_xor_si128( row3h, row1h ); + STOREU( &S->h[0], _mm_xor_si128( LOADU( &S->h[0] ), row1l ) ); + STOREU( &S->h[2], _mm_xor_si128( LOADU( &S->h[2] ), row1h ) ); + row2l = _mm_xor_si128( row4l, row2l ); + row2h = _mm_xor_si128( row4h, row2h ); + STOREU( &S->h[4], _mm_xor_si128( LOADU( &S->h[4] ), row2l ) ); + STOREU( &S->h[6], _mm_xor_si128( LOADU( &S->h[6] ), row2h ) ); +} + + +int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + if( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = BLAKE2B_BLOCKBYTES - left; + if( inlen > fill ) + { + S->buflen = 0; + memcpy( S->buf + left, in, fill ); /* Fill buffer */ + blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); + blake2b_compress( S, S->buf ); /* Compress */ + in += fill; inlen -= fill; + while(inlen > BLAKE2B_BLOCKBYTES) { + blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); + blake2b_compress( S, in ); + in += BLAKE2B_BLOCKBYTES; + inlen -= BLAKE2B_BLOCKBYTES; + } + } + memcpy( S->buf + S->buflen, in, inlen ); + S->buflen += inlen; + } + return 0; +} + + +int blake2b_final( blake2b_state *S, void *out, size_t outlen ) +{ + if( out == NULL || outlen < S->outlen ) + return -1; + + if( blake2b_is_lastblock( S ) ) + return -1; + + blake2b_increment_counter( S, S->buflen ); + blake2b_set_lastblock( S ); + memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ + blake2b_compress( S, S->buf ); + + memcpy( out, &S->h[0], S->outlen ); + return 0; +} + + +int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + blake2b_state S[1]; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if( NULL == key && keylen > 0 ) return -1; + + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + if( keylen > BLAKE2B_KEYBYTES ) return -1; + + if( keylen ) + { + if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; + } + else + { + if( blake2b_init( S, outlen ) < 0 ) return -1; + } + + blake2b_update( S, ( const uint8_t * )in, inlen ); + blake2b_final( S, out, outlen ); + return 0; +} + +int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { + return blake2b(out, outlen, in, inlen, key, keylen); +} + +#if defined(SUPERCOP) +int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) +{ + return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); +} +#endif + +#if defined(BLAKE2B_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2B_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); + + if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2b_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2b_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2b_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2bp-ref.c b/src/third_party/blake2/blake2bp-ref.c index d58a1529..052d2d07 100644 --- a/src/third_party/blake2/blake2bp-ref.c +++ b/src/third_party/blake2/blake2bp-ref.c @@ -1,359 +1,359 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include -#include - -#if defined(_OPENMP) -#include -#endif - -#include "blake2.h" -#include "blake2-impl.h" - -#define PARALLELISM_DEGREE 4 - -/* - blake2b_init_param defaults to setting the expecting output length - from the digest_length parameter block field. - - In some cases, however, we do not want this, as the output length - of these instances is given by inner_length instead. -*/ -static int blake2bp_init_leaf_param( blake2b_state *S, const blake2b_param *P ) -{ - int err = blake2b_init_param(S, P); - S->outlen = P->inner_length; - return err; -} - -static int blake2bp_init_leaf( blake2b_state *S, size_t outlen, size_t keylen, uint64_t offset ) -{ - blake2b_param P[1]; - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = PARALLELISM_DEGREE; - P->depth = 2; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, offset ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = BLAKE2B_OUTBYTES; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2bp_init_leaf_param( S, P ); -} - -static int blake2bp_init_root( blake2b_state *S, size_t outlen, size_t keylen ) -{ - blake2b_param P[1]; - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = PARALLELISM_DEGREE; - P->depth = 2; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 1; - P->inner_length = BLAKE2B_OUTBYTES; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2b_init_param( S, P ); -} - - -int blake2bp_init( blake2bp_state *S, size_t outlen ) -{ - size_t i; - - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - memset( S->buf, 0, sizeof( S->buf ) ); - S->buflen = 0; - S->outlen = outlen; - - if( blake2bp_init_root( S->R, outlen, 0 ) < 0 ) - return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2bp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1; - - S->R->last_node = 1; - S->S[PARALLELISM_DEGREE - 1]->last_node = 1; - return 0; -} - -int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ) -{ - size_t i; - - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - if( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; - - memset( S->buf, 0, sizeof( S->buf ) ); - S->buflen = 0; - S->outlen = outlen; - - if( blake2bp_init_root( S->R, outlen, keylen ) < 0 ) - return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2bp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1; - - S->R->last_node = 1; - S->S[PARALLELISM_DEGREE - 1]->last_node = 1; - { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset( block, 0, BLAKE2B_BLOCKBYTES ); - memcpy( block, key, keylen ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES ); - - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - - -int blake2bp_update( blake2bp_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - size_t left = S->buflen; - size_t fill = sizeof( S->buf ) - left; - size_t i; - - if( left && inlen >= fill ) - { - memcpy( S->buf + left, in, fill ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); - - in += fill; - inlen -= fill; - left = 0; - } - -#if defined(_OPENMP) - #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE) -#else - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) -#endif - { -#if defined(_OPENMP) - size_t i = omp_get_thread_num(); -#endif - size_t inlen__ = inlen; - const unsigned char *in__ = ( const unsigned char * )in; - in__ += i * BLAKE2B_BLOCKBYTES; - - while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ) - { - blake2b_update( S->S[i], in__, BLAKE2B_BLOCKBYTES ); - in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; - inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; - } - } - - in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ); - inlen %= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; - - if( inlen > 0 ) - memcpy( S->buf + left, in, inlen ); - - S->buflen = left + inlen; - return 0; -} - -int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ) -{ - uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES]; - size_t i; - - if(out == NULL || outlen < S->outlen) { - return -1; - } - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - { - if( S->buflen > i * BLAKE2B_BLOCKBYTES ) - { - size_t left = S->buflen - i * BLAKE2B_BLOCKBYTES; - - if( left > BLAKE2B_BLOCKBYTES ) left = BLAKE2B_BLOCKBYTES; - - blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left ); - } - - blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES ); - } - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES ); - - return blake2b_final( S->R, out, S->outlen ); -} - -int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES]; - blake2b_state S[PARALLELISM_DEGREE][1]; - blake2b_state FS[1]; - size_t i; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if( NULL == key && keylen > 0 ) return -1; - - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - if( keylen > BLAKE2B_KEYBYTES ) return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2bp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1; - - S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */ - - if( keylen > 0 ) - { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset( block, 0, BLAKE2B_BLOCKBYTES ); - memcpy( block, key, keylen ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES ); - - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ - } - -#if defined(_OPENMP) - #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE) -#else - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) -#endif - { -#if defined(_OPENMP) - size_t i = omp_get_thread_num(); -#endif - size_t inlen__ = inlen; - const unsigned char *in__ = ( const unsigned char * )in; - in__ += i * BLAKE2B_BLOCKBYTES; - - while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ) - { - blake2b_update( S[i], in__, BLAKE2B_BLOCKBYTES ); - in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; - inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; - } - - if( inlen__ > i * BLAKE2B_BLOCKBYTES ) - { - const size_t left = inlen__ - i * BLAKE2B_BLOCKBYTES; - const size_t len = left <= BLAKE2B_BLOCKBYTES ? left : BLAKE2B_BLOCKBYTES; - blake2b_update( S[i], in__, len ); - } - - blake2b_final( S[i], hash[i], BLAKE2B_OUTBYTES ); - } - - if( blake2bp_init_root( FS, outlen, keylen ) < 0 ) - return -1; - - FS->last_node = 1; /* Mark as last node */ - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES ); - - return blake2b_final( FS, out, outlen );; -} - -#if defined(BLAKE2BP_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2B_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2bp( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); - - if( 0 != memcmp( hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2bp_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2bp_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2bp_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2bp_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2bp_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include +#include + +#if defined(_OPENMP) +#include +#endif + +#include "blake2.h" +#include "blake2-impl.h" + +#define PARALLELISM_DEGREE 4 + +/* + blake2b_init_param defaults to setting the expecting output length + from the digest_length parameter block field. + + In some cases, however, we do not want this, as the output length + of these instances is given by inner_length instead. +*/ +static int blake2bp_init_leaf_param( blake2b_state *S, const blake2b_param *P ) +{ + int err = blake2b_init_param(S, P); + S->outlen = P->inner_length; + return err; +} + +static int blake2bp_init_leaf( blake2b_state *S, size_t outlen, size_t keylen, uint64_t offset ) +{ + blake2b_param P[1]; + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = PARALLELISM_DEGREE; + P->depth = 2; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, offset ); + store32( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = BLAKE2B_OUTBYTES; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2bp_init_leaf_param( S, P ); +} + +static int blake2bp_init_root( blake2b_state *S, size_t outlen, size_t keylen ) +{ + blake2b_param P[1]; + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = PARALLELISM_DEGREE; + P->depth = 2; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store32( &P->xof_length, 0 ); + P->node_depth = 1; + P->inner_length = BLAKE2B_OUTBYTES; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2b_init_param( S, P ); +} + + +int blake2bp_init( blake2bp_state *S, size_t outlen ) +{ + size_t i; + + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + S->outlen = outlen; + + if( blake2bp_init_root( S->R, outlen, 0 ) < 0 ) + return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2bp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1; + + S->R->last_node = 1; + S->S[PARALLELISM_DEGREE - 1]->last_node = 1; + return 0; +} + +int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ) +{ + size_t i; + + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + if( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; + + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + S->outlen = outlen; + + if( blake2bp_init_root( S->R, outlen, keylen ) < 0 ) + return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2bp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1; + + S->R->last_node = 1; + S->S[PARALLELISM_DEGREE - 1]->last_node = 1; + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset( block, 0, BLAKE2B_BLOCKBYTES ); + memcpy( block, key, keylen ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES ); + + secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + + +int blake2bp_update( blake2bp_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + size_t left = S->buflen; + size_t fill = sizeof( S->buf ) - left; + size_t i; + + if( left && inlen >= fill ) + { + memcpy( S->buf + left, in, fill ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); + + in += fill; + inlen -= fill; + left = 0; + } + +#if defined(_OPENMP) + #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE) +#else + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) +#endif + { +#if defined(_OPENMP) + size_t i = omp_get_thread_num(); +#endif + size_t inlen__ = inlen; + const unsigned char *in__ = ( const unsigned char * )in; + in__ += i * BLAKE2B_BLOCKBYTES; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ) + { + blake2b_update( S->S[i], in__, BLAKE2B_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; + } + } + + in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ); + inlen %= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; + + if( inlen > 0 ) + memcpy( S->buf + left, in, inlen ); + + S->buflen = left + inlen; + return 0; +} + +int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ) +{ + uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES]; + size_t i; + + if(out == NULL || outlen < S->outlen) { + return -1; + } + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + { + if( S->buflen > i * BLAKE2B_BLOCKBYTES ) + { + size_t left = S->buflen - i * BLAKE2B_BLOCKBYTES; + + if( left > BLAKE2B_BLOCKBYTES ) left = BLAKE2B_BLOCKBYTES; + + blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left ); + } + + blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES ); + } + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES ); + + return blake2b_final( S->R, out, S->outlen ); +} + +int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES]; + blake2b_state S[PARALLELISM_DEGREE][1]; + blake2b_state FS[1]; + size_t i; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if( NULL == key && keylen > 0 ) return -1; + + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + if( keylen > BLAKE2B_KEYBYTES ) return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2bp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1; + + S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */ + + if( keylen > 0 ) + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset( block, 0, BLAKE2B_BLOCKBYTES ); + memcpy( block, key, keylen ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES ); + + secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ + } + +#if defined(_OPENMP) + #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE) +#else + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) +#endif + { +#if defined(_OPENMP) + size_t i = omp_get_thread_num(); +#endif + size_t inlen__ = inlen; + const unsigned char *in__ = ( const unsigned char * )in; + in__ += i * BLAKE2B_BLOCKBYTES; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ) + { + blake2b_update( S[i], in__, BLAKE2B_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; + } + + if( inlen__ > i * BLAKE2B_BLOCKBYTES ) + { + const size_t left = inlen__ - i * BLAKE2B_BLOCKBYTES; + const size_t len = left <= BLAKE2B_BLOCKBYTES ? left : BLAKE2B_BLOCKBYTES; + blake2b_update( S[i], in__, len ); + } + + blake2b_final( S[i], hash[i], BLAKE2B_OUTBYTES ); + } + + if( blake2bp_init_root( FS, outlen, keylen ) < 0 ) + return -1; + + FS->last_node = 1; /* Mark as last node */ + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES ); + + return blake2b_final( FS, out, outlen );; +} + +#if defined(BLAKE2BP_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2B_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2bp( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); + + if( 0 != memcmp( hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2bp_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2bp_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2bp_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2bp_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2bp_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2bp.c b/src/third_party/blake2/blake2bp.c index 3eb95d0b..d825502a 100644 --- a/src/third_party/blake2/blake2bp.c +++ b/src/third_party/blake2/blake2bp.c @@ -1,361 +1,361 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include -#include - -#if defined(_OPENMP) -#include -#endif - -#include "blake2.h" -#include "blake2-impl.h" - -#define PARALLELISM_DEGREE 4 - -/* - blake2b_init_param defaults to setting the expecting output length - from the digest_length parameter block field. - - In some cases, however, we do not want this, as the output length - of these instances is given by inner_length instead. -*/ -static int blake2bp_init_leaf_param( blake2b_state *S, const blake2b_param *P ) -{ - int err = blake2b_init_param(S, P); - S->outlen = P->inner_length; - return err; -} - -static int blake2bp_init_leaf( blake2b_state *S, size_t outlen, size_t keylen, uint64_t offset ) -{ - blake2b_param P[1]; - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = PARALLELISM_DEGREE; - P->depth = 2; - P->leaf_length = 0; - P->node_offset = offset; - P->xof_length = 0; - P->node_depth = 0; - P->inner_length = BLAKE2B_OUTBYTES; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2bp_init_leaf_param( S, P ); -} - -static int blake2bp_init_root( blake2b_state *S, size_t outlen, size_t keylen ) -{ - blake2b_param P[1]; - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = PARALLELISM_DEGREE; - P->depth = 2; - P->leaf_length = 0; - P->node_offset = 0; - P->xof_length = 0; - P->node_depth = 1; - P->inner_length = BLAKE2B_OUTBYTES; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2b_init_param( S, P ); -} - - -int blake2bp_init( blake2bp_state *S, size_t outlen ) -{ - size_t i; - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - memset( S->buf, 0, sizeof( S->buf ) ); - S->buflen = 0; - S->outlen = outlen; - - if( blake2bp_init_root( S->R, outlen, 0 ) < 0 ) - return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2bp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1; - - S->R->last_node = 1; - S->S[PARALLELISM_DEGREE - 1]->last_node = 1; - return 0; -} - -int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ) -{ - size_t i; - - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - if( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; - - memset( S->buf, 0, sizeof( S->buf ) ); - S->buflen = 0; - S->outlen = outlen; - - if( blake2bp_init_root( S->R, outlen, keylen ) < 0 ) - return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2bp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1; - - S->R->last_node = 1; - S->S[PARALLELISM_DEGREE - 1]->last_node = 1; - { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset( block, 0, BLAKE2B_BLOCKBYTES ); - memcpy( block, key, keylen ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES ); - - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - - -int blake2bp_update( blake2bp_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - size_t left = S->buflen; - size_t fill = sizeof( S->buf ) - left; - size_t i; - - if( left && inlen >= fill ) - { - memcpy( S->buf + left, in, fill ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); - - in += fill; - inlen -= fill; - left = 0; - } - -#if defined(_OPENMP) - #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE) -#else - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) -#endif - { -#if defined(_OPENMP) - size_t i = omp_get_thread_num(); -#endif - size_t inlen__ = inlen; - const unsigned char *in__ = ( const unsigned char * )in; - in__ += i * BLAKE2B_BLOCKBYTES; - - while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ) - { - blake2b_update( S->S[i], in__, BLAKE2B_BLOCKBYTES ); - in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; - inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; - } - } - - in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ); - inlen %= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; - - if( inlen > 0 ) - memcpy( S->buf + left, in, inlen ); - - S->buflen = left + inlen; - return 0; -} - - - -int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ) -{ - uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES]; - size_t i; - - if(out == NULL || outlen < S->outlen) { - return -1; - } - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - { - if( S->buflen > i * BLAKE2B_BLOCKBYTES ) - { - size_t left = S->buflen - i * BLAKE2B_BLOCKBYTES; - - if( left > BLAKE2B_BLOCKBYTES ) left = BLAKE2B_BLOCKBYTES; - - blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left ); - } - - blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES ); - } - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES ); - - return blake2b_final( S->R, out, S->outlen ); -} - -int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES]; - blake2b_state S[PARALLELISM_DEGREE][1]; - blake2b_state FS[1]; - size_t i; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if( NULL == key && keylen > 0 ) return -1; - - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - if( keylen > BLAKE2B_KEYBYTES ) return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2bp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1; - - S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */ - - if( keylen > 0 ) - { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset( block, 0, BLAKE2B_BLOCKBYTES ); - memcpy( block, key, keylen ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES ); - - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ - } - -#if defined(_OPENMP) - #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE) -#else - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) -#endif - { -#if defined(_OPENMP) - size_t i = omp_get_thread_num(); -#endif - size_t inlen__ = inlen; - const unsigned char *in__ = ( const unsigned char * )in; - in__ += i * BLAKE2B_BLOCKBYTES; - - while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ) - { - blake2b_update( S[i], in__, BLAKE2B_BLOCKBYTES ); - in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; - inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; - } - - if( inlen__ > i * BLAKE2B_BLOCKBYTES ) - { - const size_t left = inlen__ - i * BLAKE2B_BLOCKBYTES; - const size_t len = left <= BLAKE2B_BLOCKBYTES ? left : BLAKE2B_BLOCKBYTES; - blake2b_update( S[i], in__, len ); - } - - blake2b_final( S[i], hash[i], BLAKE2B_OUTBYTES ); - } - - if( blake2bp_init_root( FS, outlen, keylen ) < 0 ) - return -1; - - FS->last_node = 1; /* Mark as last node */ - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES ); - - return blake2b_final( FS, out, outlen ); -} - - -#if defined(BLAKE2BP_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2B_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2bp( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); - - if( 0 != memcmp( hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2bp_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2bp_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2bp_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2bp_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2bp_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include +#include + +#if defined(_OPENMP) +#include +#endif + +#include "blake2.h" +#include "blake2-impl.h" + +#define PARALLELISM_DEGREE 4 + +/* + blake2b_init_param defaults to setting the expecting output length + from the digest_length parameter block field. + + In some cases, however, we do not want this, as the output length + of these instances is given by inner_length instead. +*/ +static int blake2bp_init_leaf_param( blake2b_state *S, const blake2b_param *P ) +{ + int err = blake2b_init_param(S, P); + S->outlen = P->inner_length; + return err; +} + +static int blake2bp_init_leaf( blake2b_state *S, size_t outlen, size_t keylen, uint64_t offset ) +{ + blake2b_param P[1]; + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = PARALLELISM_DEGREE; + P->depth = 2; + P->leaf_length = 0; + P->node_offset = offset; + P->xof_length = 0; + P->node_depth = 0; + P->inner_length = BLAKE2B_OUTBYTES; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2bp_init_leaf_param( S, P ); +} + +static int blake2bp_init_root( blake2b_state *S, size_t outlen, size_t keylen ) +{ + blake2b_param P[1]; + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = PARALLELISM_DEGREE; + P->depth = 2; + P->leaf_length = 0; + P->node_offset = 0; + P->xof_length = 0; + P->node_depth = 1; + P->inner_length = BLAKE2B_OUTBYTES; + memset( P->reserved, 0, sizeof( P->reserved ) ); + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2b_init_param( S, P ); +} + + +int blake2bp_init( blake2bp_state *S, size_t outlen ) +{ + size_t i; + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + S->outlen = outlen; + + if( blake2bp_init_root( S->R, outlen, 0 ) < 0 ) + return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2bp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1; + + S->R->last_node = 1; + S->S[PARALLELISM_DEGREE - 1]->last_node = 1; + return 0; +} + +int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ) +{ + size_t i; + + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + if( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; + + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + S->outlen = outlen; + + if( blake2bp_init_root( S->R, outlen, keylen ) < 0 ) + return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2bp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1; + + S->R->last_node = 1; + S->S[PARALLELISM_DEGREE - 1]->last_node = 1; + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset( block, 0, BLAKE2B_BLOCKBYTES ); + memcpy( block, key, keylen ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2b_update( S->S[i], block, BLAKE2B_BLOCKBYTES ); + + secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + + +int blake2bp_update( blake2bp_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + size_t left = S->buflen; + size_t fill = sizeof( S->buf ) - left; + size_t i; + + if( left && inlen >= fill ) + { + memcpy( S->buf + left, in, fill ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, BLAKE2B_BLOCKBYTES ); + + in += fill; + inlen -= fill; + left = 0; + } + +#if defined(_OPENMP) + #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE) +#else + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) +#endif + { +#if defined(_OPENMP) + size_t i = omp_get_thread_num(); +#endif + size_t inlen__ = inlen; + const unsigned char *in__ = ( const unsigned char * )in; + in__ += i * BLAKE2B_BLOCKBYTES; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ) + { + blake2b_update( S->S[i], in__, BLAKE2B_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; + } + } + + in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ); + inlen %= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; + + if( inlen > 0 ) + memcpy( S->buf + left, in, inlen ); + + S->buflen = left + inlen; + return 0; +} + + + +int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ) +{ + uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES]; + size_t i; + + if(out == NULL || outlen < S->outlen) { + return -1; + } + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + { + if( S->buflen > i * BLAKE2B_BLOCKBYTES ) + { + size_t left = S->buflen - i * BLAKE2B_BLOCKBYTES; + + if( left > BLAKE2B_BLOCKBYTES ) left = BLAKE2B_BLOCKBYTES; + + blake2b_update( S->S[i], S->buf + i * BLAKE2B_BLOCKBYTES, left ); + } + + blake2b_final( S->S[i], hash[i], BLAKE2B_OUTBYTES ); + } + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2b_update( S->R, hash[i], BLAKE2B_OUTBYTES ); + + return blake2b_final( S->R, out, S->outlen ); +} + +int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + uint8_t hash[PARALLELISM_DEGREE][BLAKE2B_OUTBYTES]; + blake2b_state S[PARALLELISM_DEGREE][1]; + blake2b_state FS[1]; + size_t i; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if( NULL == key && keylen > 0 ) return -1; + + if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; + + if( keylen > BLAKE2B_KEYBYTES ) return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2bp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1; + + S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */ + + if( keylen > 0 ) + { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset( block, 0, BLAKE2B_BLOCKBYTES ); + memcpy( block, key, keylen ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2b_update( S[i], block, BLAKE2B_BLOCKBYTES ); + + secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ + } + +#if defined(_OPENMP) + #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE) +#else + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) +#endif + { +#if defined(_OPENMP) + size_t i = omp_get_thread_num(); +#endif + size_t inlen__ = inlen; + const unsigned char *in__ = ( const unsigned char * )in; + in__ += i * BLAKE2B_BLOCKBYTES; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES ) + { + blake2b_update( S[i], in__, BLAKE2B_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2B_BLOCKBYTES; + } + + if( inlen__ > i * BLAKE2B_BLOCKBYTES ) + { + const size_t left = inlen__ - i * BLAKE2B_BLOCKBYTES; + const size_t len = left <= BLAKE2B_BLOCKBYTES ? left : BLAKE2B_BLOCKBYTES; + blake2b_update( S[i], in__, len ); + } + + blake2b_final( S[i], hash[i], BLAKE2B_OUTBYTES ); + } + + if( blake2bp_init_root( FS, outlen, keylen ) < 0 ) + return -1; + + FS->last_node = 1; /* Mark as last node */ + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2b_update( FS, hash[i], BLAKE2B_OUTBYTES ); + + return blake2b_final( FS, out, outlen ); +} + + +#if defined(BLAKE2BP_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2B_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2bp( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); + + if( 0 != memcmp( hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2B_OUTBYTES]; + blake2bp_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2bp_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2bp_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2bp_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2bp_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2bp_keyed_kat[i], BLAKE2B_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2s-load-sse2.h b/src/third_party/blake2/blake2s-load-sse2.h index 8359e81a..7a639894 100644 --- a/src/third_party/blake2/blake2s-load-sse2.h +++ b/src/third_party/blake2/blake2s-load-sse2.h @@ -1,60 +1,60 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2S_LOAD_SSE2_H -#define BLAKE2S_LOAD_SSE2_H - -#define LOAD_MSG_0_1(buf) buf = _mm_set_epi32(m6,m4,m2,m0) -#define LOAD_MSG_0_2(buf) buf = _mm_set_epi32(m7,m5,m3,m1) -#define LOAD_MSG_0_3(buf) buf = _mm_set_epi32(m12,m10,m8,m14) -#define LOAD_MSG_0_4(buf) buf = _mm_set_epi32(m13,m11,m9,m15) -#define LOAD_MSG_1_1(buf) buf = _mm_set_epi32(m13,m9,m4,m14) -#define LOAD_MSG_1_2(buf) buf = _mm_set_epi32(m6,m15,m8,m10) -#define LOAD_MSG_1_3(buf) buf = _mm_set_epi32(m11,m0,m1,m5) -#define LOAD_MSG_1_4(buf) buf = _mm_set_epi32(m7,m2,m12,m3) -#define LOAD_MSG_2_1(buf) buf = _mm_set_epi32(m15,m5,m12,m11) -#define LOAD_MSG_2_2(buf) buf = _mm_set_epi32(m13,m2,m0,m8) -#define LOAD_MSG_2_3(buf) buf = _mm_set_epi32(m7,m3,m10,m9) -#define LOAD_MSG_2_4(buf) buf = _mm_set_epi32(m1,m6,m14,m4) -#define LOAD_MSG_3_1(buf) buf = _mm_set_epi32(m11,m13,m3,m7) -#define LOAD_MSG_3_2(buf) buf = _mm_set_epi32(m14,m12,m1,m9) -#define LOAD_MSG_3_3(buf) buf = _mm_set_epi32(m4,m5,m2,m15) -#define LOAD_MSG_3_4(buf) buf = _mm_set_epi32(m0,m10,m6,m8) -#define LOAD_MSG_4_1(buf) buf = _mm_set_epi32(m10,m2,m5,m9) -#define LOAD_MSG_4_2(buf) buf = _mm_set_epi32(m15,m4,m7,m0) -#define LOAD_MSG_4_3(buf) buf = _mm_set_epi32(m6,m11,m14,m3) -#define LOAD_MSG_4_4(buf) buf = _mm_set_epi32(m8,m12,m1,m13) -#define LOAD_MSG_5_1(buf) buf = _mm_set_epi32(m8,m0,m6,m2) -#define LOAD_MSG_5_2(buf) buf = _mm_set_epi32(m3,m11,m10,m12) -#define LOAD_MSG_5_3(buf) buf = _mm_set_epi32(m15,m7,m4,m1) -#define LOAD_MSG_5_4(buf) buf = _mm_set_epi32(m14,m5,m13,m9) -#define LOAD_MSG_6_1(buf) buf = _mm_set_epi32(m4,m14,m1,m12) -#define LOAD_MSG_6_2(buf) buf = _mm_set_epi32(m10,m13,m15,m5) -#define LOAD_MSG_6_3(buf) buf = _mm_set_epi32(m9,m6,m0,m8) -#define LOAD_MSG_6_4(buf) buf = _mm_set_epi32(m2,m3,m7,m11) -#define LOAD_MSG_7_1(buf) buf = _mm_set_epi32(m3,m12,m7,m13) -#define LOAD_MSG_7_2(buf) buf = _mm_set_epi32(m9,m1,m14,m11) -#define LOAD_MSG_7_3(buf) buf = _mm_set_epi32(m8,m15,m5,m2) -#define LOAD_MSG_7_4(buf) buf = _mm_set_epi32(m6,m4,m0,m10) -#define LOAD_MSG_8_1(buf) buf = _mm_set_epi32(m0,m11,m14,m6) -#define LOAD_MSG_8_2(buf) buf = _mm_set_epi32(m8,m3,m9,m15) -#define LOAD_MSG_8_3(buf) buf = _mm_set_epi32(m1,m13,m12,m10) -#define LOAD_MSG_8_4(buf) buf = _mm_set_epi32(m4,m7,m2,m5) -#define LOAD_MSG_9_1(buf) buf = _mm_set_epi32(m1,m7,m8,m10) -#define LOAD_MSG_9_2(buf) buf = _mm_set_epi32(m5,m6,m4,m2) -#define LOAD_MSG_9_3(buf) buf = _mm_set_epi32(m3,m9,m15,m13) -#define LOAD_MSG_9_4(buf) buf = _mm_set_epi32(m12,m14,m11,m0) - - -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2S_LOAD_SSE2_H +#define BLAKE2S_LOAD_SSE2_H + +#define LOAD_MSG_0_1(buf) buf = _mm_set_epi32(m6,m4,m2,m0) +#define LOAD_MSG_0_2(buf) buf = _mm_set_epi32(m7,m5,m3,m1) +#define LOAD_MSG_0_3(buf) buf = _mm_set_epi32(m12,m10,m8,m14) +#define LOAD_MSG_0_4(buf) buf = _mm_set_epi32(m13,m11,m9,m15) +#define LOAD_MSG_1_1(buf) buf = _mm_set_epi32(m13,m9,m4,m14) +#define LOAD_MSG_1_2(buf) buf = _mm_set_epi32(m6,m15,m8,m10) +#define LOAD_MSG_1_3(buf) buf = _mm_set_epi32(m11,m0,m1,m5) +#define LOAD_MSG_1_4(buf) buf = _mm_set_epi32(m7,m2,m12,m3) +#define LOAD_MSG_2_1(buf) buf = _mm_set_epi32(m15,m5,m12,m11) +#define LOAD_MSG_2_2(buf) buf = _mm_set_epi32(m13,m2,m0,m8) +#define LOAD_MSG_2_3(buf) buf = _mm_set_epi32(m7,m3,m10,m9) +#define LOAD_MSG_2_4(buf) buf = _mm_set_epi32(m1,m6,m14,m4) +#define LOAD_MSG_3_1(buf) buf = _mm_set_epi32(m11,m13,m3,m7) +#define LOAD_MSG_3_2(buf) buf = _mm_set_epi32(m14,m12,m1,m9) +#define LOAD_MSG_3_3(buf) buf = _mm_set_epi32(m4,m5,m2,m15) +#define LOAD_MSG_3_4(buf) buf = _mm_set_epi32(m0,m10,m6,m8) +#define LOAD_MSG_4_1(buf) buf = _mm_set_epi32(m10,m2,m5,m9) +#define LOAD_MSG_4_2(buf) buf = _mm_set_epi32(m15,m4,m7,m0) +#define LOAD_MSG_4_3(buf) buf = _mm_set_epi32(m6,m11,m14,m3) +#define LOAD_MSG_4_4(buf) buf = _mm_set_epi32(m8,m12,m1,m13) +#define LOAD_MSG_5_1(buf) buf = _mm_set_epi32(m8,m0,m6,m2) +#define LOAD_MSG_5_2(buf) buf = _mm_set_epi32(m3,m11,m10,m12) +#define LOAD_MSG_5_3(buf) buf = _mm_set_epi32(m15,m7,m4,m1) +#define LOAD_MSG_5_4(buf) buf = _mm_set_epi32(m14,m5,m13,m9) +#define LOAD_MSG_6_1(buf) buf = _mm_set_epi32(m4,m14,m1,m12) +#define LOAD_MSG_6_2(buf) buf = _mm_set_epi32(m10,m13,m15,m5) +#define LOAD_MSG_6_3(buf) buf = _mm_set_epi32(m9,m6,m0,m8) +#define LOAD_MSG_6_4(buf) buf = _mm_set_epi32(m2,m3,m7,m11) +#define LOAD_MSG_7_1(buf) buf = _mm_set_epi32(m3,m12,m7,m13) +#define LOAD_MSG_7_2(buf) buf = _mm_set_epi32(m9,m1,m14,m11) +#define LOAD_MSG_7_3(buf) buf = _mm_set_epi32(m8,m15,m5,m2) +#define LOAD_MSG_7_4(buf) buf = _mm_set_epi32(m6,m4,m0,m10) +#define LOAD_MSG_8_1(buf) buf = _mm_set_epi32(m0,m11,m14,m6) +#define LOAD_MSG_8_2(buf) buf = _mm_set_epi32(m8,m3,m9,m15) +#define LOAD_MSG_8_3(buf) buf = _mm_set_epi32(m1,m13,m12,m10) +#define LOAD_MSG_8_4(buf) buf = _mm_set_epi32(m4,m7,m2,m5) +#define LOAD_MSG_9_1(buf) buf = _mm_set_epi32(m1,m7,m8,m10) +#define LOAD_MSG_9_2(buf) buf = _mm_set_epi32(m5,m6,m4,m2) +#define LOAD_MSG_9_3(buf) buf = _mm_set_epi32(m3,m9,m15,m13) +#define LOAD_MSG_9_4(buf) buf = _mm_set_epi32(m12,m14,m11,m0) + + +#endif diff --git a/src/third_party/blake2/blake2s-load-sse41.h b/src/third_party/blake2/blake2s-load-sse41.h index 8d2b6b12..a380e620 100644 --- a/src/third_party/blake2/blake2s-load-sse41.h +++ b/src/third_party/blake2/blake2s-load-sse41.h @@ -1,236 +1,236 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2S_LOAD_SSE41_H -#define BLAKE2S_LOAD_SSE41_H - -#define LOAD_MSG_0_1(buf) \ -buf = TOI(_mm_shuffle_ps(TOF(m0), TOF(m1), _MM_SHUFFLE(2,0,2,0))); - -#define LOAD_MSG_0_2(buf) \ -buf = TOI(_mm_shuffle_ps(TOF(m0), TOF(m1), _MM_SHUFFLE(3,1,3,1))); - -#define LOAD_MSG_0_3(buf) \ -t0 = _mm_shuffle_epi32(m2, _MM_SHUFFLE(3,2,0,1)); \ -t1 = _mm_shuffle_epi32(m3, _MM_SHUFFLE(0,1,3,2)); \ -buf = _mm_blend_epi16(t0, t1, 0xC3); - -#define LOAD_MSG_0_4(buf) \ -t0 = _mm_blend_epi16(t0, t1, 0x3C); \ -buf = _mm_shuffle_epi32(t0, _MM_SHUFFLE(2,3,0,1)); - -#define LOAD_MSG_1_1(buf) \ -t0 = _mm_blend_epi16(m1, m2, 0x0C); \ -t1 = _mm_slli_si128(m3, 4); \ -t2 = _mm_blend_epi16(t0, t1, 0xF0); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,0,3)); - -#define LOAD_MSG_1_2(buf) \ -t0 = _mm_shuffle_epi32(m2,_MM_SHUFFLE(0,0,2,0)); \ -t1 = _mm_blend_epi16(m1,m3,0xC0); \ -t2 = _mm_blend_epi16(t0, t1, 0xF0); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1)); - -#define LOAD_MSG_1_3(buf) \ -t0 = _mm_slli_si128(m1, 4); \ -t1 = _mm_blend_epi16(m2, t0, 0x30); \ -t2 = _mm_blend_epi16(m0, t1, 0xF0); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,0,1,2)); - -#define LOAD_MSG_1_4(buf) \ -t0 = _mm_unpackhi_epi32(m0,m1); \ -t1 = _mm_slli_si128(m3, 4); \ -t2 = _mm_blend_epi16(t0, t1, 0x0C); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,0,1,2)); - -#define LOAD_MSG_2_1(buf) \ -t0 = _mm_unpackhi_epi32(m2,m3); \ -t1 = _mm_blend_epi16(m3,m1,0x0C); \ -t2 = _mm_blend_epi16(t0, t1, 0x0F); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2)); - -#define LOAD_MSG_2_2(buf) \ -t0 = _mm_unpacklo_epi32(m2,m0); \ -t1 = _mm_blend_epi16(t0, m0, 0xF0); \ -t2 = _mm_slli_si128(m3, 8); \ -buf = _mm_blend_epi16(t1, t2, 0xC0); - -#define LOAD_MSG_2_3(buf) \ -t0 = _mm_blend_epi16(m0, m2, 0x3C); \ -t1 = _mm_srli_si128(m1, 12); \ -t2 = _mm_blend_epi16(t0,t1,0x03); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(0,3,2,1)); - -#define LOAD_MSG_2_4(buf) \ -t0 = _mm_slli_si128(m3, 4); \ -t1 = _mm_blend_epi16(m0, m1, 0x33); \ -t2 = _mm_blend_epi16(t1, t0, 0xC0); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,2,3,0)); - -#define LOAD_MSG_3_1(buf) \ -t0 = _mm_unpackhi_epi32(m0,m1); \ -t1 = _mm_unpackhi_epi32(t0, m2); \ -t2 = _mm_blend_epi16(t1, m3, 0x0C); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2)); - -#define LOAD_MSG_3_2(buf) \ -t0 = _mm_slli_si128(m2, 8); \ -t1 = _mm_blend_epi16(m3,m0,0x0C); \ -t2 = _mm_blend_epi16(t1, t0, 0xC0); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,1,3)); - -#define LOAD_MSG_3_3(buf) \ -t0 = _mm_blend_epi16(m0,m1,0x0F); \ -t1 = _mm_blend_epi16(t0, m3, 0xC0); \ -buf = _mm_shuffle_epi32(t1, _MM_SHUFFLE(0,1,2,3)); - -#define LOAD_MSG_3_4(buf) \ -t0 = _mm_alignr_epi8(m0, m1, 4); \ -buf = _mm_blend_epi16(t0, m2, 0x33); - -#define LOAD_MSG_4_1(buf) \ -t0 = _mm_unpacklo_epi64(m1,m2); \ -t1 = _mm_unpackhi_epi64(m0,m2); \ -t2 = _mm_blend_epi16(t0,t1,0x33); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,1,3)); - -#define LOAD_MSG_4_2(buf) \ -t0 = _mm_unpackhi_epi64(m1,m3); \ -t1 = _mm_unpacklo_epi64(m0,m1); \ -buf = _mm_blend_epi16(t0,t1,0x33); - -#define LOAD_MSG_4_3(buf) \ -t0 = _mm_unpackhi_epi64(m3,m1); \ -t1 = _mm_unpackhi_epi64(m2,m0); \ -t2 = _mm_blend_epi16(t1,t0,0x33); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,0,3)); - -#define LOAD_MSG_4_4(buf) \ -t0 = _mm_blend_epi16(m0,m2,0x03); \ -t1 = _mm_slli_si128(t0, 8); \ -t2 = _mm_blend_epi16(t1,m3,0x0F); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,3,1)); - -#define LOAD_MSG_5_1(buf) \ -t0 = _mm_unpackhi_epi32(m0,m1); \ -t1 = _mm_unpacklo_epi32(m0,m2); \ -buf = _mm_unpacklo_epi64(t0,t1); - -#define LOAD_MSG_5_2(buf) \ -t0 = _mm_srli_si128(m2, 4); \ -t1 = _mm_blend_epi16(m0,m3,0x03); \ -buf = _mm_blend_epi16(t1,t0,0x3C); - -#define LOAD_MSG_5_3(buf) \ -t0 = _mm_blend_epi16(m1,m0,0x0C); \ -t1 = _mm_srli_si128(m3, 4); \ -t2 = _mm_blend_epi16(t0,t1,0x30); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1)); - -#define LOAD_MSG_5_4(buf) \ -t0 = _mm_unpacklo_epi64(m2,m1); \ -t1 = _mm_shuffle_epi32(m3, _MM_SHUFFLE(2,0,1,0)); \ -t2 = _mm_srli_si128(t0, 4); \ -buf = _mm_blend_epi16(t1,t2,0x33); - -#define LOAD_MSG_6_1(buf) \ -t0 = _mm_slli_si128(m1, 12); \ -t1 = _mm_blend_epi16(m0,m3,0x33); \ -buf = _mm_blend_epi16(t1,t0,0xC0); - -#define LOAD_MSG_6_2(buf) \ -t0 = _mm_blend_epi16(m3,m2,0x30); \ -t1 = _mm_srli_si128(m1, 4); \ -t2 = _mm_blend_epi16(t0,t1,0x03); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,3,0)); - -#define LOAD_MSG_6_3(buf) \ -t0 = _mm_unpacklo_epi64(m0,m2); \ -t1 = _mm_srli_si128(m1, 4); \ -t2 = _mm_blend_epi16(t0,t1,0x0C); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2)); - -#define LOAD_MSG_6_4(buf) \ -t0 = _mm_unpackhi_epi32(m1,m2); \ -t1 = _mm_unpackhi_epi64(m0,t0); \ -buf = _mm_shuffle_epi32(t1, _MM_SHUFFLE(0,1,2,3)); - -#define LOAD_MSG_7_1(buf) \ -t0 = _mm_unpackhi_epi32(m0,m1); \ -t1 = _mm_blend_epi16(t0,m3,0x0F); \ -buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(2,0,3,1)); - -#define LOAD_MSG_7_2(buf) \ -t0 = _mm_blend_epi16(m2,m3,0x30); \ -t1 = _mm_srli_si128(m0,4); \ -t2 = _mm_blend_epi16(t0,t1,0x03); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,0,2,3)); - -#define LOAD_MSG_7_3(buf) \ -t0 = _mm_unpackhi_epi64(m0,m3); \ -t1 = _mm_unpacklo_epi64(m1,m2); \ -t2 = _mm_blend_epi16(t0,t1,0x3C); \ -buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(2,3,1,0)); - -#define LOAD_MSG_7_4(buf) \ -t0 = _mm_unpacklo_epi32(m0,m1); \ -t1 = _mm_unpackhi_epi32(m1,m2); \ -t2 = _mm_unpacklo_epi64(t0,t1); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,0,3)); - -#define LOAD_MSG_8_1(buf) \ -t0 = _mm_unpackhi_epi32(m1,m3); \ -t1 = _mm_unpacklo_epi64(t0,m0); \ -t2 = _mm_blend_epi16(t1,m2,0xC0); \ -buf = _mm_shufflehi_epi16(t2,_MM_SHUFFLE(1,0,3,2)); - -#define LOAD_MSG_8_2(buf) \ -t0 = _mm_unpackhi_epi32(m0,m3); \ -t1 = _mm_blend_epi16(m2,t0,0xF0); \ -buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(0,2,1,3)); - -#define LOAD_MSG_8_3(buf) \ -t0 = _mm_unpacklo_epi64(m0,m3); \ -t1 = _mm_srli_si128(m2,8); \ -t2 = _mm_blend_epi16(t0,t1,0x03); \ -buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,3,2,0)); - -#define LOAD_MSG_8_4(buf) \ -t0 = _mm_blend_epi16(m1,m0,0x30); \ -buf = _mm_shuffle_epi32(t0,_MM_SHUFFLE(0,3,2,1)); - -#define LOAD_MSG_9_1(buf) \ -t0 = _mm_blend_epi16(m0,m2,0x03); \ -t1 = _mm_blend_epi16(m1,m2,0x30); \ -t2 = _mm_blend_epi16(t1,t0,0x0F); \ -buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(1,3,0,2)); - -#define LOAD_MSG_9_2(buf) \ -t0 = _mm_slli_si128(m0,4); \ -t1 = _mm_blend_epi16(m1,t0,0xC0); \ -buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(1,2,0,3)); - -#define LOAD_MSG_9_3(buf) \ -t0 = _mm_unpackhi_epi32(m0,m3); \ -t1 = _mm_unpacklo_epi32(m2,m3); \ -t2 = _mm_unpackhi_epi64(t0,t1); \ -buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(0,2,1,3)); - -#define LOAD_MSG_9_4(buf) \ -t0 = _mm_blend_epi16(m3,m2,0xC0); \ -t1 = _mm_unpacklo_epi32(m0,m3); \ -t2 = _mm_blend_epi16(t0,t1,0x0F); \ -buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(1,2,3,0)); - -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2S_LOAD_SSE41_H +#define BLAKE2S_LOAD_SSE41_H + +#define LOAD_MSG_0_1(buf) \ +buf = TOI(_mm_shuffle_ps(TOF(m0), TOF(m1), _MM_SHUFFLE(2,0,2,0))); + +#define LOAD_MSG_0_2(buf) \ +buf = TOI(_mm_shuffle_ps(TOF(m0), TOF(m1), _MM_SHUFFLE(3,1,3,1))); + +#define LOAD_MSG_0_3(buf) \ +t0 = _mm_shuffle_epi32(m2, _MM_SHUFFLE(3,2,0,1)); \ +t1 = _mm_shuffle_epi32(m3, _MM_SHUFFLE(0,1,3,2)); \ +buf = _mm_blend_epi16(t0, t1, 0xC3); + +#define LOAD_MSG_0_4(buf) \ +t0 = _mm_blend_epi16(t0, t1, 0x3C); \ +buf = _mm_shuffle_epi32(t0, _MM_SHUFFLE(2,3,0,1)); + +#define LOAD_MSG_1_1(buf) \ +t0 = _mm_blend_epi16(m1, m2, 0x0C); \ +t1 = _mm_slli_si128(m3, 4); \ +t2 = _mm_blend_epi16(t0, t1, 0xF0); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,0,3)); + +#define LOAD_MSG_1_2(buf) \ +t0 = _mm_shuffle_epi32(m2,_MM_SHUFFLE(0,0,2,0)); \ +t1 = _mm_blend_epi16(m1,m3,0xC0); \ +t2 = _mm_blend_epi16(t0, t1, 0xF0); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1)); + +#define LOAD_MSG_1_3(buf) \ +t0 = _mm_slli_si128(m1, 4); \ +t1 = _mm_blend_epi16(m2, t0, 0x30); \ +t2 = _mm_blend_epi16(m0, t1, 0xF0); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,0,1,2)); + +#define LOAD_MSG_1_4(buf) \ +t0 = _mm_unpackhi_epi32(m0,m1); \ +t1 = _mm_slli_si128(m3, 4); \ +t2 = _mm_blend_epi16(t0, t1, 0x0C); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,0,1,2)); + +#define LOAD_MSG_2_1(buf) \ +t0 = _mm_unpackhi_epi32(m2,m3); \ +t1 = _mm_blend_epi16(m3,m1,0x0C); \ +t2 = _mm_blend_epi16(t0, t1, 0x0F); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2)); + +#define LOAD_MSG_2_2(buf) \ +t0 = _mm_unpacklo_epi32(m2,m0); \ +t1 = _mm_blend_epi16(t0, m0, 0xF0); \ +t2 = _mm_slli_si128(m3, 8); \ +buf = _mm_blend_epi16(t1, t2, 0xC0); + +#define LOAD_MSG_2_3(buf) \ +t0 = _mm_blend_epi16(m0, m2, 0x3C); \ +t1 = _mm_srli_si128(m1, 12); \ +t2 = _mm_blend_epi16(t0,t1,0x03); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(0,3,2,1)); + +#define LOAD_MSG_2_4(buf) \ +t0 = _mm_slli_si128(m3, 4); \ +t1 = _mm_blend_epi16(m0, m1, 0x33); \ +t2 = _mm_blend_epi16(t1, t0, 0xC0); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,2,3,0)); + +#define LOAD_MSG_3_1(buf) \ +t0 = _mm_unpackhi_epi32(m0,m1); \ +t1 = _mm_unpackhi_epi32(t0, m2); \ +t2 = _mm_blend_epi16(t1, m3, 0x0C); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2)); + +#define LOAD_MSG_3_2(buf) \ +t0 = _mm_slli_si128(m2, 8); \ +t1 = _mm_blend_epi16(m3,m0,0x0C); \ +t2 = _mm_blend_epi16(t1, t0, 0xC0); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,1,3)); + +#define LOAD_MSG_3_3(buf) \ +t0 = _mm_blend_epi16(m0,m1,0x0F); \ +t1 = _mm_blend_epi16(t0, m3, 0xC0); \ +buf = _mm_shuffle_epi32(t1, _MM_SHUFFLE(0,1,2,3)); + +#define LOAD_MSG_3_4(buf) \ +t0 = _mm_alignr_epi8(m0, m1, 4); \ +buf = _mm_blend_epi16(t0, m2, 0x33); + +#define LOAD_MSG_4_1(buf) \ +t0 = _mm_unpacklo_epi64(m1,m2); \ +t1 = _mm_unpackhi_epi64(m0,m2); \ +t2 = _mm_blend_epi16(t0,t1,0x33); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,1,3)); + +#define LOAD_MSG_4_2(buf) \ +t0 = _mm_unpackhi_epi64(m1,m3); \ +t1 = _mm_unpacklo_epi64(m0,m1); \ +buf = _mm_blend_epi16(t0,t1,0x33); + +#define LOAD_MSG_4_3(buf) \ +t0 = _mm_unpackhi_epi64(m3,m1); \ +t1 = _mm_unpackhi_epi64(m2,m0); \ +t2 = _mm_blend_epi16(t1,t0,0x33); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,0,3)); + +#define LOAD_MSG_4_4(buf) \ +t0 = _mm_blend_epi16(m0,m2,0x03); \ +t1 = _mm_slli_si128(t0, 8); \ +t2 = _mm_blend_epi16(t1,m3,0x0F); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,0,3,1)); + +#define LOAD_MSG_5_1(buf) \ +t0 = _mm_unpackhi_epi32(m0,m1); \ +t1 = _mm_unpacklo_epi32(m0,m2); \ +buf = _mm_unpacklo_epi64(t0,t1); + +#define LOAD_MSG_5_2(buf) \ +t0 = _mm_srli_si128(m2, 4); \ +t1 = _mm_blend_epi16(m0,m3,0x03); \ +buf = _mm_blend_epi16(t1,t0,0x3C); + +#define LOAD_MSG_5_3(buf) \ +t0 = _mm_blend_epi16(m1,m0,0x0C); \ +t1 = _mm_srli_si128(m3, 4); \ +t2 = _mm_blend_epi16(t0,t1,0x30); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,3,0,1)); + +#define LOAD_MSG_5_4(buf) \ +t0 = _mm_unpacklo_epi64(m2,m1); \ +t1 = _mm_shuffle_epi32(m3, _MM_SHUFFLE(2,0,1,0)); \ +t2 = _mm_srli_si128(t0, 4); \ +buf = _mm_blend_epi16(t1,t2,0x33); + +#define LOAD_MSG_6_1(buf) \ +t0 = _mm_slli_si128(m1, 12); \ +t1 = _mm_blend_epi16(m0,m3,0x33); \ +buf = _mm_blend_epi16(t1,t0,0xC0); + +#define LOAD_MSG_6_2(buf) \ +t0 = _mm_blend_epi16(m3,m2,0x30); \ +t1 = _mm_srli_si128(m1, 4); \ +t2 = _mm_blend_epi16(t0,t1,0x03); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,3,0)); + +#define LOAD_MSG_6_3(buf) \ +t0 = _mm_unpacklo_epi64(m0,m2); \ +t1 = _mm_srli_si128(m1, 4); \ +t2 = _mm_blend_epi16(t0,t1,0x0C); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(3,1,0,2)); + +#define LOAD_MSG_6_4(buf) \ +t0 = _mm_unpackhi_epi32(m1,m2); \ +t1 = _mm_unpackhi_epi64(m0,t0); \ +buf = _mm_shuffle_epi32(t1, _MM_SHUFFLE(0,1,2,3)); + +#define LOAD_MSG_7_1(buf) \ +t0 = _mm_unpackhi_epi32(m0,m1); \ +t1 = _mm_blend_epi16(t0,m3,0x0F); \ +buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(2,0,3,1)); + +#define LOAD_MSG_7_2(buf) \ +t0 = _mm_blend_epi16(m2,m3,0x30); \ +t1 = _mm_srli_si128(m0,4); \ +t2 = _mm_blend_epi16(t0,t1,0x03); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,0,2,3)); + +#define LOAD_MSG_7_3(buf) \ +t0 = _mm_unpackhi_epi64(m0,m3); \ +t1 = _mm_unpacklo_epi64(m1,m2); \ +t2 = _mm_blend_epi16(t0,t1,0x3C); \ +buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(2,3,1,0)); + +#define LOAD_MSG_7_4(buf) \ +t0 = _mm_unpacklo_epi32(m0,m1); \ +t1 = _mm_unpackhi_epi32(m1,m2); \ +t2 = _mm_unpacklo_epi64(t0,t1); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(2,1,0,3)); + +#define LOAD_MSG_8_1(buf) \ +t0 = _mm_unpackhi_epi32(m1,m3); \ +t1 = _mm_unpacklo_epi64(t0,m0); \ +t2 = _mm_blend_epi16(t1,m2,0xC0); \ +buf = _mm_shufflehi_epi16(t2,_MM_SHUFFLE(1,0,3,2)); + +#define LOAD_MSG_8_2(buf) \ +t0 = _mm_unpackhi_epi32(m0,m3); \ +t1 = _mm_blend_epi16(m2,t0,0xF0); \ +buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(0,2,1,3)); + +#define LOAD_MSG_8_3(buf) \ +t0 = _mm_unpacklo_epi64(m0,m3); \ +t1 = _mm_srli_si128(m2,8); \ +t2 = _mm_blend_epi16(t0,t1,0x03); \ +buf = _mm_shuffle_epi32(t2, _MM_SHUFFLE(1,3,2,0)); + +#define LOAD_MSG_8_4(buf) \ +t0 = _mm_blend_epi16(m1,m0,0x30); \ +buf = _mm_shuffle_epi32(t0,_MM_SHUFFLE(0,3,2,1)); + +#define LOAD_MSG_9_1(buf) \ +t0 = _mm_blend_epi16(m0,m2,0x03); \ +t1 = _mm_blend_epi16(m1,m2,0x30); \ +t2 = _mm_blend_epi16(t1,t0,0x0F); \ +buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(1,3,0,2)); + +#define LOAD_MSG_9_2(buf) \ +t0 = _mm_slli_si128(m0,4); \ +t1 = _mm_blend_epi16(m1,t0,0xC0); \ +buf = _mm_shuffle_epi32(t1,_MM_SHUFFLE(1,2,0,3)); + +#define LOAD_MSG_9_3(buf) \ +t0 = _mm_unpackhi_epi32(m0,m3); \ +t1 = _mm_unpacklo_epi32(m2,m3); \ +t2 = _mm_unpackhi_epi64(t0,t1); \ +buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(0,2,1,3)); + +#define LOAD_MSG_9_4(buf) \ +t0 = _mm_blend_epi16(m3,m2,0xC0); \ +t1 = _mm_unpacklo_epi32(m0,m3); \ +t2 = _mm_blend_epi16(t0,t1,0x0F); \ +buf = _mm_shuffle_epi32(t2,_MM_SHUFFLE(1,2,3,0)); + +#endif diff --git a/src/third_party/blake2/blake2s-load-xop.h b/src/third_party/blake2/blake2s-load-xop.h index 426edc16..183ab667 100644 --- a/src/third_party/blake2/blake2s-load-xop.h +++ b/src/third_party/blake2/blake2s-load-xop.h @@ -1,191 +1,191 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2S_LOAD_XOP_H -#define BLAKE2S_LOAD_XOP_H - -#define TOB(x) ((x)*4*0x01010101 + 0x03020100) /* ..or not TOB */ - -#if 0 -/* Basic VPPERM emulation, for testing purposes */ -static __m128i _mm_perm_epi8(const __m128i src1, const __m128i src2, const __m128i sel) -{ - const __m128i sixteen = _mm_set1_epi8(16); - const __m128i t0 = _mm_shuffle_epi8(src1, sel); - const __m128i s1 = _mm_shuffle_epi8(src2, _mm_sub_epi8(sel, sixteen)); - const __m128i mask = _mm_or_si128(_mm_cmpeq_epi8(sel, sixteen), - _mm_cmpgt_epi8(sel, sixteen)); /* (>=16) = 0xff : 00 */ - return _mm_blendv_epi8(t0, s1, mask); -} -#endif - -#define LOAD_MSG_0_1(buf) \ -buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(6),TOB(4),TOB(2),TOB(0)) ); - -#define LOAD_MSG_0_2(buf) \ -buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(7),TOB(5),TOB(3),TOB(1)) ); - -#define LOAD_MSG_0_3(buf) \ -buf = _mm_perm_epi8(m2, m3, _mm_set_epi32(TOB(4),TOB(2),TOB(0),TOB(6)) ); - -#define LOAD_MSG_0_4(buf) \ -buf = _mm_perm_epi8(m2, m3, _mm_set_epi32(TOB(5),TOB(3),TOB(1),TOB(7)) ); - -#define LOAD_MSG_1_1(buf) \ -t0 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(0),TOB(5),TOB(0),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(6)) ); - -#define LOAD_MSG_1_2(buf) \ -t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(2),TOB(0),TOB(4),TOB(6)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) ); - -#define LOAD_MSG_1_3(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(0),TOB(0),TOB(1)) ); \ -buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(7),TOB(1),TOB(0),TOB(3)) ); - -#define LOAD_MSG_1_4(buf) \ -t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(7),TOB(2),TOB(0)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(2),TOB(1),TOB(4),TOB(3)) ); - -#define LOAD_MSG_2_1(buf) \ -t0 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(0),TOB(1),TOB(0),TOB(7)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(4),TOB(0)) ); - -#define LOAD_MSG_2_2(buf) \ -t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(2),TOB(0),TOB(4)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(0)) ); - -#define LOAD_MSG_2_3(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(7),TOB(3),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(2),TOB(1),TOB(6),TOB(5)) ); - -#define LOAD_MSG_2_4(buf) \ -t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(1),TOB(6),TOB(0)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(2),TOB(1),TOB(6),TOB(3)) ); - -#define LOAD_MSG_3_1(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(3),TOB(7)) ); \ -t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(5),TOB(1),TOB(0)) ); - -#define LOAD_MSG_3_2(buf) \ -t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(0),TOB(1),TOB(5)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(6),TOB(4),TOB(1),TOB(0)) ); - -#define LOAD_MSG_3_3(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(4),TOB(5),TOB(2)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(2),TOB(1),TOB(0),TOB(7)) ); - -#define LOAD_MSG_3_4(buf) \ -t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(6)) ); \ -buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(2),TOB(6),TOB(0),TOB(4)) ); - -#define LOAD_MSG_4_1(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(2),TOB(5),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(6),TOB(2),TOB(1),TOB(5)) ); - -#define LOAD_MSG_4_2(buf) \ -t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(4),TOB(7),TOB(0)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) ); - -#define LOAD_MSG_4_3(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(6),TOB(0),TOB(0)) ); \ -t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(2),TOB(7),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(2),TOB(1),TOB(6),TOB(3)) ); - -#define LOAD_MSG_4_4(buf) \ -t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(4),TOB(0),TOB(1)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(2),TOB(4),TOB(0),TOB(5)) ); - -#define LOAD_MSG_5_1(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(6),TOB(2)) ); \ -buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(4),TOB(2),TOB(1),TOB(0)) ); - -#define LOAD_MSG_5_2(buf) \ -t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(6),TOB(0)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(4)) ); - -#define LOAD_MSG_5_3(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(1),TOB(0),TOB(7),TOB(4)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(7),TOB(1),TOB(0),TOB(3)) ); - -#define LOAD_MSG_5_4(buf) \ -t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(5),TOB(0),TOB(1),TOB(0)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(6),TOB(1),TOB(5),TOB(3)) ); - -#define LOAD_MSG_6_1(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(0),TOB(1),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(6),TOB(1),TOB(4)) ); - -#define LOAD_MSG_6_2(buf) \ -t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(6),TOB(0),TOB(0),TOB(1)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(5),TOB(7),TOB(0)) ); - -#define LOAD_MSG_6_3(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(6),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(5),TOB(1),TOB(0),TOB(4)) ); - -#define LOAD_MSG_6_4(buf) \ -t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(2),TOB(3),TOB(7)) ); \ -buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(2),TOB(1),TOB(0),TOB(7)) ); - -#define LOAD_MSG_7_1(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(0),TOB(7),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(4),TOB(1),TOB(5)) ); - -#define LOAD_MSG_7_2(buf) \ -t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(5),TOB(1),TOB(0),TOB(7)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(6),TOB(0)) ); - -#define LOAD_MSG_7_3(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(2),TOB(0),TOB(0),TOB(5)) ); \ -t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(4),TOB(1),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(2),TOB(7),TOB(0),TOB(3)) ); - -#define LOAD_MSG_7_4(buf) \ -t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(6),TOB(4),TOB(0)) ); \ -buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(2),TOB(1),TOB(0),TOB(6)) ); - -#define LOAD_MSG_8_1(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(6)) ); \ -t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(6),TOB(0)) ); - -#define LOAD_MSG_8_2(buf) \ -t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(4),TOB(3),TOB(5),TOB(0)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(7)) ); - -#define LOAD_MSG_8_3(buf) \ -t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(6),TOB(1),TOB(0),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(2),TOB(5),TOB(4),TOB(3)) ); \ - -#define LOAD_MSG_8_4(buf) \ -buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(7),TOB(2),TOB(5)) ); - -#define LOAD_MSG_9_1(buf) \ -t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(1),TOB(7),TOB(0),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(2),TOB(4),TOB(6)) ); - -#define LOAD_MSG_9_2(buf) \ -buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(6),TOB(4),TOB(2)) ); - -#define LOAD_MSG_9_3(buf) \ -t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(3),TOB(5),TOB(0)) ); \ -buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(2),TOB(1),TOB(7),TOB(5)) ); - -#define LOAD_MSG_9_4(buf) \ -t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(7)) ); \ -buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(4),TOB(6),TOB(0),TOB(3)) ); - -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2S_LOAD_XOP_H +#define BLAKE2S_LOAD_XOP_H + +#define TOB(x) ((x)*4*0x01010101 + 0x03020100) /* ..or not TOB */ + +#if 0 +/* Basic VPPERM emulation, for testing purposes */ +static __m128i _mm_perm_epi8(const __m128i src1, const __m128i src2, const __m128i sel) +{ + const __m128i sixteen = _mm_set1_epi8(16); + const __m128i t0 = _mm_shuffle_epi8(src1, sel); + const __m128i s1 = _mm_shuffle_epi8(src2, _mm_sub_epi8(sel, sixteen)); + const __m128i mask = _mm_or_si128(_mm_cmpeq_epi8(sel, sixteen), + _mm_cmpgt_epi8(sel, sixteen)); /* (>=16) = 0xff : 00 */ + return _mm_blendv_epi8(t0, s1, mask); +} +#endif + +#define LOAD_MSG_0_1(buf) \ +buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(6),TOB(4),TOB(2),TOB(0)) ); + +#define LOAD_MSG_0_2(buf) \ +buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(7),TOB(5),TOB(3),TOB(1)) ); + +#define LOAD_MSG_0_3(buf) \ +buf = _mm_perm_epi8(m2, m3, _mm_set_epi32(TOB(4),TOB(2),TOB(0),TOB(6)) ); + +#define LOAD_MSG_0_4(buf) \ +buf = _mm_perm_epi8(m2, m3, _mm_set_epi32(TOB(5),TOB(3),TOB(1),TOB(7)) ); + +#define LOAD_MSG_1_1(buf) \ +t0 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(0),TOB(5),TOB(0),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(6)) ); + +#define LOAD_MSG_1_2(buf) \ +t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(2),TOB(0),TOB(4),TOB(6)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) ); + +#define LOAD_MSG_1_3(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(0),TOB(0),TOB(1)) ); \ +buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(7),TOB(1),TOB(0),TOB(3)) ); + +#define LOAD_MSG_1_4(buf) \ +t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(7),TOB(2),TOB(0)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(2),TOB(1),TOB(4),TOB(3)) ); + +#define LOAD_MSG_2_1(buf) \ +t0 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(0),TOB(1),TOB(0),TOB(7)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(4),TOB(0)) ); + +#define LOAD_MSG_2_2(buf) \ +t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(2),TOB(0),TOB(4)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(5),TOB(2),TOB(1),TOB(0)) ); + +#define LOAD_MSG_2_3(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(7),TOB(3),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(2),TOB(1),TOB(6),TOB(5)) ); + +#define LOAD_MSG_2_4(buf) \ +t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(1),TOB(6),TOB(0)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(2),TOB(1),TOB(6),TOB(3)) ); + +#define LOAD_MSG_3_1(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(3),TOB(7)) ); \ +t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(5),TOB(1),TOB(0)) ); + +#define LOAD_MSG_3_2(buf) \ +t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(0),TOB(1),TOB(5)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(6),TOB(4),TOB(1),TOB(0)) ); + +#define LOAD_MSG_3_3(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(4),TOB(5),TOB(2)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(2),TOB(1),TOB(0),TOB(7)) ); + +#define LOAD_MSG_3_4(buf) \ +t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(6)) ); \ +buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(2),TOB(6),TOB(0),TOB(4)) ); + +#define LOAD_MSG_4_1(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(2),TOB(5),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(6),TOB(2),TOB(1),TOB(5)) ); + +#define LOAD_MSG_4_2(buf) \ +t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(4),TOB(7),TOB(0)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(7),TOB(2),TOB(1),TOB(0)) ); + +#define LOAD_MSG_4_3(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(6),TOB(0),TOB(0)) ); \ +t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(2),TOB(7),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(2),TOB(1),TOB(6),TOB(3)) ); + +#define LOAD_MSG_4_4(buf) \ +t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(4),TOB(0),TOB(1)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(2),TOB(4),TOB(0),TOB(5)) ); + +#define LOAD_MSG_5_1(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(6),TOB(2)) ); \ +buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(4),TOB(2),TOB(1),TOB(0)) ); + +#define LOAD_MSG_5_2(buf) \ +t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(6),TOB(0)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(4)) ); + +#define LOAD_MSG_5_3(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(1),TOB(0),TOB(7),TOB(4)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(7),TOB(1),TOB(0),TOB(3)) ); + +#define LOAD_MSG_5_4(buf) \ +t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(5),TOB(0),TOB(1),TOB(0)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(6),TOB(1),TOB(5),TOB(3)) ); + +#define LOAD_MSG_6_1(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(0),TOB(1),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(6),TOB(1),TOB(4)) ); + +#define LOAD_MSG_6_2(buf) \ +t1 = _mm_perm_epi8(m1, m2, _mm_set_epi32(TOB(6),TOB(0),TOB(0),TOB(1)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(5),TOB(7),TOB(0)) ); + +#define LOAD_MSG_6_3(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(6),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(5),TOB(1),TOB(0),TOB(4)) ); + +#define LOAD_MSG_6_4(buf) \ +t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(2),TOB(3),TOB(7)) ); \ +buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(2),TOB(1),TOB(0),TOB(7)) ); + +#define LOAD_MSG_7_1(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(3),TOB(0),TOB(7),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(4),TOB(1),TOB(5)) ); + +#define LOAD_MSG_7_2(buf) \ +t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(5),TOB(1),TOB(0),TOB(7)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(6),TOB(0)) ); + +#define LOAD_MSG_7_3(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(2),TOB(0),TOB(0),TOB(5)) ); \ +t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(4),TOB(1),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(2),TOB(7),TOB(0),TOB(3)) ); + +#define LOAD_MSG_7_4(buf) \ +t1 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(6),TOB(4),TOB(0)) ); \ +buf = _mm_perm_epi8(t1, m2, _mm_set_epi32(TOB(2),TOB(1),TOB(0),TOB(6)) ); + +#define LOAD_MSG_8_1(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(6)) ); \ +t0 = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(7),TOB(1),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(6),TOB(0)) ); + +#define LOAD_MSG_8_2(buf) \ +t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(4),TOB(3),TOB(5),TOB(0)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(3),TOB(2),TOB(1),TOB(7)) ); + +#define LOAD_MSG_8_3(buf) \ +t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(6),TOB(1),TOB(0),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(2),TOB(5),TOB(4),TOB(3)) ); \ + +#define LOAD_MSG_8_4(buf) \ +buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(4),TOB(7),TOB(2),TOB(5)) ); + +#define LOAD_MSG_9_1(buf) \ +t0 = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(1),TOB(7),TOB(0),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m2, _mm_set_epi32(TOB(3),TOB(2),TOB(4),TOB(6)) ); + +#define LOAD_MSG_9_2(buf) \ +buf = _mm_perm_epi8(m0, m1, _mm_set_epi32(TOB(5),TOB(6),TOB(4),TOB(2)) ); + +#define LOAD_MSG_9_3(buf) \ +t0 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(3),TOB(5),TOB(0)) ); \ +buf = _mm_perm_epi8(t0, m3, _mm_set_epi32(TOB(2),TOB(1),TOB(7),TOB(5)) ); + +#define LOAD_MSG_9_4(buf) \ +t1 = _mm_perm_epi8(m0, m2, _mm_set_epi32(TOB(0),TOB(0),TOB(0),TOB(7)) ); \ +buf = _mm_perm_epi8(t1, m3, _mm_set_epi32(TOB(4),TOB(6),TOB(0),TOB(3)) ); + +#endif diff --git a/src/third_party/blake2/blake2s-ref.c b/src/third_party/blake2/blake2s-ref.c index c8b035f6..d6a911dd 100644 --- a/src/third_party/blake2/blake2s-ref.c +++ b/src/third_party/blake2/blake2s-ref.c @@ -1,367 +1,367 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#include "blake2.h" -#include "blake2-impl.h" - -static const uint32_t blake2s_IV[8] = -{ - 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, - 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL -}; - -static const uint8_t blake2s_sigma[10][16] = -{ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , -}; - -static void blake2s_set_lastnode( blake2s_state *S ) -{ - S->f[1] = (uint32_t)-1; -} - -/* Some helper functions, not necessarily useful */ -static int blake2s_is_lastblock( const blake2s_state *S ) -{ - return S->f[0] != 0; -} - -static void blake2s_set_lastblock( blake2s_state *S ) -{ - if( S->last_node ) blake2s_set_lastnode( S ); - - S->f[0] = (uint32_t)-1; -} - -static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc ) -{ - S->t[0] += inc; - S->t[1] += ( S->t[0] < inc ); -} - -static void blake2s_init0( blake2s_state *S ) -{ - size_t i; - memset( S, 0, sizeof( blake2s_state ) ); - - for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i]; -} - -/* init2 xors IV with input parameter block */ -int blake2s_init_param( blake2s_state *S, const blake2s_param *P ) -{ - const unsigned char *p = ( const unsigned char * )( P ); - size_t i; - - blake2s_init0( S ); - - /* IV XOR ParamBlock */ - for( i = 0; i < 8; ++i ) - S->h[i] ^= load32( &p[i * 4] ); - - S->outlen = P->digest_length; - return 0; -} - - -/* Sequential blake2s initialization */ -int blake2s_init( blake2s_state *S, size_t outlen ) -{ - blake2s_param P[1]; - - /* Move interval verification here? */ - if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = 0; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store16( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - /* memset(P->reserved, 0, sizeof(P->reserved) ); */ - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2s_init_param( S, P ); -} - -int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ) -{ - blake2s_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; - - if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store16( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - /* memset(P->reserved, 0, sizeof(P->reserved) ); */ - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - if( blake2s_init_param( S, P ) < 0 ) return -1; - - { - uint8_t block[BLAKE2S_BLOCKBYTES]; - memset( block, 0, BLAKE2S_BLOCKBYTES ); - memcpy( block, key, keylen ); - blake2s_update( S, block, BLAKE2S_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - -#define G(r,i,a,b,c,d) \ - do { \ - a = a + b + m[blake2s_sigma[r][2*i+0]]; \ - d = rotr32(d ^ a, 16); \ - c = c + d; \ - b = rotr32(b ^ c, 12); \ - a = a + b + m[blake2s_sigma[r][2*i+1]]; \ - d = rotr32(d ^ a, 8); \ - c = c + d; \ - b = rotr32(b ^ c, 7); \ - } while(0) - -#define ROUND(r) \ - do { \ - G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ - G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ - G(r,2,v[ 2],v[ 6],v[10],v[14]); \ - G(r,3,v[ 3],v[ 7],v[11],v[15]); \ - G(r,4,v[ 0],v[ 5],v[10],v[15]); \ - G(r,5,v[ 1],v[ 6],v[11],v[12]); \ - G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ - G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ - } while(0) - -static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] ) -{ - uint32_t m[16]; - uint32_t v[16]; - size_t i; - - for( i = 0; i < 16; ++i ) { - m[i] = load32( in + i * sizeof( m[i] ) ); - } - - for( i = 0; i < 8; ++i ) { - v[i] = S->h[i]; - } - - v[ 8] = blake2s_IV[0]; - v[ 9] = blake2s_IV[1]; - v[10] = blake2s_IV[2]; - v[11] = blake2s_IV[3]; - v[12] = S->t[0] ^ blake2s_IV[4]; - v[13] = S->t[1] ^ blake2s_IV[5]; - v[14] = S->f[0] ^ blake2s_IV[6]; - v[15] = S->f[1] ^ blake2s_IV[7]; - - ROUND( 0 ); - ROUND( 1 ); - ROUND( 2 ); - ROUND( 3 ); - ROUND( 4 ); - ROUND( 5 ); - ROUND( 6 ); - ROUND( 7 ); - ROUND( 8 ); - ROUND( 9 ); - - for( i = 0; i < 8; ++i ) { - S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; - } -} - -#undef G -#undef ROUND - -int blake2s_update( blake2s_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - if( inlen > 0 ) - { - size_t left = S->buflen; - size_t fill = BLAKE2S_BLOCKBYTES - left; - if( inlen > fill ) - { - S->buflen = 0; - memcpy( S->buf + left, in, fill ); /* Fill buffer */ - blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); - blake2s_compress( S, S->buf ); /* Compress */ - in += fill; inlen -= fill; - while(inlen > BLAKE2S_BLOCKBYTES) { - blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); - blake2s_compress( S, in ); - in += BLAKE2S_BLOCKBYTES; - inlen -= BLAKE2S_BLOCKBYTES; - } - } - memcpy( S->buf + S->buflen, in, inlen ); - S->buflen += inlen; - } - return 0; -} - -int blake2s_final( blake2s_state *S, void *out, size_t outlen ) -{ - uint8_t buffer[BLAKE2S_OUTBYTES] = {0}; - size_t i; - - if( out == NULL || outlen < S->outlen ) - return -1; - - if( blake2s_is_lastblock( S ) ) - return -1; - - blake2s_increment_counter( S, ( uint32_t )S->buflen ); - blake2s_set_lastblock( S ); - memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ - blake2s_compress( S, S->buf ); - - for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ - store32( buffer + sizeof( S->h[i] ) * i, S->h[i] ); - - memcpy( out, buffer, outlen ); - secure_zero_memory(buffer, sizeof(buffer)); - return 0; -} - -int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - blake2s_state S[1]; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if ( NULL == key && keylen > 0) return -1; - - if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; - - if( keylen > BLAKE2S_KEYBYTES ) return -1; - - if( keylen > 0 ) - { - if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1; - } - else - { - if( blake2s_init( S, outlen ) < 0 ) return -1; - } - - blake2s_update( S, ( const uint8_t * )in, inlen ); - blake2s_final( S, out, outlen ); - return 0; -} - -#if defined(SUPERCOP) -int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) -{ - return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 ); -} -#endif - -#if defined(BLAKE2S_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2S_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2S_OUTBYTES]; - blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); - - if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2S_OUTBYTES]; - blake2s_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2s_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2s_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +static const uint32_t blake2s_IV[8] = +{ + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +}; + +static const uint8_t blake2s_sigma[10][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; + +static void blake2s_set_lastnode( blake2s_state *S ) +{ + S->f[1] = (uint32_t)-1; +} + +/* Some helper functions, not necessarily useful */ +static int blake2s_is_lastblock( const blake2s_state *S ) +{ + return S->f[0] != 0; +} + +static void blake2s_set_lastblock( blake2s_state *S ) +{ + if( S->last_node ) blake2s_set_lastnode( S ); + + S->f[0] = (uint32_t)-1; +} + +static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + +static void blake2s_init0( blake2s_state *S ) +{ + size_t i; + memset( S, 0, sizeof( blake2s_state ) ); + + for( i = 0; i < 8; ++i ) S->h[i] = blake2s_IV[i]; +} + +/* init2 xors IV with input parameter block */ +int blake2s_init_param( blake2s_state *S, const blake2s_param *P ) +{ + const unsigned char *p = ( const unsigned char * )( P ); + size_t i; + + blake2s_init0( S ); + + /* IV XOR ParamBlock */ + for( i = 0; i < 8; ++i ) + S->h[i] ^= load32( &p[i * 4] ); + + S->outlen = P->digest_length; + return 0; +} + + +/* Sequential blake2s initialization */ +int blake2s_init( blake2s_state *S, size_t outlen ) +{ + blake2s_param P[1]; + + /* Move interval verification here? */ + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + /* memset(P->reserved, 0, sizeof(P->reserved) ); */ + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2s_init_param( S, P ); +} + +int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ) +{ + blake2s_param P[1]; + + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + + if ( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + /* memset(P->reserved, 0, sizeof(P->reserved) ); */ + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + if( blake2s_init_param( S, P ) < 0 ) return -1; + + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset( block, 0, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2s_update( S, block, BLAKE2S_BLOCKBYTES ); + secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + +#define G(r,i,a,b,c,d) \ + do { \ + a = a + b + m[blake2s_sigma[r][2*i+0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2*i+1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); \ + } while(0) + +#define ROUND(r) \ + do { \ + G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ + G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ + G(r,2,v[ 2],v[ 6],v[10],v[14]); \ + G(r,3,v[ 3],v[ 7],v[11],v[15]); \ + G(r,4,v[ 0],v[ 5],v[10],v[15]); \ + G(r,5,v[ 1],v[ 6],v[11],v[12]); \ + G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ + G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ + } while(0) + +static void blake2s_compress( blake2s_state *S, const uint8_t in[BLAKE2S_BLOCKBYTES] ) +{ + uint32_t m[16]; + uint32_t v[16]; + size_t i; + + for( i = 0; i < 16; ++i ) { + m[i] = load32( in + i * sizeof( m[i] ) ); + } + + for( i = 0; i < 8; ++i ) { + v[i] = S->h[i]; + } + + v[ 8] = blake2s_IV[0]; + v[ 9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; + + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + + for( i = 0; i < 8; ++i ) { + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; + } +} + +#undef G +#undef ROUND + +int blake2s_update( blake2s_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + if( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = BLAKE2S_BLOCKBYTES - left; + if( inlen > fill ) + { + S->buflen = 0; + memcpy( S->buf + left, in, fill ); /* Fill buffer */ + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + blake2s_compress( S, S->buf ); /* Compress */ + in += fill; inlen -= fill; + while(inlen > BLAKE2S_BLOCKBYTES) { + blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); + blake2s_compress( S, in ); + in += BLAKE2S_BLOCKBYTES; + inlen -= BLAKE2S_BLOCKBYTES; + } + } + memcpy( S->buf + S->buflen, in, inlen ); + S->buflen += inlen; + } + return 0; +} + +int blake2s_final( blake2s_state *S, void *out, size_t outlen ) +{ + uint8_t buffer[BLAKE2S_OUTBYTES] = {0}; + size_t i; + + if( out == NULL || outlen < S->outlen ) + return -1; + + if( blake2s_is_lastblock( S ) ) + return -1; + + blake2s_increment_counter( S, ( uint32_t )S->buflen ); + blake2s_set_lastblock( S ); + memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ + blake2s_compress( S, S->buf ); + + for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ + store32( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + + memcpy( out, buffer, outlen ); + secure_zero_memory(buffer, sizeof(buffer)); + return 0; +} + +int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + blake2s_state S[1]; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if ( NULL == key && keylen > 0) return -1; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + if( keylen > BLAKE2S_KEYBYTES ) return -1; + + if( keylen > 0 ) + { + if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1; + } + else + { + if( blake2s_init( S, outlen ) < 0 ) return -1; + } + + blake2s_update( S, ( const uint8_t * )in, inlen ); + blake2s_final( S, out, outlen ); + return 0; +} + +#if defined(SUPERCOP) +int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) +{ + return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 ); +} +#endif + +#if defined(BLAKE2S_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2S_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); + + if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2s_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2s_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2s_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2s-round.h b/src/third_party/blake2/blake2s-round.h index b75c669c..784eebe2 100644 --- a/src/third_party/blake2/blake2s-round.h +++ b/src/third_party/blake2/blake2s-round.h @@ -1,88 +1,88 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2S_ROUND_H -#define BLAKE2S_ROUND_H - -#define LOADU(p) _mm_loadu_si128( (const __m128i *)(p) ) -#define STOREU(p,r) _mm_storeu_si128((__m128i *)(p), r) - -#define TOF(reg) _mm_castsi128_ps((reg)) -#define TOI(reg) _mm_castps_si128((reg)) - -#define LIKELY(x) __builtin_expect((x),1) - - -/* Microarchitecture-specific macros */ -#ifndef HAVE_XOP -#ifdef HAVE_SSSE3 -#define _mm_roti_epi32(r, c) ( \ - (8==-(c)) ? _mm_shuffle_epi8(r,r8) \ - : (16==-(c)) ? _mm_shuffle_epi8(r,r16) \ - : _mm_xor_si128(_mm_srli_epi32( (r), -(c) ),_mm_slli_epi32( (r), 32-(-(c)) )) ) -#else -#define _mm_roti_epi32(r, c) _mm_xor_si128(_mm_srli_epi32( (r), -(c) ),_mm_slli_epi32( (r), 32-(-(c)) )) -#endif -#else -/* ... */ -#endif - - -#define G1(row1,row2,row3,row4,buf) \ - row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ - row4 = _mm_xor_si128( row4, row1 ); \ - row4 = _mm_roti_epi32(row4, -16); \ - row3 = _mm_add_epi32( row3, row4 ); \ - row2 = _mm_xor_si128( row2, row3 ); \ - row2 = _mm_roti_epi32(row2, -12); - -#define G2(row1,row2,row3,row4,buf) \ - row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ - row4 = _mm_xor_si128( row4, row1 ); \ - row4 = _mm_roti_epi32(row4, -8); \ - row3 = _mm_add_epi32( row3, row4 ); \ - row2 = _mm_xor_si128( row2, row3 ); \ - row2 = _mm_roti_epi32(row2, -7); - -#define DIAGONALIZE(row1,row2,row3,row4) \ - row1 = _mm_shuffle_epi32( row1, _MM_SHUFFLE(2,1,0,3) ); \ - row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(1,0,3,2) ); \ - row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(0,3,2,1) ); - -#define UNDIAGONALIZE(row1,row2,row3,row4) \ - row1 = _mm_shuffle_epi32( row1, _MM_SHUFFLE(0,3,2,1) ); \ - row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(1,0,3,2) ); \ - row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(2,1,0,3) ); - -#if defined(HAVE_XOP) -#include "blake2s-load-xop.h" -#elif defined(HAVE_SSE41) -#include "blake2s-load-sse41.h" -#else -#include "blake2s-load-sse2.h" -#endif - -#define ROUND(r) \ - LOAD_MSG_ ##r ##_1(buf1); \ - G1(row1,row2,row3,row4,buf1); \ - LOAD_MSG_ ##r ##_2(buf2); \ - G2(row1,row2,row3,row4,buf2); \ - DIAGONALIZE(row1,row2,row3,row4); \ - LOAD_MSG_ ##r ##_3(buf3); \ - G1(row1,row2,row3,row4,buf3); \ - LOAD_MSG_ ##r ##_4(buf4); \ - G2(row1,row2,row3,row4,buf4); \ - UNDIAGONALIZE(row1,row2,row3,row4); \ - -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ +#ifndef BLAKE2S_ROUND_H +#define BLAKE2S_ROUND_H + +#define LOADU(p) _mm_loadu_si128( (const __m128i *)(p) ) +#define STOREU(p,r) _mm_storeu_si128((__m128i *)(p), r) + +#define TOF(reg) _mm_castsi128_ps((reg)) +#define TOI(reg) _mm_castps_si128((reg)) + +#define LIKELY(x) __builtin_expect((x),1) + + +/* Microarchitecture-specific macros */ +#ifndef HAVE_XOP +#ifdef HAVE_SSSE3 +#define _mm_roti_epi32(r, c) ( \ + (8==-(c)) ? _mm_shuffle_epi8(r,r8) \ + : (16==-(c)) ? _mm_shuffle_epi8(r,r16) \ + : _mm_xor_si128(_mm_srli_epi32( (r), -(c) ),_mm_slli_epi32( (r), 32-(-(c)) )) ) +#else +#define _mm_roti_epi32(r, c) _mm_xor_si128(_mm_srli_epi32( (r), -(c) ),_mm_slli_epi32( (r), 32-(-(c)) )) +#endif +#else +/* ... */ +#endif + + +#define G1(row1,row2,row3,row4,buf) \ + row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ + row4 = _mm_xor_si128( row4, row1 ); \ + row4 = _mm_roti_epi32(row4, -16); \ + row3 = _mm_add_epi32( row3, row4 ); \ + row2 = _mm_xor_si128( row2, row3 ); \ + row2 = _mm_roti_epi32(row2, -12); + +#define G2(row1,row2,row3,row4,buf) \ + row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ + row4 = _mm_xor_si128( row4, row1 ); \ + row4 = _mm_roti_epi32(row4, -8); \ + row3 = _mm_add_epi32( row3, row4 ); \ + row2 = _mm_xor_si128( row2, row3 ); \ + row2 = _mm_roti_epi32(row2, -7); + +#define DIAGONALIZE(row1,row2,row3,row4) \ + row1 = _mm_shuffle_epi32( row1, _MM_SHUFFLE(2,1,0,3) ); \ + row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(1,0,3,2) ); \ + row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(0,3,2,1) ); + +#define UNDIAGONALIZE(row1,row2,row3,row4) \ + row1 = _mm_shuffle_epi32( row1, _MM_SHUFFLE(0,3,2,1) ); \ + row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(1,0,3,2) ); \ + row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(2,1,0,3) ); + +#if defined(HAVE_XOP) +#include "blake2s-load-xop.h" +#elif defined(HAVE_SSE41) +#include "blake2s-load-sse41.h" +#else +#include "blake2s-load-sse2.h" +#endif + +#define ROUND(r) \ + LOAD_MSG_ ##r ##_1(buf1); \ + G1(row1,row2,row3,row4,buf1); \ + LOAD_MSG_ ##r ##_2(buf2); \ + G2(row1,row2,row3,row4,buf2); \ + DIAGONALIZE(row1,row2,row3,row4); \ + LOAD_MSG_ ##r ##_3(buf3); \ + G1(row1,row2,row3,row4,buf3); \ + LOAD_MSG_ ##r ##_4(buf4); \ + G2(row1,row2,row3,row4,buf4); \ + UNDIAGONALIZE(row1,row2,row3,row4); \ + +#endif diff --git a/src/third_party/blake2/blake2s.c b/src/third_party/blake2/blake2s.c index 569c210e..9655a96d 100644 --- a/src/third_party/blake2/blake2s.c +++ b/src/third_party/blake2/blake2s.c @@ -1,363 +1,363 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#include "blake2.h" -#include "blake2-impl.h" - -#include "blake2-config.h" - - -#include -#if defined(HAVE_SSSE3) -#include -#endif -#if defined(HAVE_SSE41) -#include -#endif -#if defined(HAVE_AVX) -#include -#endif -#if defined(HAVE_XOP) -#include -#endif - -#include "blake2s-round.h" - -static const uint32_t blake2s_IV[8] = -{ - 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, - 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL -}; - -/* Some helper functions */ -static void blake2s_set_lastnode( blake2s_state *S ) -{ - S->f[1] = (uint32_t)-1; -} - -static int blake2s_is_lastblock( const blake2s_state *S ) -{ - return S->f[0] != 0; -} - -static void blake2s_set_lastblock( blake2s_state *S ) -{ - if( S->last_node ) blake2s_set_lastnode( S ); - - S->f[0] = (uint32_t)-1; -} - -static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc ) -{ - uint64_t t = ( ( uint64_t )S->t[1] << 32 ) | S->t[0]; - t += inc; - S->t[0] = ( uint32_t )( t >> 0 ); - S->t[1] = ( uint32_t )( t >> 32 ); -} - -/* init2 xors IV with input parameter block */ -int blake2s_init_param( blake2s_state *S, const blake2s_param *P ) -{ - size_t i; - /*blake2s_init0( S ); */ - const uint8_t * v = ( const uint8_t * )( blake2s_IV ); - const uint8_t * p = ( const uint8_t * )( P ); - uint8_t * h = ( uint8_t * )( S->h ); - /* IV XOR ParamBlock */ - memset( S, 0, sizeof( blake2s_state ) ); - - for( i = 0; i < BLAKE2S_OUTBYTES; ++i ) h[i] = v[i] ^ p[i]; - - S->outlen = P->digest_length; - return 0; -} - - -/* Some sort of default parameter block initialization, for sequential blake2s */ -int blake2s_init( blake2s_state *S, size_t outlen ) -{ - blake2s_param P[1]; - - /* Move interval verification here? */ - if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = 0; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store16( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - /* memset(P->reserved, 0, sizeof(P->reserved) ); */ - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - return blake2s_init_param( S, P ); -} - - -int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ) -{ - blake2s_param P[1]; - - /* Move interval verification here? */ - if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; - - if ( ( !key ) || ( !keylen ) || keylen > BLAKE2S_KEYBYTES ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store16( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - /* memset(P->reserved, 0, sizeof(P->reserved) ); */ - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - if( blake2s_init_param( S, P ) < 0 ) - return -1; - - { - uint8_t block[BLAKE2S_BLOCKBYTES]; - memset( block, 0, BLAKE2S_BLOCKBYTES ); - memcpy( block, key, keylen ); - blake2s_update( S, block, BLAKE2S_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - - -static void blake2s_compress( blake2s_state *S, const uint8_t block[BLAKE2S_BLOCKBYTES] ) -{ - __m128i row1, row2, row3, row4; - __m128i buf1, buf2, buf3, buf4; -#if defined(HAVE_SSE41) - __m128i t0, t1; -#if !defined(HAVE_XOP) - __m128i t2; -#endif -#endif - __m128i ff0, ff1; -#if defined(HAVE_SSSE3) && !defined(HAVE_XOP) - const __m128i r8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 ); - const __m128i r16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 ); -#endif -#if defined(HAVE_SSE41) - const __m128i m0 = LOADU( block + 00 ); - const __m128i m1 = LOADU( block + 16 ); - const __m128i m2 = LOADU( block + 32 ); - const __m128i m3 = LOADU( block + 48 ); -#else - const uint32_t m0 = load32(block + 0 * sizeof(uint32_t)); - const uint32_t m1 = load32(block + 1 * sizeof(uint32_t)); - const uint32_t m2 = load32(block + 2 * sizeof(uint32_t)); - const uint32_t m3 = load32(block + 3 * sizeof(uint32_t)); - const uint32_t m4 = load32(block + 4 * sizeof(uint32_t)); - const uint32_t m5 = load32(block + 5 * sizeof(uint32_t)); - const uint32_t m6 = load32(block + 6 * sizeof(uint32_t)); - const uint32_t m7 = load32(block + 7 * sizeof(uint32_t)); - const uint32_t m8 = load32(block + 8 * sizeof(uint32_t)); - const uint32_t m9 = load32(block + 9 * sizeof(uint32_t)); - const uint32_t m10 = load32(block + 10 * sizeof(uint32_t)); - const uint32_t m11 = load32(block + 11 * sizeof(uint32_t)); - const uint32_t m12 = load32(block + 12 * sizeof(uint32_t)); - const uint32_t m13 = load32(block + 13 * sizeof(uint32_t)); - const uint32_t m14 = load32(block + 14 * sizeof(uint32_t)); - const uint32_t m15 = load32(block + 15 * sizeof(uint32_t)); -#endif - row1 = ff0 = LOADU( &S->h[0] ); - row2 = ff1 = LOADU( &S->h[4] ); - row3 = _mm_loadu_si128( (__m128i const *)&blake2s_IV[0] ); - row4 = _mm_xor_si128( _mm_loadu_si128( (__m128i const *)&blake2s_IV[4] ), LOADU( &S->t[0] ) ); - ROUND( 0 ); - ROUND( 1 ); - ROUND( 2 ); - ROUND( 3 ); - ROUND( 4 ); - ROUND( 5 ); - ROUND( 6 ); - ROUND( 7 ); - ROUND( 8 ); - ROUND( 9 ); - STOREU( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row1, row3 ) ) ); - STOREU( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row2, row4 ) ) ); -} - -int blake2s_update( blake2s_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - if( inlen > 0 ) - { - size_t left = S->buflen; - size_t fill = BLAKE2S_BLOCKBYTES - left; - if( inlen > fill ) - { - S->buflen = 0; - memcpy( S->buf + left, in, fill ); /* Fill buffer */ - blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); - blake2s_compress( S, S->buf ); /* Compress */ - in += fill; inlen -= fill; - while(inlen > BLAKE2S_BLOCKBYTES) { - blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); - blake2s_compress( S, in ); - in += BLAKE2S_BLOCKBYTES; - inlen -= BLAKE2S_BLOCKBYTES; - } - } - memcpy( S->buf + S->buflen, in, inlen ); - S->buflen += inlen; - } - return 0; -} - -int blake2s_final( blake2s_state *S, void *out, size_t outlen ) -{ - uint8_t buffer[BLAKE2S_OUTBYTES] = {0}; - size_t i; - - if( out == NULL || outlen < S->outlen ) - return -1; - - if( blake2s_is_lastblock( S ) ) - return -1; - - blake2s_increment_counter( S, (uint32_t)S->buflen ); - blake2s_set_lastblock( S ); - memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ - blake2s_compress( S, S->buf ); - - for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ - store32( buffer + sizeof( S->h[i] ) * i, S->h[i] ); - - memcpy( out, buffer, S->outlen ); - secure_zero_memory( buffer, sizeof(buffer) ); - return 0; -} - -/* inlen, at least, should be uint64_t. Others can be size_t. */ -int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - blake2s_state S[1]; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if ( NULL == key && keylen > 0) return -1; - - if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; - - if( keylen > BLAKE2S_KEYBYTES ) return -1; - - if( keylen > 0 ) - { - if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1; - } - else - { - if( blake2s_init( S, outlen ) < 0 ) return -1; - } - - blake2s_update( S, ( const uint8_t * )in, inlen ); - blake2s_final( S, out, outlen ); - return 0; -} - -#if defined(SUPERCOP) -int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) -{ - return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 ); -} -#endif - -#if defined(BLAKE2S_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2S_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2S_OUTBYTES]; - blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); - - if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2S_OUTBYTES]; - blake2s_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2s_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2s_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +#include "blake2-config.h" + + +#include +#if defined(HAVE_SSSE3) +#include +#endif +#if defined(HAVE_SSE41) +#include +#endif +#if defined(HAVE_AVX) +#include +#endif +#if defined(HAVE_XOP) +#include +#endif + +#include "blake2s-round.h" + +static const uint32_t blake2s_IV[8] = +{ + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +}; + +/* Some helper functions */ +static void blake2s_set_lastnode( blake2s_state *S ) +{ + S->f[1] = (uint32_t)-1; +} + +static int blake2s_is_lastblock( const blake2s_state *S ) +{ + return S->f[0] != 0; +} + +static void blake2s_set_lastblock( blake2s_state *S ) +{ + if( S->last_node ) blake2s_set_lastnode( S ); + + S->f[0] = (uint32_t)-1; +} + +static void blake2s_increment_counter( blake2s_state *S, const uint32_t inc ) +{ + uint64_t t = ( ( uint64_t )S->t[1] << 32 ) | S->t[0]; + t += inc; + S->t[0] = ( uint32_t )( t >> 0 ); + S->t[1] = ( uint32_t )( t >> 32 ); +} + +/* init2 xors IV with input parameter block */ +int blake2s_init_param( blake2s_state *S, const blake2s_param *P ) +{ + size_t i; + /*blake2s_init0( S ); */ + const uint8_t * v = ( const uint8_t * )( blake2s_IV ); + const uint8_t * p = ( const uint8_t * )( P ); + uint8_t * h = ( uint8_t * )( S->h ); + /* IV XOR ParamBlock */ + memset( S, 0, sizeof( blake2s_state ) ); + + for( i = 0; i < BLAKE2S_OUTBYTES; ++i ) h[i] = v[i] ^ p[i]; + + S->outlen = P->digest_length; + return 0; +} + + +/* Some sort of default parameter block initialization, for sequential blake2s */ +int blake2s_init( blake2s_state *S, size_t outlen ) +{ + blake2s_param P[1]; + + /* Move interval verification here? */ + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = 0; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + /* memset(P->reserved, 0, sizeof(P->reserved) ); */ + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + return blake2s_init_param( S, P ); +} + + +int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ) +{ + blake2s_param P[1]; + + /* Move interval verification here? */ + if ( ( !outlen ) || ( outlen > BLAKE2S_OUTBYTES ) ) return -1; + + if ( ( !key ) || ( !keylen ) || keylen > BLAKE2S_KEYBYTES ) return -1; + + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = 1; + P->depth = 1; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = 0; + /* memset(P->reserved, 0, sizeof(P->reserved) ); */ + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + + if( blake2s_init_param( S, P ) < 0 ) + return -1; + + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset( block, 0, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + blake2s_update( S, block, BLAKE2S_BLOCKBYTES ); + secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + + +static void blake2s_compress( blake2s_state *S, const uint8_t block[BLAKE2S_BLOCKBYTES] ) +{ + __m128i row1, row2, row3, row4; + __m128i buf1, buf2, buf3, buf4; +#if defined(HAVE_SSE41) + __m128i t0, t1; +#if !defined(HAVE_XOP) + __m128i t2; +#endif +#endif + __m128i ff0, ff1; +#if defined(HAVE_SSSE3) && !defined(HAVE_XOP) + const __m128i r8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 ); + const __m128i r16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 ); +#endif +#if defined(HAVE_SSE41) + const __m128i m0 = LOADU( block + 00 ); + const __m128i m1 = LOADU( block + 16 ); + const __m128i m2 = LOADU( block + 32 ); + const __m128i m3 = LOADU( block + 48 ); +#else + const uint32_t m0 = load32(block + 0 * sizeof(uint32_t)); + const uint32_t m1 = load32(block + 1 * sizeof(uint32_t)); + const uint32_t m2 = load32(block + 2 * sizeof(uint32_t)); + const uint32_t m3 = load32(block + 3 * sizeof(uint32_t)); + const uint32_t m4 = load32(block + 4 * sizeof(uint32_t)); + const uint32_t m5 = load32(block + 5 * sizeof(uint32_t)); + const uint32_t m6 = load32(block + 6 * sizeof(uint32_t)); + const uint32_t m7 = load32(block + 7 * sizeof(uint32_t)); + const uint32_t m8 = load32(block + 8 * sizeof(uint32_t)); + const uint32_t m9 = load32(block + 9 * sizeof(uint32_t)); + const uint32_t m10 = load32(block + 10 * sizeof(uint32_t)); + const uint32_t m11 = load32(block + 11 * sizeof(uint32_t)); + const uint32_t m12 = load32(block + 12 * sizeof(uint32_t)); + const uint32_t m13 = load32(block + 13 * sizeof(uint32_t)); + const uint32_t m14 = load32(block + 14 * sizeof(uint32_t)); + const uint32_t m15 = load32(block + 15 * sizeof(uint32_t)); +#endif + row1 = ff0 = LOADU( &S->h[0] ); + row2 = ff1 = LOADU( &S->h[4] ); + row3 = _mm_loadu_si128( (__m128i const *)&blake2s_IV[0] ); + row4 = _mm_xor_si128( _mm_loadu_si128( (__m128i const *)&blake2s_IV[4] ), LOADU( &S->t[0] ) ); + ROUND( 0 ); + ROUND( 1 ); + ROUND( 2 ); + ROUND( 3 ); + ROUND( 4 ); + ROUND( 5 ); + ROUND( 6 ); + ROUND( 7 ); + ROUND( 8 ); + ROUND( 9 ); + STOREU( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row1, row3 ) ) ); + STOREU( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row2, row4 ) ) ); +} + +int blake2s_update( blake2s_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + if( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = BLAKE2S_BLOCKBYTES - left; + if( inlen > fill ) + { + S->buflen = 0; + memcpy( S->buf + left, in, fill ); /* Fill buffer */ + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + blake2s_compress( S, S->buf ); /* Compress */ + in += fill; inlen -= fill; + while(inlen > BLAKE2S_BLOCKBYTES) { + blake2s_increment_counter(S, BLAKE2S_BLOCKBYTES); + blake2s_compress( S, in ); + in += BLAKE2S_BLOCKBYTES; + inlen -= BLAKE2S_BLOCKBYTES; + } + } + memcpy( S->buf + S->buflen, in, inlen ); + S->buflen += inlen; + } + return 0; +} + +int blake2s_final( blake2s_state *S, void *out, size_t outlen ) +{ + uint8_t buffer[BLAKE2S_OUTBYTES] = {0}; + size_t i; + + if( out == NULL || outlen < S->outlen ) + return -1; + + if( blake2s_is_lastblock( S ) ) + return -1; + + blake2s_increment_counter( S, (uint32_t)S->buflen ); + blake2s_set_lastblock( S ); + memset( S->buf + S->buflen, 0, BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ + blake2s_compress( S, S->buf ); + + for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ + store32( buffer + sizeof( S->h[i] ) * i, S->h[i] ); + + memcpy( out, buffer, S->outlen ); + secure_zero_memory( buffer, sizeof(buffer) ); + return 0; +} + +/* inlen, at least, should be uint64_t. Others can be size_t. */ +int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + blake2s_state S[1]; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if ( NULL == key && keylen > 0) return -1; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + if( keylen > BLAKE2S_KEYBYTES ) return -1; + + if( keylen > 0 ) + { + if( blake2s_init_key( S, outlen, key, keylen ) < 0 ) return -1; + } + else + { + if( blake2s_init( S, outlen ) < 0 ) return -1; + } + + blake2s_update( S, ( const uint8_t * )in, inlen ); + blake2s_final( S, out, outlen ); + return 0; +} + +#if defined(SUPERCOP) +int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) +{ + return blake2s( out, BLAKE2S_OUTBYTES, in, inlen, NULL, 0 ); +} +#endif + +#if defined(BLAKE2S_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2S_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2s( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); + + if( 0 != memcmp( hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2s_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2s_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2s_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2s_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2s_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2s_keyed_kat[i], BLAKE2S_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2sp-ref.c b/src/third_party/blake2/blake2sp-ref.c index b0e9baef..f91e75d0 100644 --- a/src/third_party/blake2/blake2sp-ref.c +++ b/src/third_party/blake2/blake2sp-ref.c @@ -1,359 +1,359 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#if defined(_OPENMP) -#include -#endif - -#include "blake2.h" -#include "blake2-impl.h" - -#define PARALLELISM_DEGREE 8 - -/* - blake2sp_init_param defaults to setting the expecting output length - from the digest_length parameter block field. - - In some cases, however, we do not want this, as the output length - of these instances is given by inner_length instead. -*/ -static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P ) -{ - int err = blake2s_init_param(S, P); - S->outlen = P->inner_length; - return err; -} - -static int blake2sp_init_leaf( blake2s_state *S, size_t outlen, size_t keylen, uint64_t offset ) -{ - blake2s_param P[1]; - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = PARALLELISM_DEGREE; - P->depth = 2; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, offset ); - store16( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = BLAKE2S_OUTBYTES; - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2sp_init_leaf_param( S, P ); -} - -static int blake2sp_init_root( blake2s_state *S, size_t outlen, size_t keylen ) -{ - blake2s_param P[1]; - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = PARALLELISM_DEGREE; - P->depth = 2; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store16( &P->xof_length, 0 ); - P->node_depth = 1; - P->inner_length = BLAKE2S_OUTBYTES; - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2s_init_param( S, P ); -} - - -int blake2sp_init( blake2sp_state *S, size_t outlen ) -{ - size_t i; - - if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; - - memset( S->buf, 0, sizeof( S->buf ) ); - S->buflen = 0; - S->outlen = outlen; - - if( blake2sp_init_root( S->R, outlen, 0 ) < 0 ) - return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2sp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1; - - S->R->last_node = 1; - S->S[PARALLELISM_DEGREE - 1]->last_node = 1; - return 0; -} - -int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ) -{ - size_t i; - - if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; - - if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; - - memset( S->buf, 0, sizeof( S->buf ) ); - S->buflen = 0; - S->outlen = outlen; - - if( blake2sp_init_root( S->R, outlen, keylen ) < 0 ) - return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2sp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1; - - S->R->last_node = 1; - S->S[PARALLELISM_DEGREE - 1]->last_node = 1; - { - uint8_t block[BLAKE2S_BLOCKBYTES]; - memset( block, 0, BLAKE2S_BLOCKBYTES ); - memcpy( block, key, keylen ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES ); - - secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - - -int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - size_t left = S->buflen; - size_t fill = sizeof( S->buf ) - left; - size_t i; - - if( left && inlen >= fill ) - { - memcpy( S->buf + left, in, fill ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); - - in += fill; - inlen -= fill; - left = 0; - } - -#if defined(_OPENMP) - #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE) -#else - for( i = 0; i < PARALLELISM_DEGREE; ++i ) -#endif - { -#if defined(_OPENMP) - size_t i = omp_get_thread_num(); -#endif - size_t inlen__ = inlen; - const unsigned char *in__ = ( const unsigned char * )in; - in__ += i * BLAKE2S_BLOCKBYTES; - - while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) - { - blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES ); - in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; - inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; - } - } - - in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ); - inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; - - if( inlen > 0 ) - memcpy( S->buf + left, in, inlen ); - - S->buflen = left + inlen; - return 0; -} - - -int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ) -{ - uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; - size_t i; - - if(out == NULL || outlen < S->outlen) { - return -1; - } - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - { - if( S->buflen > i * BLAKE2S_BLOCKBYTES ) - { - size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES; - - if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES; - - blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left ); - } - - blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES ); - } - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES ); - - return blake2s_final( S->R, out, S->outlen ); -} - - -int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; - blake2s_state S[PARALLELISM_DEGREE][1]; - blake2s_state FS[1]; - size_t i; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if ( NULL == key && keylen > 0) return -1; - - if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; - - if( keylen > BLAKE2S_KEYBYTES ) return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2sp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1; - - S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */ - - if( keylen > 0 ) - { - uint8_t block[BLAKE2S_BLOCKBYTES]; - memset( block, 0, BLAKE2S_BLOCKBYTES ); - memcpy( block, key, keylen ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES ); - - secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ - } - -#if defined(_OPENMP) - #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE) -#else - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) -#endif - { -#if defined(_OPENMP) - size_t i = omp_get_thread_num(); -#endif - size_t inlen__ = inlen; - const unsigned char *in__ = ( const unsigned char * )in; - in__ += i * BLAKE2S_BLOCKBYTES; - - while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) - { - blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES ); - in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; - inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; - } - - if( inlen__ > i * BLAKE2S_BLOCKBYTES ) - { - const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES; - const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES; - blake2s_update( S[i], in__, len ); - } - - blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES ); - } - - if( blake2sp_init_root( FS, outlen, keylen ) < 0 ) - return -1; - - FS->last_node = 1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES ); - - return blake2s_final( FS, out, outlen ); -} - - - -#if defined(BLAKE2SP_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2S_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2S_OUTBYTES]; - blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); - - if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2S_OUTBYTES]; - blake2sp_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2sp_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2sp_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#if defined(_OPENMP) +#include +#endif + +#include "blake2.h" +#include "blake2-impl.h" + +#define PARALLELISM_DEGREE 8 + +/* + blake2sp_init_param defaults to setting the expecting output length + from the digest_length parameter block field. + + In some cases, however, we do not want this, as the output length + of these instances is given by inner_length instead. +*/ +static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P ) +{ + int err = blake2s_init_param(S, P); + S->outlen = P->inner_length; + return err; +} + +static int blake2sp_init_leaf( blake2s_state *S, size_t outlen, size_t keylen, uint64_t offset ) +{ + blake2s_param P[1]; + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = PARALLELISM_DEGREE; + P->depth = 2; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, offset ); + store16( &P->xof_length, 0 ); + P->node_depth = 0; + P->inner_length = BLAKE2S_OUTBYTES; + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2sp_init_leaf_param( S, P ); +} + +static int blake2sp_init_root( blake2s_state *S, size_t outlen, size_t keylen ) +{ + blake2s_param P[1]; + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = PARALLELISM_DEGREE; + P->depth = 2; + store32( &P->leaf_length, 0 ); + store32( &P->node_offset, 0 ); + store16( &P->xof_length, 0 ); + P->node_depth = 1; + P->inner_length = BLAKE2S_OUTBYTES; + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2s_init_param( S, P ); +} + + +int blake2sp_init( blake2sp_state *S, size_t outlen ) +{ + size_t i; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + S->outlen = outlen; + + if( blake2sp_init_root( S->R, outlen, 0 ) < 0 ) + return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2sp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1; + + S->R->last_node = 1; + S->S[PARALLELISM_DEGREE - 1]->last_node = 1; + return 0; +} + +int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ) +{ + size_t i; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; + + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + S->outlen = outlen; + + if( blake2sp_init_root( S->R, outlen, keylen ) < 0 ) + return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2sp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1; + + S->R->last_node = 1; + S->S[PARALLELISM_DEGREE - 1]->last_node = 1; + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset( block, 0, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES ); + + secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + + +int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + size_t left = S->buflen; + size_t fill = sizeof( S->buf ) - left; + size_t i; + + if( left && inlen >= fill ) + { + memcpy( S->buf + left, in, fill ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); + + in += fill; + inlen -= fill; + left = 0; + } + +#if defined(_OPENMP) + #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE) +#else + for( i = 0; i < PARALLELISM_DEGREE; ++i ) +#endif + { +#if defined(_OPENMP) + size_t i = omp_get_thread_num(); +#endif + size_t inlen__ = inlen; + const unsigned char *in__ = ( const unsigned char * )in; + in__ += i * BLAKE2S_BLOCKBYTES; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) + { + blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + } + } + + in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ); + inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + + if( inlen > 0 ) + memcpy( S->buf + left, in, inlen ); + + S->buflen = left + inlen; + return 0; +} + + +int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ) +{ + uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; + size_t i; + + if(out == NULL || outlen < S->outlen) { + return -1; + } + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + { + if( S->buflen > i * BLAKE2S_BLOCKBYTES ) + { + size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES; + + if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES; + + blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left ); + } + + blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES ); + } + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES ); + + return blake2s_final( S->R, out, S->outlen ); +} + + +int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; + blake2s_state S[PARALLELISM_DEGREE][1]; + blake2s_state FS[1]; + size_t i; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if ( NULL == key && keylen > 0) return -1; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + if( keylen > BLAKE2S_KEYBYTES ) return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2sp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1; + + S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */ + + if( keylen > 0 ) + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset( block, 0, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES ); + + secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + +#if defined(_OPENMP) + #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE) +#else + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) +#endif + { +#if defined(_OPENMP) + size_t i = omp_get_thread_num(); +#endif + size_t inlen__ = inlen; + const unsigned char *in__ = ( const unsigned char * )in; + in__ += i * BLAKE2S_BLOCKBYTES; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) + { + blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + } + + if( inlen__ > i * BLAKE2S_BLOCKBYTES ) + { + const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES; + const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES; + blake2s_update( S[i], in__, len ); + } + + blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES ); + } + + if( blake2sp_init_root( FS, outlen, keylen ) < 0 ) + return -1; + + FS->last_node = 1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES ); + + return blake2s_final( FS, out, outlen ); +} + + + +#if defined(BLAKE2SP_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2S_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); + + if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2sp_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2sp_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2sp_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2sp.c b/src/third_party/blake2/blake2sp.c index ed0e1ad2..a9df712e 100644 --- a/src/third_party/blake2/blake2sp.c +++ b/src/third_party/blake2/blake2sp.c @@ -1,358 +1,358 @@ -/* - BLAKE2 reference source code package - optimized C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#if defined(_OPENMP) -#include -#endif - -#include "blake2.h" -#include "blake2-impl.h" - -#define PARALLELISM_DEGREE 8 - -/* - blake2sp_init_param defaults to setting the expecting output length - from the digest_length parameter block field. - - In some cases, however, we do not want this, as the output length - of these instances is given by inner_length instead. -*/ -static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P ) -{ - int err = blake2s_init_param(S, P); - S->outlen = P->inner_length; - return err; -} - -static int blake2sp_init_leaf( blake2s_state *S, size_t outlen, size_t keylen, uint64_t offset ) -{ - blake2s_param P[1]; - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = PARALLELISM_DEGREE; - P->depth = 2; - P->leaf_length = 0; - P->node_offset = offset; - P->xof_length = 0; - P->node_depth = 0; - P->inner_length = BLAKE2S_OUTBYTES; - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2sp_init_leaf_param( S, P ); -} - -static int blake2sp_init_root( blake2s_state *S, size_t outlen, size_t keylen ) -{ - blake2s_param P[1]; - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = PARALLELISM_DEGREE; - P->depth = 2; - P->leaf_length = 0; - P->node_offset = 0; - P->xof_length = 0; - P->node_depth = 1; - P->inner_length = BLAKE2S_OUTBYTES; - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2s_init_param( S, P ); -} - - -int blake2sp_init( blake2sp_state *S, size_t outlen ) -{ - size_t i; - - if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; - - memset( S->buf, 0, sizeof( S->buf ) ); - S->buflen = 0; - S->outlen = outlen; - - if( blake2sp_init_root( S->R, outlen, 0 ) < 0 ) - return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2sp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1; - - S->R->last_node = 1; - S->S[PARALLELISM_DEGREE - 1]->last_node = 1; - return 0; -} - -int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ) -{ - size_t i; - - if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; - - if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; - - memset( S->buf, 0, sizeof( S->buf ) ); - S->buflen = 0; - S->outlen = outlen; - - if( blake2sp_init_root( S->R, outlen, keylen ) < 0 ) - return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2sp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1; - - S->R->last_node = 1; - S->S[PARALLELISM_DEGREE - 1]->last_node = 1; - { - uint8_t block[BLAKE2S_BLOCKBYTES]; - memset( block, 0, BLAKE2S_BLOCKBYTES ); - memcpy( block, key, keylen ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES ); - - secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - - -int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - size_t left = S->buflen; - size_t fill = sizeof( S->buf ) - left; - size_t i; - - if( left && inlen >= fill ) - { - memcpy( S->buf + left, in, fill ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); - - in += fill; - inlen -= fill; - left = 0; - } - -#if defined(_OPENMP) - #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE) -#else - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) -#endif - { -#if defined(_OPENMP) - size_t i = omp_get_thread_num(); -#endif - size_t inlen__ = inlen; - const unsigned char *in__ = ( const unsigned char * )in; - in__ += i * BLAKE2S_BLOCKBYTES; - - while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) - { - blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES ); - in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; - inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; - } - } - - in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ); - inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; - - if( inlen > 0 ) - memcpy( S->buf + left, in, inlen ); - - S->buflen = left + inlen; - return 0; -} - - -int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ) -{ - uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; - size_t i; - - if(out == NULL || outlen < S->outlen) { - return -1; - } - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - { - if( S->buflen > i * BLAKE2S_BLOCKBYTES ) - { - size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES; - - if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES; - - blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left ); - } - - blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES ); - } - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES ); - - return blake2s_final( S->R, out, S->outlen ); -} - - -int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; - blake2s_state S[PARALLELISM_DEGREE][1]; - blake2s_state FS[1]; - size_t i; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if ( NULL == key && keylen > 0) return -1; - - if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; - - if( keylen > BLAKE2S_KEYBYTES ) return -1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - if( blake2sp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1; - - S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */ - - if( keylen > 0 ) - { - uint8_t block[BLAKE2S_BLOCKBYTES]; - memset( block, 0, BLAKE2S_BLOCKBYTES ); - memcpy( block, key, keylen ); - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES ); - - secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ - } - -#if defined(_OPENMP) - #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE) -#else - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) -#endif - { -#if defined(_OPENMP) - size_t i = omp_get_thread_num(); -#endif - size_t inlen__ = inlen; - const unsigned char *in__ = ( const unsigned char * )in; - in__ += i * BLAKE2S_BLOCKBYTES; - - while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) - { - blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES ); - in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; - inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; - } - - if( inlen__ > i * BLAKE2S_BLOCKBYTES ) - { - const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES; - const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES; - blake2s_update( S[i], in__, len ); - } - - blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES ); - } - - if( blake2sp_init_root( FS, outlen, keylen ) < 0 ) - return -1; - - FS->last_node = 1; - - for( i = 0; i < PARALLELISM_DEGREE; ++i ) - blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES ); - - return blake2s_final( FS, out, outlen ); -} - -#if defined(BLAKE2SP_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2S_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2S_OUTBYTES]; - blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); - - if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2S_OUTBYTES]; - blake2sp_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2sp_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2sp_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - optimized C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#if defined(_OPENMP) +#include +#endif + +#include "blake2.h" +#include "blake2-impl.h" + +#define PARALLELISM_DEGREE 8 + +/* + blake2sp_init_param defaults to setting the expecting output length + from the digest_length parameter block field. + + In some cases, however, we do not want this, as the output length + of these instances is given by inner_length instead. +*/ +static int blake2sp_init_leaf_param( blake2s_state *S, const blake2s_param *P ) +{ + int err = blake2s_init_param(S, P); + S->outlen = P->inner_length; + return err; +} + +static int blake2sp_init_leaf( blake2s_state *S, size_t outlen, size_t keylen, uint64_t offset ) +{ + blake2s_param P[1]; + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = PARALLELISM_DEGREE; + P->depth = 2; + P->leaf_length = 0; + P->node_offset = offset; + P->xof_length = 0; + P->node_depth = 0; + P->inner_length = BLAKE2S_OUTBYTES; + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2sp_init_leaf_param( S, P ); +} + +static int blake2sp_init_root( blake2s_state *S, size_t outlen, size_t keylen ) +{ + blake2s_param P[1]; + P->digest_length = (uint8_t)outlen; + P->key_length = (uint8_t)keylen; + P->fanout = PARALLELISM_DEGREE; + P->depth = 2; + P->leaf_length = 0; + P->node_offset = 0; + P->xof_length = 0; + P->node_depth = 1; + P->inner_length = BLAKE2S_OUTBYTES; + memset( P->salt, 0, sizeof( P->salt ) ); + memset( P->personal, 0, sizeof( P->personal ) ); + return blake2s_init_param( S, P ); +} + + +int blake2sp_init( blake2sp_state *S, size_t outlen ) +{ + size_t i; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + S->outlen = outlen; + + if( blake2sp_init_root( S->R, outlen, 0 ) < 0 ) + return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2sp_init_leaf( S->S[i], outlen, 0, i ) < 0 ) return -1; + + S->R->last_node = 1; + S->S[PARALLELISM_DEGREE - 1]->last_node = 1; + return 0; +} + +int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ) +{ + size_t i; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + if( !key || !keylen || keylen > BLAKE2S_KEYBYTES ) return -1; + + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + S->outlen = outlen; + + if( blake2sp_init_root( S->R, outlen, keylen ) < 0 ) + return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2sp_init_leaf( S->S[i], outlen, keylen, i ) < 0 ) return -1; + + S->R->last_node = 1; + S->S[PARALLELISM_DEGREE - 1]->last_node = 1; + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset( block, 0, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S->S[i], block, BLAKE2S_BLOCKBYTES ); + + secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + return 0; +} + + +int blake2sp_update( blake2sp_state *S, const void *pin, size_t inlen ) +{ + const unsigned char * in = (const unsigned char *)pin; + size_t left = S->buflen; + size_t fill = sizeof( S->buf ) - left; + size_t i; + + if( left && inlen >= fill ) + { + memcpy( S->buf + left, in, fill ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); + + in += fill; + inlen -= fill; + left = 0; + } + +#if defined(_OPENMP) + #pragma omp parallel shared(S), num_threads(PARALLELISM_DEGREE) +#else + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) +#endif + { +#if defined(_OPENMP) + size_t i = omp_get_thread_num(); +#endif + size_t inlen__ = inlen; + const unsigned char *in__ = ( const unsigned char * )in; + in__ += i * BLAKE2S_BLOCKBYTES; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) + { + blake2s_update( S->S[i], in__, BLAKE2S_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + } + } + + in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ); + inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + + if( inlen > 0 ) + memcpy( S->buf + left, in, inlen ); + + S->buflen = left + inlen; + return 0; +} + + +int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ) +{ + uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; + size_t i; + + if(out == NULL || outlen < S->outlen) { + return -1; + } + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + { + if( S->buflen > i * BLAKE2S_BLOCKBYTES ) + { + size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES; + + if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES; + + blake2s_update( S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left ); + } + + blake2s_final( S->S[i], hash[i], BLAKE2S_OUTBYTES ); + } + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S->R, hash[i], BLAKE2S_OUTBYTES ); + + return blake2s_final( S->R, out, S->outlen ); +} + + +int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) +{ + uint8_t hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; + blake2s_state S[PARALLELISM_DEGREE][1]; + blake2s_state FS[1]; + size_t i; + + /* Verify parameters */ + if ( NULL == in && inlen > 0 ) return -1; + + if ( NULL == out ) return -1; + + if ( NULL == key && keylen > 0) return -1; + + if( !outlen || outlen > BLAKE2S_OUTBYTES ) return -1; + + if( keylen > BLAKE2S_KEYBYTES ) return -1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + if( blake2sp_init_leaf( S[i], outlen, keylen, i ) < 0 ) return -1; + + S[PARALLELISM_DEGREE - 1]->last_node = 1; /* mark last node */ + + if( keylen > 0 ) + { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset( block, 0, BLAKE2S_BLOCKBYTES ); + memcpy( block, key, keylen ); + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( S[i], block, BLAKE2S_BLOCKBYTES ); + + secure_zero_memory( block, BLAKE2S_BLOCKBYTES ); /* Burn the key from stack */ + } + +#if defined(_OPENMP) + #pragma omp parallel shared(S,hash), num_threads(PARALLELISM_DEGREE) +#else + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) +#endif + { +#if defined(_OPENMP) + size_t i = omp_get_thread_num(); +#endif + size_t inlen__ = inlen; + const unsigned char *in__ = ( const unsigned char * )in; + in__ += i * BLAKE2S_BLOCKBYTES; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) + { + blake2s_update( S[i], in__, BLAKE2S_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + } + + if( inlen__ > i * BLAKE2S_BLOCKBYTES ) + { + const size_t left = inlen__ - i * BLAKE2S_BLOCKBYTES; + const size_t len = left <= BLAKE2S_BLOCKBYTES ? left : BLAKE2S_BLOCKBYTES; + blake2s_update( S[i], in__, len ); + } + + blake2s_final( S[i], hash[i], BLAKE2S_OUTBYTES ); + } + + if( blake2sp_init_root( FS, outlen, keylen ) < 0 ) + return -1; + + FS->last_node = 1; + + for( i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( FS, hash[i], BLAKE2S_OUTBYTES ); + + return blake2s_final( FS, out, outlen ); +} + +#if defined(BLAKE2SP_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2S_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step; + + for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) + key[i] = ( uint8_t )i; + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + buf[i] = ( uint8_t )i; + + /* Test simple API */ + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) + { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2sp( hash, BLAKE2S_OUTBYTES, buf, i, key, BLAKE2S_KEYBYTES ); + + if( 0 != memcmp( hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { + for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { + uint8_t hash[BLAKE2S_OUTBYTES]; + blake2sp_state S; + uint8_t * p = buf; + size_t mlen = i; + int err = 0; + + if( (err = blake2sp_init_key(&S, BLAKE2S_OUTBYTES, key, BLAKE2S_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2sp_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2sp_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2sp_final(&S, hash, BLAKE2S_OUTBYTES)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2sp_keyed_kat[i], BLAKE2S_OUTBYTES)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2xb-ref.c b/src/third_party/blake2/blake2xb-ref.c index b369ee7c..eec0e03a 100644 --- a/src/third_party/blake2/blake2xb-ref.c +++ b/src/third_party/blake2/blake2xb-ref.c @@ -1,241 +1,241 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2016, JP Aumasson . - Copyright 2016, Samuel Neves . - - You may use this under the terms of the CC0, the OpenSSL Licence, or - the Apache Public License 2.0, at your option. The terms of these - licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#include "blake2.h" -#include "blake2-impl.h" - -int blake2xb_init( blake2xb_state *S, const size_t outlen ) { - return blake2xb_init_key(S, outlen, NULL, 0); -} - -int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen) -{ - if ( outlen == 0 || outlen > 0xFFFFFFFFUL ) { - return -1; - } - - if (NULL != key && keylen > BLAKE2B_KEYBYTES) { - return -1; - } - - if (NULL == key && keylen > 0) { - return -1; - } - - /* Initialize parameter block */ - S->P->digest_length = BLAKE2B_OUTBYTES; - S->P->key_length = keylen; - S->P->fanout = 1; - S->P->depth = 1; - store32( &S->P->leaf_length, 0 ); - store32( &S->P->node_offset, 0 ); - store32( &S->P->xof_length, outlen ); - S->P->node_depth = 0; - S->P->inner_length = 0; - memset( S->P->reserved, 0, sizeof( S->P->reserved ) ); - memset( S->P->salt, 0, sizeof( S->P->salt ) ); - memset( S->P->personal, 0, sizeof( S->P->personal ) ); - - if( blake2b_init_param( S->S, S->P ) < 0 ) { - return -1; - } - - if (keylen > 0) { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset(block, 0, BLAKE2B_BLOCKBYTES); - memcpy(block, key, keylen); - blake2b_update(S->S, block, BLAKE2B_BLOCKBYTES); - secure_zero_memory(block, BLAKE2B_BLOCKBYTES); - } - return 0; -} - -int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ) { - return blake2b_update( S->S, in, inlen ); -} - -int blake2xb_final( blake2xb_state *S, void *out, size_t outlen) { - - blake2b_state C[1]; - blake2b_param P[1]; - uint32_t xof_length = load32(&S->P->xof_length); - uint8_t root[BLAKE2B_BLOCKBYTES]; - size_t i; - - if (NULL == out) { - return -1; - } - - /* outlen must match the output size defined in xof_length, */ - /* unless it was -1, in which case anything goes except 0. */ - if(xof_length == 0xFFFFFFFFUL) { - if(outlen == 0) { - return -1; - } - } else { - if(outlen != xof_length) { - return -1; - } - } - - /* Finalize the root hash */ - if (blake2b_final(S->S, root, BLAKE2B_OUTBYTES) < 0) { - return -1; - } - - /* Set common block structure values */ - /* Copy values from parent instance, and only change the ones below */ - memcpy(P, S->P, sizeof(blake2b_param)); - P->key_length = 0; - P->fanout = 0; - P->depth = 0; - store32(&P->leaf_length, BLAKE2B_OUTBYTES); - P->inner_length = BLAKE2B_OUTBYTES; - P->node_depth = 0; - - for (i = 0; outlen > 0; ++i) { - const size_t block_size = (outlen < BLAKE2B_OUTBYTES) ? outlen : BLAKE2B_OUTBYTES; - /* Initialize state */ - P->digest_length = block_size; - store32(&P->node_offset, i); - blake2b_init_param(C, P); - /* Process key if needed */ - blake2b_update(C, root, BLAKE2B_OUTBYTES); - if (blake2b_final(C, (uint8_t *)out + i * BLAKE2B_OUTBYTES, block_size) < 0 ) { - return -1; - } - outlen -= block_size; - } - secure_zero_memory(root, sizeof(root)); - secure_zero_memory(P, sizeof(P)); - secure_zero_memory(C, sizeof(C)); - /* Put blake2xb in an invalid state? cf. blake2s_is_lastblock */ - return 0; - -} - -int blake2xb(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) -{ - blake2xb_state S[1]; - - /* Verify parameters */ - if (NULL == in && inlen > 0) - return -1; - - if (NULL == out) - return -1; - - if (NULL == key && keylen > 0) - return -1; - - if (keylen > BLAKE2B_KEYBYTES) - return -1; - - if (outlen == 0) - return -1; - - /* Initialize the root block structure */ - if (blake2xb_init_key(S, outlen, key, keylen) < 0) { - return -1; - } - - /* Absorb the input message */ - blake2xb_update(S, in, inlen); - - /* Compute the root node of the tree and the final hash using the counter construction */ - return blake2xb_final(S, out, outlen); -} - -#if defined(BLAKE2XB_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2B_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step, outlen; - - for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) { - key[i] = ( uint8_t )i; - } - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) { - buf[i] = ( uint8_t )i; - } - - /* Testing length of outputs rather than inputs */ - /* (Test of input lengths mostly covered by blake2b tests) */ - - /* Test simple API */ - for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen ) - { - uint8_t hash[BLAKE2_KAT_LENGTH] = {0}; - if( blake2xb( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2B_KEYBYTES ) < 0 ) { - goto fail; - } - - if( 0 != memcmp( hash, blake2xb_keyed_kat[outlen-1], outlen ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { - for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) { - uint8_t hash[BLAKE2_KAT_LENGTH]; - blake2xb_state S; - uint8_t * p = buf; - size_t mlen = BLAKE2_KAT_LENGTH; - int err = 0; - - if( (err = blake2xb_init_key(&S, outlen, key, BLAKE2B_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2xb_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2xb_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2xb_final(&S, hash, outlen)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2xb_keyed_kat[outlen-1], outlen)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2016, JP Aumasson . + Copyright 2016, Samuel Neves . + + You may use this under the terms of the CC0, the OpenSSL Licence, or + the Apache Public License 2.0, at your option. The terms of these + licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +int blake2xb_init( blake2xb_state *S, const size_t outlen ) { + return blake2xb_init_key(S, outlen, NULL, 0); +} + +int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen) +{ + if ( outlen == 0 || outlen > 0xFFFFFFFFUL ) { + return -1; + } + + if (NULL != key && keylen > BLAKE2B_KEYBYTES) { + return -1; + } + + if (NULL == key && keylen > 0) { + return -1; + } + + /* Initialize parameter block */ + S->P->digest_length = BLAKE2B_OUTBYTES; + S->P->key_length = keylen; + S->P->fanout = 1; + S->P->depth = 1; + store32( &S->P->leaf_length, 0 ); + store32( &S->P->node_offset, 0 ); + store32( &S->P->xof_length, outlen ); + S->P->node_depth = 0; + S->P->inner_length = 0; + memset( S->P->reserved, 0, sizeof( S->P->reserved ) ); + memset( S->P->salt, 0, sizeof( S->P->salt ) ); + memset( S->P->personal, 0, sizeof( S->P->personal ) ); + + if( blake2b_init_param( S->S, S->P ) < 0 ) { + return -1; + } + + if (keylen > 0) { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset(block, 0, BLAKE2B_BLOCKBYTES); + memcpy(block, key, keylen); + blake2b_update(S->S, block, BLAKE2B_BLOCKBYTES); + secure_zero_memory(block, BLAKE2B_BLOCKBYTES); + } + return 0; +} + +int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ) { + return blake2b_update( S->S, in, inlen ); +} + +int blake2xb_final( blake2xb_state *S, void *out, size_t outlen) { + + blake2b_state C[1]; + blake2b_param P[1]; + uint32_t xof_length = load32(&S->P->xof_length); + uint8_t root[BLAKE2B_BLOCKBYTES]; + size_t i; + + if (NULL == out) { + return -1; + } + + /* outlen must match the output size defined in xof_length, */ + /* unless it was -1, in which case anything goes except 0. */ + if(xof_length == 0xFFFFFFFFUL) { + if(outlen == 0) { + return -1; + } + } else { + if(outlen != xof_length) { + return -1; + } + } + + /* Finalize the root hash */ + if (blake2b_final(S->S, root, BLAKE2B_OUTBYTES) < 0) { + return -1; + } + + /* Set common block structure values */ + /* Copy values from parent instance, and only change the ones below */ + memcpy(P, S->P, sizeof(blake2b_param)); + P->key_length = 0; + P->fanout = 0; + P->depth = 0; + store32(&P->leaf_length, BLAKE2B_OUTBYTES); + P->inner_length = BLAKE2B_OUTBYTES; + P->node_depth = 0; + + for (i = 0; outlen > 0; ++i) { + const size_t block_size = (outlen < BLAKE2B_OUTBYTES) ? outlen : BLAKE2B_OUTBYTES; + /* Initialize state */ + P->digest_length = block_size; + store32(&P->node_offset, i); + blake2b_init_param(C, P); + /* Process key if needed */ + blake2b_update(C, root, BLAKE2B_OUTBYTES); + if (blake2b_final(C, (uint8_t *)out + i * BLAKE2B_OUTBYTES, block_size) < 0 ) { + return -1; + } + outlen -= block_size; + } + secure_zero_memory(root, sizeof(root)); + secure_zero_memory(P, sizeof(P)); + secure_zero_memory(C, sizeof(C)); + /* Put blake2xb in an invalid state? cf. blake2s_is_lastblock */ + return 0; + +} + +int blake2xb(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) +{ + blake2xb_state S[1]; + + /* Verify parameters */ + if (NULL == in && inlen > 0) + return -1; + + if (NULL == out) + return -1; + + if (NULL == key && keylen > 0) + return -1; + + if (keylen > BLAKE2B_KEYBYTES) + return -1; + + if (outlen == 0) + return -1; + + /* Initialize the root block structure */ + if (blake2xb_init_key(S, outlen, key, keylen) < 0) { + return -1; + } + + /* Absorb the input message */ + blake2xb_update(S, in, inlen); + + /* Compute the root node of the tree and the final hash using the counter construction */ + return blake2xb_final(S, out, outlen); +} + +#if defined(BLAKE2XB_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2B_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step, outlen; + + for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) { + key[i] = ( uint8_t )i; + } + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) { + buf[i] = ( uint8_t )i; + } + + /* Testing length of outputs rather than inputs */ + /* (Test of input lengths mostly covered by blake2b tests) */ + + /* Test simple API */ + for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen ) + { + uint8_t hash[BLAKE2_KAT_LENGTH] = {0}; + if( blake2xb( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2B_KEYBYTES ) < 0 ) { + goto fail; + } + + if( 0 != memcmp( hash, blake2xb_keyed_kat[outlen-1], outlen ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { + for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) { + uint8_t hash[BLAKE2_KAT_LENGTH]; + blake2xb_state S; + uint8_t * p = buf; + size_t mlen = BLAKE2_KAT_LENGTH; + int err = 0; + + if( (err = blake2xb_init_key(&S, outlen, key, BLAKE2B_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2xb_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2xb_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2xb_final(&S, hash, outlen)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2xb_keyed_kat[outlen-1], outlen)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2xb.c b/src/third_party/blake2/blake2xb.c index 2da56aef..c0559f27 100644 --- a/src/third_party/blake2/blake2xb.c +++ b/src/third_party/blake2/blake2xb.c @@ -1,241 +1,241 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2016, JP Aumasson . - Copyright 2016, Samuel Neves . - - You may use this under the terms of the CC0, the OpenSSL Licence, or - the Apache Public License 2.0, at your option. The terms of these - licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#include "blake2.h" -#include "blake2-impl.h" - -int blake2xb_init( blake2xb_state *S, const size_t outlen ) { - return blake2xb_init_key(S, outlen, NULL, 0); -} - -int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen) -{ - if ( outlen == 0 || outlen > 0xFFFFFFFFUL ) { - return -1; - } - - if (NULL != key && keylen > BLAKE2B_KEYBYTES) { - return -1; - } - - if (NULL == key && keylen > 0) { - return -1; - } - - /* Initialize parameter block */ - S->P->digest_length = BLAKE2B_OUTBYTES; - S->P->key_length = keylen; - S->P->fanout = 1; - S->P->depth = 1; - store32( &S->P->leaf_length, 0 ); - store32( &S->P->node_offset, 0 ); - store32( &S->P->xof_length, outlen ); - S->P->node_depth = 0; - S->P->inner_length = 0; - memset( S->P->reserved, 0, sizeof( S->P->reserved ) ); - memset( S->P->salt, 0, sizeof( S->P->salt ) ); - memset( S->P->personal, 0, sizeof( S->P->personal ) ); - - if( blake2b_init_param( S->S, S->P ) < 0 ) { - return -1; - } - - if (keylen > 0) { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset(block, 0, BLAKE2B_BLOCKBYTES); - memcpy(block, key, keylen); - blake2b_update(S->S, block, BLAKE2B_BLOCKBYTES); - secure_zero_memory(block, BLAKE2B_BLOCKBYTES); - } - return 0; -} - -int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ) { - return blake2b_update( S->S, in, inlen ); -} - -int blake2xb_final( blake2xb_state *S, void *out, size_t outlen) { - - blake2b_state C[1]; - blake2b_param P[1]; - uint32_t xof_length = load32(&S->P->xof_length); - uint8_t root[BLAKE2B_BLOCKBYTES]; - size_t i; - - if (NULL == out) { - return -1; - } - - /* outlen must match the output size defined in xof_length, */ - /* unless it was -1, in which case anything goes except 0. */ - if(xof_length == 0xFFFFFFFFUL) { - if(outlen == 0) { - return -1; - } - } else { - if(outlen != xof_length) { - return -1; - } - } - - /* Finalize the root hash */ - if (blake2b_final(S->S, root, BLAKE2B_OUTBYTES) < 0) { - return -1; - } - - /* Set common block structure values */ - /* Copy values from parent instance, and only change the ones below */ - memcpy(P, S->P, sizeof(blake2b_param)); - P->key_length = 0; - P->fanout = 0; - P->depth = 0; - store32(&P->leaf_length, BLAKE2B_OUTBYTES); - P->inner_length = BLAKE2B_OUTBYTES; - P->node_depth = 0; - - for (i = 0; outlen > 0; ++i) { - const size_t block_size = (outlen < BLAKE2B_OUTBYTES) ? outlen : BLAKE2B_OUTBYTES; - /* Initialize state */ - P->digest_length = block_size; - store32(&P->node_offset, i); - blake2b_init_param(C, P); - /* Process key if needed */ - blake2b_update(C, root, BLAKE2B_OUTBYTES); - if (blake2b_final(C, (uint8_t *)out + i * BLAKE2B_OUTBYTES, block_size) < 0 ) { - return -1; - } - outlen -= block_size; - } - secure_zero_memory(root, sizeof(root)); - secure_zero_memory(P, sizeof(P)); - secure_zero_memory(C, sizeof(C)); - /* Put blake2xb in an invalid state? cf. blake2s_is_lastblock */ - return 0; - -} - -int blake2xb(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) -{ - blake2xb_state S[1]; - - /* Verify parameters */ - if (NULL == in && inlen > 0) - return -1; - - if (NULL == out) - return -1; - - if (NULL == key && keylen > 0) - return -1; - - if (keylen > BLAKE2B_KEYBYTES) - return -1; - - if (outlen == 0) - return -1; - - /* Initialize the root block structure */ - if (blake2xb_init_key(S, outlen, key, keylen) < 0) { - return -1; - } - - /* Absorb the input message */ - blake2xb_update(S, in, inlen); - - /* Compute the root node of the tree and the final hash using the counter construction */ - return blake2xb_final(S, out, outlen); -} - -#if defined(BLAKE2XB_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2B_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step, outlen; - - for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) { - key[i] = ( uint8_t )i; - } - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) { - buf[i] = ( uint8_t )i; - } - - /* Testing length of ouputs rather than inputs */ - /* (Test of input lengths mostly covered by blake2s tests) */ - - /* Test simple API */ - for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen ) - { - uint8_t hash[BLAKE2_KAT_LENGTH] = {0}; - if( blake2xb( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2B_KEYBYTES ) < 0 ) { - goto fail; - } - - if( 0 != memcmp( hash, blake2xb_keyed_kat[outlen-1], outlen ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { - for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) { - uint8_t hash[BLAKE2_KAT_LENGTH]; - blake2xb_state S; - uint8_t * p = buf; - size_t mlen = BLAKE2_KAT_LENGTH; - int err = 0; - - if( (err = blake2xb_init_key(&S, outlen, key, BLAKE2B_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2xb_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2xb_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2xb_final(&S, hash, outlen)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2xb_keyed_kat[outlen-1], outlen)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2016, JP Aumasson . + Copyright 2016, Samuel Neves . + + You may use this under the terms of the CC0, the OpenSSL Licence, or + the Apache Public License 2.0, at your option. The terms of these + licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +int blake2xb_init( blake2xb_state *S, const size_t outlen ) { + return blake2xb_init_key(S, outlen, NULL, 0); +} + +int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen) +{ + if ( outlen == 0 || outlen > 0xFFFFFFFFUL ) { + return -1; + } + + if (NULL != key && keylen > BLAKE2B_KEYBYTES) { + return -1; + } + + if (NULL == key && keylen > 0) { + return -1; + } + + /* Initialize parameter block */ + S->P->digest_length = BLAKE2B_OUTBYTES; + S->P->key_length = keylen; + S->P->fanout = 1; + S->P->depth = 1; + store32( &S->P->leaf_length, 0 ); + store32( &S->P->node_offset, 0 ); + store32( &S->P->xof_length, outlen ); + S->P->node_depth = 0; + S->P->inner_length = 0; + memset( S->P->reserved, 0, sizeof( S->P->reserved ) ); + memset( S->P->salt, 0, sizeof( S->P->salt ) ); + memset( S->P->personal, 0, sizeof( S->P->personal ) ); + + if( blake2b_init_param( S->S, S->P ) < 0 ) { + return -1; + } + + if (keylen > 0) { + uint8_t block[BLAKE2B_BLOCKBYTES]; + memset(block, 0, BLAKE2B_BLOCKBYTES); + memcpy(block, key, keylen); + blake2b_update(S->S, block, BLAKE2B_BLOCKBYTES); + secure_zero_memory(block, BLAKE2B_BLOCKBYTES); + } + return 0; +} + +int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ) { + return blake2b_update( S->S, in, inlen ); +} + +int blake2xb_final( blake2xb_state *S, void *out, size_t outlen) { + + blake2b_state C[1]; + blake2b_param P[1]; + uint32_t xof_length = load32(&S->P->xof_length); + uint8_t root[BLAKE2B_BLOCKBYTES]; + size_t i; + + if (NULL == out) { + return -1; + } + + /* outlen must match the output size defined in xof_length, */ + /* unless it was -1, in which case anything goes except 0. */ + if(xof_length == 0xFFFFFFFFUL) { + if(outlen == 0) { + return -1; + } + } else { + if(outlen != xof_length) { + return -1; + } + } + + /* Finalize the root hash */ + if (blake2b_final(S->S, root, BLAKE2B_OUTBYTES) < 0) { + return -1; + } + + /* Set common block structure values */ + /* Copy values from parent instance, and only change the ones below */ + memcpy(P, S->P, sizeof(blake2b_param)); + P->key_length = 0; + P->fanout = 0; + P->depth = 0; + store32(&P->leaf_length, BLAKE2B_OUTBYTES); + P->inner_length = BLAKE2B_OUTBYTES; + P->node_depth = 0; + + for (i = 0; outlen > 0; ++i) { + const size_t block_size = (outlen < BLAKE2B_OUTBYTES) ? outlen : BLAKE2B_OUTBYTES; + /* Initialize state */ + P->digest_length = block_size; + store32(&P->node_offset, i); + blake2b_init_param(C, P); + /* Process key if needed */ + blake2b_update(C, root, BLAKE2B_OUTBYTES); + if (blake2b_final(C, (uint8_t *)out + i * BLAKE2B_OUTBYTES, block_size) < 0 ) { + return -1; + } + outlen -= block_size; + } + secure_zero_memory(root, sizeof(root)); + secure_zero_memory(P, sizeof(P)); + secure_zero_memory(C, sizeof(C)); + /* Put blake2xb in an invalid state? cf. blake2s_is_lastblock */ + return 0; + +} + +int blake2xb(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) +{ + blake2xb_state S[1]; + + /* Verify parameters */ + if (NULL == in && inlen > 0) + return -1; + + if (NULL == out) + return -1; + + if (NULL == key && keylen > 0) + return -1; + + if (keylen > BLAKE2B_KEYBYTES) + return -1; + + if (outlen == 0) + return -1; + + /* Initialize the root block structure */ + if (blake2xb_init_key(S, outlen, key, keylen) < 0) { + return -1; + } + + /* Absorb the input message */ + blake2xb_update(S, in, inlen); + + /* Compute the root node of the tree and the final hash using the counter construction */ + return blake2xb_final(S, out, outlen); +} + +#if defined(BLAKE2XB_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2B_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step, outlen; + + for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) { + key[i] = ( uint8_t )i; + } + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) { + buf[i] = ( uint8_t )i; + } + + /* Testing length of ouputs rather than inputs */ + /* (Test of input lengths mostly covered by blake2s tests) */ + + /* Test simple API */ + for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen ) + { + uint8_t hash[BLAKE2_KAT_LENGTH] = {0}; + if( blake2xb( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2B_KEYBYTES ) < 0 ) { + goto fail; + } + + if( 0 != memcmp( hash, blake2xb_keyed_kat[outlen-1], outlen ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { + for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) { + uint8_t hash[BLAKE2_KAT_LENGTH]; + blake2xb_state S; + uint8_t * p = buf; + size_t mlen = BLAKE2_KAT_LENGTH; + int err = 0; + + if( (err = blake2xb_init_key(&S, outlen, key, BLAKE2B_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2xb_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2xb_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2xb_final(&S, hash, outlen)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2xb_keyed_kat[outlen-1], outlen)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2xs-ref.c b/src/third_party/blake2/blake2xs-ref.c index 4261e7ac..60702fa4 100644 --- a/src/third_party/blake2/blake2xs-ref.c +++ b/src/third_party/blake2/blake2xs-ref.c @@ -1,239 +1,239 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2016, JP Aumasson . - Copyright 2016, Samuel Neves . - - You may use this under the terms of the CC0, the OpenSSL Licence, or - the Apache Public License 2.0, at your option. The terms of these - licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#include "blake2.h" -#include "blake2-impl.h" - -int blake2xs_init( blake2xs_state *S, const size_t outlen ) { - return blake2xs_init_key(S, outlen, NULL, 0); -} - -int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ) -{ - if ( outlen == 0 || outlen > 0xFFFFUL ) { - return -1; - } - - if (NULL != key && keylen > BLAKE2S_KEYBYTES) { - return -1; - } - - if (NULL == key && keylen > 0) { - return -1; - } - - /* Initialize parameter block */ - S->P->digest_length = BLAKE2S_OUTBYTES; - S->P->key_length = keylen; - S->P->fanout = 1; - S->P->depth = 1; - store32( &S->P->leaf_length, 0 ); - store32( &S->P->node_offset, 0 ); - store16( &S->P->xof_length, outlen ); - S->P->node_depth = 0; - S->P->inner_length = 0; - memset( S->P->salt, 0, sizeof( S->P->salt ) ); - memset( S->P->personal, 0, sizeof( S->P->personal ) ); - - if( blake2s_init_param( S->S, S->P ) < 0 ) { - return -1; - } - - if (keylen > 0) { - uint8_t block[BLAKE2S_BLOCKBYTES]; - memset(block, 0, BLAKE2S_BLOCKBYTES); - memcpy(block, key, keylen); - blake2s_update(S->S, block, BLAKE2S_BLOCKBYTES); - secure_zero_memory(block, BLAKE2S_BLOCKBYTES); - } - return 0; -} - -int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ) { - return blake2s_update( S->S, in, inlen ); -} - -int blake2xs_final(blake2xs_state *S, void *out, size_t outlen) { - - blake2s_state C[1]; - blake2s_param P[1]; - uint16_t xof_length = load16(&S->P->xof_length); - uint8_t root[BLAKE2S_BLOCKBYTES]; - size_t i; - - if (NULL == out) { - return -1; - } - - /* outlen must match the output size defined in xof_length, */ - /* unless it was -1, in which case anything goes except 0. */ - if(xof_length == 0xFFFFUL) { - if(outlen == 0) { - return -1; - } - } else { - if(outlen != xof_length) { - return -1; - } - } - - /* Finalize the root hash */ - if (blake2s_final(S->S, root, BLAKE2S_OUTBYTES) < 0) { - return -1; - } - - /* Set common block structure values */ - /* Copy values from parent instance, and only change the ones below */ - memcpy(P, S->P, sizeof(blake2s_param)); - P->key_length = 0; - P->fanout = 0; - P->depth = 0; - store32(&P->leaf_length, BLAKE2S_OUTBYTES); - P->inner_length = BLAKE2S_OUTBYTES; - P->node_depth = 0; - - for (i = 0; outlen > 0; ++i) { - const size_t block_size = (outlen < BLAKE2S_OUTBYTES) ? outlen : BLAKE2S_OUTBYTES; - /* Initialize state */ - P->digest_length = block_size; - store32(&P->node_offset, i); - blake2s_init_param(C, P); - /* Process key if needed */ - blake2s_update(C, root, BLAKE2S_OUTBYTES); - if (blake2s_final(C, (uint8_t *)out + i * BLAKE2S_OUTBYTES, block_size) < 0) { - return -1; - } - outlen -= block_size; - } - secure_zero_memory(root, sizeof(root)); - secure_zero_memory(P, sizeof(P)); - secure_zero_memory(C, sizeof(C)); - /* Put blake2xs in an invalid state? cf. blake2s_is_lastblock */ - return 0; -} - -int blake2xs(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) -{ - blake2xs_state S[1]; - - /* Verify parameters */ - if (NULL == in && inlen > 0) - return -1; - - if (NULL == out) - return -1; - - if (NULL == key && keylen > 0) - return -1; - - if (keylen > BLAKE2S_KEYBYTES) - return -1; - - if (outlen == 0) - return -1; - - /* Initialize the root block structure */ - if (blake2xs_init_key(S, outlen, key, keylen) < 0) { - return -1; - } - - /* Absorb the input message */ - blake2xs_update(S, in, inlen); - - /* Compute the root node of the tree and the final hash using the counter construction */ - return blake2xs_final(S, out, outlen); -} - -#if defined(BLAKE2XS_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2S_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step, outlen; - - for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) { - key[i] = ( uint8_t )i; - } - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) { - buf[i] = ( uint8_t )i; - } - - /* Testing length of outputs rather than inputs */ - /* (Test of input lengths mostly covered by blake2s tests) */ - - /* Test simple API */ - for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen ) - { - uint8_t hash[BLAKE2_KAT_LENGTH] = {0}; - if( blake2xs( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2S_KEYBYTES ) < 0 ) { - goto fail; - } - - if( 0 != memcmp( hash, blake2xs_keyed_kat[outlen-1], outlen ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { - for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) { - uint8_t hash[BLAKE2_KAT_LENGTH]; - blake2xs_state S; - uint8_t * p = buf; - size_t mlen = BLAKE2_KAT_LENGTH; - int err = 0; - - if( (err = blake2xs_init_key(&S, outlen, key, BLAKE2S_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2xs_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2xs_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2xs_final(&S, hash, outlen)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2xs_keyed_kat[outlen-1], outlen)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2016, JP Aumasson . + Copyright 2016, Samuel Neves . + + You may use this under the terms of the CC0, the OpenSSL Licence, or + the Apache Public License 2.0, at your option. The terms of these + licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +int blake2xs_init( blake2xs_state *S, const size_t outlen ) { + return blake2xs_init_key(S, outlen, NULL, 0); +} + +int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ) +{ + if ( outlen == 0 || outlen > 0xFFFFUL ) { + return -1; + } + + if (NULL != key && keylen > BLAKE2S_KEYBYTES) { + return -1; + } + + if (NULL == key && keylen > 0) { + return -1; + } + + /* Initialize parameter block */ + S->P->digest_length = BLAKE2S_OUTBYTES; + S->P->key_length = keylen; + S->P->fanout = 1; + S->P->depth = 1; + store32( &S->P->leaf_length, 0 ); + store32( &S->P->node_offset, 0 ); + store16( &S->P->xof_length, outlen ); + S->P->node_depth = 0; + S->P->inner_length = 0; + memset( S->P->salt, 0, sizeof( S->P->salt ) ); + memset( S->P->personal, 0, sizeof( S->P->personal ) ); + + if( blake2s_init_param( S->S, S->P ) < 0 ) { + return -1; + } + + if (keylen > 0) { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset(block, 0, BLAKE2S_BLOCKBYTES); + memcpy(block, key, keylen); + blake2s_update(S->S, block, BLAKE2S_BLOCKBYTES); + secure_zero_memory(block, BLAKE2S_BLOCKBYTES); + } + return 0; +} + +int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ) { + return blake2s_update( S->S, in, inlen ); +} + +int blake2xs_final(blake2xs_state *S, void *out, size_t outlen) { + + blake2s_state C[1]; + blake2s_param P[1]; + uint16_t xof_length = load16(&S->P->xof_length); + uint8_t root[BLAKE2S_BLOCKBYTES]; + size_t i; + + if (NULL == out) { + return -1; + } + + /* outlen must match the output size defined in xof_length, */ + /* unless it was -1, in which case anything goes except 0. */ + if(xof_length == 0xFFFFUL) { + if(outlen == 0) { + return -1; + } + } else { + if(outlen != xof_length) { + return -1; + } + } + + /* Finalize the root hash */ + if (blake2s_final(S->S, root, BLAKE2S_OUTBYTES) < 0) { + return -1; + } + + /* Set common block structure values */ + /* Copy values from parent instance, and only change the ones below */ + memcpy(P, S->P, sizeof(blake2s_param)); + P->key_length = 0; + P->fanout = 0; + P->depth = 0; + store32(&P->leaf_length, BLAKE2S_OUTBYTES); + P->inner_length = BLAKE2S_OUTBYTES; + P->node_depth = 0; + + for (i = 0; outlen > 0; ++i) { + const size_t block_size = (outlen < BLAKE2S_OUTBYTES) ? outlen : BLAKE2S_OUTBYTES; + /* Initialize state */ + P->digest_length = block_size; + store32(&P->node_offset, i); + blake2s_init_param(C, P); + /* Process key if needed */ + blake2s_update(C, root, BLAKE2S_OUTBYTES); + if (blake2s_final(C, (uint8_t *)out + i * BLAKE2S_OUTBYTES, block_size) < 0) { + return -1; + } + outlen -= block_size; + } + secure_zero_memory(root, sizeof(root)); + secure_zero_memory(P, sizeof(P)); + secure_zero_memory(C, sizeof(C)); + /* Put blake2xs in an invalid state? cf. blake2s_is_lastblock */ + return 0; +} + +int blake2xs(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) +{ + blake2xs_state S[1]; + + /* Verify parameters */ + if (NULL == in && inlen > 0) + return -1; + + if (NULL == out) + return -1; + + if (NULL == key && keylen > 0) + return -1; + + if (keylen > BLAKE2S_KEYBYTES) + return -1; + + if (outlen == 0) + return -1; + + /* Initialize the root block structure */ + if (blake2xs_init_key(S, outlen, key, keylen) < 0) { + return -1; + } + + /* Absorb the input message */ + blake2xs_update(S, in, inlen); + + /* Compute the root node of the tree and the final hash using the counter construction */ + return blake2xs_final(S, out, outlen); +} + +#if defined(BLAKE2XS_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2S_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step, outlen; + + for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) { + key[i] = ( uint8_t )i; + } + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) { + buf[i] = ( uint8_t )i; + } + + /* Testing length of outputs rather than inputs */ + /* (Test of input lengths mostly covered by blake2s tests) */ + + /* Test simple API */ + for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen ) + { + uint8_t hash[BLAKE2_KAT_LENGTH] = {0}; + if( blake2xs( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2S_KEYBYTES ) < 0 ) { + goto fail; + } + + if( 0 != memcmp( hash, blake2xs_keyed_kat[outlen-1], outlen ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { + for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) { + uint8_t hash[BLAKE2_KAT_LENGTH]; + blake2xs_state S; + uint8_t * p = buf; + size_t mlen = BLAKE2_KAT_LENGTH; + int err = 0; + + if( (err = blake2xs_init_key(&S, outlen, key, BLAKE2S_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2xs_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2xs_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2xs_final(&S, hash, outlen)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2xs_keyed_kat[outlen-1], outlen)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/blake2xs.c b/src/third_party/blake2/blake2xs.c index 4b208e27..ed46df19 100644 --- a/src/third_party/blake2/blake2xs.c +++ b/src/third_party/blake2/blake2xs.c @@ -1,239 +1,239 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2016, JP Aumasson . - Copyright 2016, Samuel Neves . - - You may use this under the terms of the CC0, the OpenSSL Licence, or - the Apache Public License 2.0, at your option. The terms of these - licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#include "blake2.h" -#include "blake2-impl.h" - -int blake2xs_init( blake2xs_state *S, const size_t outlen ) { - return blake2xs_init_key(S, outlen, NULL, 0); -} - -int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ) -{ - if ( outlen == 0 || outlen > 0xFFFFUL ) { - return -1; - } - - if (NULL != key && keylen > BLAKE2S_KEYBYTES) { - return -1; - } - - if (NULL == key && keylen > 0) { - return -1; - } - - /* Initialize parameter block */ - S->P->digest_length = BLAKE2S_OUTBYTES; - S->P->key_length = keylen; - S->P->fanout = 1; - S->P->depth = 1; - store32( &S->P->leaf_length, 0 ); - store32( &S->P->node_offset, 0 ); - store16( &S->P->xof_length, outlen ); - S->P->node_depth = 0; - S->P->inner_length = 0; - memset( S->P->salt, 0, sizeof( S->P->salt ) ); - memset( S->P->personal, 0, sizeof( S->P->personal ) ); - - if( blake2s_init_param( S->S, S->P ) < 0 ) { - return -1; - } - - if (keylen > 0) { - uint8_t block[BLAKE2S_BLOCKBYTES]; - memset(block, 0, BLAKE2S_BLOCKBYTES); - memcpy(block, key, keylen); - blake2s_update(S->S, block, BLAKE2S_BLOCKBYTES); - secure_zero_memory(block, BLAKE2S_BLOCKBYTES); - } - return 0; -} - -int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ) { - return blake2s_update( S->S, in, inlen ); -} - -int blake2xs_final(blake2xs_state *S, void *out, size_t outlen) { - - blake2s_state C[1]; - blake2s_param P[1]; - uint16_t xof_length = load16(&S->P->xof_length); - uint8_t root[BLAKE2S_BLOCKBYTES]; - size_t i; - - if (NULL == out) { - return -1; - } - - /* outlen must match the output size defined in xof_length, */ - /* unless it was -1, in which case anything goes except 0. */ - if(xof_length == 0xFFFFUL) { - if(outlen == 0) { - return -1; - } - } else { - if(outlen != xof_length) { - return -1; - } - } - - /* Finalize the root hash */ - if (blake2s_final(S->S, root, BLAKE2S_OUTBYTES) < 0) { - return -1; - } - - /* Set common block structure values */ - /* Copy values from parent instance, and only change the ones below */ - memcpy(P, S->P, sizeof(blake2s_param)); - P->key_length = 0; - P->fanout = 0; - P->depth = 0; - store32(&P->leaf_length, BLAKE2S_OUTBYTES); - P->inner_length = BLAKE2S_OUTBYTES; - P->node_depth = 0; - - for (i = 0; outlen > 0; ++i) { - const size_t block_size = (outlen < BLAKE2S_OUTBYTES) ? outlen : BLAKE2S_OUTBYTES; - /* Initialize state */ - P->digest_length = block_size; - store32(&P->node_offset, i); - blake2s_init_param(C, P); - /* Process key if needed */ - blake2s_update(C, root, BLAKE2S_OUTBYTES); - if (blake2s_final(C, (uint8_t *)out + i * BLAKE2S_OUTBYTES, block_size) < 0) { - return -1; - } - outlen -= block_size; - } - secure_zero_memory(root, sizeof(root)); - secure_zero_memory(P, sizeof(P)); - secure_zero_memory(C, sizeof(C)); - /* Put blake2xs in an invalid state? cf. blake2s_is_lastblock */ - return 0; -} - -int blake2xs(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) -{ - blake2xs_state S[1]; - - /* Verify parameters */ - if (NULL == in && inlen > 0) - return -1; - - if (NULL == out) - return -1; - - if (NULL == key && keylen > 0) - return -1; - - if (keylen > BLAKE2S_KEYBYTES) - return -1; - - if (outlen == 0) - return -1; - - /* Initialize the root block structure */ - if (blake2xs_init_key(S, outlen, key, keylen) < 0) { - return -1; - } - - /* Absorb the input message */ - blake2xs_update(S, in, inlen); - - /* Compute the root node of the tree and the final hash using the counter construction */ - return blake2xs_final(S, out, outlen); -} - -#if defined(BLAKE2XS_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2S_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step, outlen; - - for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) { - key[i] = ( uint8_t )i; - } - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) { - buf[i] = ( uint8_t )i; - } - - /* Testing length of ouputs rather than inputs */ - /* (Test of input lengths mostly covered by blake2s tests) */ - - /* Test simple API */ - for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen ) - { - uint8_t hash[BLAKE2_KAT_LENGTH] = {0}; - if( blake2xs( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2S_KEYBYTES ) < 0 ) { - goto fail; - } - - if( 0 != memcmp( hash, blake2xs_keyed_kat[outlen-1], outlen ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { - for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) { - uint8_t hash[BLAKE2_KAT_LENGTH]; - blake2xs_state S; - uint8_t * p = buf; - size_t mlen = BLAKE2_KAT_LENGTH; - int err = 0; - - if( (err = blake2xs_init_key(&S, outlen, key, BLAKE2S_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2xs_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2xs_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2xs_final(&S, hash, outlen)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2xs_keyed_kat[outlen-1], outlen)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2016, JP Aumasson . + Copyright 2016, Samuel Neves . + + You may use this under the terms of the CC0, the OpenSSL Licence, or + the Apache Public License 2.0, at your option. The terms of these + licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include + +#include "blake2.h" +#include "blake2-impl.h" + +int blake2xs_init( blake2xs_state *S, const size_t outlen ) { + return blake2xs_init_key(S, outlen, NULL, 0); +} + +int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ) +{ + if ( outlen == 0 || outlen > 0xFFFFUL ) { + return -1; + } + + if (NULL != key && keylen > BLAKE2S_KEYBYTES) { + return -1; + } + + if (NULL == key && keylen > 0) { + return -1; + } + + /* Initialize parameter block */ + S->P->digest_length = BLAKE2S_OUTBYTES; + S->P->key_length = keylen; + S->P->fanout = 1; + S->P->depth = 1; + store32( &S->P->leaf_length, 0 ); + store32( &S->P->node_offset, 0 ); + store16( &S->P->xof_length, outlen ); + S->P->node_depth = 0; + S->P->inner_length = 0; + memset( S->P->salt, 0, sizeof( S->P->salt ) ); + memset( S->P->personal, 0, sizeof( S->P->personal ) ); + + if( blake2s_init_param( S->S, S->P ) < 0 ) { + return -1; + } + + if (keylen > 0) { + uint8_t block[BLAKE2S_BLOCKBYTES]; + memset(block, 0, BLAKE2S_BLOCKBYTES); + memcpy(block, key, keylen); + blake2s_update(S->S, block, BLAKE2S_BLOCKBYTES); + secure_zero_memory(block, BLAKE2S_BLOCKBYTES); + } + return 0; +} + +int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ) { + return blake2s_update( S->S, in, inlen ); +} + +int blake2xs_final(blake2xs_state *S, void *out, size_t outlen) { + + blake2s_state C[1]; + blake2s_param P[1]; + uint16_t xof_length = load16(&S->P->xof_length); + uint8_t root[BLAKE2S_BLOCKBYTES]; + size_t i; + + if (NULL == out) { + return -1; + } + + /* outlen must match the output size defined in xof_length, */ + /* unless it was -1, in which case anything goes except 0. */ + if(xof_length == 0xFFFFUL) { + if(outlen == 0) { + return -1; + } + } else { + if(outlen != xof_length) { + return -1; + } + } + + /* Finalize the root hash */ + if (blake2s_final(S->S, root, BLAKE2S_OUTBYTES) < 0) { + return -1; + } + + /* Set common block structure values */ + /* Copy values from parent instance, and only change the ones below */ + memcpy(P, S->P, sizeof(blake2s_param)); + P->key_length = 0; + P->fanout = 0; + P->depth = 0; + store32(&P->leaf_length, BLAKE2S_OUTBYTES); + P->inner_length = BLAKE2S_OUTBYTES; + P->node_depth = 0; + + for (i = 0; outlen > 0; ++i) { + const size_t block_size = (outlen < BLAKE2S_OUTBYTES) ? outlen : BLAKE2S_OUTBYTES; + /* Initialize state */ + P->digest_length = block_size; + store32(&P->node_offset, i); + blake2s_init_param(C, P); + /* Process key if needed */ + blake2s_update(C, root, BLAKE2S_OUTBYTES); + if (blake2s_final(C, (uint8_t *)out + i * BLAKE2S_OUTBYTES, block_size) < 0) { + return -1; + } + outlen -= block_size; + } + secure_zero_memory(root, sizeof(root)); + secure_zero_memory(P, sizeof(P)); + secure_zero_memory(C, sizeof(C)); + /* Put blake2xs in an invalid state? cf. blake2s_is_lastblock */ + return 0; +} + +int blake2xs(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) +{ + blake2xs_state S[1]; + + /* Verify parameters */ + if (NULL == in && inlen > 0) + return -1; + + if (NULL == out) + return -1; + + if (NULL == key && keylen > 0) + return -1; + + if (keylen > BLAKE2S_KEYBYTES) + return -1; + + if (outlen == 0) + return -1; + + /* Initialize the root block structure */ + if (blake2xs_init_key(S, outlen, key, keylen) < 0) { + return -1; + } + + /* Absorb the input message */ + blake2xs_update(S, in, inlen); + + /* Compute the root node of the tree and the final hash using the counter construction */ + return blake2xs_final(S, out, outlen); +} + +#if defined(BLAKE2XS_SELFTEST) +#include +#include "blake2-kat.h" +int main( void ) +{ + uint8_t key[BLAKE2S_KEYBYTES]; + uint8_t buf[BLAKE2_KAT_LENGTH]; + size_t i, step, outlen; + + for( i = 0; i < BLAKE2S_KEYBYTES; ++i ) { + key[i] = ( uint8_t )i; + } + + for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) { + buf[i] = ( uint8_t )i; + } + + /* Testing length of ouputs rather than inputs */ + /* (Test of input lengths mostly covered by blake2s tests) */ + + /* Test simple API */ + for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen ) + { + uint8_t hash[BLAKE2_KAT_LENGTH] = {0}; + if( blake2xs( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2S_KEYBYTES ) < 0 ) { + goto fail; + } + + if( 0 != memcmp( hash, blake2xs_keyed_kat[outlen-1], outlen ) ) + { + goto fail; + } + } + + /* Test streaming API */ + for(step = 1; step < BLAKE2S_BLOCKBYTES; ++step) { + for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) { + uint8_t hash[BLAKE2_KAT_LENGTH]; + blake2xs_state S; + uint8_t * p = buf; + size_t mlen = BLAKE2_KAT_LENGTH; + int err = 0; + + if( (err = blake2xs_init_key(&S, outlen, key, BLAKE2S_KEYBYTES)) < 0 ) { + goto fail; + } + + while (mlen >= step) { + if ( (err = blake2xs_update(&S, p, step)) < 0 ) { + goto fail; + } + mlen -= step; + p += step; + } + if ( (err = blake2xs_update(&S, p, mlen)) < 0) { + goto fail; + } + if ( (err = blake2xs_final(&S, hash, outlen)) < 0) { + goto fail; + } + + if (0 != memcmp(hash, blake2xs_keyed_kat[outlen-1], outlen)) { + goto fail; + } + } + } + + puts( "ok" ); + return 0; +fail: + puts("error"); + return -1; +} +#endif diff --git a/src/third_party/blake2/genkat-c.c b/src/third_party/blake2/genkat-c.c index 58a48fda..b124c7ac 100644 --- a/src/third_party/blake2/genkat-c.c +++ b/src/third_party/blake2/genkat-c.c @@ -1,139 +1,139 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include -#include - -#include "blake2.h" - -#define STR_(x) #x -#define STR(x) STR_(x) - -#define LENGTH 256 - -#define MAKE_KAT(name, size_prefix) \ - do { \ - printf("static const uint8_t " #name "_kat[BLAKE2_KAT_LENGTH][" #size_prefix \ - "_OUTBYTES] = \n{\n"); \ - \ - for (i = 0; i < LENGTH; ++i) { \ - name(hash, size_prefix##_OUTBYTES, in, i, NULL, 0); \ - printf("\t{\n\t\t"); \ - \ - for (j = 0; j < size_prefix##_OUTBYTES; ++j) \ - printf("0x%02X%s", hash[j], \ - (j + 1) == size_prefix##_OUTBYTES ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ - \ - printf("\t},\n"); \ - } \ - \ - printf("};\n\n\n\n\n"); \ - } while (0) - -#define MAKE_KEYED_KAT(name, size_prefix) \ - do { \ - printf("static const uint8_t " #name "_keyed_kat[BLAKE2_KAT_LENGTH][" #size_prefix \ - "_OUTBYTES] = \n{\n"); \ - \ - for (i = 0; i < LENGTH; ++i) { \ - name(hash, size_prefix##_OUTBYTES, in, i, key, size_prefix##_KEYBYTES); \ - printf("\t{\n\t\t"); \ - \ - for (j = 0; j < size_prefix##_OUTBYTES; ++j) \ - printf("0x%02X%s", hash[j], \ - (j + 1) == size_prefix##_OUTBYTES ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ - \ - printf("\t},\n"); \ - } \ - \ - printf("};\n\n\n\n\n"); \ - } while (0) - -#define MAKE_XOF_KAT(name) \ - do { \ - printf("static const uint8_t " #name "_kat[BLAKE2_KAT_LENGTH][BLAKE2_KAT_LENGTH] = \n{\n"); \ - \ - for (i = 1; i <= LENGTH; ++i) { \ - name(hash, i, in, LENGTH, NULL, 0); \ - printf("\t{\n\t\t"); \ - \ - for (j = 0; j < i; ++j) \ - printf("0x%02X%s", hash[j], \ - (j + 1) == LENGTH ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ - \ - for (j = i; j < LENGTH; ++j) \ - printf("0x00%s", (j + 1) == LENGTH ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ - \ - printf("\t},\n"); \ - } \ - \ - printf("};\n\n\n\n\n"); \ - } while (0) - -#define MAKE_XOF_KEYED_KAT(name, size_prefix) \ - do { \ - printf("static const uint8_t " #name \ - "_keyed_kat[BLAKE2_KAT_LENGTH][BLAKE2_KAT_LENGTH] = \n{\n"); \ - \ - for (i = 1; i <= LENGTH; ++i) { \ - name(hash, i, in, LENGTH, key, size_prefix##_KEYBYTES); \ - printf("\t{\n\t\t"); \ - \ - for (j = 0; j < i; ++j) \ - printf("0x%02X%s", hash[j], \ - (j + 1) == LENGTH ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ - \ - for (j = i; j < LENGTH; ++j) \ - printf("0x00%s", (j + 1) == LENGTH ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ - \ - printf("\t},\n"); \ - } \ - \ - printf("};\n\n\n\n\n"); \ - } while (0) - -int main() { - uint8_t key[64] = {0}; - uint8_t in[LENGTH] = {0}; - uint8_t hash[LENGTH] = {0}; - size_t i, j; - - for (i = 0; i < sizeof(in); ++i) - in[i] = i; - - for (i = 0; i < sizeof(key); ++i) - key[i] = i; - - puts("#ifndef BLAKE2_KAT_H\n" - "#define BLAKE2_KAT_H\n\n\n" - "#include \n\n" - "#define BLAKE2_KAT_LENGTH " STR(LENGTH) "\n\n\n"); - MAKE_KAT(blake2s, BLAKE2S); - MAKE_KEYED_KAT(blake2s, BLAKE2S); - MAKE_KAT(blake2b, BLAKE2B); - MAKE_KEYED_KAT(blake2b, BLAKE2B); - MAKE_KAT(blake2sp, BLAKE2S); - MAKE_KEYED_KAT(blake2sp, BLAKE2S); - MAKE_KAT(blake2bp, BLAKE2B); - MAKE_KEYED_KAT(blake2bp, BLAKE2B); - MAKE_XOF_KAT(blake2xs); - MAKE_XOF_KEYED_KAT(blake2xs, BLAKE2S); - MAKE_XOF_KAT(blake2xb); - MAKE_XOF_KEYED_KAT(blake2xb, BLAKE2B); - puts("#endif"); - return 0; -} +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include +#include + +#include "blake2.h" + +#define STR_(x) #x +#define STR(x) STR_(x) + +#define LENGTH 256 + +#define MAKE_KAT(name, size_prefix) \ + do { \ + printf("static const uint8_t " #name "_kat[BLAKE2_KAT_LENGTH][" #size_prefix \ + "_OUTBYTES] = \n{\n"); \ + \ + for (i = 0; i < LENGTH; ++i) { \ + name(hash, size_prefix##_OUTBYTES, in, i, NULL, 0); \ + printf("\t{\n\t\t"); \ + \ + for (j = 0; j < size_prefix##_OUTBYTES; ++j) \ + printf("0x%02X%s", hash[j], \ + (j + 1) == size_prefix##_OUTBYTES ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ + \ + printf("\t},\n"); \ + } \ + \ + printf("};\n\n\n\n\n"); \ + } while (0) + +#define MAKE_KEYED_KAT(name, size_prefix) \ + do { \ + printf("static const uint8_t " #name "_keyed_kat[BLAKE2_KAT_LENGTH][" #size_prefix \ + "_OUTBYTES] = \n{\n"); \ + \ + for (i = 0; i < LENGTH; ++i) { \ + name(hash, size_prefix##_OUTBYTES, in, i, key, size_prefix##_KEYBYTES); \ + printf("\t{\n\t\t"); \ + \ + for (j = 0; j < size_prefix##_OUTBYTES; ++j) \ + printf("0x%02X%s", hash[j], \ + (j + 1) == size_prefix##_OUTBYTES ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ + \ + printf("\t},\n"); \ + } \ + \ + printf("};\n\n\n\n\n"); \ + } while (0) + +#define MAKE_XOF_KAT(name) \ + do { \ + printf("static const uint8_t " #name "_kat[BLAKE2_KAT_LENGTH][BLAKE2_KAT_LENGTH] = \n{\n"); \ + \ + for (i = 1; i <= LENGTH; ++i) { \ + name(hash, i, in, LENGTH, NULL, 0); \ + printf("\t{\n\t\t"); \ + \ + for (j = 0; j < i; ++j) \ + printf("0x%02X%s", hash[j], \ + (j + 1) == LENGTH ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ + \ + for (j = i; j < LENGTH; ++j) \ + printf("0x00%s", (j + 1) == LENGTH ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ + \ + printf("\t},\n"); \ + } \ + \ + printf("};\n\n\n\n\n"); \ + } while (0) + +#define MAKE_XOF_KEYED_KAT(name, size_prefix) \ + do { \ + printf("static const uint8_t " #name \ + "_keyed_kat[BLAKE2_KAT_LENGTH][BLAKE2_KAT_LENGTH] = \n{\n"); \ + \ + for (i = 1; i <= LENGTH; ++i) { \ + name(hash, i, in, LENGTH, key, size_prefix##_KEYBYTES); \ + printf("\t{\n\t\t"); \ + \ + for (j = 0; j < i; ++j) \ + printf("0x%02X%s", hash[j], \ + (j + 1) == LENGTH ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ + \ + for (j = i; j < LENGTH; ++j) \ + printf("0x00%s", (j + 1) == LENGTH ? "\n" : j && !((j + 1) % 8) ? ",\n\t\t" : ", "); \ + \ + printf("\t},\n"); \ + } \ + \ + printf("};\n\n\n\n\n"); \ + } while (0) + +int main() { + uint8_t key[64] = {0}; + uint8_t in[LENGTH] = {0}; + uint8_t hash[LENGTH] = {0}; + size_t i, j; + + for (i = 0; i < sizeof(in); ++i) + in[i] = i; + + for (i = 0; i < sizeof(key); ++i) + key[i] = i; + + puts("#ifndef BLAKE2_KAT_H\n" + "#define BLAKE2_KAT_H\n\n\n" + "#include \n\n" + "#define BLAKE2_KAT_LENGTH " STR(LENGTH) "\n\n\n"); + MAKE_KAT(blake2s, BLAKE2S); + MAKE_KEYED_KAT(blake2s, BLAKE2S); + MAKE_KAT(blake2b, BLAKE2B); + MAKE_KEYED_KAT(blake2b, BLAKE2B); + MAKE_KAT(blake2sp, BLAKE2S); + MAKE_KEYED_KAT(blake2sp, BLAKE2S); + MAKE_KAT(blake2bp, BLAKE2B); + MAKE_KEYED_KAT(blake2bp, BLAKE2B); + MAKE_XOF_KAT(blake2xs); + MAKE_XOF_KEYED_KAT(blake2xs, BLAKE2S); + MAKE_XOF_KAT(blake2xb); + MAKE_XOF_KEYED_KAT(blake2xb, BLAKE2B); + puts("#endif"); + return 0; +} diff --git a/src/third_party/blake2/genkat-json.c b/src/third_party/blake2/genkat-json.c index 0275fb51..78d3afb6 100644 --- a/src/third_party/blake2/genkat-json.c +++ b/src/third_party/blake2/genkat-json.c @@ -1,154 +1,154 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include -#include - -#include "blake2.h" - -#define STR_(x) #x -#define STR(x) STR_(x) - -#define LENGTH 256 - -#define MAKE_KAT(name, size_prefix, first) \ - do { \ - for (i = 0; i < LENGTH; ++i) { \ - printf("%s\n{\n", i == 0 && first ? "" : ","); \ - \ - printf(" \"hash\": \"" #name "\",\n"); \ - printf(" \"in\": \""); \ - for (j = 0; j < i; ++j) \ - printf("%02x", in[j]); \ - \ - printf("\",\n"); \ - printf(" \"key\": \"\",\n"); \ - printf(" \"out\": \""); \ - \ - name(hash, size_prefix##_OUTBYTES, in, i, NULL, 0); \ - \ - for (j = 0; j < size_prefix##_OUTBYTES; ++j) \ - printf("%02x", hash[j]); \ - printf("\"\n"); \ - printf("}"); \ - } \ - } while (0) - -#define MAKE_KEYED_KAT(name, size_prefix, first) \ - do { \ - for (i = 0; i < LENGTH; ++i) { \ - printf("%s\n{\n", i == 0 && first ? "" : ","); \ - \ - printf(" \"hash\": \"" #name "\",\n"); \ - printf(" \"in\": \""); \ - for (j = 0; j < i; ++j) \ - printf("%02x", in[j]); \ - \ - printf("\",\n"); \ - printf(" \"key\": \""); \ - for (j = 0; j < size_prefix##_KEYBYTES; ++j) \ - printf("%02x", key[j]); \ - printf("\",\n"); \ - printf(" \"out\": \""); \ - \ - name(hash, size_prefix##_OUTBYTES, in, i, key, size_prefix##_KEYBYTES); \ - \ - for (j = 0; j < size_prefix##_OUTBYTES; ++j) \ - printf("%02x", hash[j]); \ - printf("\"\n"); \ - printf("}"); \ - } \ - } while (0) - -#define MAKE_XOF_KAT(name, first) \ - do { \ - for (i = 1; i <= LENGTH; ++i) { \ - printf("%s\n{\n", i == 1 && first ? "" : ","); \ - \ - printf(" \"hash\": \"" #name "\",\n"); \ - printf(" \"in\": \""); \ - for (j = 0; j < LENGTH; ++j) \ - printf("%02x", in[j]); \ - \ - printf("\",\n"); \ - printf(" \"key\": \"\",\n"); \ - printf(" \"out\": \""); \ - \ - name(hash, i, in, LENGTH, NULL, 0); \ - \ - for (j = 0; j < i; ++j) \ - printf("%02x", hash[j]); \ - printf("\"\n"); \ - printf("}"); \ - } \ - } while (0) - -#define MAKE_XOF_KEYED_KAT(name, size_prefix, first) \ - do { \ - for (i = 1; i <= LENGTH; ++i) { \ - printf("%s\n{\n", i == 1 && first ? "" : ","); \ - \ - printf(" \"hash\": \"" #name "\",\n"); \ - printf(" \"in\": \""); \ - for (j = 0; j < LENGTH; ++j) \ - printf("%02x", in[j]); \ - \ - printf("\",\n"); \ - printf(" \"key\": \""); \ - for (j = 0; j < size_prefix##_KEYBYTES; ++j) \ - printf("%02x", key[j]); \ - printf("\",\n"); \ - printf(" \"out\": \""); \ - \ - name(hash, i, in, LENGTH, key, size_prefix##_KEYBYTES); \ - \ - for (j = 0; j < i; ++j) \ - printf("%02x", hash[j]); \ - printf("\"\n"); \ - printf("}"); \ - } \ - } while (0) - -int main() { - uint8_t key[64] = {0}; - uint8_t in[LENGTH] = {0}; - uint8_t hash[LENGTH] = {0}; - size_t i, j; - - for (i = 0; i < sizeof(in); ++i) - in[i] = i; - - for (i = 0; i < sizeof(key); ++i) - key[i] = i; - - printf("["); - MAKE_KAT(blake2s, BLAKE2S, 1); - MAKE_KEYED_KAT(blake2s, BLAKE2S, 0); - MAKE_KAT(blake2b, BLAKE2B, 0); - MAKE_KEYED_KAT(blake2b, BLAKE2B, 0); - MAKE_KAT(blake2sp, BLAKE2S, 0); - MAKE_KEYED_KAT(blake2sp, BLAKE2S, 0); - MAKE_KAT(blake2bp, BLAKE2B, 0); - MAKE_KEYED_KAT(blake2bp, BLAKE2B, 0); - MAKE_XOF_KAT(blake2xs, 0); - MAKE_XOF_KEYED_KAT(blake2xs, BLAKE2S, 0); - MAKE_XOF_KAT(blake2xb, 0); - MAKE_XOF_KEYED_KAT(blake2xb, BLAKE2B, 0); - printf("\n]\n"); - fflush(stdout); - return 0; -} +/* + BLAKE2 reference source code package - reference C implementations + + Copyright 2012, Samuel Neves . You may use this under the + terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at + your option. The terms of these licenses can be found at: + + - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 + - OpenSSL license : https://www.openssl.org/source/license.html + - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 + + More information about the BLAKE2 hash function can be found at + https://blake2.net. +*/ + +#include +#include +#include +#include + +#include "blake2.h" + +#define STR_(x) #x +#define STR(x) STR_(x) + +#define LENGTH 256 + +#define MAKE_KAT(name, size_prefix, first) \ + do { \ + for (i = 0; i < LENGTH; ++i) { \ + printf("%s\n{\n", i == 0 && first ? "" : ","); \ + \ + printf(" \"hash\": \"" #name "\",\n"); \ + printf(" \"in\": \""); \ + for (j = 0; j < i; ++j) \ + printf("%02x", in[j]); \ + \ + printf("\",\n"); \ + printf(" \"key\": \"\",\n"); \ + printf(" \"out\": \""); \ + \ + name(hash, size_prefix##_OUTBYTES, in, i, NULL, 0); \ + \ + for (j = 0; j < size_prefix##_OUTBYTES; ++j) \ + printf("%02x", hash[j]); \ + printf("\"\n"); \ + printf("}"); \ + } \ + } while (0) + +#define MAKE_KEYED_KAT(name, size_prefix, first) \ + do { \ + for (i = 0; i < LENGTH; ++i) { \ + printf("%s\n{\n", i == 0 && first ? "" : ","); \ + \ + printf(" \"hash\": \"" #name "\",\n"); \ + printf(" \"in\": \""); \ + for (j = 0; j < i; ++j) \ + printf("%02x", in[j]); \ + \ + printf("\",\n"); \ + printf(" \"key\": \""); \ + for (j = 0; j < size_prefix##_KEYBYTES; ++j) \ + printf("%02x", key[j]); \ + printf("\",\n"); \ + printf(" \"out\": \""); \ + \ + name(hash, size_prefix##_OUTBYTES, in, i, key, size_prefix##_KEYBYTES); \ + \ + for (j = 0; j < size_prefix##_OUTBYTES; ++j) \ + printf("%02x", hash[j]); \ + printf("\"\n"); \ + printf("}"); \ + } \ + } while (0) + +#define MAKE_XOF_KAT(name, first) \ + do { \ + for (i = 1; i <= LENGTH; ++i) { \ + printf("%s\n{\n", i == 1 && first ? "" : ","); \ + \ + printf(" \"hash\": \"" #name "\",\n"); \ + printf(" \"in\": \""); \ + for (j = 0; j < LENGTH; ++j) \ + printf("%02x", in[j]); \ + \ + printf("\",\n"); \ + printf(" \"key\": \"\",\n"); \ + printf(" \"out\": \""); \ + \ + name(hash, i, in, LENGTH, NULL, 0); \ + \ + for (j = 0; j < i; ++j) \ + printf("%02x", hash[j]); \ + printf("\"\n"); \ + printf("}"); \ + } \ + } while (0) + +#define MAKE_XOF_KEYED_KAT(name, size_prefix, first) \ + do { \ + for (i = 1; i <= LENGTH; ++i) { \ + printf("%s\n{\n", i == 1 && first ? "" : ","); \ + \ + printf(" \"hash\": \"" #name "\",\n"); \ + printf(" \"in\": \""); \ + for (j = 0; j < LENGTH; ++j) \ + printf("%02x", in[j]); \ + \ + printf("\",\n"); \ + printf(" \"key\": \""); \ + for (j = 0; j < size_prefix##_KEYBYTES; ++j) \ + printf("%02x", key[j]); \ + printf("\",\n"); \ + printf(" \"out\": \""); \ + \ + name(hash, i, in, LENGTH, key, size_prefix##_KEYBYTES); \ + \ + for (j = 0; j < i; ++j) \ + printf("%02x", hash[j]); \ + printf("\"\n"); \ + printf("}"); \ + } \ + } while (0) + +int main() { + uint8_t key[64] = {0}; + uint8_t in[LENGTH] = {0}; + uint8_t hash[LENGTH] = {0}; + size_t i, j; + + for (i = 0; i < sizeof(in); ++i) + in[i] = i; + + for (i = 0; i < sizeof(key); ++i) + key[i] = i; + + printf("["); + MAKE_KAT(blake2s, BLAKE2S, 1); + MAKE_KEYED_KAT(blake2s, BLAKE2S, 0); + MAKE_KAT(blake2b, BLAKE2B, 0); + MAKE_KEYED_KAT(blake2b, BLAKE2B, 0); + MAKE_KAT(blake2sp, BLAKE2S, 0); + MAKE_KEYED_KAT(blake2sp, BLAKE2S, 0); + MAKE_KAT(blake2bp, BLAKE2B, 0); + MAKE_KEYED_KAT(blake2bp, BLAKE2B, 0); + MAKE_XOF_KAT(blake2xs, 0); + MAKE_XOF_KEYED_KAT(blake2xs, BLAKE2S, 0); + MAKE_XOF_KAT(blake2xb, 0); + MAKE_XOF_KEYED_KAT(blake2xb, BLAKE2B, 0); + printf("\n]\n"); + fflush(stdout); + return 0; +} diff --git a/src/third_party/blake2/makefile b/src/third_party/blake2/makefile index a59476ba..7f19022c 100644 --- a/src/third_party/blake2/makefile +++ b/src/third_party/blake2/makefile @@ -1,40 +1,40 @@ -CC=gcc -CFLAGS=-O3 -I../testvectors -Wall -Wextra -std=c89 -pedantic -Wno-long-long -BLAKEBINS=blake2s blake2b blake2sp blake2bp blake2xs blake2xb - -all: $(BLAKEBINS) check - -blake2s: blake2s.c - $(CC) blake2s.c -o $@ $(CFLAGS) -DBLAKE2S_SELFTEST - -blake2b: blake2b.c - $(CC) blake2b.c -o $@ $(CFLAGS) -DBLAKE2B_SELFTEST - -blake2sp: blake2sp.c blake2s.c - $(CC) blake2sp.c blake2s.c -o $@ $(CFLAGS) -DBLAKE2SP_SELFTEST - -blake2bp: blake2bp.c blake2b.c - $(CC) blake2bp.c blake2b.c -o $@ $(CFLAGS) -DBLAKE2BP_SELFTEST - -blake2xs: blake2xs.c blake2s.c - $(CC) blake2xs.c blake2s.c -o $@ $(CFLAGS) -DBLAKE2XS_SELFTEST - -blake2xb: blake2xb.c blake2b.c - $(CC) blake2xb.c blake2b.c -o $@ $(CFLAGS) -DBLAKE2XB_SELFTEST - -check: blake2s blake2b blake2sp blake2bp blake2xs blake2xb - ./blake2s - ./blake2b - ./blake2sp - ./blake2bp - ./blake2xs - ./blake2xb - -kat: - $(CC) $(CFLAGS) -o genkat-c genkat-c.c blake2b.c blake2s.c blake2sp.c blake2bp.c blake2xs.c blake2xb.c - $(CC) $(CFLAGS) -g -o genkat-json genkat-json.c blake2b.c blake2s.c blake2sp.c blake2bp.c blake2xs.c blake2xb.c - ./genkat-c > blake2-kat.h - ./genkat-json > blake2-kat.json - -clean: - rm -rf *.o genkat-c genkat-json blake2-kat.h blake2-kat.json $(BLAKEBINS) +CC=gcc +CFLAGS=-O3 -I../testvectors -Wall -Wextra -std=c89 -pedantic -Wno-long-long +BLAKEBINS=blake2s blake2b blake2sp blake2bp blake2xs blake2xb + +all: $(BLAKEBINS) check + +blake2s: blake2s.c + $(CC) blake2s.c -o $@ $(CFLAGS) -DBLAKE2S_SELFTEST + +blake2b: blake2b.c + $(CC) blake2b.c -o $@ $(CFLAGS) -DBLAKE2B_SELFTEST + +blake2sp: blake2sp.c blake2s.c + $(CC) blake2sp.c blake2s.c -o $@ $(CFLAGS) -DBLAKE2SP_SELFTEST + +blake2bp: blake2bp.c blake2b.c + $(CC) blake2bp.c blake2b.c -o $@ $(CFLAGS) -DBLAKE2BP_SELFTEST + +blake2xs: blake2xs.c blake2s.c + $(CC) blake2xs.c blake2s.c -o $@ $(CFLAGS) -DBLAKE2XS_SELFTEST + +blake2xb: blake2xb.c blake2b.c + $(CC) blake2xb.c blake2b.c -o $@ $(CFLAGS) -DBLAKE2XB_SELFTEST + +check: blake2s blake2b blake2sp blake2bp blake2xs blake2xb + ./blake2s + ./blake2b + ./blake2sp + ./blake2bp + ./blake2xs + ./blake2xb + +kat: + $(CC) $(CFLAGS) -o genkat-c genkat-c.c blake2b.c blake2s.c blake2sp.c blake2bp.c blake2xs.c blake2xb.c + $(CC) $(CFLAGS) -g -o genkat-json genkat-json.c blake2b.c blake2s.c blake2sp.c blake2bp.c blake2xs.c blake2xb.c + ./genkat-c > blake2-kat.h + ./genkat-json > blake2-kat.json + +clean: + rm -rf *.o genkat-c genkat-json blake2-kat.h blake2-kat.json $(BLAKEBINS) diff --git a/src/third_party/stb/stb_image.h b/src/third_party/stb/stb_image.h index 33461f1f..a2f44c8d 100644 --- a/src/third_party/stb/stb_image.h +++ b/src/third_party/stb/stb_image.h @@ -1,7985 +1,7985 @@ -/* stb_image - v2.29 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.29 (2023-05-xx) optimizations - 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff - 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes - 2.26 (2020-07-13) many minor fixes - 2.25 (2020-02-02) fix warnings - 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically - 2.23 (2019-08-11) fix clang static analysis warning - 2.22 (2019-03-04) gif fixes, fix warnings - 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine Simon Breuss (16-bit PNM) - John-Mark Allen - Carmelo J Fdez-Aguera - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski - Phil Jordan Dave Moore Roy Eltham - Hayaki Saito Nathan Reed Won Chun - Luke Graham Johan Duparc Nick Verigakis the Horde3D community - Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Eugene Golushkov Laurent Gomila Cort Stratton github:snagar - Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex - Cass Everitt Ryamond Barbiero github:grim210 - Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw - Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo - Julian Raschke Gregory Mullen Christian Floisand github:darealshinji - Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko github:mosra - Luca Sas Alexander Veselov Zack Middleton [reserved] - Ryan C. Gordon [reserved] [reserved] - DO NOT ADD YOUR NAME HERE - - Jacko Dirks - - To add your name to the credits, pick a random blank space in the middle and fill it. - 80% of merge conflicts on stb PRs are due to people adding their name at the end - of the credits. -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data); -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// To query the width, height and component count of an image without having to -// decode the full file, you can use the stbi_info family of functions: -// -// int x,y,n,ok; -// ok = stbi_info(filename, &x, &y, &n); -// // returns ok=1 and sets x, y, n if image is a supported format, -// // 0 otherwise. -// -// Note that stb_image pervasively uses ints in its public API for sizes, -// including sizes of memory buffers. This is now part of the API and thus -// hard to change without causing breakage. As a result, the various image -// loaders all have certain limits on image size; these differ somewhat -// by format but generally boil down to either just under 2GB or just under -// 1GB. When the decoded image would be larger than this, stb_image decoding -// will fail. -// -// Additionally, stb_image will reject image files that have any of their -// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, -// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, -// the only way to have an image with such dimensions load correctly -// is for it to have a rather extreme aspect ratio. Either way, the -// assumption here is that such larger images are likely to be malformed -// or malicious. If you do need to load an image with individual dimensions -// larger than that, and it still fits in the overall size limit, you can -// #define STBI_MAX_DIMENSIONS on your own to be something larger. -// -// =========================================================================== -// -// UNICODE: -// -// If compiling for Windows and you wish to use Unicode filenames, compile -// with -// #define STBI_WINDOWS_UTF8 -// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert -// Windows wchar_t filenames to utf8. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy-to-use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// provide more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image supports loading HDR images in general, and currently the Radiance -// .HDR file format specifically. You can still load any file through the existing -// interface; if you attempt to load an HDR file, it will be automatically remapped -// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// We optionally support converting iPhone-formatted PNGs (which store -// premultiplied BGRA) back to RGB, even though they're internally encoded -// differently. To enable this conversion, call -// stbi_convert_iphone_png_to_rgb(1). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// -// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater -// than that size (in either width or height) without further processing. -// This is to let programs in the wild set an upper bound to prevent -// denial-of-service attacks on untrusted data, as one could generate a -// valid image of gigantic dimensions and force stb_image to allocate a -// huge block of memory and spend disproportionate time decoding it. By -// default this is set to (1 << 24), which is 16777216, but that's still -// very big. - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -#include -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef STBIDEF -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif -#endif - - ////////////////////////////////////////////////////////////////////////////// - // - // PRIMARY API - works on images of any type - // - - // - // load image by filename, open file, or memory buffer - // - - typedef struct - { - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data - } stbi_io_callbacks; - - //////////////////////////////////// - // - // 8-bits-per-channel interface - // - - STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO - STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - // for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_GIF - STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - -#ifdef STBI_WINDOWS_UTF8 - STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); -#endif - - //////////////////////////////////// - // - // 16-bits-per-channel interface - // - - STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO - STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - - //////////////////////////////////// - // - // float-per-channel interface - // -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - - // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR - STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); - STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO - STBIDEF int stbi_is_hdr (char const *filename); - STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - - // get a VERY brief reason for failure - // on most compilers (and ALL modern mainstream compilers) this is threadsafe - STBIDEF const char *stbi_failure_reason (void); - - // free the loaded image -- this is just free() - STBIDEF void stbi_image_free (void *retval_from_stbi_load); - - // get image dimensions & components without fully decoding - STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); - STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); - STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); - STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO - STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); - STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); - STBIDEF int stbi_is_16_bit (char const *filename); - STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - - // for image formats that explicitly notate that they have premultiplied alpha, - // we just return the colors as stored in the file. set this flag to force - // unpremultiplication. results are undefined if the unpremultiply overflow. - STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - - // indicate whether we should process iphone images back to canonical format, - // or just pass them through "as-is" - STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - - // flip the image vertically, so the first pixel in the output array is the bottom left - STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - - // as above, but only applies to images loaded on the thread that calls the function - // this function is only available if your compiler supports thread-local variables; - // calling it will fail to link if your compiler doesn't - STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); - STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); - STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); - - // ZLIB client - used by PNG, available for other purposes - - STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); - STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); - STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); - STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); - STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ -|| defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ -|| defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ -|| defined(STBI_ONLY_ZLIB) -#ifndef STBI_ONLY_JPEG -#define STBI_NO_JPEG -#endif -#ifndef STBI_ONLY_PNG -#define STBI_NO_PNG -#endif -#ifndef STBI_ONLY_BMP -#define STBI_NO_BMP -#endif -#ifndef STBI_ONLY_PSD -#define STBI_NO_PSD -#endif -#ifndef STBI_ONLY_TGA -#define STBI_NO_TGA -#endif -#ifndef STBI_ONLY_GIF -#define STBI_NO_GIF -#endif -#ifndef STBI_ONLY_HDR -#define STBI_NO_HDR -#endif -#ifndef STBI_ONLY_PIC -#define STBI_NO_PIC -#endif -#ifndef STBI_ONLY_PNM -#define STBI_NO_PNM -#endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - -#ifdef __cplusplus -#define STBI_EXTERN extern "C" -#else -#define STBI_EXTERN extern -#endif - - -#ifndef _MSC_VER -#ifdef __cplusplus -#define stbi_inline inline -#else -#define stbi_inline -#endif -#else -#define stbi_inline __forceinline -#endif - -#ifndef STBI_NO_THREAD_LOCALS -#if defined(__cplusplus) && __cplusplus >= 201103L -#define STBI_THREAD_LOCAL thread_local -#elif defined(__GNUC__) && __GNUC__ < 5 -#define STBI_THREAD_LOCAL __thread -#elif defined(_MSC_VER) -#define STBI_THREAD_LOCAL __declspec(thread) -#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) -#define STBI_THREAD_LOCAL _Thread_local -#endif - -#ifndef STBI_THREAD_LOCAL -#if defined(__GNUC__) -#define STBI_THREAD_LOCAL __thread -#endif -#endif -#endif - -#if defined(_MSC_VER) || defined(__SYMBIAN32__) -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL -#define stbi_lrot(x,y) _lrotl(x,y) -#else -#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#endif - -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif - -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -#ifdef _MSC_VER -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name -#else -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -#ifndef STBI_MAX_DIMENSIONS -#define STBI_MAX_DIMENSIONS (1 << 24) -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - int callback_already_read; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - int ch; - fseek((FILE*) user, n, SEEK_CUR); - ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ - if (ch != EOF) { - ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ - } -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user) || ferror((FILE *) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__pnm_is16(stbi__context *s); -#endif - -static -#ifdef STBI_THREAD_LOCAL -STBI_THREAD_LOCAL -#endif -const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -#ifndef STBI_NO_FAILURE_STRINGS -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} -#endif - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} -#endif - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} -#endif - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. -static int stbi__addints_valid(int a, int b) -{ - if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow - if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. - return a <= INT_MAX - b; -} - -// returns 1 if the product of two ints fits in a signed short, 0 on overflow. -static int stbi__mul2shorts_valid(int a, int b) -{ - if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow - if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid - if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN - return a >= SHRT_MIN / b; -} - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS -#define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) -#define stbi__err(x,y) stbi__err(y) -#else -#define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load_global = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global -#else -static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; - -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; -} - -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ -? stbi__vertically_flip_on_load_local \ -: stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - // test the formats with a very explicit header first (at least a FOURCC - // or distinctive magic number first) -#ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); -#endif -#ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); -#endif -#ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); -#endif -#ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); -#else - STBI_NOTUSED(bpc); -#endif -#ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); -#endif - - // then the formats that can end up attempting to load with just 1 or 2 - // bytes matching expectations; these are prone to false positives, so - // try them later -#ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); -#endif -#ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); -#endif - -#ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } -#endif - -#ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); -#endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -#ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} -#endif - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 8) { - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 16) { - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) -STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); -#endif - -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); -} -#endif - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) - return 0; - -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; -#else - f = _wfopen(wFilename, wMode); -#endif - -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; -#ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } -#endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ -#ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); -#else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; -#endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ -#ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; -#else - STBI_NOTUSED(f); - return 0; -#endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ -#ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); -#else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; -#endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) -// nothing -#else -static void stbi__skip(stbi__context *s, int n) -{ - if (n == 0) return; // already there! - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) -// nothing -#else -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} -#endif - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - z += (stbi__uint32)stbi__get16le(s) << 16; - return z; -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - -#define STBI__COMBO(a,b) ((a)*8+(b)) -#define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); - } -#undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - -#define STBI__COMBO(a,b) ((a)*8+(b)) -#define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); - } -#undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - } - if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - - // sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - - // definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - - // kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) { - for (j=0; j < count[i]; ++j) { - h->size[k++] = (stbi_uc) (i+1); - if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); - } - } - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - if(c < 0 || c >= 256) // symbol id out of bounds! - return -1; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing - - sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & (sgn - 1)); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; - - if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - data[0] = (short) (dc * (1 << j->succ_low)); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * (1 << shift)); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ -int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ -p2 = s2; \ -p3 = s6; \ -p1 = (p2+p3) * stbi__f2f(0.5411961f); \ -t2 = p1 + p3*stbi__f2f(-1.847759065f); \ -t3 = p1 + p2*stbi__f2f( 0.765366865f); \ -p2 = s0; \ -p3 = s4; \ -t0 = stbi__fsh(p2+p3); \ -t1 = stbi__fsh(p2-p3); \ -x0 = t0+t3; \ -x3 = t0-t3; \ -x1 = t1+t2; \ -x2 = t1-t2; \ -t0 = s7; \ -t1 = s5; \ -t2 = s3; \ -t3 = s1; \ -p3 = t0+t2; \ -p4 = t1+t3; \ -p1 = t0+t3; \ -p2 = t1+t2; \ -p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ -t0 = t0*stbi__f2f( 0.298631336f); \ -t1 = t1*stbi__f2f( 2.053119869f); \ -t2 = t2*stbi__f2f( 3.072711026f); \ -t3 = t3*stbi__f2f( 1.501321110f); \ -p1 = p5 + p1*stbi__f2f(-0.899976223f); \ -p2 = p5 + p2*stbi__f2f(-2.562915447f); \ -p3 = p3*stbi__f2f(-1.961570560f); \ -p4 = p4*stbi__f2f(-0.390180644f); \ -t3 += p1+p4; \ -t2 += p2+p3; \ -t1 += p2+p4; \ -t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y -#define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y -#define dct_rot(out0,out1, x,y,c0,c1) \ -__m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ -__m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ -__m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ -__m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ -__m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ -__m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) -#define dct_widen(out, in) \ -__m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ -__m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add -#define dct_wadd(out, a, b) \ -__m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ -__m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub -#define dct_wsub(out, a, b) \ -__m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ -__m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack -#define dct_bfly32o(out0, out1, a,b,bias,s) \ -{ \ -__m128i abiased_l = _mm_add_epi32(a##_l, bias); \ -__m128i abiased_h = _mm_add_epi32(a##_h, bias); \ -dct_wadd(sum, abiased, b); \ -dct_wsub(dif, abiased, b); \ -out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ -out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ -} - - // 8-bit interleave step (for transposes) -#define dct_interleave8(a, b) \ -tmp = a; \ -a = _mm_unpacklo_epi8(a, b); \ -b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) -#define dct_interleave16(a, b) \ -tmp = a; \ -a = _mm_unpacklo_epi16(a, b); \ -b = _mm_unpackhi_epi16(tmp, b) - -#define dct_pass(bias,shift) \ -{ \ -/* even part */ \ -dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ -__m128i sum04 = _mm_add_epi16(row0, row4); \ -__m128i dif04 = _mm_sub_epi16(row0, row4); \ -dct_widen(t0e, sum04); \ -dct_widen(t1e, dif04); \ -dct_wadd(x0, t0e, t3e); \ -dct_wsub(x3, t0e, t3e); \ -dct_wadd(x1, t1e, t2e); \ -dct_wsub(x2, t1e, t2e); \ -/* odd part */ \ -dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ -dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ -__m128i sum17 = _mm_add_epi16(row1, row7); \ -__m128i sum35 = _mm_add_epi16(row3, row5); \ -dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ -dct_wadd(x4, y0o, y4o); \ -dct_wadd(x5, y1o, y5o); \ -dct_wadd(x6, y2o, y5o); \ -dct_wadd(x7, y3o, y4o); \ -dct_bfly32o(row0,row7, x0,x7,bias,shift); \ -dct_bfly32o(row1,row6, x1,x6,bias,shift); \ -dct_bfly32o(row2,row5, x2,x5,bias,shift); \ -dct_bfly32o(row3,row4, x3,x4,bias,shift); \ -} - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ -int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ -int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ -int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ -int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ -int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ -int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - - // wide add -#define dct_wadd(out, a, b) \ -int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ -int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - - // wide sub -#define dct_wsub(out, a, b) \ -int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ -int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - - // butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ -{ \ -dct_wadd(sum, a, b); \ -dct_wsub(dif, a, b); \ -out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ -out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ -} - -#define dct_pass(shiftop, shift) \ -{ \ -/* even part */ \ -int16x8_t sum26 = vaddq_s16(row2, row6); \ -dct_long_mul(p1e, sum26, rot0_0); \ -dct_long_mac(t2e, p1e, row6, rot0_1); \ -dct_long_mac(t3e, p1e, row2, rot0_2); \ -int16x8_t sum04 = vaddq_s16(row0, row4); \ -int16x8_t dif04 = vsubq_s16(row0, row4); \ -dct_widen(t0e, sum04); \ -dct_widen(t1e, dif04); \ -dct_wadd(x0, t0e, t3e); \ -dct_wsub(x3, t0e, t3e); \ -dct_wadd(x1, t1e, t2e); \ -dct_wsub(x2, t1e, t2e); \ -/* odd part */ \ -int16x8_t sum15 = vaddq_s16(row1, row5); \ -int16x8_t sum17 = vaddq_s16(row1, row7); \ -int16x8_t sum35 = vaddq_s16(row3, row5); \ -int16x8_t sum37 = vaddq_s16(row3, row7); \ -int16x8_t sumodd = vaddq_s16(sum17, sum35); \ -dct_long_mul(p5o, sumodd, rot1_0); \ -dct_long_mac(p1o, p5o, sum17, rot1_1); \ -dct_long_mac(p2o, p5o, sum35, rot1_2); \ -dct_long_mul(p3o, sum37, rot2_0); \ -dct_long_mul(p4o, sum15, rot2_1); \ -dct_wadd(sump13o, p1o, p3o); \ -dct_wadd(sump24o, p2o, p4o); \ -dct_wadd(sump23o, p2o, p3o); \ -dct_wadd(sump14o, p1o, p4o); \ -dct_long_mac(x4, sump13o, row7, rot3_0); \ -dct_long_mac(x5, sump24o, row5, rot3_1); \ -dct_long_mac(x6, sump23o, row3, rot3_2); \ -dct_long_mac(x7, sump14o, row1, rot3_3); \ -dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ -dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ -dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ -dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ -} - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { - // these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. - // whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios - // and I've never seen a non-corrupted JPEG file actually use them - for (i=0; i < s->img_n; ++i) { - if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); - if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) -{ - // some JPEGs have junk at end, skip over it but if we find what looks - // like a valid marker, resume there - while (!stbi__at_eof(j->s)) { - stbi_uc x = stbi__get8(j->s); - while (x == 0xff) { // might be a marker - if (stbi__at_eof(j->s)) return STBI__MARKER_none; - x = stbi__get8(j->s); - if (x != 0x00 && x != 0xff) { - // not a stuffed zero or lead-in to another marker, looks - // like an actual marker, return it - return x; - } - // stuffed zero has x=0 now which ends the loop, meaning we go - // back to regular scan loop. - // repeated 0xff keeps trying to read the next byte of the marker. - } - } - return STBI__MARKER_none; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - j->marker = stbi__skip_jpeg_junk_at_end(j); - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - m = stbi__get_marker(j); - if (STBI__RESTART(m)) - m = stbi__get_marker(j); - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - m = stbi__get_marker(j); - } else { - if (!stbi__process_marker(j, m)) return 1; - m = stbi__get_marker(j); - } - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // nothing to do if no components requested; check this now to avoid - // accessing uninitialized coutput[0] later - if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return stbi__errpuc("outofmem", "Out of memory"); - memset(j, 0, sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - if (!j) return stbi__err("outofmem", "Out of memory"); - memset(j, 0, sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - if (!j) return stbi__err("outofmem", "Out of memory"); - memset(j, 0, sizeof(stbi__jpeg)); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) -#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[STBI__ZNSYMS]; - stbi__uint16 value[STBI__ZNSYMS]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - int hit_zeof_once; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static int stbi__zeof(stbi__zbuf *z) -{ - return (z->zbuffer >= z->zbuffer_end); -} - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - return stbi__zeof(z) ? 0 : *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - if (z->code_buffer >= (1U << z->num_bits)) { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s >= 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! - if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) { - if (stbi__zeof(a)) { - if (!a->hit_zeof_once) { - // This is the first time we hit eof, insert 16 extra padding btis - // to allow us to keep going; if we actually consume any of them - // though, that is invalid data. This is caught later. - a->hit_zeof_once = 1; - a->num_bits += 16; // add 16 implicit zero bits - } else { - // We already inserted our extra 16 padding bits and are again - // out, this stream is actually prematurely terminated. - return -1; - } - } else { - stbi__fill_bits(a); - } - } - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - unsigned int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (unsigned int) (z->zout - z->zout_start); - limit = old_limit = (unsigned) (z->zout_end - z->zout_start); - if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); - while (cur + n > limit) { - if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); - limit *= 2; - } - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, - 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - if (a->hit_zeof_once && a->num_bits < 16) { - // The first time we hit zeof, we inserted 16 extra zero bits into our bit - // buffer so the decoder can just do its speculative decoding. But if we - // actually consumed any of those bits (which is the case when num_bits < 16), - // the stream actually read past the end so it is malformed. - return stbi__err("unexpected end","Corrupt PNG"); - } - return 1; - } - if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (len > a->zout_end - zout) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - } else if (c == 18) { - c = stbi__zreceive(a,7)+11; - } else { - return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - a->hit_zeof_once = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filter used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub -}; - -static int stbi__paeth(int a, int b, int c) -{ - // This formulation looks very different from the reference in the PNG spec, but is - // actually equivalent and has favorable data dependencies and admits straightforward - // generation of branch-free code, which helps performance significantly. - int thresh = c*3 - (a + b); - int lo = a < b ? a : b; - int hi = a < b ? b : a; - int t0 = (hi <= thresh) ? lo : c; - int t1 = (thresh <= lo) ? hi : t0; - return t1; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// adds an extra all-255 alpha channel -// dest == src is legal -// img_n must be 1 or 3 -static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) -{ - int i; - // must process data backwards since we allow dest==src - if (img_n == 1) { - for (i=x-1; i >= 0; --i) { - dest[i*2+1] = 255; - dest[i*2+0] = src[i]; - } - } else { - STBI_ASSERT(img_n == 3); - for (i=x-1; i >= 0; --i) { - dest[i*4+3] = 255; - dest[i*4+2] = src[i*3+2]; - dest[i*4+1] = src[i*3+1]; - dest[i*4+0] = src[i*3+0]; - } - } -} - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16 ? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - stbi_uc *filter_buf; - int all_ok = 1; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - // note: error exits here don't need to clean up a->out individually, - // stbi__do_png always does on error. - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - // Allocate two scan lines worth of filter workspace buffer. - filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); - if (!filter_buf) return stbi__err("outofmem", "Out of memory"); - - // Filtering for low-bit-depth images - if (depth < 8) { - filter_bytes = 1; - width = img_width_bytes; - } - - for (j=0; j < y; ++j) { - // cur/prior filter buffers alternate - stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; - stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; - stbi_uc *dest = a->out + stride*j; - int nk = width * filter_bytes; - int filter = *raw++; - - // check filter type - if (filter > 4) { - all_ok = stbi__err("invalid filter","Corrupt PNG"); - break; - } - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // perform actual filtering - switch (filter) { - case STBI__F_none: - memcpy(cur, raw, nk); - break; - case STBI__F_sub: - memcpy(cur, raw, filter_bytes); - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); - break; - case STBI__F_up: - for (k = 0; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + prior[k]); - break; - case STBI__F_avg: - for (k = 0; k < filter_bytes; ++k) - cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); - break; - case STBI__F_paeth: - for (k = 0; k < filter_bytes; ++k) - cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); - break; - case STBI__F_avg_first: - memcpy(cur, raw, filter_bytes); - for (k = filter_bytes; k < nk; ++k) - cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); - break; - } - - raw += nk; - - // expand decoded bits in cur to dest, also adding an extra alpha channel if desired - if (depth < 8) { - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - stbi_uc *in = cur; - stbi_uc *out = dest; - stbi_uc inb = 0; - stbi__uint32 nsmp = x*img_n; - - // expand bits to bytes first - if (depth == 4) { - for (i=0; i < nsmp; ++i) { - if ((i & 1) == 0) inb = *in++; - *out++ = scale * (inb >> 4); - inb <<= 4; - } - } else if (depth == 2) { - for (i=0; i < nsmp; ++i) { - if ((i & 3) == 0) inb = *in++; - *out++ = scale * (inb >> 6); - inb <<= 2; - } - } else { - STBI_ASSERT(depth == 1); - for (i=0; i < nsmp; ++i) { - if ((i & 7) == 0) inb = *in++; - *out++ = scale * (inb >> 7); - inb <<= 1; - } - } - - // insert alpha=255 values if desired - if (img_n != out_n) - stbi__create_png_alpha_expand8(dest, dest, x, img_n); - } else if (depth == 8) { - if (img_n == out_n) - memcpy(dest, cur, x*img_n); - else - stbi__create_png_alpha_expand8(dest, cur, x, img_n); - } else if (depth == 16) { - // convert the image data from big-endian to platform-native - stbi__uint16 *dest16 = (stbi__uint16*)dest; - stbi__uint32 nsmp = x*img_n; - - if (img_n == out_n) { - for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) - *dest16 = (cur[0] << 8) | cur[1]; - } else { - STBI_ASSERT(img_n+1 == out_n); - if (img_n == 1) { - for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { - dest16[0] = (cur[0] << 8) | cur[1]; - dest16[1] = 0xffff; - } - } else { - STBI_ASSERT(img_n == 3); - for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { - dest16[0] = (cur[0] << 8) | cur[1]; - dest16[1] = (cur[2] << 8) | cur[3]; - dest16[2] = (cur[4] << 8) | cur[5]; - dest16[3] = 0xffff; - } - } - } - } - } - - STBI_FREE(filter_buf); - if (!all_ok) return 0; - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - if (!final) return stbi__err("outofmem", "Out of memory"); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load_global = 0; -static int stbi__de_iphone_flag_global = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag_global = flag_true_if_should_convert; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global -#define stbi__de_iphone_flag stbi__de_iphone_flag_global -#else -static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; -static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; - -STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; - stbi__unpremultiply_on_load_set = 1; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag_local = flag_true_if_should_convert; - stbi__de_iphone_flag_set = 1; -} - -#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ -? stbi__unpremultiply_on_load_local \ -: stbi__unpremultiply_on_load_global) -#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ -? stbi__de_iphone_flag_local \ -: stbi__de_iphone_flag_global) -#endif // STBI_THREAD_LOCAL - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]={0}; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - } - // even with SCAN_header, have to scan to see if we have a tRNS - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. - if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { - // header scan definitely stops at first IDAT - if (pal_img_n) - s->img_n = pal_img_n; - return 1; - } - if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { -#ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); -#endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) { n += 16; z >>= 16; } - if (z >= 0x00100) { n += 8; z >>= 8; } - if (z >= 0x00010) { n += 4; z >>= 4; } - if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1;/* >>= 1;*/ } - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; - int extra_read; -} stbi__bmp_data; - -static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) -{ - // BI_BITFIELDS specifies masks explicitly, don't override - if (compress == 3) - return 1; - - if (compress == 0) { - if (info->bpp == 16) { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } else if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - // otherwise, use defaults, which is all-0 - info->mr = info->mg = info->mb = info->ma = 0; - } - return 1; - } - return 0; // error -} - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; - - if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes - if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - stbi__bmp_set_mask_defaults(info, compress); - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - // V4/V5 header - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs - stbi__bmp_set_mask_defaults(info, compress); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) { - // accept some number of extra bytes after the header, but if the offset points either to before - // the header ends or implies a large amount of extra data, reject the file as malformed - int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); - int header_limit = 1024; // max we actually read is below 256 bytes currently. - int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. - if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { - return stbi__errpuc("bad header", "Corrupt BMP"); - } - // we established that bytes_read_so_far is positive and sensible. - // the first half of this test rejects offsets that are either too small positives, or - // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn - // ensures the number computed in the second half of the test can't overflow. - if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } else { - stbi__skip(s, info.offset - bytes_read_so_far); - } - } - - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i]; p1[i] = p2[i]; p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - - errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO - - if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - if (tga_palette_len == 0) { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - - if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - if (!result) return stbi__errpuc("outofmem", "Out of memory"); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!g) return stbi__err("outofmem", "Out of memory"); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (!o) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) -{ - STBI_FREE(g->out); - STBI_FREE(g->history); - STBI_FREE(g->background); - - if (out) STBI_FREE(out); - if (delays && *delays) STBI_FREE(*delays); - return stbi__errpuc("outofmem", "Out of memory"); -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - int out_size = 0; - int delays_size = 0; - - STBI_NOTUSED(out_size); - STBI_NOTUSED(delays_size); - - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); - if (!tmp) - return stbi__load_gif_main_outofmem(&g, out, delays); - else { - out = (stbi_uc*) tmp; - out_size = layers * stride; - } - - if (delays) { - int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); - if (!new_delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - *delays = new_delays; - delays_size = layers * sizeof(int); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (!out) - return stbi__load_gif_main_outofmem(&g, out, delays); - out_size = layers * stride; - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - if (!*delays) - return stbi__load_gif_main_outofmem(&g, out, delays); - delays_size = layers * sizeof(int); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - if (p == NULL) { - stbi__rewind( s ); - return 0; - } - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - STBI_NOTUSED(stbi__get32be(s)); - STBI_NOTUSED(stbi__get32be(s)); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); - if (ri->bits_per_channel == 0) - return 0; - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { - STBI_FREE(out); - return stbi__errpuc("bad PNM", "PNM file truncated"); - } - - if (req_comp && req_comp != s->img_n) { - if (ri->bits_per_channel == 16) { - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); - } else { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - } - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - if((value > 214748364) || (value == 214748364 && *c > '7')) - return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - if(*x == 0) - return stbi__err("invalid width", "PPM image header had zero or overflowing width"); - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - if (*y == 0) - return stbi__err("invalid width", "PPM image header had zero or overflowing width"); - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - if (maxv > 65535) - return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); - else if (maxv > 255) - return 16; - else - return 8; -} - -static int stbi__pnm_is16(stbi__context *s) -{ - if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) - return 1; - return 0; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ -#ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; -#endif - -#ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; -#endif - -#ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; -#endif - -#ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; -#endif - -#ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; -#endif - -#ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; -#endif - -#ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; -#endif - -#ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; -#endif - - // test tga last because it's a crappy test! -#ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; -#endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ -#ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; -#endif - -#ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; -#endif - -#ifndef STBI_NO_PNM - if (stbi__pnm_is16(s)) return 1; -#endif - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ +/* stb_image - v2.29 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.29 (2023-05-xx) optimizations + 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff + 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine Simon Breuss (16-bit PNM) + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Eugene Golushkov Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko github:mosra + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + Jacko Dirks + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data); +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// To query the width, height and component count of an image without having to +// decode the full file, you can use the stbi_info family of functions: +// +// int x,y,n,ok; +// ok = stbi_info(filename, &x, &y, &n); +// // returns ok=1 and sets x, y, n if image is a supported format, +// // 0 otherwise. +// +// Note that stb_image pervasively uses ints in its public API for sizes, +// including sizes of memory buffers. This is now part of the API and thus +// hard to change without causing breakage. As a result, the various image +// loaders all have certain limits on image size; these differ somewhat +// by format but generally boil down to either just under 2GB or just under +// 1GB. When the decoded image would be larger than this, stb_image decoding +// will fail. +// +// Additionally, stb_image will reject image files that have any of their +// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS, +// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit, +// the only way to have an image with such dimensions load correctly +// is for it to have a rather extreme aspect ratio. Either way, the +// assumption here is that such larger images are likely to be malformed +// or malicious. If you do need to load an image with individual dimensions +// larger than that, and it still fits in the overall size limit, you can +// #define STBI_MAX_DIMENSIONS on your own to be something larger. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// We optionally support converting iPhone-formatted PNGs (which store +// premultiplied BGRA) back to RGB, even though they're internally encoded +// differently. To enable this conversion, call +// stbi_convert_iphone_png_to_rgb(1). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + + ////////////////////////////////////////////////////////////////////////////// + // + // PRIMARY API - works on images of any type + // + + // + // load image by filename, open file, or memory buffer + // + + typedef struct + { + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data + } stbi_io_callbacks; + + //////////////////////////////////// + // + // 8-bits-per-channel interface + // + + STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO + STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + // for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF + STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 + STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + + //////////////////////////////////// + // + // 16-bits-per-channel interface + // + + STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO + STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + + //////////////////////////////////// + // + // float-per-channel interface + // +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + + // stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR + STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); + STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO + STBIDEF int stbi_is_hdr (char const *filename); + STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + + // get a VERY brief reason for failure + // on most compilers (and ALL modern mainstream compilers) this is threadsafe + STBIDEF const char *stbi_failure_reason (void); + + // free the loaded image -- this is just free() + STBIDEF void stbi_image_free (void *retval_from_stbi_load); + + // get image dimensions & components without fully decoding + STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); + STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); + STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO + STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); + STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + STBIDEF int stbi_is_16_bit (char const *filename); + STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + + // for image formats that explicitly notate that they have premultiplied alpha, + // we just return the colors as stored in the file. set this flag to force + // unpremultiplication. results are undefined if the unpremultiply overflow. + STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + + // indicate whether we should process iphone images back to canonical format, + // or just pass them through "as-is" + STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + + // flip the image vertically, so the first pixel in the output array is the bottom left + STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + + // as above, but only applies to images loaded on the thread that calls the function + // this function is only available if your compiler supports thread-local variables; + // calling it will fail to link if your compiler doesn't + STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply); + STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert); + STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + + // ZLIB client - used by PNG, available for other purposes + + STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); + STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); + STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); + STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); + STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ +|| defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ +|| defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ +|| defined(STBI_ONLY_ZLIB) +#ifndef STBI_ONLY_JPEG +#define STBI_NO_JPEG +#endif +#ifndef STBI_ONLY_PNG +#define STBI_NO_PNG +#endif +#ifndef STBI_ONLY_BMP +#define STBI_NO_BMP +#endif +#ifndef STBI_ONLY_PSD +#define STBI_NO_PSD +#endif +#ifndef STBI_ONLY_TGA +#define STBI_NO_TGA +#endif +#ifndef STBI_ONLY_GIF +#define STBI_NO_GIF +#endif +#ifndef STBI_ONLY_HDR +#define STBI_NO_HDR +#endif +#ifndef STBI_ONLY_PIC +#define STBI_NO_PIC +#endif +#ifndef STBI_ONLY_PNM +#define STBI_NO_PNM +#endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER +#ifdef __cplusplus +#define stbi_inline inline +#else +#define stbi_inline +#endif +#else +#define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS +#if defined(__cplusplus) && __cplusplus >= 201103L +#define STBI_THREAD_LOCAL thread_local +#elif defined(__GNUC__) && __GNUC__ < 5 +#define STBI_THREAD_LOCAL __thread +#elif defined(_MSC_VER) +#define STBI_THREAD_LOCAL __declspec(thread) +#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) +#define STBI_THREAD_LOCAL _Thread_local +#endif + +#ifndef STBI_THREAD_LOCAL +#if defined(__GNUC__) +#define STBI_THREAD_LOCAL __thread +#endif +#endif +#endif + +#if defined(_MSC_VER) || defined(__SYMBIAN32__) +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL +#define stbi_lrot(x,y) _lrotl(x,y) +#else +#define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (-(y) & 31))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +#ifdef _MSC_VER +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#else +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__pnm_is16(stbi__context *s); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) || !defined(STBI_NO_PNM) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// returns 1 if the sum of two signed ints is valid (between -2^31 and 2^31-1 inclusive), 0 on overflow. +static int stbi__addints_valid(int a, int b) +{ + if ((a >= 0) != (b >= 0)) return 1; // a and b have different signs, so no overflow + if (a < 0 && b < 0) return a >= INT_MIN - b; // same as a + b >= INT_MIN; INT_MIN - b cannot overflow since b < 0. + return a <= INT_MAX - b; +} + +// returns 1 if the product of two ints fits in a signed short, 0 on overflow. +static int stbi__mul2shorts_valid(int a, int b) +{ + if (b == 0 || b == -1) return 1; // multiplication by 0 is always 0; check for -1 so SHRT_MIN/b doesn't overflow + if ((a >= 0) == (b >= 0)) return a <= SHRT_MAX/b; // product is positive, so similar to mul2sizes_valid + if (b < 0) return a <= SHRT_MIN / b; // same as a * b >= SHRT_MIN + return a >= SHRT_MIN / b; +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS +#define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) +#define stbi__err(x,y) stbi__err(y) +#else +#define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ +? stbi__vertically_flip_on_load_local \ +: stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + // test the formats with a very explicit header first (at least a FOURCC + // or distinctive magic number first) +#ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); +#endif +#ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); +#endif +#ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); +#endif +#ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); +#else + STBI_NOTUSED(bpc); +#endif +#ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); +#endif + + // then the formats that can end up attempting to load with just 1 or 2 + // bytes matching expectations; these are prone to false positives, so + // try them later +#ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); +#endif +#ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); +#endif + +#ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } +#endif + +#ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); +#endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; +#ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } +#endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ +#ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); +#else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; +#endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ +#ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; +#else + STBI_NOTUSED(f); + return 0; +#endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ +#ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); +#else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; +#endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + z += (stbi__uint32)stbi__get16le(s) << 16; + return z; +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + +#define STBI__COMBO(a,b) ((a)*8+(b)) +#define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } +#undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + +#define STBI__COMBO(a,b) ((a)*8+(b)) +#define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } +#undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + + // sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + + // definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + + // kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) { + for (j=0; j < count[i]; ++j) { + h->size[k++] = (stbi_uc) (i+1); + if(k >= 257) return stbi__err("bad size list","Corrupt JPEG"); + } + } + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + if(c < 0 || c >= 256) // symbol id out of bounds! + return -1; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + + sgn = j->code_buffer >> 31; // sign bit always in MSB; 0 if MSB clear (positive), 1 if MSB set (negative) + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & (sgn - 1)); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + if (j->code_bits < n) return 0; // ran out of bits from stream, return 0s intead of continuing + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + if (j->code_bits < 1) return 0; // ran out of bits from stream, return 0s intead of continuing + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta","Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, dequant[0])) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0 || t > 15) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + if (!stbi__addints_valid(j->img_comp[b].dc_pred, diff)) return stbi__err("bad delta", "Corrupt JPEG"); + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + if (!stbi__mul2shorts_valid(dc, 1 << j->succ_low)) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + data[0] = (short) (dc * (1 << j->succ_low)); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + if (s > j->code_bits) return stbi__err("bad huffman code", "Combined length longer than code bits available"); + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * (1 << shift)); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * (1 << shift)); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ +int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ +p2 = s2; \ +p3 = s6; \ +p1 = (p2+p3) * stbi__f2f(0.5411961f); \ +t2 = p1 + p3*stbi__f2f(-1.847759065f); \ +t3 = p1 + p2*stbi__f2f( 0.765366865f); \ +p2 = s0; \ +p3 = s4; \ +t0 = stbi__fsh(p2+p3); \ +t1 = stbi__fsh(p2-p3); \ +x0 = t0+t3; \ +x3 = t0-t3; \ +x1 = t1+t2; \ +x2 = t1-t2; \ +t0 = s7; \ +t1 = s5; \ +t2 = s3; \ +t3 = s1; \ +p3 = t0+t2; \ +p4 = t1+t3; \ +p1 = t0+t3; \ +p2 = t1+t2; \ +p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ +t0 = t0*stbi__f2f( 0.298631336f); \ +t1 = t1*stbi__f2f( 2.053119869f); \ +t2 = t2*stbi__f2f( 3.072711026f); \ +t3 = t3*stbi__f2f( 1.501321110f); \ +p1 = p5 + p1*stbi__f2f(-0.899976223f); \ +p2 = p5 + p2*stbi__f2f(-2.562915447f); \ +p3 = p3*stbi__f2f(-1.961570560f); \ +p4 = p4*stbi__f2f(-0.390180644f); \ +t3 += p1+p4; \ +t2 += p2+p3; \ +t1 += p2+p4; \ +t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y +#define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y +#define dct_rot(out0,out1, x,y,c0,c1) \ +__m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ +__m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ +__m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ +__m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ +__m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ +__m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) +#define dct_widen(out, in) \ +__m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ +__m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add +#define dct_wadd(out, a, b) \ +__m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ +__m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub +#define dct_wsub(out, a, b) \ +__m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ +__m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack +#define dct_bfly32o(out0, out1, a,b,bias,s) \ +{ \ +__m128i abiased_l = _mm_add_epi32(a##_l, bias); \ +__m128i abiased_h = _mm_add_epi32(a##_h, bias); \ +dct_wadd(sum, abiased, b); \ +dct_wsub(dif, abiased, b); \ +out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ +out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ +} + + // 8-bit interleave step (for transposes) +#define dct_interleave8(a, b) \ +tmp = a; \ +a = _mm_unpacklo_epi8(a, b); \ +b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) +#define dct_interleave16(a, b) \ +tmp = a; \ +a = _mm_unpacklo_epi16(a, b); \ +b = _mm_unpackhi_epi16(tmp, b) + +#define dct_pass(bias,shift) \ +{ \ +/* even part */ \ +dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ +__m128i sum04 = _mm_add_epi16(row0, row4); \ +__m128i dif04 = _mm_sub_epi16(row0, row4); \ +dct_widen(t0e, sum04); \ +dct_widen(t1e, dif04); \ +dct_wadd(x0, t0e, t3e); \ +dct_wsub(x3, t0e, t3e); \ +dct_wadd(x1, t1e, t2e); \ +dct_wsub(x2, t1e, t2e); \ +/* odd part */ \ +dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ +dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ +__m128i sum17 = _mm_add_epi16(row1, row7); \ +__m128i sum35 = _mm_add_epi16(row3, row5); \ +dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ +dct_wadd(x4, y0o, y4o); \ +dct_wadd(x5, y1o, y5o); \ +dct_wadd(x6, y2o, y5o); \ +dct_wadd(x7, y3o, y4o); \ +dct_bfly32o(row0,row7, x0,x7,bias,shift); \ +dct_bfly32o(row1,row6, x1,x6,bias,shift); \ +dct_bfly32o(row2,row5, x2,x5,bias,shift); \ +dct_bfly32o(row3,row4, x3,x4,bias,shift); \ +} + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ +int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ +int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ +int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ +int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ +int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ +int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + + // wide add +#define dct_wadd(out, a, b) \ +int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ +int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + + // wide sub +#define dct_wsub(out, a, b) \ +int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ +int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + + // butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ +{ \ +dct_wadd(sum, a, b); \ +dct_wsub(dif, a, b); \ +out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ +out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ +} + +#define dct_pass(shiftop, shift) \ +{ \ +/* even part */ \ +int16x8_t sum26 = vaddq_s16(row2, row6); \ +dct_long_mul(p1e, sum26, rot0_0); \ +dct_long_mac(t2e, p1e, row6, rot0_1); \ +dct_long_mac(t3e, p1e, row2, rot0_2); \ +int16x8_t sum04 = vaddq_s16(row0, row4); \ +int16x8_t dif04 = vsubq_s16(row0, row4); \ +dct_widen(t0e, sum04); \ +dct_widen(t1e, dif04); \ +dct_wadd(x0, t0e, t3e); \ +dct_wsub(x3, t0e, t3e); \ +dct_wadd(x1, t1e, t2e); \ +dct_wsub(x2, t1e, t2e); \ +/* odd part */ \ +int16x8_t sum15 = vaddq_s16(row1, row5); \ +int16x8_t sum17 = vaddq_s16(row1, row7); \ +int16x8_t sum35 = vaddq_s16(row3, row5); \ +int16x8_t sum37 = vaddq_s16(row3, row7); \ +int16x8_t sumodd = vaddq_s16(sum17, sum35); \ +dct_long_mul(p5o, sumodd, rot1_0); \ +dct_long_mac(p1o, p5o, sum17, rot1_1); \ +dct_long_mac(p2o, p5o, sum35, rot1_2); \ +dct_long_mul(p3o, sum37, rot2_0); \ +dct_long_mul(p4o, sum15, rot2_1); \ +dct_wadd(sump13o, p1o, p3o); \ +dct_wadd(sump24o, p2o, p4o); \ +dct_wadd(sump23o, p2o, p3o); \ +dct_wadd(sump14o, p1o, p4o); \ +dct_long_mac(x4, sump13o, row7, rot3_0); \ +dct_long_mac(x5, sump24o, row5, rot3_1); \ +dct_long_mac(x6, sump23o, row3, rot3_2); \ +dct_long_mac(x7, sump14o, row1, rot3_3); \ +dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ +dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ +dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ +dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ +} + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { + // these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. + // whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + if(n > 256) return stbi__err("bad DHT header","Corrupt JPEG"); // Loop over i < n would write past end of values! + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // check that plane subsampling factors are integer ratios; our resamplers can't deal with fractional ratios + // and I've never seen a non-corrupted JPEG file actually use them + for (i=0; i < s->img_n; ++i) { + if (h_max % z->img_comp[i].h != 0) return stbi__err("bad H","Corrupt JPEG"); + if (v_max % z->img_comp[i].v != 0) return stbi__err("bad V","Corrupt JPEG"); + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +static stbi_uc stbi__skip_jpeg_junk_at_end(stbi__jpeg *j) +{ + // some JPEGs have junk at end, skip over it but if we find what looks + // like a valid marker, resume there + while (!stbi__at_eof(j->s)) { + stbi_uc x = stbi__get8(j->s); + while (x == 0xff) { // might be a marker + if (stbi__at_eof(j->s)) return STBI__MARKER_none; + x = stbi__get8(j->s); + if (x != 0x00 && x != 0xff) { + // not a stuffed zero or lead-in to another marker, looks + // like an actual marker, return it + return x; + } + // stuffed zero has x=0 now which ends the loop, meaning we go + // back to regular scan loop. + // repeated 0xff keeps trying to read the next byte of the marker. + } + } + return STBI__MARKER_none; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + j->marker = stbi__skip_jpeg_junk_at_end(j); + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + m = stbi__get_marker(j); + if (STBI__RESTART(m)) + m = stbi__get_marker(j); + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + m = stbi__get_marker(j); + } else { + if (!stbi__process_marker(j, m)) return 1; + m = stbi__get_marker(j); + } + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // nothing to do if no components requested; check this now to avoid + // accessing uninitialized coutput[0] later + if (decode_n <= 0) { stbi__cleanup_jpeg(z); return NULL; } + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__errpuc("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + if (!j) return stbi__err("outofmem", "Out of memory"); + memset(j, 0, sizeof(stbi__jpeg)); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) +#define STBI__ZNSYMS 288 // number of symbols in literal/length alphabet + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[STBI__ZNSYMS]; + stbi__uint16 value[STBI__ZNSYMS]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + int hit_zeof_once; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= STBI__ZNSYMS) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + if (!a->hit_zeof_once) { + // This is the first time we hit eof, insert 16 extra padding btis + // to allow us to keep going; if we actually consume any of them + // though, that is invalid data. This is caught later. + a->hit_zeof_once = 1; + a->num_bits += 16; // add 16 implicit zero bits + } else { + // We already inserted our extra 16 padding bits and are again + // out, this stream is actually prematurely terminated. + return -1; + } + } else { + stbi__fill_bits(a); + } + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, + 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + if (a->hit_zeof_once && a->num_bits < 16) { + // The first time we hit zeof, we inserted 16 extra zero bits into our bit + // buffer so the decoder can just do its speculative decoding. But if we + // actually consumed any of those bits (which is the case when num_bits < 16), + // the stream actually read past the end so it is malformed. + return stbi__err("unexpected end","Corrupt PNG"); + } + return 1; + } + if (z >= 286) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, length codes 286 and 287 must not appear in compressed data + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0 || z >= 30) return stbi__err("bad huffman code","Corrupt PNG"); // per DEFLATE, distance codes 30 and 31 must not appear in compressed data + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (len > a->zout_end - zout) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[STBI__ZNSYMS] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + a->hit_zeof_once = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , STBI__ZNSYMS)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filter used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_sub // Paeth with b=c=0 turns out to be equivalent to sub +}; + +static int stbi__paeth(int a, int b, int c) +{ + // This formulation looks very different from the reference in the PNG spec, but is + // actually equivalent and has favorable data dependencies and admits straightforward + // generation of branch-free code, which helps performance significantly. + int thresh = c*3 - (a + b); + int lo = a < b ? a : b; + int hi = a < b ? b : a; + int t0 = (hi <= thresh) ? lo : c; + int t1 = (thresh <= lo) ? hi : t0; + return t1; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// adds an extra all-255 alpha channel +// dest == src is legal +// img_n must be 1 or 3 +static void stbi__create_png_alpha_expand8(stbi_uc *dest, stbi_uc *src, stbi__uint32 x, int img_n) +{ + int i; + // must process data backwards since we allow dest==src + if (img_n == 1) { + for (i=x-1; i >= 0; --i) { + dest[i*2+1] = 255; + dest[i*2+0] = src[i]; + } + } else { + STBI_ASSERT(img_n == 3); + for (i=x-1; i >= 0; --i) { + dest[i*4+3] = 255; + dest[i*4+2] = src[i*3+2]; + dest[i*4+1] = src[i*3+1]; + dest[i*4+0] = src[i*3+0]; + } + } +} + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16 ? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + stbi_uc *filter_buf; + int all_ok = 1; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + // note: error exits here don't need to clean up a->out individually, + // stbi__do_png always does on error. + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + if (!stbi__mad2sizes_valid(img_width_bytes, y, img_width_bytes)) return stbi__err("too large", "Corrupt PNG"); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + // Allocate two scan lines worth of filter workspace buffer. + filter_buf = (stbi_uc *) stbi__malloc_mad2(img_width_bytes, 2, 0); + if (!filter_buf) return stbi__err("outofmem", "Out of memory"); + + // Filtering for low-bit-depth images + if (depth < 8) { + filter_bytes = 1; + width = img_width_bytes; + } + + for (j=0; j < y; ++j) { + // cur/prior filter buffers alternate + stbi_uc *cur = filter_buf + (j & 1)*img_width_bytes; + stbi_uc *prior = filter_buf + (~j & 1)*img_width_bytes; + stbi_uc *dest = a->out + stride*j; + int nk = width * filter_bytes; + int filter = *raw++; + + // check filter type + if (filter > 4) { + all_ok = stbi__err("invalid filter","Corrupt PNG"); + break; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // perform actual filtering + switch (filter) { + case STBI__F_none: + memcpy(cur, raw, nk); + break; + case STBI__F_sub: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); + break; + case STBI__F_up: + for (k = 0; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); + break; + case STBI__F_avg: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); + break; + case STBI__F_paeth: + for (k = 0; k < filter_bytes; ++k) + cur[k] = STBI__BYTECAST(raw[k] + prior[k]); // prior[k] == stbi__paeth(0,prior[k],0) + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes], prior[k], prior[k-filter_bytes])); + break; + case STBI__F_avg_first: + memcpy(cur, raw, filter_bytes); + for (k = filter_bytes; k < nk; ++k) + cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); + break; + } + + raw += nk; + + // expand decoded bits in cur to dest, also adding an extra alpha channel if desired + if (depth < 8) { + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + stbi_uc *in = cur; + stbi_uc *out = dest; + stbi_uc inb = 0; + stbi__uint32 nsmp = x*img_n; + + // expand bits to bytes first + if (depth == 4) { + for (i=0; i < nsmp; ++i) { + if ((i & 1) == 0) inb = *in++; + *out++ = scale * (inb >> 4); + inb <<= 4; + } + } else if (depth == 2) { + for (i=0; i < nsmp; ++i) { + if ((i & 3) == 0) inb = *in++; + *out++ = scale * (inb >> 6); + inb <<= 2; + } + } else { + STBI_ASSERT(depth == 1); + for (i=0; i < nsmp; ++i) { + if ((i & 7) == 0) inb = *in++; + *out++ = scale * (inb >> 7); + inb <<= 1; + } + } + + // insert alpha=255 values if desired + if (img_n != out_n) + stbi__create_png_alpha_expand8(dest, dest, x, img_n); + } else if (depth == 8) { + if (img_n == out_n) + memcpy(dest, cur, x*img_n); + else + stbi__create_png_alpha_expand8(dest, cur, x, img_n); + } else if (depth == 16) { + // convert the image data from big-endian to platform-native + stbi__uint16 *dest16 = (stbi__uint16*)dest; + stbi__uint32 nsmp = x*img_n; + + if (img_n == out_n) { + for (i = 0; i < nsmp; ++i, ++dest16, cur += 2) + *dest16 = (cur[0] << 8) | cur[1]; + } else { + STBI_ASSERT(img_n+1 == out_n); + if (img_n == 1) { + for (i = 0; i < x; ++i, dest16 += 2, cur += 2) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = 0xffff; + } + } else { + STBI_ASSERT(img_n == 3); + for (i = 0; i < x; ++i, dest16 += 4, cur += 6) { + dest16[0] = (cur[0] << 8) | cur[1]; + dest16[1] = (cur[2] << 8) | cur[3]; + dest16[2] = (cur[4] << 8) | cur[5]; + dest16[3] = 0xffff; + } + } + } + } + } + + STBI_FREE(filter_buf); + if (!all_ok) return 0; + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + if (!final) return stbi__err("outofmem", "Out of memory"); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load_global = 0; +static int stbi__de_iphone_flag_global = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_global = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_global = flag_true_if_should_convert; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__unpremultiply_on_load stbi__unpremultiply_on_load_global +#define stbi__de_iphone_flag stbi__de_iphone_flag_global +#else +static STBI_THREAD_LOCAL int stbi__unpremultiply_on_load_local, stbi__unpremultiply_on_load_set; +static STBI_THREAD_LOCAL int stbi__de_iphone_flag_local, stbi__de_iphone_flag_set; + +STBIDEF void stbi_set_unpremultiply_on_load_thread(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load_local = flag_true_if_should_unpremultiply; + stbi__unpremultiply_on_load_set = 1; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb_thread(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag_local = flag_true_if_should_convert; + stbi__de_iphone_flag_set = 1; +} + +#define stbi__unpremultiply_on_load (stbi__unpremultiply_on_load_set \ +? stbi__unpremultiply_on_load_local \ +: stbi__unpremultiply_on_load_global) +#define stbi__de_iphone_flag (stbi__de_iphone_flag_set \ +? stbi__de_iphone_flag_local \ +: stbi__de_iphone_flag_global) +#endif // STBI_THREAD_LOCAL + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + } + // even with SCAN_header, have to scan to see if we have a tRNS + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + // non-paletted with tRNS = constant alpha. if header-scanning, we can stop now. + if (scan == STBI__SCAN_header) { ++s->img_n; return 1; } + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { + // header scan definitely stops at first IDAT + if (pal_img_n) + s->img_n = pal_img_n; + return 1; + } + if (c.length > (1u << 30)) return stbi__err("IDAT size limit", "IDAT section larger than 2^30 bytes"); + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { +#ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); +#endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static int stbi__bmp_set_mask_defaults(stbi__bmp_data *info, int compress) +{ + // BI_BITFIELDS specifies masks explicitly, don't override + if (compress == 3) + return 1; + + if (compress == 0) { + if (info->bpp == 16) { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } else if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + // otherwise, use defaults, which is all-0 + info->mr = info->mg = info->mb = info->ma = 0; + } + return 1; + } + return 0; // error +} + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + if (compress >= 4) return stbi__errpuc("BMP JPEG/PNG", "BMP type not supported: unsupported compression"); // this includes PNG/JPEG modes + if (compress == 3 && info->bpp != 16 && info->bpp != 32) return stbi__errpuc("bad BMP", "bad BMP"); // bitfields requires 16 or 32 bits/pixel + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + stbi__bmp_set_mask_defaults(info, compress); + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + // V4/V5 header + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + if (compress != 3) // override mr/mg/mb unless in BI_BITFIELDS mode, as per docs + stbi__bmp_set_mask_defaults(info, compress); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + // accept some number of extra bytes after the header, but if the offset points either to before + // the header ends or implies a large amount of extra data, reject the file as malformed + int bytes_read_so_far = s->callback_already_read + (int)(s->img_buffer - s->img_buffer_original); + int header_limit = 1024; // max we actually read is below 256 bytes currently. + int extra_data_limit = 256*4; // what ordinarily goes here is a palette; 256 entries*4 bytes is its max size. + if (bytes_read_so_far <= 0 || bytes_read_so_far > header_limit) { + return stbi__errpuc("bad header", "Corrupt BMP"); + } + // we established that bytes_read_so_far is positive and sensible. + // the first half of this test rejects offsets that are either too small positives, or + // negative, and guarantees that info.offset >= bytes_read_so_far > 0. this in turn + // ensures the number computed in the second half of the test can't overflow. + if (info.offset < bytes_read_so_far || info.offset - bytes_read_so_far > extra_data_limit) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } else { + stbi__skip(s, info.offset - bytes_read_so_far); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + + errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + if (!result) return stbi__errpuc("outofmem", "Out of memory"); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!g) return stbi__err("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays) +{ + STBI_FREE(g->out); + STBI_FREE(g->history); + STBI_FREE(g->background); + + if (out) STBI_FREE(out); + if (delays && *delays) STBI_FREE(*delays); + return stbi__errpuc("outofmem", "Out of memory"); +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + + STBI_NOTUSED(out_size); + STBI_NOTUSED(delays_size); + + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (!tmp) + return stbi__load_gif_main_outofmem(&g, out, delays); + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + if (!new_delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + *delays = new_delays; + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + if (!out) + return stbi__load_gif_main_outofmem(&g, out, delays); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + if (!*delays) + return stbi__load_gif_main_outofmem(&g, out, delays); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if ((count == 0) || (count > nleft)) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + if (p == NULL) { + stbi__rewind( s ); + return 0; + } + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + STBI_NOTUSED(stbi__get32be(s)); + STBI_NOTUSED(stbi__get32be(s)); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + ri->bits_per_channel = stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n); + if (ri->bits_per_channel == 0) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad4sizes_valid(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad4(s->img_n, s->img_x, s->img_y, ri->bits_per_channel / 8, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__getn(s, out, s->img_n * s->img_x * s->img_y * (ri->bits_per_channel / 8))) { + STBI_FREE(out); + return stbi__errpuc("bad PNM", "PNM file truncated"); + } + + if (req_comp && req_comp != s->img_n) { + if (ri->bits_per_channel == 16) { + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, s->img_n, req_comp, s->img_x, s->img_y); + } else { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + } + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + if((value > 214748364) || (value == 214748364 && *c > '7')) + return stbi__err("integer parse overflow", "Parsing an integer in the PPM header overflowed a 32-bit int"); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + if(*x == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + if (*y == 0) + return stbi__err("invalid width", "PPM image header had zero or overflowing width"); + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + if (maxv > 65535) + return stbi__err("max value > 65535", "PPM image supports only 8-bit and 16-bit images"); + else if (maxv > 255) + return 16; + else + return 8; +} + +static int stbi__pnm_is16(stbi__context *s) +{ + if (stbi__pnm_info(s, NULL, NULL, NULL) == 16) + return 1; + return 0; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ +#ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; +#endif + +#ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; +#endif + +#ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; +#endif + +#ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; +#endif + +#ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; +#endif + +#ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; +#endif + +#ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; +#endif + +#ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; +#endif + + // test tga last because it's a crappy test! +#ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; +#endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ +#ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; +#endif + +#ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; +#endif + +#ifndef STBI_NO_PNM + if (stbi__pnm_is16(s)) return 1; +#endif + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/third_party/stb/stb_sprintf.h b/src/third_party/stb/stb_sprintf.h index 6a65f957..8966f7f2 100644 --- a/src/third_party/stb/stb_sprintf.h +++ b/src/third_party/stb/stb_sprintf.h @@ -1,1943 +1,1943 @@ -// ---------------------------------------------------------------------------- -// -// NOTE(rjf): This has been modified to support extra format specifiers -// for the debugger project - this is *not* an unmodified copy of the original -// stb_sprintf v1.10 code. -// -// ---------------------------------------------------------------------------- - -// stb_sprintf - v1.10 - public domain snprintf() implementation -// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 -// http://github.com/nothings/stb -// -// allowed types: sc uidBboXx p AaGgEef n -// lengths : hh h ll j z t I64 I32 I -// -// Contributors: -// Fabian "ryg" Giesen (reformatting) -// github:aganm (attribute format) -// -// Contributors (bugfixes): -// github:d26435 -// github:trex78 -// github:account-login -// Jari Komppa (SI suffixes) -// Rohit Nirmal -// Marcin Wojdyr -// Leonard Ritter -// Stefano Zanotti -// Adam Allison -// Arvid Gerstmann -// Markus Kolb -// -// LICENSE: -// -// See end of file for license information. - -#ifndef STB_SPRINTF_H_INCLUDE -#define STB_SPRINTF_H_INCLUDE - -/* -Single file sprintf replacement. - -Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. -Hereby placed in public domain. - -This is a full sprintf replacement that supports everything that -the C runtime sprintfs support, including float/double, 64-bit integers, -hex floats, field parameters (%*.*d stuff), length reads backs, etc. - -Why would you need this if sprintf already exists? Well, first off, -it's *much* faster (see below). It's also much smaller than the CRT -versions code-space-wise. We've also added some simple improvements -that are super handy (commas in thousands, callbacks at buffer full, -for example). Finally, the format strings for MSVC and GCC differ -for 64-bit integers (among other small things), so this lets you use -the same format strings in cross platform code. - -It uses the standard single file trick of being both the header file -and the source itself. If you just include it normally, you just get -the header file function definitions. To get the code, you include -it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. - -It only uses va_args macros from the C runtime to do it's work. It -does cast doubles to S64s and shifts and divides U64s, which does -drag in CRT code on most platforms. - -It compiles to roughly 8K with float support, and 4K without. -As a comparison, when using MSVC static libs, calling sprintf drags -in 16K. - -API: -==== -int stbsp_sprintf( char * buf, char const * fmt, ... ) -int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) - Convert an arg list into a buffer. stbsp_snprintf always returns - a zero-terminated string (unlike regular snprintf). - -int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) -int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) - Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns - a zero-terminated string (unlike regular snprintf). - -int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) - typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); - Convert into a buffer, calling back every STB_SPRINTF_MIN chars. - Your callback can then copy the chars out, print them or whatever. - This function is actually the workhorse for everything else. - The buffer you pass in must hold at least STB_SPRINTF_MIN characters. - // you return the next buffer to use or 0 to stop converting - -void stbsp_set_separators( char comma, char period ) - Set the comma and period characters to use. - -FLOATS/DOUBLES: -=============== -This code uses a internal float->ascii conversion method that uses -doubles with error correction (double-doubles, for ~105 bits of -precision). This conversion is round-trip perfect - that is, an atof -of the values output here will give you the bit-exact double back. - -One difference is that our insignificant digits will be different than -with MSVC or GCC (but they don't match each other either). We also -don't attempt to find the minimum length matching float (pre-MSVC15 -doesn't either). - -If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT -and you'll save 4K of code space. - -64-BIT INTS: -============ -This library also supports 64-bit integers and you can use MSVC style or -GCC style indicators (%I64d or %lld). It supports the C99 specifiers -for size_t and ptr_diff_t (%jd %zd) as well. - -EXTRAS: -======= -Like some GCCs, for integers and floats, you can use a ' (single quote) -specifier and commas will be inserted on the thousands: "%'d" on 12345 -would print 12,345. - -For integers and floats, you can use a "$" specifier and the number -will be converted to float and then divided to get kilo, mega, giga or -tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is -"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn -2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three -$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the -suffix, add "_" specifier: "%_$d" -> "2.53M". - -In addition to octal and hexadecimal conversions, you can print -integers in binary: "%b" for 256 would print 100. - -PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): -=================================================================== -"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) -"%24d" across all 32-bit ints (4.5x/4.2x faster) -"%x" across all 32-bit ints (4.5x/3.8x faster) -"%08x" across all 32-bit ints (4.3x/3.8x faster) -"%f" across e-10 to e+10 floats (7.3x/6.0x faster) -"%e" across e-10 to e+10 floats (8.1x/6.0x faster) -"%g" across e-10 to e+10 floats (10.0x/7.1x faster) -"%f" for values near e-300 (7.9x/6.5x faster) -"%f" for values near e+300 (10.0x/9.1x faster) -"%e" for values near e-300 (10.1x/7.0x faster) -"%e" for values near e+300 (9.2x/6.0x faster) -"%.320f" for values near e-300 (12.6x/11.2x faster) -"%a" for random values (8.6x/4.3x faster) -"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) -"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) -"%s%s%s" for 64 char strings (7.1x/7.3x faster) -"...512 char string..." ( 35.0x/32.5x faster!) -*/ - -#if defined(__clang__) -# if defined(__has_feature) && defined(__has_attribute) -# if __has_feature(address_sanitizer) -# if __has_attribute(__no_sanitize__) -# define STBSP__ASAN __attribute__((__no_sanitize__("address"))) -# elif __has_attribute(__no_sanitize_address__) -# define STBSP__ASAN __attribute__((__no_sanitize_address__)) -# elif __has_attribute(__no_address_safety_analysis__) -# define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) -# endif -# endif -# endif -#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) -# if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ -# define STBSP__ASAN __attribute__((__no_sanitize_address__)) -# endif -#elif defined(_MSC_VER) -# if defined(__SANITIZE_ADDRESS__) -# define STBSP__ASAN __declspec(no_sanitize_address) -# endif -#endif - -#ifndef STBSP__ASAN -#define STBSP__ASAN -#endif - -#ifdef STB_SPRINTF_STATIC -#define STBSP__PUBLICDEC static STBSP__ASAN -#define STBSP__PUBLICDEF static STBSP__ASAN -#else -#ifdef __cplusplus -#define STBSP__PUBLICDEC extern "C" STBSP__ASAN -#define STBSP__PUBLICDEF extern "C" STBSP__ASAN -#else -#define STBSP__PUBLICDEC extern STBSP__ASAN -#define STBSP__PUBLICDEF STBSP__ASAN -#endif -#endif - -#if defined(__has_attribute) -#if __has_attribute(format) -#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) -#endif -#endif - -#ifndef STBSP__ATTRIBUTE_FORMAT -#define STBSP__ATTRIBUTE_FORMAT(fmt,va) -#endif - -#ifdef _MSC_VER -#define STBSP__NOTUSED(v) (void)(v) -#else -#define STBSP__NOTUSED(v) (void)sizeof(v) -#endif - -#include // for va_arg(), va_list() -#include // size_t, ptrdiff_t - -#ifndef STB_SPRINTF_MIN -#define STB_SPRINTF_MIN 512 // how many characters per callback -#endif -typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); - -#ifndef STB_SPRINTF_DECORATE -#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names -#endif - -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); - -STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); -STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); - -#endif // STB_SPRINTF_H_INCLUDE - -#ifdef STB_SPRINTF_IMPLEMENTATION - -#define stbsp__uint32 unsigned int -#define stbsp__int32 signed int - -#ifdef _MSC_VER -#define stbsp__uint64 unsigned __int64 -#define stbsp__int64 signed __int64 -#else -#define stbsp__uint64 unsigned long long -#define stbsp__int64 signed long long -#endif -#define stbsp__uint16 unsigned short - -#ifndef stbsp__uintptr -#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) -#define stbsp__uintptr stbsp__uint64 -#else -#define stbsp__uintptr stbsp__uint32 -#endif -#endif - -#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) -#if defined(_MSC_VER) && (_MSC_VER < 1900) -#define STB_SPRINTF_MSVC_MODE -#endif -#endif - -#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses -#define STBSP__UNALIGNED(code) -#else -#define STBSP__UNALIGNED(code) code -#endif - -#ifndef STB_SPRINTF_NOFLOAT -// internal float utility functions -static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); -static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); -#define STBSP__SPECIAL 0x7000 -#endif - -static char stbsp__period = '.'; -static char stbsp__comma = ','; -static struct -{ - short temp; // force next field to be 2-byte aligned - char pair[201]; -} stbsp__digitpair = -{ - 0, - "00010203040506070809101112131415161718192021222324" - "25262728293031323334353637383940414243444546474849" - "50515253545556575859606162636465666768697071727374" - "75767778798081828384858687888990919293949596979899" -}; - -STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) -{ - stbsp__period = pperiod; - stbsp__comma = pcomma; -} - -#define STBSP__LEFTJUST 1 -#define STBSP__LEADINGPLUS 2 -#define STBSP__LEADINGSPACE 4 -#define STBSP__LEADING_0X 8 -#define STBSP__LEADINGZERO 16 -#define STBSP__INTMAX 32 -#define STBSP__TRIPLET_COMMA 64 -#define STBSP__NEGATIVE 128 -#define STBSP__METRIC_SUFFIX 256 -#define STBSP__HALFWIDTH 512 -#define STBSP__METRIC_NOSPACE 1024 -#define STBSP__METRIC_1024 2048 -#define STBSP__METRIC_JEDEC 4096 - -static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) -{ - sign[0] = 0; - if (fl & STBSP__NEGATIVE) { - sign[0] = 1; - sign[1] = '-'; - } else if (fl & STBSP__LEADINGSPACE) { - sign[0] = 1; - sign[1] = ' '; - } else if (fl & STBSP__LEADINGPLUS) { - sign[0] = 1; - sign[1] = '+'; - } -} - -static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) -{ - char const * sn = s; - - // get up to 4-byte alignment - for (;;) { - if (((stbsp__uintptr)sn & 3) == 0) - break; - - if (!limit || *sn == 0) - return (stbsp__uint32)(sn - s); - - ++sn; - --limit; - } - - // scan over 4 bytes at a time to find terminating 0 - // this will intentionally scan up to 3 bytes past the end of buffers, - // but becase it works 4B aligned, it will never cross page boundaries - // (hence the STBSP__ASAN markup; the over-read here is intentional - // and harmless) - while (limit >= 4) { - stbsp__uint32 v = *(stbsp__uint32 *)sn; - // bit hack to find if there's a 0 byte in there - if ((v - 0x01010101) & (~v) & 0x80808080UL) - break; - - sn += 4; - limit -= 4; - } - - // handle the last few characters to find actual size - while (limit && *sn) { - ++sn; - --limit; - } - - return (stbsp__uint32)(sn - s); -} - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) -{ - static char hex[] = "0123456789abcdefxp"; - static char hexu[] = "0123456789ABCDEFXP"; - char *bf; - char const *f; - int tlen = 0; - - bf = buf; - f = fmt; - for (;;) { - stbsp__int32 fw, pr, tz; - stbsp__uint32 fl; - - // macros for the callback buffer stuff -#define stbsp__chk_cb_bufL(bytes) \ -{ \ -int len = (int)(bf - buf); \ -if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ -tlen += len; \ -if (0 == (bf = buf = callback(buf, user, len))) \ -goto done; \ -} \ -} -#define stbsp__chk_cb_buf(bytes) \ -{ \ -if (callback) { \ -stbsp__chk_cb_bufL(bytes); \ -} \ -} -#define stbsp__flush_cb() \ -{ \ -stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ -} // flush if there is even one byte in the buffer -#define stbsp__cb_buf_clamp(cl, v) \ -cl = v; \ -if (callback) { \ -int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ -if (cl > lg) \ -cl = lg; \ -} - - // fast copy everything up to the next % (or end of string) - for (;;) { - while (((stbsp__uintptr)f) & 3) { - schk1: - if (f[0] == '%') - goto scandd; - schk2: - if (f[0] == 0) - goto endfmt; - stbsp__chk_cb_buf(1); - *bf++ = f[0]; - ++f; - } - for (;;) { - // Check if the next 4 bytes contain %(0x25) or end of string. - // Using the 'hasless' trick: - // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord - stbsp__uint32 v, c; - v = *(stbsp__uint32 *)f; - c = (~v) & 0x80808080; - if (((v ^ 0x25252525) - 0x01010101) & c) - goto schk1; - if ((v - 0x01010101) & c) - goto schk2; - if (callback) - if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) - goto schk1; -#ifdef STB_SPRINTF_NOUNALIGNED - if(((stbsp__uintptr)bf) & 3) { - bf[0] = f[0]; - bf[1] = f[1]; - bf[2] = f[2]; - bf[3] = f[3]; - } else -#endif - { - *(stbsp__uint32 *)bf = v; - } - bf += 4; - f += 4; - } - } - scandd: - - ++f; - - // ok, we have a percent, read the modifiers first - fw = 0; - pr = -1; - fl = 0; - tz = 0; - - // flags - for (;;) { - switch (f[0]) { - // if we have left justify - case '-': - fl |= STBSP__LEFTJUST; - ++f; - continue; - // if we have leading plus - case '+': - fl |= STBSP__LEADINGPLUS; - ++f; - continue; - // if we have leading space - case ' ': - fl |= STBSP__LEADINGSPACE; - ++f; - continue; - // if we have leading 0x - case '#': - fl |= STBSP__LEADING_0X; - ++f; - continue; - // if we have thousand commas - case '\'': - fl |= STBSP__TRIPLET_COMMA; - ++f; - continue; - // if we have kilo marker (none->kilo->kibi->jedec) - case '$': - if (fl & STBSP__METRIC_SUFFIX) { - if (fl & STBSP__METRIC_1024) { - fl |= STBSP__METRIC_JEDEC; - } else { - fl |= STBSP__METRIC_1024; - } - } else { - fl |= STBSP__METRIC_SUFFIX; - } - ++f; - continue; - // if we don't want space between metric suffix and number - case '_': - fl |= STBSP__METRIC_NOSPACE; - ++f; - continue; - // if we have leading zero - case '0': - fl |= STBSP__LEADINGZERO; - ++f; - goto flags_done; - default: goto flags_done; - } - } - flags_done: - - // get the field width - if (f[0] == '*') { - fw = va_arg(va, stbsp__uint32); - ++f; - } else { - while ((f[0] >= '0') && (f[0] <= '9')) { - fw = fw * 10 + f[0] - '0'; - f++; - } - } - // get the precision - if (f[0] == '.') { - ++f; - if (f[0] == '*') { - pr = va_arg(va, stbsp__uint32); - ++f; - } else { - pr = 0; - while ((f[0] >= '0') && (f[0] <= '9')) { - pr = pr * 10 + f[0] - '0'; - f++; - } - } - } - - // handle integer size overrides - switch (f[0]) { - // are we halfwidth? - case 'h': - fl |= STBSP__HALFWIDTH; - ++f; - if (f[0] == 'h') - ++f; // QUARTERWIDTH - break; - // are we 64-bit (unix style) - case 'l': - fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); - ++f; - if (f[0] == 'l') { - fl |= STBSP__INTMAX; - ++f; - } - break; - // are we 64-bit on intmax? (c99) - case 'j': - fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; - ++f; - break; - // are we 64-bit on size_t or ptrdiff_t? (c99) - case 'z': - fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; - ++f; - break; - case 't': - fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; - ++f; - break; - // are we 64-bit (msft style) - case 'I': - if ((f[1] == '6') && (f[2] == '4')) { - fl |= STBSP__INTMAX; - f += 3; - } else if ((f[1] == '3') && (f[2] == '2')) { - f += 3; - } else { - fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); - ++f; - } - break; - default: break; - } - - // handle each replacement - switch (f[0]) { -#define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 - char num[STBSP__NUMSZ]; - char lead[8]; - char tail[8]; - char *s; - char const *h; - stbsp__uint32 l, n, cs; - stbsp__uint64 n64; -#ifndef STB_SPRINTF_NOFLOAT - double fv; -#endif - stbsp__int32 dp; - char const *sn; - - case 's': - // get the string - s = va_arg(va, char *); - if (s == 0) - s = (char *)"null"; - // get the length, limited to desired precision - // always limit to ~0u chars since our counts are 32b - l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - // copy the string in - goto scopy; - - //- - //- - //- - // NOTE(rjf): DEBUGGER PROJECT ADDITION vvv - // - // The following additions are for using the debugger project's base layer - // types in format strings. - // - case 'S': - { - String8 string = va_arg(va, String8); - s = (char *)string.str; - l = (U32)string.size; - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - }goto scopy; - // - // NOTE(rjf): DEBUGGER PROJECT ADDITION ^^^ - //- - //- - //- - - case 'c': // char - // get the character - s = num + STBSP__NUMSZ - 1; - *s = (char)va_arg(va, int); - l = 1; - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - goto scopy; - - case 'n': // weird write-bytes specifier - { - int *d = va_arg(va, int *); - *d = tlen + (int)(bf - buf); - } break; - -#ifdef STB_SPRINTF_NOFLOAT - case 'A': // float - case 'a': // hex float - case 'G': // float - case 'g': // float - case 'E': // float - case 'e': // float - case 'f': // float - va_arg(va, double); // eat it - s = (char *)"No float"; - l = 8; - lead[0] = 0; - tail[0] = 0; - pr = 0; - cs = 0; - STBSP__NOTUSED(dp); - goto scopy; -#else - case 'A': // hex float - case 'a': // hex float - h = (f[0] == 'A') ? hexu : hex; - fv = va_arg(va, double); - if (pr == -1) - pr = 6; // default is 6 - // read the double into a string - if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) - fl |= STBSP__NEGATIVE; - - s = num + 64; - - stbsp__lead_sign(fl, lead); - - if (dp == -1023) - dp = (n64) ? -1022 : 0; - else - n64 |= (((stbsp__uint64)1) << 52); - n64 <<= (64 - 56); - if (pr < 15) - n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); - // add leading chars - -#ifdef STB_SPRINTF_MSVC_MODE - *s++ = '0'; - *s++ = 'x'; -#else - lead[1 + lead[0]] = '0'; - lead[2 + lead[0]] = 'x'; - lead[0] += 2; -#endif - *s++ = h[(n64 >> 60) & 15]; - n64 <<= 4; - if (pr) - *s++ = stbsp__period; - sn = s; - - // print the bits - n = pr; - if (n > 13) - n = 13; - if (pr > (stbsp__int32)n) - tz = pr - n; - pr = 0; - while (n--) { - *s++ = h[(n64 >> 60) & 15]; - n64 <<= 4; - } - - // print the expo - tail[1] = h[17]; - if (dp < 0) { - tail[2] = '-'; - dp = -dp; - } else - tail[2] = '+'; - n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); - tail[0] = (char)n; - for (;;) { - tail[n] = '0' + dp % 10; - if (n <= 3) - break; - --n; - dp /= 10; - } - - dp = (int)(s - sn); - l = (int)(s - (num + 64)); - s = num + 64; - cs = 1 + (3 << 24); - goto scopy; - - case 'G': // float - case 'g': // float - h = (f[0] == 'G') ? hexu : hex; - fv = va_arg(va, double); - if (pr == -1) - pr = 6; - else if (pr == 0) - pr = 1; // default is 6 - // read the double into a string - if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) - fl |= STBSP__NEGATIVE; - - // clamp the precision and delete extra zeros after clamp - n = pr; - if (l > (stbsp__uint32)pr) - l = pr; - while ((l > 1) && (pr) && (sn[l - 1] == '0')) { - --pr; - --l; - } - - // should we use %e - if ((dp <= -4) || (dp > (stbsp__int32)n)) { - if (pr > (stbsp__int32)l) - pr = l - 1; - else if (pr) - --pr; // when using %e, there is one digit before the decimal - goto doexpfromg; - } - // this is the insane action to get the pr to match %g semantics for %f - if (dp > 0) { - pr = (dp < (stbsp__int32)l) ? l - dp : 0; - } else { - pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); - } - goto dofloatfromg; - - case 'E': // float - case 'e': // float - h = (f[0] == 'E') ? hexu : hex; - fv = va_arg(va, double); - if (pr == -1) - pr = 6; // default is 6 - // read the double into a string - if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) - fl |= STBSP__NEGATIVE; - doexpfromg: - tail[0] = 0; - stbsp__lead_sign(fl, lead); - if (dp == STBSP__SPECIAL) { - s = (char *)sn; - cs = 0; - pr = 0; - goto scopy; - } - s = num + 64; - // handle leading chars - *s++ = sn[0]; - - if (pr) - *s++ = stbsp__period; - - // handle after decimal - if ((l - 1) > (stbsp__uint32)pr) - l = pr + 1; - for (n = 1; n < l; n++) - *s++ = sn[n]; - // trailing zeros - tz = pr - (l - 1); - pr = 0; - // dump expo - tail[1] = h[0xe]; - dp -= 1; - if (dp < 0) { - tail[2] = '-'; - dp = -dp; - } else - tail[2] = '+'; -#ifdef STB_SPRINTF_MSVC_MODE - n = 5; -#else - n = (dp >= 100) ? 5 : 4; -#endif - tail[0] = (char)n; - for (;;) { - tail[n] = '0' + dp % 10; - if (n <= 3) - break; - --n; - dp /= 10; - } - cs = 1 + (3 << 24); // how many tens - goto flt_lead; - - case 'f': // float - fv = va_arg(va, double); - doafloat: - // do kilos - if (fl & STBSP__METRIC_SUFFIX) { - double divisor; - divisor = 1000.0f; - if (fl & STBSP__METRIC_1024) - divisor = 1024.0; - while (fl < 0x4000000) { - if ((fv < divisor) && (fv > -divisor)) - break; - fv /= divisor; - fl += 0x1000000; - } - } - if (pr == -1) - pr = 6; // default is 6 - // read the double into a string - if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) - fl |= STBSP__NEGATIVE; - dofloatfromg: - tail[0] = 0; - stbsp__lead_sign(fl, lead); - if (dp == STBSP__SPECIAL) { - s = (char *)sn; - cs = 0; - pr = 0; - goto scopy; - } - s = num + 64; - - // handle the three decimal varieties - if (dp <= 0) { - stbsp__int32 i; - // handle 0.000*000xxxx - *s++ = '0'; - if (pr) - *s++ = stbsp__period; - n = -dp; - if ((stbsp__int32)n > pr) - n = pr; - i = n; - while (i) { - if ((((stbsp__uintptr)s) & 3) == 0) - break; - *s++ = '0'; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)s = 0x30303030; - s += 4; - i -= 4; - } - while (i) { - *s++ = '0'; - --i; - } - if ((stbsp__int32)(l + n) > pr) - l = pr - n; - i = l; - while (i) { - *s++ = *sn++; - --i; - } - tz = pr - (n + l); - cs = 1 + (3 << 24); // how many tens did we write (for commas below) - } else { - cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; - if ((stbsp__uint32)dp >= l) { - // handle xxxx000*000.0 - n = 0; - for (;;) { - if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { - cs = 0; - *s++ = stbsp__comma; - } else { - *s++ = sn[n]; - ++n; - if (n >= l) - break; - } - } - if (n < (stbsp__uint32)dp) { - n = dp - n; - if ((fl & STBSP__TRIPLET_COMMA) == 0) { - while (n) { - if ((((stbsp__uintptr)s) & 3) == 0) - break; - *s++ = '0'; - --n; - } - while (n >= 4) { - *(stbsp__uint32 *)s = 0x30303030; - s += 4; - n -= 4; - } - } - while (n) { - if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { - cs = 0; - *s++ = stbsp__comma; - } else { - *s++ = '0'; - --n; - } - } - } - cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens - if (pr) { - *s++ = stbsp__period; - tz = pr; - } - } else { - // handle xxxxx.xxxx000*000 - n = 0; - for (;;) { - if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { - cs = 0; - *s++ = stbsp__comma; - } else { - *s++ = sn[n]; - ++n; - if (n >= (stbsp__uint32)dp) - break; - } - } - cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens - if (pr) - *s++ = stbsp__period; - if ((l - dp) > (stbsp__uint32)pr) - l = pr + dp; - while (n < l) { - *s++ = sn[n]; - ++n; - } - tz = pr - (l - dp); - } - } - pr = 0; - - // handle k,m,g,t - if (fl & STBSP__METRIC_SUFFIX) { - char idx; - idx = 1; - if (fl & STBSP__METRIC_NOSPACE) - idx = 0; - tail[0] = idx; - tail[1] = ' '; - { - if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. - if (fl & STBSP__METRIC_1024) - tail[idx + 1] = "_KMGT"[fl >> 24]; - else - tail[idx + 1] = "_kMGT"[fl >> 24]; - idx++; - // If printing kibits and not in jedec, add the 'i'. - if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { - tail[idx + 1] = 'i'; - idx++; - } - tail[0] = idx; - } - } - }; - - flt_lead: - // get the length that we copied - l = (stbsp__uint32)(s - (num + 64)); - s = num + 64; - goto scopy; -#endif - - case 'B': // upper binary - case 'b': // lower binary - h = (f[0] == 'B') ? hexu : hex; - lead[0] = 0; - if (fl & STBSP__LEADING_0X) { - lead[0] = 2; - lead[1] = '0'; - lead[2] = h[0xb]; - } - l = (8 << 4) | (1 << 8); - goto radixnum; - - case 'o': // octal - h = hexu; - lead[0] = 0; - if (fl & STBSP__LEADING_0X) { - lead[0] = 1; - lead[1] = '0'; - } - l = (3 << 4) | (3 << 8); - goto radixnum; - - case 'p': // pointer - fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; - pr = sizeof(void *) * 2; - fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros - // fall through - to X - - case 'X': // upper hex - case 'x': // lower hex - h = (f[0] == 'X') ? hexu : hex; - l = (4 << 4) | (4 << 8); - lead[0] = 0; - if (fl & STBSP__LEADING_0X) { - lead[0] = 2; - lead[1] = '0'; - lead[2] = h[16]; - } - radixnum: - // get the number - if (fl & STBSP__INTMAX) - n64 = va_arg(va, stbsp__uint64); - else - n64 = va_arg(va, stbsp__uint32); - - s = num + STBSP__NUMSZ; - dp = 0; - // clear tail, and clear leading if value is zero - tail[0] = 0; - if (n64 == 0) { - lead[0] = 0; - if (pr == 0) { - l = 0; - cs = 0; - goto scopy; - } - } - // convert to string - for (;;) { - *--s = h[n64 & ((1 << (l >> 8)) - 1)]; - n64 >>= (l >> 8); - if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) - break; - if (fl & STBSP__TRIPLET_COMMA) { - ++l; - if ((l & 15) == ((l >> 4) & 15)) { - l &= ~15; - *--s = stbsp__comma; - } - } - }; - // get the tens and the comma pos - cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); - // get the length that we copied - l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); - // copy it - goto scopy; - - case 'u': // unsigned - case 'i': - case 'd': // integer - // get the integer and abs it - if (fl & STBSP__INTMAX) { - stbsp__int64 i64 = va_arg(va, stbsp__int64); - n64 = (stbsp__uint64)i64; - if ((f[0] != 'u') && (i64 < 0)) { - n64 = (stbsp__uint64)-i64; - fl |= STBSP__NEGATIVE; - } - } else { - stbsp__int32 i = va_arg(va, stbsp__int32); - n64 = (stbsp__uint32)i; - if ((f[0] != 'u') && (i < 0)) { - n64 = (stbsp__uint32)-i; - fl |= STBSP__NEGATIVE; - } - } - -#ifndef STB_SPRINTF_NOFLOAT - if (fl & STBSP__METRIC_SUFFIX) { - if (n64 < 1024) - pr = 0; - else if (pr == -1) - pr = 1; - fv = (double)(stbsp__int64)n64; - goto doafloat; - } -#endif - - // convert to string - s = num + STBSP__NUMSZ; - l = 0; - - for (;;) { - // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) - char *o = s - 8; - if (n64 >= 100000000) { - n = (stbsp__uint32)(n64 % 100000000); - n64 /= 100000000; - } else { - n = (stbsp__uint32)n64; - n64 = 0; - } - if ((fl & STBSP__TRIPLET_COMMA) == 0) { - do { - s -= 2; - *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; - n /= 100; - } while (n); - } - while (n) { - if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { - l = 0; - *--s = stbsp__comma; - --o; - } else { - *--s = (char)(n % 10) + '0'; - n /= 10; - } - } - if (n64 == 0) { - if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) - ++s; - break; - } - while (s != o) - if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { - l = 0; - *--s = stbsp__comma; - --o; - } else { - *--s = '0'; - } - } - - tail[0] = 0; - stbsp__lead_sign(fl, lead); - - // get the length that we copied - l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); - if (l == 0) { - *--s = '0'; - l = 1; - } - cs = l + (3 << 24); - if (pr < 0) - pr = 0; - - scopy: - // get fw=leading/trailing space, pr=leading zeros - if (pr < (stbsp__int32)l) - pr = l; - n = pr + lead[0] + tail[0] + tz; - if (fw < (stbsp__int32)n) - fw = n; - fw -= n; - pr -= l; - - // handle right justify and leading zeros - if ((fl & STBSP__LEFTJUST) == 0) { - if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr - { - pr = (fw > pr) ? fw : pr; - fw = 0; - } else { - fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas - } - } - - // copy the spaces and/or zeros - if (fw + pr) { - stbsp__int32 i; - stbsp__uint32 c; - - // copy leading spaces (or when doing %8.4d stuff) - if ((fl & STBSP__LEFTJUST) == 0) - while (fw > 0) { - stbsp__cb_buf_clamp(i, fw); - fw -= i; - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = ' '; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x20202020; - bf += 4; - i -= 4; - } - while (i) { - *bf++ = ' '; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy leader - sn = lead + 1; - while (lead[0]) { - stbsp__cb_buf_clamp(i, lead[0]); - lead[0] -= (char)i; - while (i) { - *bf++ = *sn++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy leading zeros - c = cs >> 24; - cs &= 0xffffff; - cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; - while (pr > 0) { - stbsp__cb_buf_clamp(i, pr); - pr -= i; - if ((fl & STBSP__TRIPLET_COMMA) == 0) { - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = '0'; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x30303030; - bf += 4; - i -= 4; - } - } - while (i) { - if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { - cs = 0; - *bf++ = stbsp__comma; - } else - *bf++ = '0'; - --i; - } - stbsp__chk_cb_buf(1); - } - } - - // copy leader if there is still one - sn = lead + 1; - while (lead[0]) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, lead[0]); - lead[0] -= (char)i; - while (i) { - *bf++ = *sn++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy the string - n = l; - while (n) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, n); - n -= i; - STBSP__UNALIGNED(while (i >= 4) { - *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; - bf += 4; - s += 4; - i -= 4; - }) - while (i) { - *bf++ = *s++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy trailing zeros - while (tz) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, tz); - tz -= i; - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = '0'; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x30303030; - bf += 4; - i -= 4; - } - while (i) { - *bf++ = '0'; - --i; - } - stbsp__chk_cb_buf(1); - } - - // copy tail if there is one - sn = tail + 1; - while (tail[0]) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, tail[0]); - tail[0] -= (char)i; - while (i) { - *bf++ = *sn++; - --i; - } - stbsp__chk_cb_buf(1); - } - - // handle the left justify - if (fl & STBSP__LEFTJUST) - if (fw > 0) { - while (fw) { - stbsp__int32 i; - stbsp__cb_buf_clamp(i, fw); - fw -= i; - while (i) { - if ((((stbsp__uintptr)bf) & 3) == 0) - break; - *bf++ = ' '; - --i; - } - while (i >= 4) { - *(stbsp__uint32 *)bf = 0x20202020; - bf += 4; - i -= 4; - } - while (i--) - *bf++ = ' '; - stbsp__chk_cb_buf(1); - } - } - break; - - default: // unknown, just copy code - s = num + STBSP__NUMSZ - 1; - *s = f[0]; - l = 1; - fw = fl = 0; - lead[0] = 0; - tail[0] = 0; - pr = 0; - dp = 0; - cs = 0; - goto scopy; - } - ++f; - } - endfmt: - - if (!callback) - *bf = 0; - else - stbsp__flush_cb(); - - done: - return tlen + (int)(bf - buf); -} - -// cleanup -#undef STBSP__LEFTJUST -#undef STBSP__LEADINGPLUS -#undef STBSP__LEADINGSPACE -#undef STBSP__LEADING_0X -#undef STBSP__LEADINGZERO -#undef STBSP__INTMAX -#undef STBSP__TRIPLET_COMMA -#undef STBSP__NEGATIVE -#undef STBSP__METRIC_SUFFIX -#undef STBSP__NUMSZ -#undef stbsp__chk_cb_bufL -#undef stbsp__chk_cb_buf -#undef stbsp__flush_cb -#undef stbsp__cb_buf_clamp - -// ============================================================================ -// wrapper functions - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) -{ - int result; - va_list va; - va_start(va, fmt); - result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); - va_end(va); - return result; -} - -typedef struct stbsp__context { - char *buf; - int count; - int length; - char tmp[STB_SPRINTF_MIN]; -} stbsp__context; - -static char *stbsp__clamp_callback(const char *buf, void *user, int len) -{ - stbsp__context *c = (stbsp__context *)user; - c->length += len; - - if (len > c->count) - len = c->count; - - if (len) { - if (buf != c->buf) { - const char *s, *se; - char *d; - d = c->buf; - s = buf; - se = buf + len; - do { - *d++ = *s++; - } while (s < se); - } - c->buf += len; - c->count -= len; - } - - if (c->count <= 0) - return c->tmp; - return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can -} - -static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) -{ - stbsp__context * c = (stbsp__context*)user; - (void) sizeof(buf); - - c->length += len; - return c->tmp; // go direct into buffer if you can -} - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) -{ - stbsp__context c; - - if ( (count == 0) && !buf ) - { - c.length = 0; - - STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); - } - else - { - int l; - - c.buf = buf; - c.count = count; - c.length = 0; - - STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); - - // zero-terminate - l = (int)( c.buf - buf ); - if ( l >= count ) // should never be greater, only equal (or less) than count - l = count - 1; - buf[l] = 0; - } - - return c.length; -} - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) -{ - int result; - va_list va; - va_start(va, fmt); - - result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); - va_end(va); - - return result; -} - -STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) -{ - return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); -} - -// ======================================================================= -// low level float utility functions - -#ifndef STB_SPRINTF_NOFLOAT - -// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) -#define STBSP__COPYFP(dest, src) \ -{ \ -int cn; \ -for (cn = 0; cn < 8; cn++) \ -((char *)&dest)[cn] = ((char *)&src)[cn]; \ -} - -// get float info -static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) -{ - double d; - stbsp__int64 b = 0; - - // load value and round at the frac_digits - d = value; - - STBSP__COPYFP(b, d); - - *bits = b & ((((stbsp__uint64)1) << 52) - 1); - *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); - - return (stbsp__int32)((stbsp__uint64) b >> 63); -} - -static double const stbsp__bot[23] = { - 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, - 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 -}; -static double const stbsp__negbot[22] = { - 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, - 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 -}; -static double const stbsp__negboterr[22] = { - -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, - 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, - -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, - 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 -}; -static double const stbsp__top[13] = { - 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 -}; -static double const stbsp__negtop[13] = { - 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 -}; -static double const stbsp__toperr[13] = { - 8388608, - 6.8601809640529717e+028, - -7.253143638152921e+052, - -4.3377296974619174e+075, - -1.5559416129466825e+098, - -3.2841562489204913e+121, - -3.7745893248228135e+144, - -1.7356668416969134e+167, - -3.8893577551088374e+190, - -9.9566444326005119e+213, - 6.3641293062232429e+236, - -5.2069140800249813e+259, - -5.2504760255204387e+282 -}; -static double const stbsp__negtoperr[13] = { - 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, - -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, - 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, - 8.0970921678014997e-317 -}; - -#if defined(_MSC_VER) && (_MSC_VER <= 1200) -static stbsp__uint64 const stbsp__powten[20] = { - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000, - 100000000000, - 1000000000000, - 10000000000000, - 100000000000000, - 1000000000000000, - 10000000000000000, - 100000000000000000, - 1000000000000000000, - 10000000000000000000U -}; -#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) -#else -static stbsp__uint64 const stbsp__powten[20] = { - 1, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, - 10000000000ULL, - 100000000000ULL, - 1000000000000ULL, - 10000000000000ULL, - 100000000000000ULL, - 1000000000000000ULL, - 10000000000000000ULL, - 100000000000000000ULL, - 1000000000000000000ULL, - 10000000000000000000ULL -}; -#define stbsp__tento19th (1000000000000000000ULL) -#endif - -#define stbsp__ddmulthi(oh, ol, xh, yh) \ -{ \ -double ahi = 0, alo, bhi = 0, blo; \ -stbsp__int64 bt; \ -oh = xh * yh; \ -STBSP__COPYFP(bt, xh); \ -bt &= ((~(stbsp__uint64)0) << 27); \ -STBSP__COPYFP(ahi, bt); \ -alo = xh - ahi; \ -STBSP__COPYFP(bt, yh); \ -bt &= ((~(stbsp__uint64)0) << 27); \ -STBSP__COPYFP(bhi, bt); \ -blo = yh - bhi; \ -ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ -} - -#define stbsp__ddtoS64(ob, xh, xl) \ -{ \ -double ahi = 0, alo, vh, t; \ -ob = (stbsp__int64)xh; \ -vh = (double)ob; \ -ahi = (xh - vh); \ -t = (ahi - xh); \ -alo = (xh - (ahi - t)) - (vh + t); \ -ob += (stbsp__int64)(ahi + alo + xl); \ -} - -#define stbsp__ddrenorm(oh, ol) \ -{ \ -double s; \ -s = oh + ol; \ -ol = ol - (s - oh); \ -oh = s; \ -} - -#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); - -#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); - -static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 -{ - double ph, pl; - if ((power >= 0) && (power <= 22)) { - stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); - } else { - stbsp__int32 e, et, eb; - double p2h, p2l; - - e = power; - if (power < 0) - e = -e; - et = (e * 0x2c9) >> 14; /* %23 */ - if (et > 13) - et = 13; - eb = e - (et * 23); - - ph = d; - pl = 0.0; - if (power < 0) { - if (eb) { - --eb; - stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); - stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); - } - if (et) { - stbsp__ddrenorm(ph, pl); - --et; - stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); - stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); - ph = p2h; - pl = p2l; - } - } else { - if (eb) { - e = eb; - if (eb > 22) - eb = 22; - e -= eb; - stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); - if (e) { - stbsp__ddrenorm(ph, pl); - stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); - stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); - ph = p2h; - pl = p2l; - } - } - if (et) { - stbsp__ddrenorm(ph, pl); - --et; - stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); - stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); - ph = p2h; - pl = p2l; - } - } - } - stbsp__ddrenorm(ph, pl); - *ohi = ph; - *olo = pl; -} - -// given a float value, returns the significant bits in bits, and the position of the -// decimal point in decimal_pos. +/-INF and NAN are specified by special values -// returned in the decimal_pos parameter. -// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 -static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) -{ - double d; - stbsp__int64 bits = 0; - stbsp__int32 expo, e, ng, tens; - - d = value; - STBSP__COPYFP(bits, d); - expo = (stbsp__int32)((bits >> 52) & 2047); - ng = (stbsp__int32)((stbsp__uint64) bits >> 63); - if (ng) - d = -d; - - if (expo == 2047) // is nan or inf? - { - *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; - *decimal_pos = STBSP__SPECIAL; - *len = 3; - return ng; - } - - if (expo == 0) // is zero or denormal - { - if (((stbsp__uint64) bits << 1) == 0) // do zero - { - *decimal_pos = 1; - *start = out; - out[0] = '0'; - *len = 1; - return ng; - } - // find the right expo for denormals - { - stbsp__int64 v = ((stbsp__uint64)1) << 51; - while ((bits & v) == 0) { - --expo; - v >>= 1; - } - } - } - - // find the decimal exponent as well as the decimal bits of the value - { - double ph, pl; - - // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 - tens = expo - 1023; - tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); - - // move the significant bits into position and stick them into an int - stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); - - // get full as much precision from double-double as possible - stbsp__ddtoS64(bits, ph, pl); - - // check if we undershot - if (((stbsp__uint64)bits) >= stbsp__tento19th) - ++tens; - } - - // now do the rounding in integer land - frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); - if ((frac_digits < 24)) { - stbsp__uint32 dg = 1; - if ((stbsp__uint64)bits >= stbsp__powten[9]) - dg = 10; - while ((stbsp__uint64)bits >= stbsp__powten[dg]) { - ++dg; - if (dg == 20) - goto noround; - } - if (frac_digits < dg) { - stbsp__uint64 r; - // add 0.5 at the right position and round - e = dg - frac_digits; - if ((stbsp__uint32)e >= 24) - goto noround; - r = stbsp__powten[e]; - bits = bits + (r / 2); - if ((stbsp__uint64)bits >= stbsp__powten[dg]) - ++tens; - bits /= r; - } - noround:; - } - - // kill long trailing runs of zeros - if (bits) { - stbsp__uint32 n; - for (;;) { - if (bits <= 0xffffffff) - break; - if (bits % 1000) - goto donez; - bits /= 1000; - } - n = (stbsp__uint32)bits; - while ((n % 1000) == 0) - n /= 1000; - bits = n; - donez:; - } - - // convert to string - out += 64; - e = 0; - for (;;) { - stbsp__uint32 n; - char *o = out - 8; - // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) - if (bits >= 100000000) { - n = (stbsp__uint32)(bits % 100000000); - bits /= 100000000; - } else { - n = (stbsp__uint32)bits; - bits = 0; - } - while (n) { - out -= 2; - *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; - n /= 100; - e += 2; - } - if (bits == 0) { - if ((e) && (out[0] == '0')) { - ++out; - --e; - } - break; - } - while (out != o) { - *--out = '0'; - ++e; - } - } - - *decimal_pos = tens; - *start = out; - *len = e; - return ng; -} - -#undef stbsp__ddmulthi -#undef stbsp__ddrenorm -#undef stbsp__ddmultlo -#undef stbsp__ddmultlos -#undef STBSP__SPECIAL -#undef STBSP__COPYFP - -#endif // STB_SPRINTF_NOFLOAT - -// clean up -#undef stbsp__uint16 -#undef stbsp__uint32 -#undef stbsp__int32 -#undef stbsp__uint64 -#undef stbsp__int64 -#undef STBSP__UNALIGNED - -#endif // STB_SPRINTF_IMPLEMENTATION - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ +// ---------------------------------------------------------------------------- +// +// NOTE(rjf): This has been modified to support extra format specifiers +// for the debugger project - this is *not* an unmodified copy of the original +// stb_sprintf v1.10 code. +// +// ---------------------------------------------------------------------------- + +// stb_sprintf - v1.10 - public domain snprintf() implementation +// originally by Jeff Roberts / RAD Game Tools, 2015/10/20 +// http://github.com/nothings/stb +// +// allowed types: sc uidBboXx p AaGgEef n +// lengths : hh h ll j z t I64 I32 I +// +// Contributors: +// Fabian "ryg" Giesen (reformatting) +// github:aganm (attribute format) +// +// Contributors (bugfixes): +// github:d26435 +// github:trex78 +// github:account-login +// Jari Komppa (SI suffixes) +// Rohit Nirmal +// Marcin Wojdyr +// Leonard Ritter +// Stefano Zanotti +// Adam Allison +// Arvid Gerstmann +// Markus Kolb +// +// LICENSE: +// +// See end of file for license information. + +#ifndef STB_SPRINTF_H_INCLUDE +#define STB_SPRINTF_H_INCLUDE + +/* +Single file sprintf replacement. + +Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20. +Hereby placed in public domain. + +This is a full sprintf replacement that supports everything that +the C runtime sprintfs support, including float/double, 64-bit integers, +hex floats, field parameters (%*.*d stuff), length reads backs, etc. + +Why would you need this if sprintf already exists? Well, first off, +it's *much* faster (see below). It's also much smaller than the CRT +versions code-space-wise. We've also added some simple improvements +that are super handy (commas in thousands, callbacks at buffer full, +for example). Finally, the format strings for MSVC and GCC differ +for 64-bit integers (among other small things), so this lets you use +the same format strings in cross platform code. + +It uses the standard single file trick of being both the header file +and the source itself. If you just include it normally, you just get +the header file function definitions. To get the code, you include +it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first. + +It only uses va_args macros from the C runtime to do it's work. It +does cast doubles to S64s and shifts and divides U64s, which does +drag in CRT code on most platforms. + +It compiles to roughly 8K with float support, and 4K without. +As a comparison, when using MSVC static libs, calling sprintf drags +in 16K. + +API: +==== +int stbsp_sprintf( char * buf, char const * fmt, ... ) +int stbsp_snprintf( char * buf, int count, char const * fmt, ... ) + Convert an arg list into a buffer. stbsp_snprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintf( char * buf, char const * fmt, va_list va ) +int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va ) + Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns + a zero-terminated string (unlike regular snprintf). + +int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va ) + typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len ); + Convert into a buffer, calling back every STB_SPRINTF_MIN chars. + Your callback can then copy the chars out, print them or whatever. + This function is actually the workhorse for everything else. + The buffer you pass in must hold at least STB_SPRINTF_MIN characters. + // you return the next buffer to use or 0 to stop converting + +void stbsp_set_separators( char comma, char period ) + Set the comma and period characters to use. + +FLOATS/DOUBLES: +=============== +This code uses a internal float->ascii conversion method that uses +doubles with error correction (double-doubles, for ~105 bits of +precision). This conversion is round-trip perfect - that is, an atof +of the values output here will give you the bit-exact double back. + +One difference is that our insignificant digits will be different than +with MSVC or GCC (but they don't match each other either). We also +don't attempt to find the minimum length matching float (pre-MSVC15 +doesn't either). + +If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT +and you'll save 4K of code space. + +64-BIT INTS: +============ +This library also supports 64-bit integers and you can use MSVC style or +GCC style indicators (%I64d or %lld). It supports the C99 specifiers +for size_t and ptr_diff_t (%jd %zd) as well. + +EXTRAS: +======= +Like some GCCs, for integers and floats, you can use a ' (single quote) +specifier and commas will be inserted on the thousands: "%'d" on 12345 +would print 12,345. + +For integers and floats, you can use a "$" specifier and the number +will be converted to float and then divided to get kilo, mega, giga or +tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is +"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn +2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three +$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the +suffix, add "_" specifier: "%_$d" -> "2.53M". + +In addition to octal and hexadecimal conversions, you can print +integers in binary: "%b" for 256 would print 100. + +PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC): +=================================================================== +"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC) +"%24d" across all 32-bit ints (4.5x/4.2x faster) +"%x" across all 32-bit ints (4.5x/3.8x faster) +"%08x" across all 32-bit ints (4.3x/3.8x faster) +"%f" across e-10 to e+10 floats (7.3x/6.0x faster) +"%e" across e-10 to e+10 floats (8.1x/6.0x faster) +"%g" across e-10 to e+10 floats (10.0x/7.1x faster) +"%f" for values near e-300 (7.9x/6.5x faster) +"%f" for values near e+300 (10.0x/9.1x faster) +"%e" for values near e-300 (10.1x/7.0x faster) +"%e" for values near e+300 (9.2x/6.0x faster) +"%.320f" for values near e-300 (12.6x/11.2x faster) +"%a" for random values (8.6x/4.3x faster) +"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster) +"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster) +"%s%s%s" for 64 char strings (7.1x/7.3x faster) +"...512 char string..." ( 35.0x/32.5x faster!) +*/ + +#if defined(__clang__) +# if defined(__has_feature) && defined(__has_attribute) +# if __has_feature(address_sanitizer) +# if __has_attribute(__no_sanitize__) +# define STBSP__ASAN __attribute__((__no_sanitize__("address"))) +# elif __has_attribute(__no_sanitize_address__) +# define STBSP__ASAN __attribute__((__no_sanitize_address__)) +# elif __has_attribute(__no_address_safety_analysis__) +# define STBSP__ASAN __attribute__((__no_address_safety_analysis__)) +# endif +# endif +# endif +#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) +# if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__ +# define STBSP__ASAN __attribute__((__no_sanitize_address__)) +# endif +#elif defined(_MSC_VER) +# if defined(__SANITIZE_ADDRESS__) +# define STBSP__ASAN __declspec(no_sanitize_address) +# endif +#endif + +#ifndef STBSP__ASAN +#define STBSP__ASAN +#endif + +#ifdef STB_SPRINTF_STATIC +#define STBSP__PUBLICDEC static STBSP__ASAN +#define STBSP__PUBLICDEF static STBSP__ASAN +#else +#ifdef __cplusplus +#define STBSP__PUBLICDEC extern "C" STBSP__ASAN +#define STBSP__PUBLICDEF extern "C" STBSP__ASAN +#else +#define STBSP__PUBLICDEC extern STBSP__ASAN +#define STBSP__PUBLICDEF STBSP__ASAN +#endif +#endif + +#if defined(__has_attribute) +#if __has_attribute(format) +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) __attribute__((format(printf,fmt,va))) +#endif +#endif + +#ifndef STBSP__ATTRIBUTE_FORMAT +#define STBSP__ATTRIBUTE_FORMAT(fmt,va) +#endif + +#ifdef _MSC_VER +#define STBSP__NOTUSED(v) (void)(v) +#else +#define STBSP__NOTUSED(v) (void)sizeof(v) +#endif + +#include // for va_arg(), va_list() +#include // size_t, ptrdiff_t + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len); + +#ifndef STB_SPRINTF_DECORATE +#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names +#endif + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2,3); +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3,4); + +STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va); +STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period); + +#endif // STB_SPRINTF_H_INCLUDE + +#ifdef STB_SPRINTF_IMPLEMENTATION + +#define stbsp__uint32 unsigned int +#define stbsp__int32 signed int + +#ifdef _MSC_VER +#define stbsp__uint64 unsigned __int64 +#define stbsp__int64 signed __int64 +#else +#define stbsp__uint64 unsigned long long +#define stbsp__int64 signed long long +#endif +#define stbsp__uint16 unsigned short + +#ifndef stbsp__uintptr +#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64) || defined(__s390x__) +#define stbsp__uintptr stbsp__uint64 +#else +#define stbsp__uintptr stbsp__uint32 +#endif +#endif + +#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC) +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define STB_SPRINTF_MSVC_MODE +#endif +#endif + +#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses +#define STBSP__UNALIGNED(code) +#else +#define STBSP__UNALIGNED(code) code +#endif + +#ifndef STB_SPRINTF_NOFLOAT +// internal float utility functions +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits); +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value); +#define STBSP__SPECIAL 0x7000 +#endif + +static char stbsp__period = '.'; +static char stbsp__comma = ','; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} stbsp__digitpair = +{ + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899" +}; + +STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod) +{ + stbsp__period = pperiod; + stbsp__comma = pcomma; +} + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void stbsp__lead_sign(stbsp__uint32 fl, char *sign) +{ + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static STBSP__ASAN stbsp__uint32 stbsp__strlen_limited(char const *s, stbsp__uint32 limit) +{ + char const * sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((stbsp__uintptr)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (stbsp__uint32)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + stbsp__uint32 v = *(stbsp__uint32 *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (stbsp__uint32)(sn - s); +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) +{ + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + stbsp__int32 fw, pr, tz; + stbsp__uint32 fl; + + // macros for the callback buffer stuff +#define stbsp__chk_cb_bufL(bytes) \ +{ \ +int len = (int)(bf - buf); \ +if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ +tlen += len; \ +if (0 == (bf = buf = callback(buf, user, len))) \ +goto done; \ +} \ +} +#define stbsp__chk_cb_buf(bytes) \ +{ \ +if (callback) { \ +stbsp__chk_cb_bufL(bytes); \ +} \ +} +#define stbsp__flush_cb() \ +{ \ +stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ +} // flush if there is even one byte in the buffer +#define stbsp__cb_buf_clamp(cl, v) \ +cl = v; \ +if (callback) { \ +int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ +if (cl > lg) \ +cl = lg; \ +} + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((stbsp__uintptr)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + stbsp__uint32 v, c; + v = *(stbsp__uint32 *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; +#ifdef STB_SPRINTF_NOUNALIGNED + if(((stbsp__uintptr)bf) & 3) { + bf[0] = f[0]; + bf[1] = f[1]; + bf[2] = f[2]; + bf[3] = f[3]; + } else +#endif + { + *(stbsp__uint32 *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, stbsp__uint32); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, stbsp__uint32); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { +#define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + stbsp__uint32 l, n, cs; + stbsp__uint64 n64; +#ifndef STB_SPRINTF_NOFLOAT + double fv; +#endif + stbsp__int32 dp; + char const *sn; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + //- + //- + //- + // NOTE(rjf): DEBUGGER PROJECT ADDITION vvv + // + // The following additions are for using the debugger project's base layer + // types in format strings. + // + case 'S': + { + String8 string = va_arg(va, String8); + s = (char *)string.str; + l = (U32)string.size; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + }goto scopy; + // + // NOTE(rjf): DEBUGGER PROJECT ADDITION ^^^ + //- + //- + //- + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + +#ifdef STB_SPRINTF_NOFLOAT + case 'A': // float + case 'a': // hex float + case 'G': // float + case 'g': // float + case 'E': // float + case 'e': // float + case 'f': // float + va_arg(va, double); // eat it + s = (char *)"No float"; + l = 8; + lead[0] = 0; + tail[0] = 0; + pr = 0; + cs = 0; + STBSP__NOTUSED(dp); + goto scopy; +#else + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((stbsp__uint64)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4)); + // add leading chars + +#ifdef STB_SPRINTF_MSVC_MODE + *s++ = '0'; + *s++ = 'x'; +#else + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; +#endif + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (stbsp__int32)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (stbsp__uint32)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (stbsp__int32)n)) { + if (pr > (stbsp__int32)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (stbsp__int32)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = stbsp__period; + + // handle after decimal + if ((l - 1) > (stbsp__uint32)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; +#ifdef STB_SPRINTF_MSVC_MODE + n = 5; +#else + n = (dp >= 100) ? 5 : 4; +#endif + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + stbsp__int32 i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = stbsp__period; + n = -dp; + if ((stbsp__int32)n > pr) + n = pr; + i = n; + while (i) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((stbsp__int32)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0; + if ((stbsp__uint32)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (stbsp__uint32)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((stbsp__uintptr)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(stbsp__uint32 *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (stbsp__uint32)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = stbsp__period; + if ((l - dp) > (stbsp__uint32)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (stbsp__uint32)(s - (num + 64)); + s = num + 64; + goto scopy; +#endif + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, stbsp__uint64); + else + n64 = va_arg(va, stbsp__uint32); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + stbsp__int64 i64 = va_arg(va, stbsp__int64); + n64 = (stbsp__uint64)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (stbsp__uint64)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + stbsp__int32 i = va_arg(va, stbsp__int32); + n64 = (stbsp__uint32)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (stbsp__uint32)-i; + fl |= STBSP__NEGATIVE; + } + } + +#ifndef STB_SPRINTF_NOFLOAT + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(stbsp__int64)n64; + goto doafloat; + } +#endif + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (stbsp__uint32)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (stbsp__uint32)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (stbsp__uint32)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (stbsp__int32)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (stbsp__int32)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + stbsp__int32 i; + stbsp__uint32 c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, n); + n -= i; + STBSP__UNALIGNED(while (i >= 4) { + *(stbsp__uint32 volatile *)bf = *(stbsp__uint32 volatile *)s; + bf += 4; + s += 4; + i -= 4; + }) + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + stbsp__int32 i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((stbsp__uintptr)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(stbsp__uint32 *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } + endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + + done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) +{ + stbsp__context *c = (stbsp__context *)user; + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can +} + +static char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) +{ + stbsp__context * c = (stbsp__context*)user; + (void) sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va ) +{ + stbsp__context c; + + if ( (count == 0) && !buf ) + { + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + } + else + { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + + // zero-terminate + l = (int)( c.buf - buf ); + if ( l >= count ) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + + result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va); + va_end(va); + + return result; +} + +STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va) +{ + return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +#ifndef STB_SPRINTF_NOFLOAT + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) \ +{ \ +int cn; \ +for (cn = 0; cn < 8; cn++) \ +((char *)&dest)[cn] = ((char *)&src)[cn]; \ +} + +// get float info +static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value) +{ + double d; + stbsp__int64 b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((stbsp__uint64)1) << 52) - 1); + *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023); + + return (stbsp__int32)((stbsp__uint64) b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 +}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 +}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 +}; +static double const stbsp__top[13] = { + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 +}; +static double const stbsp__negtop[13] = { + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 +}; +static double const stbsp__toperr[13] = { + 8388608, + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282 +}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317 +}; + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000, + 100000000000, + 1000000000000, + 10000000000000, + 100000000000000, + 1000000000000000, + 10000000000000000, + 100000000000000000, + 1000000000000000000, + 10000000000000000000U +}; +#define stbsp__tento19th ((stbsp__uint64)1000000000000000000) +#else +static stbsp__uint64 const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL +}; +#define stbsp__tento19th (1000000000000000000ULL) +#endif + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ +{ \ +double ahi = 0, alo, bhi = 0, blo; \ +stbsp__int64 bt; \ +oh = xh * yh; \ +STBSP__COPYFP(bt, xh); \ +bt &= ((~(stbsp__uint64)0) << 27); \ +STBSP__COPYFP(ahi, bt); \ +alo = xh - ahi; \ +STBSP__COPYFP(bt, yh); \ +bt &= ((~(stbsp__uint64)0) << 27); \ +STBSP__COPYFP(bhi, bt); \ +blo = yh - bhi; \ +ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ +} + +#define stbsp__ddtoS64(ob, xh, xl) \ +{ \ +double ahi = 0, alo, vh, t; \ +ob = (stbsp__int64)xh; \ +vh = (double)ob; \ +ahi = (xh - vh); \ +t = (ahi - xh); \ +alo = (xh - (ahi - t)) - (vh + t); \ +ob += (stbsp__int64)(ahi + alo + xl); \ +} + +#define stbsp__ddrenorm(oh, ol) \ +{ \ +double s; \ +s = oh + ol; \ +ol = ol - (s - oh); \ +oh = s; \ +} + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + stbsp__int32 e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special values +// returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 +static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits) +{ + double d; + stbsp__int64 bits = 0; + stbsp__int32 expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (stbsp__int32)((bits >> 52) & 2047); + ng = (stbsp__int32)((stbsp__uint64) bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((stbsp__uint64) bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + stbsp__int64 v = ((stbsp__uint64)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((stbsp__uint64)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + stbsp__uint32 dg = 1; + if ((stbsp__uint64)bits >= stbsp__powten[9]) + dg = 10; + while ((stbsp__uint64)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + stbsp__uint64 r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((stbsp__uint32)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((stbsp__uint64)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + stbsp__uint32 n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (stbsp__uint32)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + stbsp__uint32 n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (stbsp__uint32)(bits % 100000000); + bits /= 100000000; + } else { + n = (stbsp__uint32)bits; + bits = 0; + } + while (n) { + out -= 2; + *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +#endif // STB_SPRINTF_NOFLOAT + +// clean up +#undef stbsp__uint16 +#undef stbsp__uint32 +#undef stbsp__int32 +#undef stbsp__uint64 +#undef stbsp__int64 +#undef STBSP__UNALIGNED + +#endif // STB_SPRINTF_IMPLEMENTATION + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/type_graph/type_graph.c b/src/type_graph/type_graph.c index a8df878c..06fcfa84 100644 --- a/src/type_graph/type_graph.c +++ b/src/type_graph/type_graph.c @@ -1,1357 +1,1357 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/type_graph.meta.c" - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal U64 -tg_hash_from_string(U64 seed, String8 string) -{ - U64 result = seed; - for(U64 idx = 0; idx < string.size; idx += 1) - { - result = ((result<<5)+result) + string.str[idx]; - } - return result; -} - -internal int -tg_qsort_compare_members_offset(TG_Member *a, TG_Member *b) -{ - int result = 0; - if(a->off < b->off) - { - result = -1; - } - else if(a->off > b->off) - { - result = +1; - } - return result; -} - -internal void -tg_key_list_push(Arena *arena, TG_KeyList *list, TG_Key key) -{ - TG_KeyNode *n = push_array(arena, TG_KeyNode, 1); - n->v = key; - SLLQueuePush(list->first, list->last, n); - list->count += 1; -} - -internal TG_KeyList -tg_key_list_copy(Arena *arena, TG_KeyList *src) -{ - TG_KeyList dst = {0}; - for(TG_KeyNode *n = src->first; n != 0; n = n->next) - { - tg_key_list_push(arena, &dst, n->v); - } - return dst; -} - -//////////////////////////////// -//~ rjf: RADDBG <-> TG Enum Conversions - -internal TG_Kind -tg_kind_from_rdi_type_kind(RDI_TypeKind kind) -{ - TG_Kind result = TG_Kind_Null; - switch(kind) - { - default:{}break; - case RDI_TypeKind_Void: {result = TG_Kind_Void;}break; - case RDI_TypeKind_Handle: {result = TG_Kind_Handle;}break; - case RDI_TypeKind_Char8: {result = TG_Kind_Char8;}break; - case RDI_TypeKind_Char16: {result = TG_Kind_Char16;}break; - case RDI_TypeKind_Char32: {result = TG_Kind_Char32;}break; - case RDI_TypeKind_UChar8: {result = TG_Kind_UChar8;}break; - case RDI_TypeKind_UChar16: {result = TG_Kind_UChar16;}break; - case RDI_TypeKind_UChar32: {result = TG_Kind_UChar32;}break; - case RDI_TypeKind_U8: {result = TG_Kind_U8;}break; - case RDI_TypeKind_U16: {result = TG_Kind_U16;}break; - case RDI_TypeKind_U32: {result = TG_Kind_U32;}break; - case RDI_TypeKind_U64: {result = TG_Kind_U64;}break; - case RDI_TypeKind_U128: {result = TG_Kind_U128;}break; - case RDI_TypeKind_U256: {result = TG_Kind_U256;}break; - case RDI_TypeKind_U512: {result = TG_Kind_U512;}break; - case RDI_TypeKind_S8: {result = TG_Kind_S8;}break; - case RDI_TypeKind_S16: {result = TG_Kind_S16;}break; - case RDI_TypeKind_S32: {result = TG_Kind_S32;}break; - case RDI_TypeKind_S64: {result = TG_Kind_S64;}break; - case RDI_TypeKind_S128: {result = TG_Kind_S128;}break; - case RDI_TypeKind_S256: {result = TG_Kind_S256;}break; - case RDI_TypeKind_S512: {result = TG_Kind_S512;}break; - case RDI_TypeKind_Bool: {result = TG_Kind_Bool;}break; - case RDI_TypeKind_F16: {result = TG_Kind_F16;}break; - case RDI_TypeKind_F32: {result = TG_Kind_F32;}break; - case RDI_TypeKind_F32PP: {result = TG_Kind_F32PP;}break; - case RDI_TypeKind_F48: {result = TG_Kind_F48;}break; - case RDI_TypeKind_F64: {result = TG_Kind_F64;}break; - case RDI_TypeKind_F80: {result = TG_Kind_F80;}break; - case RDI_TypeKind_F128: {result = TG_Kind_F128;}break; - case RDI_TypeKind_ComplexF32: {result = TG_Kind_ComplexF32;}break; - case RDI_TypeKind_ComplexF64: {result = TG_Kind_ComplexF64;}break; - case RDI_TypeKind_ComplexF80: {result = TG_Kind_ComplexF80;}break; - case RDI_TypeKind_ComplexF128: {result = TG_Kind_ComplexF128;}break; - case RDI_TypeKind_Modifier: {result = TG_Kind_Modifier;}break; - case RDI_TypeKind_Ptr: {result = TG_Kind_Ptr;}break; - case RDI_TypeKind_LRef: {result = TG_Kind_LRef;}break; - case RDI_TypeKind_RRef: {result = TG_Kind_RRef;}break; - case RDI_TypeKind_Array: {result = TG_Kind_Array;}break; - case RDI_TypeKind_Function: {result = TG_Kind_Function;}break; - case RDI_TypeKind_Method: {result = TG_Kind_Method;}break; - case RDI_TypeKind_MemberPtr: {result = TG_Kind_MemberPtr;}break; - case RDI_TypeKind_Struct: {result = TG_Kind_Struct;}break; - case RDI_TypeKind_Class: {result = TG_Kind_Class;}break; - case RDI_TypeKind_Union: {result = TG_Kind_Union;}break; - case RDI_TypeKind_Enum: {result = TG_Kind_Enum;}break; - case RDI_TypeKind_Alias: {result = TG_Kind_Alias;}break; - case RDI_TypeKind_IncompleteStruct: {result = TG_Kind_IncompleteStruct;}break; - case RDI_TypeKind_IncompleteUnion: {result = TG_Kind_IncompleteUnion;}break; - case RDI_TypeKind_IncompleteClass: {result = TG_Kind_IncompleteClass;}break; - case RDI_TypeKind_IncompleteEnum: {result = TG_Kind_IncompleteEnum;}break; - case RDI_TypeKind_Bitfield: {result = TG_Kind_Bitfield;}break; - case RDI_TypeKind_Variadic: {result = TG_Kind_Variadic;}break; - } - return result; -} - -internal TG_MemberKind -tg_member_kind_from_rdi_member_kind(RDI_MemberKind kind) -{ - TG_MemberKind result = TG_MemberKind_Null; - switch(kind) - { - default:{}break; - case RDI_MemberKind_DataField: {result = TG_MemberKind_DataField;}break; - case RDI_MemberKind_StaticData: {result = TG_MemberKind_StaticData;}break; - case RDI_MemberKind_Method: {result = TG_MemberKind_Method;}break; - case RDI_MemberKind_StaticMethod: {result = TG_MemberKind_StaticMethod;}break; - case RDI_MemberKind_VirtualMethod: {result = TG_MemberKind_VirtualMethod;}break; - case RDI_MemberKind_VTablePtr: {result = TG_MemberKind_VTablePtr;}break; - case RDI_MemberKind_Base: {result = TG_MemberKind_Base;}break; - case RDI_MemberKind_VirtualBase: {result = TG_MemberKind_VirtualBase;}break; - case RDI_MemberKind_NestedType: {result = TG_MemberKind_NestedType;}break; - } - return result; -} - -//////////////////////////////// -//~ rjf: Key Type Functions - -internal TG_Key -tg_key_zero(void) -{ - TG_Key key = zero_struct; - return key; -} - -internal TG_Key -tg_key_basic(TG_Kind kind) -{ - TG_Key key = {TG_KeyKind_Basic}; - key.u32[0] = (U32)kind; - return key; -} - -internal TG_Key -tg_key_ext(TG_Kind kind, U64 id) -{ - TG_Key key = {TG_KeyKind_Ext}; - key.u32[0] = (U32)kind; - if(TG_Kind_FirstBasic <= kind && kind <= TG_Kind_LastBasic) - { - key.kind = TG_KeyKind_Basic; - } - else - { - key.u64[0] = id; - } - return key; -} - -internal TG_Key -tg_key_reg(Architecture arch, REGS_RegCode code) -{ - TG_Key key = {TG_KeyKind_Reg}; - key.u32[0] = (U32)arch; - key.u64[0] = (U64)code; - return key; -} - -internal TG_Key -tg_key_reg_alias(Architecture arch, REGS_AliasCode code) -{ - TG_Key key = {TG_KeyKind_RegAlias}; - key.u32[0] = (U32)arch; - key.u64[0] = (U64)code; - return key; -} - -internal B32 -tg_key_match(TG_Key a, TG_Key b) -{ - B32 result = MemoryMatchStruct(&a, &b); - return result; -} - -//////////////////////////////// -//~ rjf: Graph Construction API - -internal TG_Graph * -tg_graph_begin(U64 address_size, U64 slot_count) -{ - if(tg_build_arena == 0) - { - tg_build_arena = arena_alloc(); - } - else - { - arena_clear(tg_build_arena); - } - TG_Graph *graph = push_array(tg_build_arena, TG_Graph, 1); - graph->address_size = address_size; - graph->content_hash_slots_count = slot_count; - graph->content_hash_slots = push_array(tg_build_arena, TG_Slot, graph->content_hash_slots_count); - graph->key_hash_slots_count = slot_count; - graph->key_hash_slots = push_array(tg_build_arena, TG_Slot, graph->key_hash_slots_count); - return graph; -} - -internal TG_Key -tg_cons_type_make(TG_Graph *graph, TG_Kind kind, TG_Key direct_type_key, U64 u64) -{ - U32 buffer[] = - { - (U32)kind, - (U32)direct_type_key.kind, - (U32)direct_type_key.u32[0], - (U32)((direct_type_key.u64[0] & 0x00000000ffffffffull)>> 0), - (U32)((direct_type_key.u64[0] & 0xffffffff00000000ull)>>32), - (U32)((u64 & 0x00000000ffffffffull)>> 0), - (U32)((u64 & 0xffffffff00000000ull)>> 32), - }; - U64 content_hash = tg_hash_from_string(5381, str8((U8 *)buffer, sizeof(buffer))); - U64 content_slot_idx = content_hash%graph->content_hash_slots_count; - TG_Slot *content_slot = &graph->content_hash_slots[content_slot_idx]; - TG_Node *node = 0; - for(TG_Node *n = content_slot->first; n != 0; n = n->content_hash_next) - { - if(n->cons_type.kind == kind && tg_key_match(n->cons_type.direct_type_key, direct_type_key)) - { - node = n; - break; - } - } - TG_Key result = zero_struct; - if(node == 0) - { - TG_Key key = {TG_KeyKind_Cons}; - key.u32[0] = (U32)kind; - key.u64[0] = graph->cons_id_gen; - U64 key_hash = tg_hash_from_string(5381, str8_struct(&key)); - U64 key_slot_idx = key_hash%graph->key_hash_slots_count; - TG_Slot *key_slot = &graph->key_hash_slots[key_slot_idx]; - graph->cons_id_gen += 1; - TG_Node *node = push_array(tg_build_arena, TG_Node, 1); - SLLQueuePush_N(content_slot->first, content_slot->last, node, content_hash_next); - SLLQueuePush_N(key_slot->first, key_slot->last, node, key_hash_next); - node->key = key; - node->cons_type.kind = kind; - node->cons_type.direct_type_key = direct_type_key; - node->cons_type.u64 = u64; - result = key; - } - else - { - result = node->key; - } - return result; -} - -//////////////////////////////// -//~ rjf: Graph Introspection API - -internal TG_Type * -tg_type_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - TG_Type *type = &tg_type_nil; - U64 reg_byte_count = 0; - { - switch(key.kind) - { - default:{}break; - - //- rjf: basic type keys - case TG_KeyKind_Basic: - { - TG_Kind kind = (TG_Kind)key.u32[0]; - if(TG_Kind_FirstBasic <= kind && kind <= TG_Kind_LastBasic) - { - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->name = tg_kind_basic_string_table[kind]; - type->byte_size = tg_kind_basic_byte_size_table[kind]; - } - }break; - - //- rjf: constructed type keys - case TG_KeyKind_Cons: - { - U64 key_hash = tg_hash_from_string(5381, str8_struct(&key)); - U64 key_slot_idx = key_hash%graph->key_hash_slots_count; - TG_Slot *key_slot = &graph->key_hash_slots[key_slot_idx]; - for(TG_Node *node = key_slot->first; node != 0; node = node->key_hash_next) - { - if(tg_key_match(node->key, key)) - { - TG_ConsType *cons_type = &node->cons_type; - type = push_array(arena, TG_Type, 1); - type->kind = cons_type->kind; - type->direct_type_key = cons_type->direct_type_key; - type->count = cons_type->u64; - switch(type->kind) - { - default: - { - type->byte_size = graph->address_size; - }break; - case TG_Kind_Array: - { - U64 ptee_size = tg_byte_size_from_graph_rdi_key(graph, rdi, cons_type->direct_type_key); - type->byte_size = ptee_size * type->count; - }break; - } - } - } - }break; - - //- rjf: external (raddbg) type keys - case TG_KeyKind_Ext: - { - U64 type_node_idx = key.u64[0]; - RDI_TypeNode *rdi_type = rdi_element_from_name_idx(rdi, TypeNodes, type_node_idx); - if(rdi_type->kind != RDI_TypeKind_NULL) - { - TG_Kind kind = tg_kind_from_rdi_type_kind(rdi_type->kind); - - //- rjf: record types => unpack name * members & produce - if(RDI_TypeKind_FirstRecord <= rdi_type->kind && rdi_type->kind <= RDI_TypeKind_LastRecord) - { - // rjf: unpack name - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, rdi_type->user_defined.name_string_idx, &name.size); - - // rjf: unpack UDT info - RDI_UDT *udt = rdi_element_from_name_idx(rdi, UDTs, rdi_type->user_defined.udt_idx); - - // rjf: unpack members - TG_Member *members = 0; - U32 members_count = 0; - { - members_count = udt->member_count; - members = push_array(arena, TG_Member, members_count); - if(members_count != 0) - { - for(U32 member_idx = udt->member_first; - member_idx < udt->member_first+udt->member_count; - member_idx += 1) - { - RDI_Member *src = rdi_element_from_name_idx(rdi, Members, member_idx); - TG_Kind member_type_kind = TG_Kind_Null; - RDI_TypeNode *member_type = rdi_element_from_name_idx(rdi, TypeNodes, src->type_idx); - member_type_kind = tg_kind_from_rdi_type_kind(member_type->kind); - TG_Member *dst = &members[member_idx-udt->member_first]; - dst->kind = tg_member_kind_from_rdi_member_kind(src->kind); - dst->type_key = tg_key_ext(member_type_kind, (U64)src->type_idx); - dst->name.str = rdi_string_from_idx(rdi, src->name_string_idx, &dst->name.size); - dst->off = (U64)src->off; - } - } - } - - // rjf: produce - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->name = push_str8_copy(arena, name); - type->byte_size = (U64)rdi_type->byte_size; - type->count = members_count; - type->members = members; - } - - //- rjf: enum types => unpack name * values & produce - else if(rdi_type->kind == RDI_TypeKind_Enum) - { - // rjf: unpack name - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, rdi_type->user_defined.name_string_idx, &name.size); - - // rjf: unpack direct type - TG_Key direct_type_key = zero_struct; - if(rdi_type->user_defined.direct_type_idx < type_node_idx) - { - RDI_TypeNode *direct_type_node = rdi_element_from_name_idx(rdi, TypeNodes, rdi_type->user_defined.direct_type_idx); - TG_Kind direct_type_kind = tg_kind_from_rdi_type_kind(direct_type_node->kind); - direct_type_key = tg_key_ext(direct_type_kind, (U64)rdi_type->user_defined.direct_type_idx); - } - - // rjf: unpack members - TG_EnumVal *enum_vals = 0; - U32 enum_vals_count = 0; - { - U32 udt_idx = rdi_type->user_defined.udt_idx; - RDI_UDT *udt = rdi_element_from_name_idx(rdi, UDTs, udt_idx); - enum_vals_count = udt->member_count; - enum_vals = push_array(arena, TG_EnumVal, enum_vals_count); - for(U32 member_idx = udt->member_first; - member_idx < udt->member_first+udt->member_count; - member_idx += 1) - { - RDI_EnumMember *src = rdi_element_from_name_idx(rdi, EnumMembers, member_idx); - TG_EnumVal *dst = &enum_vals[member_idx-udt->member_first]; - dst->name.str = rdi_string_from_idx(rdi, src->name_string_idx, &dst->name.size); - dst->val = src->val; - } - } - - // rjf: produce - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->name = push_str8_copy(arena, name); - type->byte_size = (U64)rdi_type->byte_size; - type->count = enum_vals_count; - type->enum_vals = enum_vals; - type->direct_type_key = direct_type_key; - } - - //- rjf: constructed types - else if(RDI_TypeKind_FirstConstructed <= rdi_type->kind && rdi_type->kind <= RDI_TypeKind_LastConstructed) - { - // rjf: unpack direct type - B32 direct_type_is_good = 0; - TG_Key direct_type_key = zero_struct; - U64 direct_type_byte_size = 0; - if(rdi_type->constructed.direct_type_idx < type_node_idx) - { - RDI_TypeNode *direct_type_node = rdi_element_from_name_idx(rdi, TypeNodes, rdi_type->constructed.direct_type_idx); - TG_Kind direct_type_kind = tg_kind_from_rdi_type_kind(direct_type_node->kind); - direct_type_key = tg_key_ext(direct_type_kind, (U64)rdi_type->constructed.direct_type_idx); - direct_type_is_good = 1; - direct_type_byte_size = (U64)direct_type_node->byte_size; - } - - // rjf: construct based on kind - switch(rdi_type->kind) - { - case RDI_TypeKind_Modifier: - { - TG_Flags flags = 0; - if(rdi_type->flags & RDI_TypeModifierFlag_Const) - { - flags |= TG_Flag_Const; - } - if(rdi_type->flags & RDI_TypeModifierFlag_Volatile) - { - flags |= TG_Flag_Volatile; - } - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->direct_type_key = direct_type_key; - type->byte_size = direct_type_byte_size; - type->flags = flags; - }break; - case RDI_TypeKind_Ptr: - case RDI_TypeKind_LRef: - case RDI_TypeKind_RRef: - { - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->direct_type_key = direct_type_key; - type->byte_size = graph->address_size; - }break; - - case RDI_TypeKind_Array: - { - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->direct_type_key = direct_type_key; - type->count = rdi_type->constructed.count; - type->byte_size = direct_type_byte_size * type->count; - }break; - case RDI_TypeKind_Function: - { - U32 count = rdi_type->constructed.count; - U32 idx_run_first = rdi_type->constructed.param_idx_run_first; - U32 check_count = 0; - U32 *idx_run = rdi_idx_run_from_first_count(rdi, idx_run_first, count, &check_count); - if(check_count == count) - { - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->byte_size = graph->address_size; - type->direct_type_key = direct_type_key; - type->count = count; - type->param_type_keys = push_array_no_zero(arena, TG_Key, type->count); - for(U32 idx = 0; idx < type->count; idx += 1) - { - U32 param_type_idx = idx_run[idx]; - if(param_type_idx < type_node_idx) - { - RDI_TypeNode *param_type_node = rdi_element_from_name_idx(rdi, TypeNodes, param_type_idx); - TG_Kind param_kind = tg_kind_from_rdi_type_kind(param_type_node->kind); - type->param_type_keys[idx] = tg_key_ext(param_kind, (U64)param_type_idx); - } - else - { - break; - } - } - } - }break; - case RDI_TypeKind_Method: - { - // NOTE(rjf): for methods, the `direct` type points at the owner type. - // the return type, instead of being encoded via the `direct` type, is - // encoded via the first parameter. - U32 count = rdi_type->constructed.count; - U32 idx_run_first = rdi_type->constructed.param_idx_run_first; - U32 check_count = 0; - U32 *idx_run = rdi_idx_run_from_first_count(rdi, idx_run_first, count, &check_count); - if(check_count == count) - { - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->byte_size = graph->address_size; - type->owner_type_key = direct_type_key; - type->count = count; - type->param_type_keys = push_array_no_zero(arena, TG_Key, type->count); - for(U32 idx = 0; idx < type->count; idx += 1) - { - U32 param_type_idx = idx_run[idx]; - if(param_type_idx < type_node_idx) - { - RDI_TypeNode *param_type_node = rdi_element_from_name_idx(rdi, TypeNodes, param_type_idx); - TG_Kind param_kind = tg_kind_from_rdi_type_kind(param_type_node->kind); - type->param_type_keys[idx] = tg_key_ext(param_kind, (U64)param_type_idx); - } - else - { - break; - } - } - if(type->count > 0) - { - type->direct_type_key = type->param_type_keys[0]; - type->count -= 1; - type->param_type_keys += 1; - } - } - }break; - case RDI_TypeKind_MemberPtr: - { - // rjf: unpack owner type - TG_Key owner_type_key = zero_struct; - if(rdi_type->constructed.owner_type_idx < type_node_idx) - { - RDI_TypeNode *owner_type_node = rdi_element_from_name_idx(rdi, TypeNodes, rdi_type->constructed.owner_type_idx); - TG_Kind owner_type_kind = tg_kind_from_rdi_type_kind(owner_type_node->kind); - owner_type_key = tg_key_ext(owner_type_kind, (U64)rdi_type->constructed.owner_type_idx); - } - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->byte_size = graph->address_size; - type->owner_type_key = owner_type_key; - type->direct_type_key = direct_type_key; - }break; - } - } - - //- rjf: alias types - else if(rdi_type->kind == RDI_TypeKind_Alias) - { - // rjf: unpack name - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, rdi_type->user_defined.name_string_idx, &name.size); - - // rjf: unpack direct type - TG_Key direct_type_key = zero_struct; - U64 direct_type_byte_size = 0; - if(rdi_type->user_defined.direct_type_idx < type_node_idx) - { - RDI_TypeNode *direct_type_node = rdi_element_from_name_idx(rdi, TypeNodes, rdi_type->user_defined.direct_type_idx); - TG_Kind direct_type_kind = tg_kind_from_rdi_type_kind(direct_type_node->kind); - direct_type_key = tg_key_ext(direct_type_kind, (U64)rdi_type->user_defined.direct_type_idx); - direct_type_byte_size = direct_type_node->byte_size; - } - - // rjf: produce - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->name = push_str8_copy(arena, name); - type->byte_size = direct_type_byte_size; - type->direct_type_key = direct_type_key; - } - - //- rjf: bitfields - else if(RDI_TypeKind_Bitfield == rdi_type->kind) - { - // rjf: unpack direct type - TG_Key direct_type_key = zero_struct; - U64 direct_type_byte_size = 0; - if(rdi_type->bitfield.direct_type_idx < type_node_idx) - { - RDI_TypeNode *direct_type_node = rdi_element_from_name_idx(rdi, TypeNodes, rdi_type->bitfield.direct_type_idx); - TG_Kind direct_type_kind = tg_kind_from_rdi_type_kind(direct_type_node->kind); - direct_type_key = tg_key_ext(direct_type_kind, (U64)rdi_type->bitfield.direct_type_idx); - direct_type_byte_size = direct_type_node->byte_size; - } - - // rjf: produce - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->byte_size = direct_type_byte_size; - type->direct_type_key = direct_type_key; - type->off = (U32)rdi_type->bitfield.off; - type->count = (U64)rdi_type->bitfield.size; - } - - //- rjf: incomplete types - else if(RDI_TypeKind_FirstIncomplete <= rdi_type->kind && rdi_type->kind <= RDI_TypeKind_LastIncomplete) - { - // rjf: unpack name - String8 name = {0}; - name.str = rdi_string_from_idx(rdi, rdi_type->user_defined.name_string_idx, &name.size); - - // rjf: produce - type = push_array(arena, TG_Type, 1); - type->kind = kind; - type->name = push_str8_copy(arena, name); - } - - } - }break; - - //- rjf: reg type keys - case TG_KeyKind_Reg: - { - Architecture arch = (Architecture)key.u32[0]; - REGS_RegCode code = (REGS_RegCode)key.u64[0]; - REGS_Rng rng = regs_reg_code_rng_table_from_architecture(arch)[code]; - reg_byte_count = (U64)rng.byte_size; - }goto build_reg_type; - case TG_KeyKind_RegAlias: - { - Architecture arch = (Architecture)key.u32[0]; - REGS_AliasCode code = (REGS_AliasCode)key.u64[0]; - REGS_Slice slice = regs_alias_code_slice_table_from_architecture(arch)[code]; - reg_byte_count = (U64)slice.byte_size; - }goto build_reg_type; - build_reg_type: - { - Temp scratch = scratch_begin(&arena, 1); - type = push_array(arena, TG_Type, 1); - type->kind = TG_Kind_Union; - type->name = push_str8f(arena, "reg_%I64u_bit", reg_byte_count*8); - type->byte_size = (U64)reg_byte_count; - - // rjf: build register type members - TG_MemberList members = {0}; - { - // rjf: build exact-sized members - { - if(type->byte_size == 16) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("u128"); - mem->type_key = tg_key_basic(TG_Kind_U128); - } - if(type->byte_size == 8) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("u64"); - mem->type_key = tg_key_basic(TG_Kind_U64); - } - if(type->byte_size == 4) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("u32"); - mem->type_key = tg_key_basic(TG_Kind_U32); - } - if(type->byte_size == 2) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("u16"); - mem->type_key = tg_key_basic(TG_Kind_U16); - } - if(type->byte_size == 1) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("u8"); - mem->type_key = tg_key_basic(TG_Kind_U8); - } - } - - // rjf: build arrays for subdivisions - { - if(type->byte_size > 16 && type->byte_size%16 == 0) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("u128s"); - mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U128), reg_byte_count/16); - } - if(type->byte_size > 8 && type->byte_size%8 == 0) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("u64s"); - mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U64), reg_byte_count/8); - } - if(type->byte_size > 4 && type->byte_size%4 == 0) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("u32s"); - mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U32), reg_byte_count/4); - } - if(type->byte_size > 2 && type->byte_size%2 == 0) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("u16s"); - mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U16), reg_byte_count/2); - } - if(type->byte_size > 1) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("u8s"); - mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U8), reg_byte_count); - } - if(type->byte_size > 4 && type->byte_size%4 == 0) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("f32s"); - mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_F32), reg_byte_count/4); - } - if(type->byte_size > 8 && type->byte_size%8 == 0) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - SLLQueuePush(members.first, members.last, n); - members.count += 1; - TG_Member *mem = &n->v; - mem->kind = TG_MemberKind_DataField; - mem->name = str8_lit("f64s"); - mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_F64), reg_byte_count/8); - } - } - } - - // rjf: commit members - type->count = members.count; - type->members = push_array_no_zero(arena, TG_Member, members.count); - U64 idx = 0; - for(TG_MemberNode *n = members.first; n != 0; n = n->next, idx += 1) - { - MemoryCopyStruct(&type->members[idx], &n->v); - } - - scratch_end(scratch); - }break; - } - } - return type; -} - -internal TG_Key -tg_direct_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - TG_Key result = zero_struct; - switch(key.kind) - { - default:{}break; - case TG_KeyKind_Ext: - case TG_KeyKind_Cons: - { - Temp scratch = scratch_begin(0, 0); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - result = type->direct_type_key; - scratch_end(scratch); - }break; - } - return result; -} - -internal TG_Key -tg_unwrapped_direct_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - key = tg_unwrapped_from_graph_rdi_key(graph, rdi, key); - key = tg_direct_from_graph_rdi_key(graph, rdi, key); - key = tg_unwrapped_from_graph_rdi_key(graph, rdi, key); - return key; -} - -internal TG_Key -tg_owner_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - TG_Key result = zero_struct; - switch(key.kind) - { - default:{}break; - case TG_KeyKind_Ext: - case TG_KeyKind_Cons: - { - Temp scratch = scratch_begin(0, 0); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - result = type->owner_type_key; - scratch_end(scratch); - }break; - } - return result; -} - -internal TG_Key -tg_ptee_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - TG_Key result = key; - B32 passed_ptr = 0; - for(;;) - { - TG_Kind kind = tg_kind_from_key(result); - result = tg_direct_from_graph_rdi_key(graph, rdi, result); - if(kind == TG_Kind_Ptr || kind == TG_Kind_LRef || kind == TG_Kind_RRef) - { - passed_ptr = 1; - } - TG_Kind next_kind = tg_kind_from_key(result); - if(passed_ptr && - next_kind != TG_Kind_IncompleteStruct && - next_kind != TG_Kind_IncompleteUnion && - next_kind != TG_Kind_IncompleteEnum && - next_kind != TG_Kind_IncompleteClass && - next_kind != TG_Kind_Alias && - next_kind != TG_Kind_Modifier) - { - break; - } - if(kind == TG_Kind_Null) - { - break; - } - } - return result; -} - -internal TG_Key -tg_unwrapped_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - TG_Key result = key; - for(B32 good = 1; good;) - { - TG_Kind kind = tg_kind_from_key(result); - if((TG_Kind_FirstIncomplete <= kind && kind <= TG_Kind_LastIncomplete) || - kind == TG_Kind_Modifier || - kind == TG_Kind_Alias) - { - result = tg_direct_from_graph_rdi_key(graph, rdi, result); - } - else - { - good = 0; - } - } - return result; -} - -internal U64 -tg_byte_size_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - U64 result = 0; - switch(key.kind) - { - default:{}break; - case TG_KeyKind_Basic: - { - TG_Kind kind = (TG_Kind)key.u32[0]; - result = tg_kind_basic_byte_size_table[kind]; - }break; - case TG_KeyKind_Ext: - case TG_KeyKind_Cons: - { - Temp scratch = scratch_begin(0, 0); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - result = type->byte_size; - scratch_end(scratch); - }break; - } - return result; -} - -internal TG_Kind -tg_kind_from_key(TG_Key key) -{ - TG_Kind kind = TG_Kind_Null; - switch(key.kind) - { - default:{}break; - case TG_KeyKind_Basic: {kind = (TG_Kind)key.u32[0];}break; - case TG_KeyKind_Ext: {kind = (TG_Kind)key.u32[0];}break; - case TG_KeyKind_Cons: {kind = (TG_Kind)key.u32[0];}break; - case TG_KeyKind_Reg: {kind = TG_Kind_Union;}break; - case TG_KeyKind_RegAlias:{kind = TG_Kind_Union;}break; - } - return kind; -} - -internal TG_Member * -tg_member_copy(Arena *arena, TG_Member *src) -{ - TG_Member *dst = push_array(arena, TG_Member, 1); - MemoryCopyStruct(dst, src); - dst->name = push_str8_copy(arena, src->name); - dst->inheritance_key_chain = tg_key_list_copy(arena, &src->inheritance_key_chain); - return dst; -} - -internal TG_MemberArray -tg_members_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - TG_MemberArray result = {0}; - Temp scratch = scratch_begin(&arena, 1); - { - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - if(type->members != 0) - { - result.count = type->count; - result.v = push_array_no_zero(arena, TG_Member, result.count); - MemoryCopy(result.v, type->members, sizeof(TG_Member)*result.count); - for(U64 idx = 0; idx < result.count; idx += 1) - { - result.v[idx].name = push_str8_copy(arena, result.v[idx].name); - } - } - } - scratch_end(scratch); - return result; -} - -internal TG_MemberArray -tg_data_members_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - Temp scratch = scratch_begin(&arena, 1); - TG_Kind root_type_kind = tg_kind_from_key(key); - - //- rjf: walk type tree; gather members list - TG_MemberList members_list = {0}; - B32 members_need_offset_sort = 0; - { - TG_Type *root_type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - typedef struct Task Task; - struct Task - { - Task *next; - U64 base_off; - TG_KeyList inheritance_chain; - TG_Key type_key; - TG_Type *type; - }; - Task start_task = {0, 0, {0}, key, root_type}; - Task *first_task = &start_task; - Task *last_task = &start_task; - for(Task *task = first_task; task != 0; task = task->next) - { - TG_Type *type = task->type; - if(type->members != 0) - { - U64 last_member_off = 0; - for(U64 member_idx = 0; member_idx < type->count; member_idx += 1) - { - if(type->members[member_idx].kind == TG_MemberKind_DataField) - { - TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); - MemoryCopyStruct(&n->v, &type->members[member_idx]); - n->v.off += task->base_off; - n->v.inheritance_key_chain = task->inheritance_chain; - SLLQueuePush(members_list.first, members_list.last, n); - members_list.count += 1; - members_need_offset_sort = members_need_offset_sort || (n->v.off < last_member_off); - last_member_off = n->v.off; - } - else if(type->members[member_idx].kind == TG_MemberKind_Base) - { - Task *t = push_array(scratch.arena, Task, 1); - t->base_off = type->members[member_idx].off + task->base_off; - t->inheritance_chain = tg_key_list_copy(scratch.arena, &task->inheritance_chain); - tg_key_list_push(scratch.arena, &t->inheritance_chain, type->members[member_idx].type_key); - t->type_key = type->members[member_idx].type_key; - t->type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, type->members[member_idx].type_key); - SLLQueuePush(first_task, last_task, t); - members_need_offset_sort = 1; - } - } - } - } - } - - //- rjf: convert to array - TG_MemberArray members = {0}; - { - members.count = members_list.count; - members.v = push_array(arena, TG_Member, members.count); - U64 idx = 0; - for(TG_MemberNode *n = members_list.first; n != 0; n = n->next) - { - MemoryCopyStruct(&members.v[idx], &n->v); - members.v[idx].name = push_str8_copy(arena, members.v[idx].name); - members.v[idx].inheritance_key_chain = tg_key_list_copy(arena, &members.v[idx].inheritance_key_chain); - idx += 1; - } - } - - //- rjf: sort array by offset if needed - if(members_need_offset_sort) - { - quick_sort(members.v, members.count, sizeof(TG_Member), tg_qsort_compare_members_offset); - } - - //- rjf: find all padding instances - typedef struct PaddingNode PaddingNode; - struct PaddingNode - { - PaddingNode *next; - U64 off; - U64 size; - U64 prev_member_idx; - }; - PaddingNode *first_padding = 0; - PaddingNode *last_padding = 0; - U64 padding_count = 0; - if(root_type_kind == TG_Kind_Struct || root_type_kind == TG_Kind_Class) - { - for(U64 idx = 0; idx < members.count; idx += 1) - { - TG_Member *member = &members.v[idx]; - if(idx+1 < members.count) - { - U64 member_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, member->type_key); - Rng1U64 member_byte_range = r1u64(member->off, member->off + member_byte_size); - if(member[1].off > member_byte_range.max) - { - PaddingNode *n = push_array(scratch.arena, PaddingNode, 1); - SLLQueuePush(first_padding, last_padding, n); - n->off = member_byte_range.max; - n->size = member[1].off - member_byte_range.max; - n->prev_member_idx = idx; - padding_count += 1; - } - } - } - } - - //- rjf: produce new members array, if we have any padding - if(padding_count != 0) - { - TG_MemberArray new_members = {0}; - new_members.count = members.count + padding_count; - new_members.v = push_array(arena, TG_Member, new_members.count); - MemoryCopy(new_members.v, members.v, sizeof(TG_Member)*members.count); - U64 padding_idx = 0; - for(PaddingNode *n = first_padding; n != 0; n = n->next) - { - if(members.count+padding_idx > n->prev_member_idx+1) - { - MemoryCopy(new_members.v + n->prev_member_idx + padding_idx + 2, - new_members.v + n->prev_member_idx + padding_idx + 1, - sizeof(TG_Member) * (members.count + padding_idx - (n->prev_member_idx + padding_idx + 1))); - } - TG_Member *padding_member = &new_members.v[n->prev_member_idx+padding_idx+1]; - MemoryZeroStruct(padding_member); - padding_member->kind = TG_MemberKind_Padding; - padding_member->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U8), n->size); - padding_member->off = n->off; - padding_member->name = str8_lit("padding"); - padding_idx += 1; - } - members = new_members; - } - - scratch_end(scratch); - return members; -} - -internal void -tg_lhs_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key, String8List *out, U32 prec, B32 skip_return) -{ - String8 keyword = {0}; - TG_Kind kind = tg_kind_from_key(key); - switch(kind) - { - default: - { - Temp scratch = scratch_begin(&arena, 1); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - str8_list_push(arena, out, push_str8_copy(arena, type->name)); - str8_list_push(arena, out, str8_lit(" ")); - scratch_end(scratch); - }break; - - case TG_Kind_Bitfield: - { - Temp scratch = scratch_begin(&arena, 1); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - tg_lhs_string_from_key(arena, graph, rdi, type->direct_type_key, out, prec, skip_return); - str8_list_pushf(arena, out, ": %I64u", type->count); - scratch_end(scratch); - }break; - - case TG_Kind_Modifier: - { - Temp scratch = scratch_begin(&arena, 1); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - TG_Key direct = type->direct_type_key; - tg_lhs_string_from_key(arena, graph, rdi, direct, out, 1, skip_return); - if(type->flags & TG_Flag_Const) - { - str8_list_push(arena, out, str8_lit("const ")); - } - if(type->flags & TG_Flag_Volatile) - { - str8_list_push(arena, out, str8_lit("volatile ")); - } - scratch_end(scratch); - }break; - - case TG_Kind_Variadic: - { - str8_list_push(arena, out, str8_lit("...")); - }break; - - case TG_Kind_Struct: - case TG_Kind_Union: - case TG_Kind_Enum: - case TG_Kind_Class: - case TG_Kind_Alias: - { - Temp scratch = scratch_begin(&arena, 1); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - str8_list_push(arena, out, push_str8_copy(arena, type->name)); - str8_list_push(arena, out, str8_lit(" ")); - scratch_end(scratch); - }break; - - case TG_Kind_IncompleteStruct: keyword = str8_lit("struct"); goto fwd_udt; - case TG_Kind_IncompleteUnion: keyword = str8_lit("union"); goto fwd_udt; - case TG_Kind_IncompleteEnum: keyword = str8_lit("enum"); goto fwd_udt; - case TG_Kind_IncompleteClass: keyword = str8_lit("class"); goto fwd_udt; - fwd_udt:; - { - Temp scratch = scratch_begin(&arena, 1); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - str8_list_push(arena, out, keyword); - str8_list_push(arena, out, str8_lit(" ")); - str8_list_push(arena, out, push_str8_copy(arena, type->name)); - str8_list_push(arena, out, str8_lit(" ")); - scratch_end(scratch); - }break; - - case TG_Kind_Array: - { - TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); - tg_lhs_string_from_key(arena, graph, rdi, direct, out, 2, skip_return); - if(prec == 1) - { - str8_list_push(arena, out, str8_lit("(")); - } - }break; - - case TG_Kind_Function: - { - if(!skip_return) - { - TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); - tg_lhs_string_from_key(arena, graph, rdi, direct, out, 2, 0); - } - if(prec == 1) - { - str8_list_push(arena, out, str8_lit("(")); - } - }break; - - case TG_Kind_Ptr: - { - TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); - tg_lhs_string_from_key(arena, graph, rdi, direct, out, 1, skip_return); - str8_list_push(arena, out, str8_lit("*")); - }break; - - case TG_Kind_LRef: - { - TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); - tg_lhs_string_from_key(arena, graph, rdi, direct, out, 1, skip_return); - str8_list_push(arena, out, str8_lit("&")); - }break; - - case TG_Kind_RRef: - { - TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); - tg_lhs_string_from_key(arena, graph, rdi, direct, out, 1, skip_return); - str8_list_push(arena, out, str8_lit("&&")); - }break; - - case TG_Kind_MemberPtr: - { - Temp scratch = scratch_begin(&arena, 1); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - TG_Key direct = type->direct_type_key; - tg_lhs_string_from_key(arena, graph, rdi, direct, out, 1, skip_return); - TG_Type *container = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, type->owner_type_key); - if(container->kind != TG_Kind_Null) - { - str8_list_push(arena, out, push_str8_copy(arena, container->name)); - } - else - { - str8_list_push(arena, out, str8_lit("")); - } - str8_list_push(arena, out, str8_lit("::*")); - scratch_end(scratch); - }break; - } -} - -internal void -tg_rhs_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key, String8List *out, U32 prec) -{ - TG_Kind kind = tg_kind_from_key(key); - switch(kind) - { - default:{}break; - - case TG_Kind_Bitfield: - { - TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); - tg_rhs_string_from_key(arena, graph, rdi, direct, out, prec); - }break; - - case TG_Kind_Modifier: - case TG_Kind_Ptr: - case TG_Kind_LRef: - case TG_Kind_RRef: - case TG_Kind_MemberPtr: - { - TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); - tg_rhs_string_from_key(arena, graph, rdi, direct, out, 1); - }break; - - case TG_Kind_Array: - { - Temp scratch = scratch_begin(&arena, 1); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - if(prec == 1) - { - str8_list_push(arena, out, str8_lit(")")); - } - String8 count_str = str8_from_u64(arena, type->count, 10, 0, 0); - str8_list_push(arena, out, str8_lit("[")); - str8_list_push(arena, out, count_str); - str8_list_push(arena, out, str8_lit("]")); - TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); - tg_rhs_string_from_key(arena, graph, rdi, direct, out, 2); - scratch_end(scratch); - }break; - - case TG_Kind_Function: - { - Temp scratch = scratch_begin(&arena, 1); - TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); - if(prec == 1) - { - str8_list_push(arena, out, str8_lit(")")); - } - - // parameters - if(type->count == 0) - { - str8_list_push(arena, out, str8_lit("(void)")); - } - else - { - str8_list_push(arena, out, str8_lit("(")); - U64 param_count = type->count; - TG_Key *param_type_keys = type->param_type_keys; - for(U64 param_idx = 0; param_idx < param_count; param_idx += 1) - { - TG_Key param_type_key = param_type_keys[param_idx]; - String8 param_str = tg_string_from_key(arena, graph, rdi, param_type_key); - String8 param_str_trimmed = str8_skip_chop_whitespace(param_str); - str8_list_push(arena, out, param_str_trimmed); - if(param_idx+1 < param_count) - { - str8_list_push(arena, out, str8_lit(", ")); - } - } - str8_list_push(arena, out, str8_lit(")")); - } - TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); - tg_rhs_string_from_key(arena, graph, rdi, direct, out, 2); - scratch_end(scratch); - }break; - } -} - -internal String8 -tg_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) -{ - Temp scratch = scratch_begin(&arena, 1); - String8List list = {0}; - tg_lhs_string_from_key(scratch.arena, graph, rdi, key, &list, 0, 0); - tg_rhs_string_from_key(scratch.arena, graph, rdi, key, &list, 0); - String8 result = str8_list_join(arena, &list, 0); - scratch_end(scratch); - return result; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Generated Code + +#include "generated/type_graph.meta.c" + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal U64 +tg_hash_from_string(U64 seed, String8 string) +{ + U64 result = seed; + for(U64 idx = 0; idx < string.size; idx += 1) + { + result = ((result<<5)+result) + string.str[idx]; + } + return result; +} + +internal int +tg_qsort_compare_members_offset(TG_Member *a, TG_Member *b) +{ + int result = 0; + if(a->off < b->off) + { + result = -1; + } + else if(a->off > b->off) + { + result = +1; + } + return result; +} + +internal void +tg_key_list_push(Arena *arena, TG_KeyList *list, TG_Key key) +{ + TG_KeyNode *n = push_array(arena, TG_KeyNode, 1); + n->v = key; + SLLQueuePush(list->first, list->last, n); + list->count += 1; +} + +internal TG_KeyList +tg_key_list_copy(Arena *arena, TG_KeyList *src) +{ + TG_KeyList dst = {0}; + for(TG_KeyNode *n = src->first; n != 0; n = n->next) + { + tg_key_list_push(arena, &dst, n->v); + } + return dst; +} + +//////////////////////////////// +//~ rjf: RADDBG <-> TG Enum Conversions + +internal TG_Kind +tg_kind_from_rdi_type_kind(RDI_TypeKind kind) +{ + TG_Kind result = TG_Kind_Null; + switch(kind) + { + default:{}break; + case RDI_TypeKind_Void: {result = TG_Kind_Void;}break; + case RDI_TypeKind_Handle: {result = TG_Kind_Handle;}break; + case RDI_TypeKind_Char8: {result = TG_Kind_Char8;}break; + case RDI_TypeKind_Char16: {result = TG_Kind_Char16;}break; + case RDI_TypeKind_Char32: {result = TG_Kind_Char32;}break; + case RDI_TypeKind_UChar8: {result = TG_Kind_UChar8;}break; + case RDI_TypeKind_UChar16: {result = TG_Kind_UChar16;}break; + case RDI_TypeKind_UChar32: {result = TG_Kind_UChar32;}break; + case RDI_TypeKind_U8: {result = TG_Kind_U8;}break; + case RDI_TypeKind_U16: {result = TG_Kind_U16;}break; + case RDI_TypeKind_U32: {result = TG_Kind_U32;}break; + case RDI_TypeKind_U64: {result = TG_Kind_U64;}break; + case RDI_TypeKind_U128: {result = TG_Kind_U128;}break; + case RDI_TypeKind_U256: {result = TG_Kind_U256;}break; + case RDI_TypeKind_U512: {result = TG_Kind_U512;}break; + case RDI_TypeKind_S8: {result = TG_Kind_S8;}break; + case RDI_TypeKind_S16: {result = TG_Kind_S16;}break; + case RDI_TypeKind_S32: {result = TG_Kind_S32;}break; + case RDI_TypeKind_S64: {result = TG_Kind_S64;}break; + case RDI_TypeKind_S128: {result = TG_Kind_S128;}break; + case RDI_TypeKind_S256: {result = TG_Kind_S256;}break; + case RDI_TypeKind_S512: {result = TG_Kind_S512;}break; + case RDI_TypeKind_Bool: {result = TG_Kind_Bool;}break; + case RDI_TypeKind_F16: {result = TG_Kind_F16;}break; + case RDI_TypeKind_F32: {result = TG_Kind_F32;}break; + case RDI_TypeKind_F32PP: {result = TG_Kind_F32PP;}break; + case RDI_TypeKind_F48: {result = TG_Kind_F48;}break; + case RDI_TypeKind_F64: {result = TG_Kind_F64;}break; + case RDI_TypeKind_F80: {result = TG_Kind_F80;}break; + case RDI_TypeKind_F128: {result = TG_Kind_F128;}break; + case RDI_TypeKind_ComplexF32: {result = TG_Kind_ComplexF32;}break; + case RDI_TypeKind_ComplexF64: {result = TG_Kind_ComplexF64;}break; + case RDI_TypeKind_ComplexF80: {result = TG_Kind_ComplexF80;}break; + case RDI_TypeKind_ComplexF128: {result = TG_Kind_ComplexF128;}break; + case RDI_TypeKind_Modifier: {result = TG_Kind_Modifier;}break; + case RDI_TypeKind_Ptr: {result = TG_Kind_Ptr;}break; + case RDI_TypeKind_LRef: {result = TG_Kind_LRef;}break; + case RDI_TypeKind_RRef: {result = TG_Kind_RRef;}break; + case RDI_TypeKind_Array: {result = TG_Kind_Array;}break; + case RDI_TypeKind_Function: {result = TG_Kind_Function;}break; + case RDI_TypeKind_Method: {result = TG_Kind_Method;}break; + case RDI_TypeKind_MemberPtr: {result = TG_Kind_MemberPtr;}break; + case RDI_TypeKind_Struct: {result = TG_Kind_Struct;}break; + case RDI_TypeKind_Class: {result = TG_Kind_Class;}break; + case RDI_TypeKind_Union: {result = TG_Kind_Union;}break; + case RDI_TypeKind_Enum: {result = TG_Kind_Enum;}break; + case RDI_TypeKind_Alias: {result = TG_Kind_Alias;}break; + case RDI_TypeKind_IncompleteStruct: {result = TG_Kind_IncompleteStruct;}break; + case RDI_TypeKind_IncompleteUnion: {result = TG_Kind_IncompleteUnion;}break; + case RDI_TypeKind_IncompleteClass: {result = TG_Kind_IncompleteClass;}break; + case RDI_TypeKind_IncompleteEnum: {result = TG_Kind_IncompleteEnum;}break; + case RDI_TypeKind_Bitfield: {result = TG_Kind_Bitfield;}break; + case RDI_TypeKind_Variadic: {result = TG_Kind_Variadic;}break; + } + return result; +} + +internal TG_MemberKind +tg_member_kind_from_rdi_member_kind(RDI_MemberKind kind) +{ + TG_MemberKind result = TG_MemberKind_Null; + switch(kind) + { + default:{}break; + case RDI_MemberKind_DataField: {result = TG_MemberKind_DataField;}break; + case RDI_MemberKind_StaticData: {result = TG_MemberKind_StaticData;}break; + case RDI_MemberKind_Method: {result = TG_MemberKind_Method;}break; + case RDI_MemberKind_StaticMethod: {result = TG_MemberKind_StaticMethod;}break; + case RDI_MemberKind_VirtualMethod: {result = TG_MemberKind_VirtualMethod;}break; + case RDI_MemberKind_VTablePtr: {result = TG_MemberKind_VTablePtr;}break; + case RDI_MemberKind_Base: {result = TG_MemberKind_Base;}break; + case RDI_MemberKind_VirtualBase: {result = TG_MemberKind_VirtualBase;}break; + case RDI_MemberKind_NestedType: {result = TG_MemberKind_NestedType;}break; + } + return result; +} + +//////////////////////////////// +//~ rjf: Key Type Functions + +internal TG_Key +tg_key_zero(void) +{ + TG_Key key = zero_struct; + return key; +} + +internal TG_Key +tg_key_basic(TG_Kind kind) +{ + TG_Key key = {TG_KeyKind_Basic}; + key.u32[0] = (U32)kind; + return key; +} + +internal TG_Key +tg_key_ext(TG_Kind kind, U64 id) +{ + TG_Key key = {TG_KeyKind_Ext}; + key.u32[0] = (U32)kind; + if(TG_Kind_FirstBasic <= kind && kind <= TG_Kind_LastBasic) + { + key.kind = TG_KeyKind_Basic; + } + else + { + key.u64[0] = id; + } + return key; +} + +internal TG_Key +tg_key_reg(Architecture arch, REGS_RegCode code) +{ + TG_Key key = {TG_KeyKind_Reg}; + key.u32[0] = (U32)arch; + key.u64[0] = (U64)code; + return key; +} + +internal TG_Key +tg_key_reg_alias(Architecture arch, REGS_AliasCode code) +{ + TG_Key key = {TG_KeyKind_RegAlias}; + key.u32[0] = (U32)arch; + key.u64[0] = (U64)code; + return key; +} + +internal B32 +tg_key_match(TG_Key a, TG_Key b) +{ + B32 result = MemoryMatchStruct(&a, &b); + return result; +} + +//////////////////////////////// +//~ rjf: Graph Construction API + +internal TG_Graph * +tg_graph_begin(U64 address_size, U64 slot_count) +{ + if(tg_build_arena == 0) + { + tg_build_arena = arena_alloc(); + } + else + { + arena_clear(tg_build_arena); + } + TG_Graph *graph = push_array(tg_build_arena, TG_Graph, 1); + graph->address_size = address_size; + graph->content_hash_slots_count = slot_count; + graph->content_hash_slots = push_array(tg_build_arena, TG_Slot, graph->content_hash_slots_count); + graph->key_hash_slots_count = slot_count; + graph->key_hash_slots = push_array(tg_build_arena, TG_Slot, graph->key_hash_slots_count); + return graph; +} + +internal TG_Key +tg_cons_type_make(TG_Graph *graph, TG_Kind kind, TG_Key direct_type_key, U64 u64) +{ + U32 buffer[] = + { + (U32)kind, + (U32)direct_type_key.kind, + (U32)direct_type_key.u32[0], + (U32)((direct_type_key.u64[0] & 0x00000000ffffffffull)>> 0), + (U32)((direct_type_key.u64[0] & 0xffffffff00000000ull)>>32), + (U32)((u64 & 0x00000000ffffffffull)>> 0), + (U32)((u64 & 0xffffffff00000000ull)>> 32), + }; + U64 content_hash = tg_hash_from_string(5381, str8((U8 *)buffer, sizeof(buffer))); + U64 content_slot_idx = content_hash%graph->content_hash_slots_count; + TG_Slot *content_slot = &graph->content_hash_slots[content_slot_idx]; + TG_Node *node = 0; + for(TG_Node *n = content_slot->first; n != 0; n = n->content_hash_next) + { + if(n->cons_type.kind == kind && tg_key_match(n->cons_type.direct_type_key, direct_type_key)) + { + node = n; + break; + } + } + TG_Key result = zero_struct; + if(node == 0) + { + TG_Key key = {TG_KeyKind_Cons}; + key.u32[0] = (U32)kind; + key.u64[0] = graph->cons_id_gen; + U64 key_hash = tg_hash_from_string(5381, str8_struct(&key)); + U64 key_slot_idx = key_hash%graph->key_hash_slots_count; + TG_Slot *key_slot = &graph->key_hash_slots[key_slot_idx]; + graph->cons_id_gen += 1; + TG_Node *node = push_array(tg_build_arena, TG_Node, 1); + SLLQueuePush_N(content_slot->first, content_slot->last, node, content_hash_next); + SLLQueuePush_N(key_slot->first, key_slot->last, node, key_hash_next); + node->key = key; + node->cons_type.kind = kind; + node->cons_type.direct_type_key = direct_type_key; + node->cons_type.u64 = u64; + result = key; + } + else + { + result = node->key; + } + return result; +} + +//////////////////////////////// +//~ rjf: Graph Introspection API + +internal TG_Type * +tg_type_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + TG_Type *type = &tg_type_nil; + U64 reg_byte_count = 0; + { + switch(key.kind) + { + default:{}break; + + //- rjf: basic type keys + case TG_KeyKind_Basic: + { + TG_Kind kind = (TG_Kind)key.u32[0]; + if(TG_Kind_FirstBasic <= kind && kind <= TG_Kind_LastBasic) + { + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->name = tg_kind_basic_string_table[kind]; + type->byte_size = tg_kind_basic_byte_size_table[kind]; + } + }break; + + //- rjf: constructed type keys + case TG_KeyKind_Cons: + { + U64 key_hash = tg_hash_from_string(5381, str8_struct(&key)); + U64 key_slot_idx = key_hash%graph->key_hash_slots_count; + TG_Slot *key_slot = &graph->key_hash_slots[key_slot_idx]; + for(TG_Node *node = key_slot->first; node != 0; node = node->key_hash_next) + { + if(tg_key_match(node->key, key)) + { + TG_ConsType *cons_type = &node->cons_type; + type = push_array(arena, TG_Type, 1); + type->kind = cons_type->kind; + type->direct_type_key = cons_type->direct_type_key; + type->count = cons_type->u64; + switch(type->kind) + { + default: + { + type->byte_size = graph->address_size; + }break; + case TG_Kind_Array: + { + U64 ptee_size = tg_byte_size_from_graph_rdi_key(graph, rdi, cons_type->direct_type_key); + type->byte_size = ptee_size * type->count; + }break; + } + } + } + }break; + + //- rjf: external (raddbg) type keys + case TG_KeyKind_Ext: + { + U64 type_node_idx = key.u64[0]; + RDI_TypeNode *rdi_type = rdi_element_from_name_idx(rdi, TypeNodes, type_node_idx); + if(rdi_type->kind != RDI_TypeKind_NULL) + { + TG_Kind kind = tg_kind_from_rdi_type_kind(rdi_type->kind); + + //- rjf: record types => unpack name * members & produce + if(RDI_TypeKind_FirstRecord <= rdi_type->kind && rdi_type->kind <= RDI_TypeKind_LastRecord) + { + // rjf: unpack name + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, rdi_type->user_defined.name_string_idx, &name.size); + + // rjf: unpack UDT info + RDI_UDT *udt = rdi_element_from_name_idx(rdi, UDTs, rdi_type->user_defined.udt_idx); + + // rjf: unpack members + TG_Member *members = 0; + U32 members_count = 0; + { + members_count = udt->member_count; + members = push_array(arena, TG_Member, members_count); + if(members_count != 0) + { + for(U32 member_idx = udt->member_first; + member_idx < udt->member_first+udt->member_count; + member_idx += 1) + { + RDI_Member *src = rdi_element_from_name_idx(rdi, Members, member_idx); + TG_Kind member_type_kind = TG_Kind_Null; + RDI_TypeNode *member_type = rdi_element_from_name_idx(rdi, TypeNodes, src->type_idx); + member_type_kind = tg_kind_from_rdi_type_kind(member_type->kind); + TG_Member *dst = &members[member_idx-udt->member_first]; + dst->kind = tg_member_kind_from_rdi_member_kind(src->kind); + dst->type_key = tg_key_ext(member_type_kind, (U64)src->type_idx); + dst->name.str = rdi_string_from_idx(rdi, src->name_string_idx, &dst->name.size); + dst->off = (U64)src->off; + } + } + } + + // rjf: produce + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->name = push_str8_copy(arena, name); + type->byte_size = (U64)rdi_type->byte_size; + type->count = members_count; + type->members = members; + } + + //- rjf: enum types => unpack name * values & produce + else if(rdi_type->kind == RDI_TypeKind_Enum) + { + // rjf: unpack name + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, rdi_type->user_defined.name_string_idx, &name.size); + + // rjf: unpack direct type + TG_Key direct_type_key = zero_struct; + if(rdi_type->user_defined.direct_type_idx < type_node_idx) + { + RDI_TypeNode *direct_type_node = rdi_element_from_name_idx(rdi, TypeNodes, rdi_type->user_defined.direct_type_idx); + TG_Kind direct_type_kind = tg_kind_from_rdi_type_kind(direct_type_node->kind); + direct_type_key = tg_key_ext(direct_type_kind, (U64)rdi_type->user_defined.direct_type_idx); + } + + // rjf: unpack members + TG_EnumVal *enum_vals = 0; + U32 enum_vals_count = 0; + { + U32 udt_idx = rdi_type->user_defined.udt_idx; + RDI_UDT *udt = rdi_element_from_name_idx(rdi, UDTs, udt_idx); + enum_vals_count = udt->member_count; + enum_vals = push_array(arena, TG_EnumVal, enum_vals_count); + for(U32 member_idx = udt->member_first; + member_idx < udt->member_first+udt->member_count; + member_idx += 1) + { + RDI_EnumMember *src = rdi_element_from_name_idx(rdi, EnumMembers, member_idx); + TG_EnumVal *dst = &enum_vals[member_idx-udt->member_first]; + dst->name.str = rdi_string_from_idx(rdi, src->name_string_idx, &dst->name.size); + dst->val = src->val; + } + } + + // rjf: produce + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->name = push_str8_copy(arena, name); + type->byte_size = (U64)rdi_type->byte_size; + type->count = enum_vals_count; + type->enum_vals = enum_vals; + type->direct_type_key = direct_type_key; + } + + //- rjf: constructed types + else if(RDI_TypeKind_FirstConstructed <= rdi_type->kind && rdi_type->kind <= RDI_TypeKind_LastConstructed) + { + // rjf: unpack direct type + B32 direct_type_is_good = 0; + TG_Key direct_type_key = zero_struct; + U64 direct_type_byte_size = 0; + if(rdi_type->constructed.direct_type_idx < type_node_idx) + { + RDI_TypeNode *direct_type_node = rdi_element_from_name_idx(rdi, TypeNodes, rdi_type->constructed.direct_type_idx); + TG_Kind direct_type_kind = tg_kind_from_rdi_type_kind(direct_type_node->kind); + direct_type_key = tg_key_ext(direct_type_kind, (U64)rdi_type->constructed.direct_type_idx); + direct_type_is_good = 1; + direct_type_byte_size = (U64)direct_type_node->byte_size; + } + + // rjf: construct based on kind + switch(rdi_type->kind) + { + case RDI_TypeKind_Modifier: + { + TG_Flags flags = 0; + if(rdi_type->flags & RDI_TypeModifierFlag_Const) + { + flags |= TG_Flag_Const; + } + if(rdi_type->flags & RDI_TypeModifierFlag_Volatile) + { + flags |= TG_Flag_Volatile; + } + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->direct_type_key = direct_type_key; + type->byte_size = direct_type_byte_size; + type->flags = flags; + }break; + case RDI_TypeKind_Ptr: + case RDI_TypeKind_LRef: + case RDI_TypeKind_RRef: + { + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->direct_type_key = direct_type_key; + type->byte_size = graph->address_size; + }break; + + case RDI_TypeKind_Array: + { + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->direct_type_key = direct_type_key; + type->count = rdi_type->constructed.count; + type->byte_size = direct_type_byte_size * type->count; + }break; + case RDI_TypeKind_Function: + { + U32 count = rdi_type->constructed.count; + U32 idx_run_first = rdi_type->constructed.param_idx_run_first; + U32 check_count = 0; + U32 *idx_run = rdi_idx_run_from_first_count(rdi, idx_run_first, count, &check_count); + if(check_count == count) + { + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->byte_size = graph->address_size; + type->direct_type_key = direct_type_key; + type->count = count; + type->param_type_keys = push_array_no_zero(arena, TG_Key, type->count); + for(U32 idx = 0; idx < type->count; idx += 1) + { + U32 param_type_idx = idx_run[idx]; + if(param_type_idx < type_node_idx) + { + RDI_TypeNode *param_type_node = rdi_element_from_name_idx(rdi, TypeNodes, param_type_idx); + TG_Kind param_kind = tg_kind_from_rdi_type_kind(param_type_node->kind); + type->param_type_keys[idx] = tg_key_ext(param_kind, (U64)param_type_idx); + } + else + { + break; + } + } + } + }break; + case RDI_TypeKind_Method: + { + // NOTE(rjf): for methods, the `direct` type points at the owner type. + // the return type, instead of being encoded via the `direct` type, is + // encoded via the first parameter. + U32 count = rdi_type->constructed.count; + U32 idx_run_first = rdi_type->constructed.param_idx_run_first; + U32 check_count = 0; + U32 *idx_run = rdi_idx_run_from_first_count(rdi, idx_run_first, count, &check_count); + if(check_count == count) + { + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->byte_size = graph->address_size; + type->owner_type_key = direct_type_key; + type->count = count; + type->param_type_keys = push_array_no_zero(arena, TG_Key, type->count); + for(U32 idx = 0; idx < type->count; idx += 1) + { + U32 param_type_idx = idx_run[idx]; + if(param_type_idx < type_node_idx) + { + RDI_TypeNode *param_type_node = rdi_element_from_name_idx(rdi, TypeNodes, param_type_idx); + TG_Kind param_kind = tg_kind_from_rdi_type_kind(param_type_node->kind); + type->param_type_keys[idx] = tg_key_ext(param_kind, (U64)param_type_idx); + } + else + { + break; + } + } + if(type->count > 0) + { + type->direct_type_key = type->param_type_keys[0]; + type->count -= 1; + type->param_type_keys += 1; + } + } + }break; + case RDI_TypeKind_MemberPtr: + { + // rjf: unpack owner type + TG_Key owner_type_key = zero_struct; + if(rdi_type->constructed.owner_type_idx < type_node_idx) + { + RDI_TypeNode *owner_type_node = rdi_element_from_name_idx(rdi, TypeNodes, rdi_type->constructed.owner_type_idx); + TG_Kind owner_type_kind = tg_kind_from_rdi_type_kind(owner_type_node->kind); + owner_type_key = tg_key_ext(owner_type_kind, (U64)rdi_type->constructed.owner_type_idx); + } + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->byte_size = graph->address_size; + type->owner_type_key = owner_type_key; + type->direct_type_key = direct_type_key; + }break; + } + } + + //- rjf: alias types + else if(rdi_type->kind == RDI_TypeKind_Alias) + { + // rjf: unpack name + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, rdi_type->user_defined.name_string_idx, &name.size); + + // rjf: unpack direct type + TG_Key direct_type_key = zero_struct; + U64 direct_type_byte_size = 0; + if(rdi_type->user_defined.direct_type_idx < type_node_idx) + { + RDI_TypeNode *direct_type_node = rdi_element_from_name_idx(rdi, TypeNodes, rdi_type->user_defined.direct_type_idx); + TG_Kind direct_type_kind = tg_kind_from_rdi_type_kind(direct_type_node->kind); + direct_type_key = tg_key_ext(direct_type_kind, (U64)rdi_type->user_defined.direct_type_idx); + direct_type_byte_size = direct_type_node->byte_size; + } + + // rjf: produce + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->name = push_str8_copy(arena, name); + type->byte_size = direct_type_byte_size; + type->direct_type_key = direct_type_key; + } + + //- rjf: bitfields + else if(RDI_TypeKind_Bitfield == rdi_type->kind) + { + // rjf: unpack direct type + TG_Key direct_type_key = zero_struct; + U64 direct_type_byte_size = 0; + if(rdi_type->bitfield.direct_type_idx < type_node_idx) + { + RDI_TypeNode *direct_type_node = rdi_element_from_name_idx(rdi, TypeNodes, rdi_type->bitfield.direct_type_idx); + TG_Kind direct_type_kind = tg_kind_from_rdi_type_kind(direct_type_node->kind); + direct_type_key = tg_key_ext(direct_type_kind, (U64)rdi_type->bitfield.direct_type_idx); + direct_type_byte_size = direct_type_node->byte_size; + } + + // rjf: produce + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->byte_size = direct_type_byte_size; + type->direct_type_key = direct_type_key; + type->off = (U32)rdi_type->bitfield.off; + type->count = (U64)rdi_type->bitfield.size; + } + + //- rjf: incomplete types + else if(RDI_TypeKind_FirstIncomplete <= rdi_type->kind && rdi_type->kind <= RDI_TypeKind_LastIncomplete) + { + // rjf: unpack name + String8 name = {0}; + name.str = rdi_string_from_idx(rdi, rdi_type->user_defined.name_string_idx, &name.size); + + // rjf: produce + type = push_array(arena, TG_Type, 1); + type->kind = kind; + type->name = push_str8_copy(arena, name); + } + + } + }break; + + //- rjf: reg type keys + case TG_KeyKind_Reg: + { + Architecture arch = (Architecture)key.u32[0]; + REGS_RegCode code = (REGS_RegCode)key.u64[0]; + REGS_Rng rng = regs_reg_code_rng_table_from_architecture(arch)[code]; + reg_byte_count = (U64)rng.byte_size; + }goto build_reg_type; + case TG_KeyKind_RegAlias: + { + Architecture arch = (Architecture)key.u32[0]; + REGS_AliasCode code = (REGS_AliasCode)key.u64[0]; + REGS_Slice slice = regs_alias_code_slice_table_from_architecture(arch)[code]; + reg_byte_count = (U64)slice.byte_size; + }goto build_reg_type; + build_reg_type: + { + Temp scratch = scratch_begin(&arena, 1); + type = push_array(arena, TG_Type, 1); + type->kind = TG_Kind_Union; + type->name = push_str8f(arena, "reg_%I64u_bit", reg_byte_count*8); + type->byte_size = (U64)reg_byte_count; + + // rjf: build register type members + TG_MemberList members = {0}; + { + // rjf: build exact-sized members + { + if(type->byte_size == 16) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("u128"); + mem->type_key = tg_key_basic(TG_Kind_U128); + } + if(type->byte_size == 8) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("u64"); + mem->type_key = tg_key_basic(TG_Kind_U64); + } + if(type->byte_size == 4) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("u32"); + mem->type_key = tg_key_basic(TG_Kind_U32); + } + if(type->byte_size == 2) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("u16"); + mem->type_key = tg_key_basic(TG_Kind_U16); + } + if(type->byte_size == 1) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("u8"); + mem->type_key = tg_key_basic(TG_Kind_U8); + } + } + + // rjf: build arrays for subdivisions + { + if(type->byte_size > 16 && type->byte_size%16 == 0) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("u128s"); + mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U128), reg_byte_count/16); + } + if(type->byte_size > 8 && type->byte_size%8 == 0) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("u64s"); + mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U64), reg_byte_count/8); + } + if(type->byte_size > 4 && type->byte_size%4 == 0) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("u32s"); + mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U32), reg_byte_count/4); + } + if(type->byte_size > 2 && type->byte_size%2 == 0) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("u16s"); + mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U16), reg_byte_count/2); + } + if(type->byte_size > 1) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("u8s"); + mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U8), reg_byte_count); + } + if(type->byte_size > 4 && type->byte_size%4 == 0) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("f32s"); + mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_F32), reg_byte_count/4); + } + if(type->byte_size > 8 && type->byte_size%8 == 0) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + SLLQueuePush(members.first, members.last, n); + members.count += 1; + TG_Member *mem = &n->v; + mem->kind = TG_MemberKind_DataField; + mem->name = str8_lit("f64s"); + mem->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_F64), reg_byte_count/8); + } + } + } + + // rjf: commit members + type->count = members.count; + type->members = push_array_no_zero(arena, TG_Member, members.count); + U64 idx = 0; + for(TG_MemberNode *n = members.first; n != 0; n = n->next, idx += 1) + { + MemoryCopyStruct(&type->members[idx], &n->v); + } + + scratch_end(scratch); + }break; + } + } + return type; +} + +internal TG_Key +tg_direct_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + TG_Key result = zero_struct; + switch(key.kind) + { + default:{}break; + case TG_KeyKind_Ext: + case TG_KeyKind_Cons: + { + Temp scratch = scratch_begin(0, 0); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + result = type->direct_type_key; + scratch_end(scratch); + }break; + } + return result; +} + +internal TG_Key +tg_unwrapped_direct_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + key = tg_unwrapped_from_graph_rdi_key(graph, rdi, key); + key = tg_direct_from_graph_rdi_key(graph, rdi, key); + key = tg_unwrapped_from_graph_rdi_key(graph, rdi, key); + return key; +} + +internal TG_Key +tg_owner_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + TG_Key result = zero_struct; + switch(key.kind) + { + default:{}break; + case TG_KeyKind_Ext: + case TG_KeyKind_Cons: + { + Temp scratch = scratch_begin(0, 0); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + result = type->owner_type_key; + scratch_end(scratch); + }break; + } + return result; +} + +internal TG_Key +tg_ptee_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + TG_Key result = key; + B32 passed_ptr = 0; + for(;;) + { + TG_Kind kind = tg_kind_from_key(result); + result = tg_direct_from_graph_rdi_key(graph, rdi, result); + if(kind == TG_Kind_Ptr || kind == TG_Kind_LRef || kind == TG_Kind_RRef) + { + passed_ptr = 1; + } + TG_Kind next_kind = tg_kind_from_key(result); + if(passed_ptr && + next_kind != TG_Kind_IncompleteStruct && + next_kind != TG_Kind_IncompleteUnion && + next_kind != TG_Kind_IncompleteEnum && + next_kind != TG_Kind_IncompleteClass && + next_kind != TG_Kind_Alias && + next_kind != TG_Kind_Modifier) + { + break; + } + if(kind == TG_Kind_Null) + { + break; + } + } + return result; +} + +internal TG_Key +tg_unwrapped_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + TG_Key result = key; + for(B32 good = 1; good;) + { + TG_Kind kind = tg_kind_from_key(result); + if((TG_Kind_FirstIncomplete <= kind && kind <= TG_Kind_LastIncomplete) || + kind == TG_Kind_Modifier || + kind == TG_Kind_Alias) + { + result = tg_direct_from_graph_rdi_key(graph, rdi, result); + } + else + { + good = 0; + } + } + return result; +} + +internal U64 +tg_byte_size_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + U64 result = 0; + switch(key.kind) + { + default:{}break; + case TG_KeyKind_Basic: + { + TG_Kind kind = (TG_Kind)key.u32[0]; + result = tg_kind_basic_byte_size_table[kind]; + }break; + case TG_KeyKind_Ext: + case TG_KeyKind_Cons: + { + Temp scratch = scratch_begin(0, 0); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + result = type->byte_size; + scratch_end(scratch); + }break; + } + return result; +} + +internal TG_Kind +tg_kind_from_key(TG_Key key) +{ + TG_Kind kind = TG_Kind_Null; + switch(key.kind) + { + default:{}break; + case TG_KeyKind_Basic: {kind = (TG_Kind)key.u32[0];}break; + case TG_KeyKind_Ext: {kind = (TG_Kind)key.u32[0];}break; + case TG_KeyKind_Cons: {kind = (TG_Kind)key.u32[0];}break; + case TG_KeyKind_Reg: {kind = TG_Kind_Union;}break; + case TG_KeyKind_RegAlias:{kind = TG_Kind_Union;}break; + } + return kind; +} + +internal TG_Member * +tg_member_copy(Arena *arena, TG_Member *src) +{ + TG_Member *dst = push_array(arena, TG_Member, 1); + MemoryCopyStruct(dst, src); + dst->name = push_str8_copy(arena, src->name); + dst->inheritance_key_chain = tg_key_list_copy(arena, &src->inheritance_key_chain); + return dst; +} + +internal TG_MemberArray +tg_members_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + TG_MemberArray result = {0}; + Temp scratch = scratch_begin(&arena, 1); + { + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + if(type->members != 0) + { + result.count = type->count; + result.v = push_array_no_zero(arena, TG_Member, result.count); + MemoryCopy(result.v, type->members, sizeof(TG_Member)*result.count); + for(U64 idx = 0; idx < result.count; idx += 1) + { + result.v[idx].name = push_str8_copy(arena, result.v[idx].name); + } + } + } + scratch_end(scratch); + return result; +} + +internal TG_MemberArray +tg_data_members_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + Temp scratch = scratch_begin(&arena, 1); + TG_Kind root_type_kind = tg_kind_from_key(key); + + //- rjf: walk type tree; gather members list + TG_MemberList members_list = {0}; + B32 members_need_offset_sort = 0; + { + TG_Type *root_type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + typedef struct Task Task; + struct Task + { + Task *next; + U64 base_off; + TG_KeyList inheritance_chain; + TG_Key type_key; + TG_Type *type; + }; + Task start_task = {0, 0, {0}, key, root_type}; + Task *first_task = &start_task; + Task *last_task = &start_task; + for(Task *task = first_task; task != 0; task = task->next) + { + TG_Type *type = task->type; + if(type->members != 0) + { + U64 last_member_off = 0; + for(U64 member_idx = 0; member_idx < type->count; member_idx += 1) + { + if(type->members[member_idx].kind == TG_MemberKind_DataField) + { + TG_MemberNode *n = push_array(scratch.arena, TG_MemberNode, 1); + MemoryCopyStruct(&n->v, &type->members[member_idx]); + n->v.off += task->base_off; + n->v.inheritance_key_chain = task->inheritance_chain; + SLLQueuePush(members_list.first, members_list.last, n); + members_list.count += 1; + members_need_offset_sort = members_need_offset_sort || (n->v.off < last_member_off); + last_member_off = n->v.off; + } + else if(type->members[member_idx].kind == TG_MemberKind_Base) + { + Task *t = push_array(scratch.arena, Task, 1); + t->base_off = type->members[member_idx].off + task->base_off; + t->inheritance_chain = tg_key_list_copy(scratch.arena, &task->inheritance_chain); + tg_key_list_push(scratch.arena, &t->inheritance_chain, type->members[member_idx].type_key); + t->type_key = type->members[member_idx].type_key; + t->type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, type->members[member_idx].type_key); + SLLQueuePush(first_task, last_task, t); + members_need_offset_sort = 1; + } + } + } + } + } + + //- rjf: convert to array + TG_MemberArray members = {0}; + { + members.count = members_list.count; + members.v = push_array(arena, TG_Member, members.count); + U64 idx = 0; + for(TG_MemberNode *n = members_list.first; n != 0; n = n->next) + { + MemoryCopyStruct(&members.v[idx], &n->v); + members.v[idx].name = push_str8_copy(arena, members.v[idx].name); + members.v[idx].inheritance_key_chain = tg_key_list_copy(arena, &members.v[idx].inheritance_key_chain); + idx += 1; + } + } + + //- rjf: sort array by offset if needed + if(members_need_offset_sort) + { + quick_sort(members.v, members.count, sizeof(TG_Member), tg_qsort_compare_members_offset); + } + + //- rjf: find all padding instances + typedef struct PaddingNode PaddingNode; + struct PaddingNode + { + PaddingNode *next; + U64 off; + U64 size; + U64 prev_member_idx; + }; + PaddingNode *first_padding = 0; + PaddingNode *last_padding = 0; + U64 padding_count = 0; + if(root_type_kind == TG_Kind_Struct || root_type_kind == TG_Kind_Class) + { + for(U64 idx = 0; idx < members.count; idx += 1) + { + TG_Member *member = &members.v[idx]; + if(idx+1 < members.count) + { + U64 member_byte_size = tg_byte_size_from_graph_rdi_key(graph, rdi, member->type_key); + Rng1U64 member_byte_range = r1u64(member->off, member->off + member_byte_size); + if(member[1].off > member_byte_range.max) + { + PaddingNode *n = push_array(scratch.arena, PaddingNode, 1); + SLLQueuePush(first_padding, last_padding, n); + n->off = member_byte_range.max; + n->size = member[1].off - member_byte_range.max; + n->prev_member_idx = idx; + padding_count += 1; + } + } + } + } + + //- rjf: produce new members array, if we have any padding + if(padding_count != 0) + { + TG_MemberArray new_members = {0}; + new_members.count = members.count + padding_count; + new_members.v = push_array(arena, TG_Member, new_members.count); + MemoryCopy(new_members.v, members.v, sizeof(TG_Member)*members.count); + U64 padding_idx = 0; + for(PaddingNode *n = first_padding; n != 0; n = n->next) + { + if(members.count+padding_idx > n->prev_member_idx+1) + { + MemoryCopy(new_members.v + n->prev_member_idx + padding_idx + 2, + new_members.v + n->prev_member_idx + padding_idx + 1, + sizeof(TG_Member) * (members.count + padding_idx - (n->prev_member_idx + padding_idx + 1))); + } + TG_Member *padding_member = &new_members.v[n->prev_member_idx+padding_idx+1]; + MemoryZeroStruct(padding_member); + padding_member->kind = TG_MemberKind_Padding; + padding_member->type_key = tg_cons_type_make(graph, TG_Kind_Array, tg_key_basic(TG_Kind_U8), n->size); + padding_member->off = n->off; + padding_member->name = str8_lit("padding"); + padding_idx += 1; + } + members = new_members; + } + + scratch_end(scratch); + return members; +} + +internal void +tg_lhs_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key, String8List *out, U32 prec, B32 skip_return) +{ + String8 keyword = {0}; + TG_Kind kind = tg_kind_from_key(key); + switch(kind) + { + default: + { + Temp scratch = scratch_begin(&arena, 1); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + str8_list_push(arena, out, push_str8_copy(arena, type->name)); + str8_list_push(arena, out, str8_lit(" ")); + scratch_end(scratch); + }break; + + case TG_Kind_Bitfield: + { + Temp scratch = scratch_begin(&arena, 1); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + tg_lhs_string_from_key(arena, graph, rdi, type->direct_type_key, out, prec, skip_return); + str8_list_pushf(arena, out, ": %I64u", type->count); + scratch_end(scratch); + }break; + + case TG_Kind_Modifier: + { + Temp scratch = scratch_begin(&arena, 1); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + TG_Key direct = type->direct_type_key; + tg_lhs_string_from_key(arena, graph, rdi, direct, out, 1, skip_return); + if(type->flags & TG_Flag_Const) + { + str8_list_push(arena, out, str8_lit("const ")); + } + if(type->flags & TG_Flag_Volatile) + { + str8_list_push(arena, out, str8_lit("volatile ")); + } + scratch_end(scratch); + }break; + + case TG_Kind_Variadic: + { + str8_list_push(arena, out, str8_lit("...")); + }break; + + case TG_Kind_Struct: + case TG_Kind_Union: + case TG_Kind_Enum: + case TG_Kind_Class: + case TG_Kind_Alias: + { + Temp scratch = scratch_begin(&arena, 1); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + str8_list_push(arena, out, push_str8_copy(arena, type->name)); + str8_list_push(arena, out, str8_lit(" ")); + scratch_end(scratch); + }break; + + case TG_Kind_IncompleteStruct: keyword = str8_lit("struct"); goto fwd_udt; + case TG_Kind_IncompleteUnion: keyword = str8_lit("union"); goto fwd_udt; + case TG_Kind_IncompleteEnum: keyword = str8_lit("enum"); goto fwd_udt; + case TG_Kind_IncompleteClass: keyword = str8_lit("class"); goto fwd_udt; + fwd_udt:; + { + Temp scratch = scratch_begin(&arena, 1); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + str8_list_push(arena, out, keyword); + str8_list_push(arena, out, str8_lit(" ")); + str8_list_push(arena, out, push_str8_copy(arena, type->name)); + str8_list_push(arena, out, str8_lit(" ")); + scratch_end(scratch); + }break; + + case TG_Kind_Array: + { + TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); + tg_lhs_string_from_key(arena, graph, rdi, direct, out, 2, skip_return); + if(prec == 1) + { + str8_list_push(arena, out, str8_lit("(")); + } + }break; + + case TG_Kind_Function: + { + if(!skip_return) + { + TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); + tg_lhs_string_from_key(arena, graph, rdi, direct, out, 2, 0); + } + if(prec == 1) + { + str8_list_push(arena, out, str8_lit("(")); + } + }break; + + case TG_Kind_Ptr: + { + TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); + tg_lhs_string_from_key(arena, graph, rdi, direct, out, 1, skip_return); + str8_list_push(arena, out, str8_lit("*")); + }break; + + case TG_Kind_LRef: + { + TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); + tg_lhs_string_from_key(arena, graph, rdi, direct, out, 1, skip_return); + str8_list_push(arena, out, str8_lit("&")); + }break; + + case TG_Kind_RRef: + { + TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); + tg_lhs_string_from_key(arena, graph, rdi, direct, out, 1, skip_return); + str8_list_push(arena, out, str8_lit("&&")); + }break; + + case TG_Kind_MemberPtr: + { + Temp scratch = scratch_begin(&arena, 1); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + TG_Key direct = type->direct_type_key; + tg_lhs_string_from_key(arena, graph, rdi, direct, out, 1, skip_return); + TG_Type *container = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, type->owner_type_key); + if(container->kind != TG_Kind_Null) + { + str8_list_push(arena, out, push_str8_copy(arena, container->name)); + } + else + { + str8_list_push(arena, out, str8_lit("")); + } + str8_list_push(arena, out, str8_lit("::*")); + scratch_end(scratch); + }break; + } +} + +internal void +tg_rhs_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key, String8List *out, U32 prec) +{ + TG_Kind kind = tg_kind_from_key(key); + switch(kind) + { + default:{}break; + + case TG_Kind_Bitfield: + { + TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); + tg_rhs_string_from_key(arena, graph, rdi, direct, out, prec); + }break; + + case TG_Kind_Modifier: + case TG_Kind_Ptr: + case TG_Kind_LRef: + case TG_Kind_RRef: + case TG_Kind_MemberPtr: + { + TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); + tg_rhs_string_from_key(arena, graph, rdi, direct, out, 1); + }break; + + case TG_Kind_Array: + { + Temp scratch = scratch_begin(&arena, 1); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + if(prec == 1) + { + str8_list_push(arena, out, str8_lit(")")); + } + String8 count_str = str8_from_u64(arena, type->count, 10, 0, 0); + str8_list_push(arena, out, str8_lit("[")); + str8_list_push(arena, out, count_str); + str8_list_push(arena, out, str8_lit("]")); + TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); + tg_rhs_string_from_key(arena, graph, rdi, direct, out, 2); + scratch_end(scratch); + }break; + + case TG_Kind_Function: + { + Temp scratch = scratch_begin(&arena, 1); + TG_Type *type = tg_type_from_graph_rdi_key(scratch.arena, graph, rdi, key); + if(prec == 1) + { + str8_list_push(arena, out, str8_lit(")")); + } + + // parameters + if(type->count == 0) + { + str8_list_push(arena, out, str8_lit("(void)")); + } + else + { + str8_list_push(arena, out, str8_lit("(")); + U64 param_count = type->count; + TG_Key *param_type_keys = type->param_type_keys; + for(U64 param_idx = 0; param_idx < param_count; param_idx += 1) + { + TG_Key param_type_key = param_type_keys[param_idx]; + String8 param_str = tg_string_from_key(arena, graph, rdi, param_type_key); + String8 param_str_trimmed = str8_skip_chop_whitespace(param_str); + str8_list_push(arena, out, param_str_trimmed); + if(param_idx+1 < param_count) + { + str8_list_push(arena, out, str8_lit(", ")); + } + } + str8_list_push(arena, out, str8_lit(")")); + } + TG_Key direct = tg_direct_from_graph_rdi_key(graph, rdi, key); + tg_rhs_string_from_key(arena, graph, rdi, direct, out, 2); + scratch_end(scratch); + }break; + } +} + +internal String8 +tg_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key) +{ + Temp scratch = scratch_begin(&arena, 1); + String8List list = {0}; + tg_lhs_string_from_key(scratch.arena, graph, rdi, key, &list, 0, 0); + tg_rhs_string_from_key(scratch.arena, graph, rdi, key, &list, 0); + String8 result = str8_list_join(arena, &list, 0); + scratch_end(scratch); + return result; +} diff --git a/src/type_graph/type_graph.h b/src/type_graph/type_graph.h index 7299a1c9..61ecee41 100644 --- a/src/type_graph/type_graph.h +++ b/src/type_graph/type_graph.h @@ -1,243 +1,243 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef TYPE_GRAPH_H -#define TYPE_GRAPH_H - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/type_graph.meta.h" - -//////////////////////////////// -//~ rjf: Key Types - -typedef enum TG_KeyKind -{ - TG_KeyKind_Null, - TG_KeyKind_Basic, - TG_KeyKind_Ext, - TG_KeyKind_Cons, - TG_KeyKind_Reg, - TG_KeyKind_RegAlias, -} -TG_KeyKind; - -typedef struct TG_Key TG_Key; -struct TG_Key -{ - TG_KeyKind kind; - U32 u32[1]; // basic -> type_kind; cons -> type_kind; ext -> type_kind; reg -> arch - U64 u64[1]; // ext -> unique id; cons -> idx; reg -> code -}; - -typedef struct TG_KeyNode TG_KeyNode; -struct TG_KeyNode -{ - TG_KeyNode *next; - TG_Key v; -}; - -typedef struct TG_KeyList TG_KeyList; -struct TG_KeyList -{ - TG_KeyNode *first; - TG_KeyNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Graph Types - -typedef struct TG_ConsType TG_ConsType; -struct TG_ConsType -{ - TG_Kind kind; - TG_Key direct_type_key; - U64 u64; -}; - -typedef struct TG_Node TG_Node; -struct TG_Node -{ - TG_Node *key_hash_next; - TG_Node *content_hash_next; - TG_Key key; - TG_ConsType cons_type; -}; - -typedef struct TG_Slot TG_Slot; -struct TG_Slot -{ - TG_Node *first; - TG_Node *last; -}; - -typedef struct TG_Graph TG_Graph; -struct TG_Graph -{ - U64 address_size; - U64 cons_id_gen; - U64 content_hash_slots_count; - TG_Slot *content_hash_slots; - U64 key_hash_slots_count; - TG_Slot *key_hash_slots; -}; - -//////////////////////////////// -//~ rjf: Extracted Info Types - -typedef enum TG_MemberKind -{ - TG_MemberKind_Null, - TG_MemberKind_DataField, - TG_MemberKind_StaticData, - TG_MemberKind_Method, - TG_MemberKind_StaticMethod, - TG_MemberKind_VirtualMethod, - TG_MemberKind_VTablePtr, - TG_MemberKind_Base, - TG_MemberKind_VirtualBase, - TG_MemberKind_NestedType, - TG_MemberKind_Padding, - TG_MemberKind_COUNT -} -TG_MemberKind; - -typedef U32 TG_Flags; -enum -{ - TG_Flag_Const = (1<<0), - TG_Flag_Volatile = (1<<1), -}; - -typedef struct TG_Member TG_Member; -struct TG_Member -{ - TG_MemberKind kind; - TG_Key type_key; - String8 name; - U64 off; - TG_KeyList inheritance_key_chain; -}; - -typedef struct TG_MemberNode TG_MemberNode; -struct TG_MemberNode -{ - TG_MemberNode *next; - TG_Member v; -}; - -typedef struct TG_MemberList TG_MemberList; -struct TG_MemberList -{ - TG_MemberNode *first; - TG_MemberNode *last; - U64 count; -}; - -typedef struct TG_MemberArray TG_MemberArray; -struct TG_MemberArray -{ - TG_Member *v; - U64 count; -}; - -typedef struct TG_EnumVal TG_EnumVal; -struct TG_EnumVal -{ - String8 name; - U64 val; -}; - -typedef struct TG_EnumValArray TG_EnumValArray; -struct TG_EnumValArray -{ - TG_EnumVal *v; - U64 count; -}; - -typedef struct TG_Type TG_Type; -struct TG_Type -{ - TG_Kind kind; - TG_Flags flags; - String8 name; - U64 byte_size; - U64 count; - U32 off; - TG_Key direct_type_key; - TG_Key owner_type_key; - TG_Key *param_type_keys; - TG_Member *members; - TG_EnumVal *enum_vals; -}; - -//////////////////////////////// -//~ rjf: Globals - -global read_only TG_Type tg_type_nil = -{ - /* kind */ TG_Kind_Null, - /* flags */ 0, - /* name */ {(U8*)"???",3}, -}; - -global read_only TG_Type tg_type_variadic = -{ - /* kind */ TG_Kind_Variadic, - /* flags */ 0, - /* name */ {(U8*)"...",3}, -}; - -thread_static Arena *tg_build_arena = 0; - -//////////////////////////////// -//~ rjf: Basic Helpers - -internal U64 tg_hash_from_string(U64 seed, String8 string); -internal int tg_qsort_compare_members_offset(TG_Member *a, TG_Member *b); -internal void tg_key_list_push(Arena *arena, TG_KeyList *list, TG_Key key); -internal TG_KeyList tg_key_list_copy(Arena *arena, TG_KeyList *src); - -//////////////////////////////// -//~ rjf: RADDBG <-> TG Enum Conversions - -internal TG_Kind tg_kind_from_rdi_type_kind(RDI_TypeKind kind); -internal TG_MemberKind tg_member_kind_from_rdi_member_kind(RDI_MemberKind kind); - -//////////////////////////////// -//~ rjf: Key Type Functions - -internal TG_Key tg_key_zero(void); -internal TG_Key tg_key_basic(TG_Kind kind); -internal TG_Key tg_key_ext(TG_Kind kind, U64 id); -internal TG_Key tg_key_reg(Architecture arch, REGS_RegCode code); -internal TG_Key tg_key_reg_alias(Architecture arch, REGS_AliasCode code); -internal B32 tg_key_match(TG_Key a, TG_Key b); - -//////////////////////////////// -//~ rjf: Graph Construction API - -internal TG_Graph *tg_graph_begin(U64 address_size, U64 slot_count); -internal TG_Key tg_cons_type_make(TG_Graph *graph, TG_Kind kind, TG_Key direct_type_key, U64 u64); - -//////////////////////////////// -//~ rjf: Graph Introspection API - -internal TG_Type *tg_type_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal TG_Key tg_direct_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal TG_Key tg_unwrapped_direct_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal TG_Key tg_owner_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal TG_Key tg_ptee_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal TG_Key tg_unwrapped_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal U64 tg_byte_size_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal TG_Kind tg_kind_from_key(TG_Key key); -internal TG_Member *tg_member_copy(Arena *arena, TG_Member *src); -internal TG_MemberArray tg_members_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal TG_MemberArray tg_data_members_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); -internal void tg_lhs_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key, String8List *out, U32 prec, B32 skip_return); -internal void tg_rhs_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key, String8List *out, U32 prec); -internal String8 tg_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); - -#endif // TYPE_GRAPH_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef TYPE_GRAPH_H +#define TYPE_GRAPH_H + +//////////////////////////////// +//~ rjf: Generated Code + +#include "generated/type_graph.meta.h" + +//////////////////////////////// +//~ rjf: Key Types + +typedef enum TG_KeyKind +{ + TG_KeyKind_Null, + TG_KeyKind_Basic, + TG_KeyKind_Ext, + TG_KeyKind_Cons, + TG_KeyKind_Reg, + TG_KeyKind_RegAlias, +} +TG_KeyKind; + +typedef struct TG_Key TG_Key; +struct TG_Key +{ + TG_KeyKind kind; + U32 u32[1]; // basic -> type_kind; cons -> type_kind; ext -> type_kind; reg -> arch + U64 u64[1]; // ext -> unique id; cons -> idx; reg -> code +}; + +typedef struct TG_KeyNode TG_KeyNode; +struct TG_KeyNode +{ + TG_KeyNode *next; + TG_Key v; +}; + +typedef struct TG_KeyList TG_KeyList; +struct TG_KeyList +{ + TG_KeyNode *first; + TG_KeyNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Graph Types + +typedef struct TG_ConsType TG_ConsType; +struct TG_ConsType +{ + TG_Kind kind; + TG_Key direct_type_key; + U64 u64; +}; + +typedef struct TG_Node TG_Node; +struct TG_Node +{ + TG_Node *key_hash_next; + TG_Node *content_hash_next; + TG_Key key; + TG_ConsType cons_type; +}; + +typedef struct TG_Slot TG_Slot; +struct TG_Slot +{ + TG_Node *first; + TG_Node *last; +}; + +typedef struct TG_Graph TG_Graph; +struct TG_Graph +{ + U64 address_size; + U64 cons_id_gen; + U64 content_hash_slots_count; + TG_Slot *content_hash_slots; + U64 key_hash_slots_count; + TG_Slot *key_hash_slots; +}; + +//////////////////////////////// +//~ rjf: Extracted Info Types + +typedef enum TG_MemberKind +{ + TG_MemberKind_Null, + TG_MemberKind_DataField, + TG_MemberKind_StaticData, + TG_MemberKind_Method, + TG_MemberKind_StaticMethod, + TG_MemberKind_VirtualMethod, + TG_MemberKind_VTablePtr, + TG_MemberKind_Base, + TG_MemberKind_VirtualBase, + TG_MemberKind_NestedType, + TG_MemberKind_Padding, + TG_MemberKind_COUNT +} +TG_MemberKind; + +typedef U32 TG_Flags; +enum +{ + TG_Flag_Const = (1<<0), + TG_Flag_Volatile = (1<<1), +}; + +typedef struct TG_Member TG_Member; +struct TG_Member +{ + TG_MemberKind kind; + TG_Key type_key; + String8 name; + U64 off; + TG_KeyList inheritance_key_chain; +}; + +typedef struct TG_MemberNode TG_MemberNode; +struct TG_MemberNode +{ + TG_MemberNode *next; + TG_Member v; +}; + +typedef struct TG_MemberList TG_MemberList; +struct TG_MemberList +{ + TG_MemberNode *first; + TG_MemberNode *last; + U64 count; +}; + +typedef struct TG_MemberArray TG_MemberArray; +struct TG_MemberArray +{ + TG_Member *v; + U64 count; +}; + +typedef struct TG_EnumVal TG_EnumVal; +struct TG_EnumVal +{ + String8 name; + U64 val; +}; + +typedef struct TG_EnumValArray TG_EnumValArray; +struct TG_EnumValArray +{ + TG_EnumVal *v; + U64 count; +}; + +typedef struct TG_Type TG_Type; +struct TG_Type +{ + TG_Kind kind; + TG_Flags flags; + String8 name; + U64 byte_size; + U64 count; + U32 off; + TG_Key direct_type_key; + TG_Key owner_type_key; + TG_Key *param_type_keys; + TG_Member *members; + TG_EnumVal *enum_vals; +}; + +//////////////////////////////// +//~ rjf: Globals + +global read_only TG_Type tg_type_nil = +{ + /* kind */ TG_Kind_Null, + /* flags */ 0, + /* name */ {(U8*)"???",3}, +}; + +global read_only TG_Type tg_type_variadic = +{ + /* kind */ TG_Kind_Variadic, + /* flags */ 0, + /* name */ {(U8*)"...",3}, +}; + +thread_static Arena *tg_build_arena = 0; + +//////////////////////////////// +//~ rjf: Basic Helpers + +internal U64 tg_hash_from_string(U64 seed, String8 string); +internal int tg_qsort_compare_members_offset(TG_Member *a, TG_Member *b); +internal void tg_key_list_push(Arena *arena, TG_KeyList *list, TG_Key key); +internal TG_KeyList tg_key_list_copy(Arena *arena, TG_KeyList *src); + +//////////////////////////////// +//~ rjf: RADDBG <-> TG Enum Conversions + +internal TG_Kind tg_kind_from_rdi_type_kind(RDI_TypeKind kind); +internal TG_MemberKind tg_member_kind_from_rdi_member_kind(RDI_MemberKind kind); + +//////////////////////////////// +//~ rjf: Key Type Functions + +internal TG_Key tg_key_zero(void); +internal TG_Key tg_key_basic(TG_Kind kind); +internal TG_Key tg_key_ext(TG_Kind kind, U64 id); +internal TG_Key tg_key_reg(Architecture arch, REGS_RegCode code); +internal TG_Key tg_key_reg_alias(Architecture arch, REGS_AliasCode code); +internal B32 tg_key_match(TG_Key a, TG_Key b); + +//////////////////////////////// +//~ rjf: Graph Construction API + +internal TG_Graph *tg_graph_begin(U64 address_size, U64 slot_count); +internal TG_Key tg_cons_type_make(TG_Graph *graph, TG_Kind kind, TG_Key direct_type_key, U64 u64); + +//////////////////////////////// +//~ rjf: Graph Introspection API + +internal TG_Type *tg_type_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal TG_Key tg_direct_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal TG_Key tg_unwrapped_direct_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal TG_Key tg_owner_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal TG_Key tg_ptee_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal TG_Key tg_unwrapped_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal U64 tg_byte_size_from_graph_rdi_key(TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal TG_Kind tg_kind_from_key(TG_Key key); +internal TG_Member *tg_member_copy(Arena *arena, TG_Member *src); +internal TG_MemberArray tg_members_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal TG_MemberArray tg_data_members_from_graph_rdi_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); +internal void tg_lhs_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key, String8List *out, U32 prec, B32 skip_return); +internal void tg_rhs_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key, String8List *out, U32 prec); +internal String8 tg_string_from_key(Arena *arena, TG_Graph *graph, RDI_Parsed *rdi, TG_Key key); + +#endif // TYPE_GRAPH_H diff --git a/src/type_graph/type_graph.mdesk b/src/type_graph/type_graph.mdesk index ec342cdf..73eb9f3d 100644 --- a/src/type_graph/type_graph.mdesk +++ b/src/type_graph/type_graph.mdesk @@ -1,94 +1,94 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Tables - -@table(name basic_string basic_byte_size) -// NOTE(rjf): basic_byte_size == 0xFF? => address sized -TG_KindTable: -{ - {Null "" 0 } - {Void "void" 0 } - {Handle "HANDLE" 0xFF } - {Char8 "char8" 1 } - {Char16 "char16" 2 } - {Char32 "char32" 4 } - {UChar8 "uchar8" 1 } - {UChar16 "uchar16" 2 } - {UChar32 "uchar32" 4 } - {U8 "U8" 1 } - {U16 "U16" 2 } - {U32 "U32" 4 } - {U64 "U64" 8 } - {U128 "U128" 16 } - {U256 "U256" 32 } - {U512 "U512" 64 } - {S8 "S8" 1 } - {S16 "S16" 2 } - {S32 "S32" 4 } - {S64 "S64" 8 } - {S128 "S128" 16 } - {S256 "S256" 32 } - {S512 "S512" 64 } - {Bool "bool" 1 } - {F16 "F16" 2 } - {F32 "F32" 4 } - {F32PP "F32PP" 4 } - {F48 "F48" 6 } - {F64 "F64" 8 } - {F80 "F80" 10 } - {F128 "F128" 16 } - {ComplexF32 "ComplexF32" 8 } - {ComplexF64 "ComplexF64" 16 } - {ComplexF80 "ComplexF80" 20 } - {ComplexF128 "ComplexF128" 32 } - {Modifier "" 0 } - {Ptr "" 0 } - {LRef "" 0 } - {RRef "" 0 } - {Array "" 0 } - {Function "" 0 } - {Method "" 0 } - {MemberPtr "" 0 } - {Struct "struct" 0 } - {Class "class" 0 } - {Union "union" 0 } - {Enum "enum" 0 } - {Alias "typedef" 0 } - {IncompleteStruct "struct" 0 } - {IncompleteUnion "union" 0 } - {IncompleteClass "class" 0 } - {IncompleteEnum "enum" 0 } - {Bitfield "" 0 } - {Variadic "" 0 } -} - -//////////////////////////////// -//~ rjf: Generators - -@enum TG_Kind: -{ - @expand(TG_KindTable a) `$(a.name)`, - COUNT, - `FirstBasic = TG_Kind_Void`, - `LastBasic = TG_Kind_ComplexF128`, - `FirstInteger = TG_Kind_Char8`, - `LastInteger = TG_Kind_S512`, - `FirstSigned1 = TG_Kind_Char8`, - `LastSigned1 = TG_Kind_Char32`, - `FirstSigned2 = TG_Kind_S8`, - `LastSigned2 = TG_Kind_S512`, - `FirstIncomplete = TG_Kind_IncompleteStruct`, - `LastIncomplete = TG_Kind_IncompleteEnum`, -} - -@data(U8) tg_kind_basic_byte_size_table: -{ - @expand(TG_KindTable a) `$(a.basic_byte_size)`; -} - -@data(String8) tg_kind_basic_string_table: -{ - @expand(TG_KindTable a) `str8_lit_comp("$(a.basic_string)")`; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Tables + +@table(name basic_string basic_byte_size) +// NOTE(rjf): basic_byte_size == 0xFF? => address sized +TG_KindTable: +{ + {Null "" 0 } + {Void "void" 0 } + {Handle "HANDLE" 0xFF } + {Char8 "char8" 1 } + {Char16 "char16" 2 } + {Char32 "char32" 4 } + {UChar8 "uchar8" 1 } + {UChar16 "uchar16" 2 } + {UChar32 "uchar32" 4 } + {U8 "U8" 1 } + {U16 "U16" 2 } + {U32 "U32" 4 } + {U64 "U64" 8 } + {U128 "U128" 16 } + {U256 "U256" 32 } + {U512 "U512" 64 } + {S8 "S8" 1 } + {S16 "S16" 2 } + {S32 "S32" 4 } + {S64 "S64" 8 } + {S128 "S128" 16 } + {S256 "S256" 32 } + {S512 "S512" 64 } + {Bool "bool" 1 } + {F16 "F16" 2 } + {F32 "F32" 4 } + {F32PP "F32PP" 4 } + {F48 "F48" 6 } + {F64 "F64" 8 } + {F80 "F80" 10 } + {F128 "F128" 16 } + {ComplexF32 "ComplexF32" 8 } + {ComplexF64 "ComplexF64" 16 } + {ComplexF80 "ComplexF80" 20 } + {ComplexF128 "ComplexF128" 32 } + {Modifier "" 0 } + {Ptr "" 0 } + {LRef "" 0 } + {RRef "" 0 } + {Array "" 0 } + {Function "" 0 } + {Method "" 0 } + {MemberPtr "" 0 } + {Struct "struct" 0 } + {Class "class" 0 } + {Union "union" 0 } + {Enum "enum" 0 } + {Alias "typedef" 0 } + {IncompleteStruct "struct" 0 } + {IncompleteUnion "union" 0 } + {IncompleteClass "class" 0 } + {IncompleteEnum "enum" 0 } + {Bitfield "" 0 } + {Variadic "" 0 } +} + +//////////////////////////////// +//~ rjf: Generators + +@enum TG_Kind: +{ + @expand(TG_KindTable a) `$(a.name)`, + COUNT, + `FirstBasic = TG_Kind_Void`, + `LastBasic = TG_Kind_ComplexF128`, + `FirstInteger = TG_Kind_Char8`, + `LastInteger = TG_Kind_S512`, + `FirstSigned1 = TG_Kind_Char8`, + `LastSigned1 = TG_Kind_Char32`, + `FirstSigned2 = TG_Kind_S8`, + `LastSigned2 = TG_Kind_S512`, + `FirstIncomplete = TG_Kind_IncompleteStruct`, + `LastIncomplete = TG_Kind_IncompleteEnum`, +} + +@data(U8) tg_kind_basic_byte_size_table: +{ + @expand(TG_KindTable a) `$(a.basic_byte_size)`; +} + +@data(String8) tg_kind_basic_string_table: +{ + @expand(TG_KindTable a) `str8_lit_comp("$(a.basic_string)")`; +} diff --git a/src/ui/ui.mdesk b/src/ui/ui.mdesk index 8740fe4b..cd25c6d0 100644 --- a/src/ui/ui.mdesk +++ b/src/ui/ui.mdesk @@ -1,159 +1,159 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//- rjf: stack table - -@table(name, name_lower, type, default) -UI_StackTable: -{ - //- rjf: parents - { Parent parent `UI_Box *` `&ui_g_nil_box` } - - //- rjf: layout params - { ChildLayoutAxis child_layout_axis Axis2 `Axis2_X` } - - //- rjf: size/position - { FixedX fixed_x F32 0 } - { FixedY fixed_y F32 0 } - { FixedWidth fixed_width F32 0 } - { FixedHeight fixed_height F32 0 } - { PrefWidth pref_width UI_Size `ui_px(250.f, 1.f)` } - { PrefHeight pref_height UI_Size `ui_px(30.f, 1.f)` } - - //- rjf: flags - { Flags flags UI_BoxFlags 0 } - - //- rjf: interaction - { FocusHot focus_hot UI_FocusKind UI_FocusKind_Null } - { FocusActive focus_active UI_FocusKind UI_FocusKind_Null } - { FastpathCodepoint fastpath_codepoint U32 0 } - { GroupKey group_key UI_Key `ui_key_zero()` } - - //- rjf: colors - { Transparency transparency F32 0 } - { Palette palette `UI_Palette* ` `&ui_g_nil_palette` } - - //- rjf: squish - { Squish squish F32 0 } - - //- rjf: hover cursor - { HoverCursor hover_cursor OS_Cursor OS_Cursor_Pointer } - - //- rjf: font - { Font font F_Tag `f_tag_zero()` } - { FontSize font_size F32 24.f } - { TextRasterFlags text_raster_flags F_RasterFlags F_RasterFlag_Hinted } - { TabSize tab_size F32 `24.f*4.f` } - - //- rjf: corner radii - { CornerRadius00 corner_radius_00 F32 0 } - { CornerRadius01 corner_radius_01 F32 0 } - { CornerRadius10 corner_radius_10 F32 0 } - { CornerRadius11 corner_radius_11 F32 0 } - - //- rjf: blur size - { BlurSize blur_size F32 0 } - - //- rjf: text parameters - { TextPadding text_padding F32 0 } - { TextAlignment text_alignment UI_TextAlign UI_TextAlign_Left } -} - -//- rjf: declaring stack node types - -@gen -{ - @expand(UI_StackTable a) `typedef struct UI_$(a.name)Node UI_$(a.name)Node; struct UI_$(a.name)Node{UI_$(a.name)Node *next; $(a.type) v;};` -} - -//- rjf: declaring all default stack tops - -@gen -{ - `#define UI_DeclStackNils \\`; - `struct\\`; - `{\\`; - @expand(UI_StackTable a) `UI_$(a.name)Node $(a.name_lower)_nil_stack_top;\\`; - `}`; -} - -//- rjf: initializing all default stack tops - -@gen -{ - `#define UI_InitStackNils(state) \\`; - @expand(UI_StackTable a) `state->$(a.name_lower)_nil_stack_top.v = $(a.default);\\`; - ``; -} - -//- rjf: declaring all stack nodes & free lists - -@gen -{ - `#define UI_DeclStacks \\`; - `struct\\`; - `{\\`; - @expand(UI_StackTable a) `struct { UI_$(a.name)Node *top; $(a.type) bottom_val; UI_$(a.name)Node *free; B32 auto_pop; } $(a.name_lower)_stack;\\`; - `}`; -} - -//- rjf: initing all stack nodes - -@gen -{ - `#define UI_InitStacks(state) \\`; - @expand(UI_StackTable a) `state->$(a.name_lower)_stack.top = &state->$(a.name_lower)_nil_stack_top; state->$(a.name_lower)_stack.bottom_val = $(a.default); state->$(a.name_lower)_stack.free = 0; state->$(a.name_lower)_stack.auto_pop = 0;\\`; - ``; -} - -//- rjf: auto-popping all stacks - -@gen -{ - `#define UI_AutoPopStacks(state) \\` - @expand(UI_StackTable a) - `if(state->$(a.name_lower)_stack.auto_pop) { ui_pop_$(a.name_lower)(); state->$(a.name_lower)_stack.auto_pop = 0; }\\`; - ``; -} - -//- rjf: decls for the stack function operation headers - -@gen -{ - @expand(UI_StackTable a) - `internal $(a.type) $(=>35) ui_top_$(a.name_lower)(void);` - @expand(UI_StackTable a) - `internal $(a.type) $(=>35) ui_bottom_$(a.name_lower)(void);` - @expand(UI_StackTable a) - `internal $(a.type) $(=>35) ui_push_$(a.name_lower)($(a.type) v);` - @expand(UI_StackTable a) - `internal $(a.type) $(=>35) ui_pop_$(a.name_lower)(void);` - @expand(UI_StackTable a) - `internal $(a.type) $(=>35) ui_set_next_$(a.name_lower)($(a.type) v);` -} - -//- rjf: defer-loop helpers - -@gen @c_file -{ - `#if 0`; - @expand(UI_StackTable a) - `#define UI_$(a.name)(v) DeferLoop(ui_push_$(a.name_lower)(v), ui_pop_$(a.name_lower)())` - `#endif`; -} - -//- rjf: decls for the stack operation implementations - -@gen @c_file -{ - @expand(UI_StackTable a) - `internal $(a.type) ui_top_$(a.name_lower)(void) { UI_StackTopImpl(ui_state, $(a.name), $(a.name_lower)) }`; - @expand(UI_StackTable a) - `internal $(a.type) ui_bottom_$(a.name_lower)(void) { UI_StackBottomImpl(ui_state, $(a.name), $(a.name_lower)) }`; - @expand(UI_StackTable a) - `internal $(a.type) ui_push_$(a.name_lower)($(a.type) v) { UI_StackPushImpl(ui_state, $(a.name), $(a.name_lower), $(a.type), v) }`; - @expand(UI_StackTable a) - `internal $(a.type) ui_pop_$(a.name_lower)(void) { UI_StackPopImpl(ui_state, $(a.name), $(a.name_lower)) }`; - @expand(UI_StackTable a) - `internal $(a.type) ui_set_next_$(a.name_lower)($(a.type) v) { UI_StackSetNextImpl(ui_state, $(a.name), $(a.name_lower), $(a.type), v) }`; -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//- rjf: stack table + +@table(name, name_lower, type, default) +UI_StackTable: +{ + //- rjf: parents + { Parent parent `UI_Box *` `&ui_g_nil_box` } + + //- rjf: layout params + { ChildLayoutAxis child_layout_axis Axis2 `Axis2_X` } + + //- rjf: size/position + { FixedX fixed_x F32 0 } + { FixedY fixed_y F32 0 } + { FixedWidth fixed_width F32 0 } + { FixedHeight fixed_height F32 0 } + { PrefWidth pref_width UI_Size `ui_px(250.f, 1.f)` } + { PrefHeight pref_height UI_Size `ui_px(30.f, 1.f)` } + + //- rjf: flags + { Flags flags UI_BoxFlags 0 } + + //- rjf: interaction + { FocusHot focus_hot UI_FocusKind UI_FocusKind_Null } + { FocusActive focus_active UI_FocusKind UI_FocusKind_Null } + { FastpathCodepoint fastpath_codepoint U32 0 } + { GroupKey group_key UI_Key `ui_key_zero()` } + + //- rjf: colors + { Transparency transparency F32 0 } + { Palette palette `UI_Palette* ` `&ui_g_nil_palette` } + + //- rjf: squish + { Squish squish F32 0 } + + //- rjf: hover cursor + { HoverCursor hover_cursor OS_Cursor OS_Cursor_Pointer } + + //- rjf: font + { Font font F_Tag `f_tag_zero()` } + { FontSize font_size F32 24.f } + { TextRasterFlags text_raster_flags F_RasterFlags F_RasterFlag_Hinted } + { TabSize tab_size F32 `24.f*4.f` } + + //- rjf: corner radii + { CornerRadius00 corner_radius_00 F32 0 } + { CornerRadius01 corner_radius_01 F32 0 } + { CornerRadius10 corner_radius_10 F32 0 } + { CornerRadius11 corner_radius_11 F32 0 } + + //- rjf: blur size + { BlurSize blur_size F32 0 } + + //- rjf: text parameters + { TextPadding text_padding F32 0 } + { TextAlignment text_alignment UI_TextAlign UI_TextAlign_Left } +} + +//- rjf: declaring stack node types + +@gen +{ + @expand(UI_StackTable a) `typedef struct UI_$(a.name)Node UI_$(a.name)Node; struct UI_$(a.name)Node{UI_$(a.name)Node *next; $(a.type) v;};` +} + +//- rjf: declaring all default stack tops + +@gen +{ + `#define UI_DeclStackNils \\`; + `struct\\`; + `{\\`; + @expand(UI_StackTable a) `UI_$(a.name)Node $(a.name_lower)_nil_stack_top;\\`; + `}`; +} + +//- rjf: initializing all default stack tops + +@gen +{ + `#define UI_InitStackNils(state) \\`; + @expand(UI_StackTable a) `state->$(a.name_lower)_nil_stack_top.v = $(a.default);\\`; + ``; +} + +//- rjf: declaring all stack nodes & free lists + +@gen +{ + `#define UI_DeclStacks \\`; + `struct\\`; + `{\\`; + @expand(UI_StackTable a) `struct { UI_$(a.name)Node *top; $(a.type) bottom_val; UI_$(a.name)Node *free; B32 auto_pop; } $(a.name_lower)_stack;\\`; + `}`; +} + +//- rjf: initing all stack nodes + +@gen +{ + `#define UI_InitStacks(state) \\`; + @expand(UI_StackTable a) `state->$(a.name_lower)_stack.top = &state->$(a.name_lower)_nil_stack_top; state->$(a.name_lower)_stack.bottom_val = $(a.default); state->$(a.name_lower)_stack.free = 0; state->$(a.name_lower)_stack.auto_pop = 0;\\`; + ``; +} + +//- rjf: auto-popping all stacks + +@gen +{ + `#define UI_AutoPopStacks(state) \\` + @expand(UI_StackTable a) + `if(state->$(a.name_lower)_stack.auto_pop) { ui_pop_$(a.name_lower)(); state->$(a.name_lower)_stack.auto_pop = 0; }\\`; + ``; +} + +//- rjf: decls for the stack function operation headers + +@gen +{ + @expand(UI_StackTable a) + `internal $(a.type) $(=>35) ui_top_$(a.name_lower)(void);` + @expand(UI_StackTable a) + `internal $(a.type) $(=>35) ui_bottom_$(a.name_lower)(void);` + @expand(UI_StackTable a) + `internal $(a.type) $(=>35) ui_push_$(a.name_lower)($(a.type) v);` + @expand(UI_StackTable a) + `internal $(a.type) $(=>35) ui_pop_$(a.name_lower)(void);` + @expand(UI_StackTable a) + `internal $(a.type) $(=>35) ui_set_next_$(a.name_lower)($(a.type) v);` +} + +//- rjf: defer-loop helpers + +@gen @c_file +{ + `#if 0`; + @expand(UI_StackTable a) + `#define UI_$(a.name)(v) DeferLoop(ui_push_$(a.name_lower)(v), ui_pop_$(a.name_lower)())` + `#endif`; +} + +//- rjf: decls for the stack operation implementations + +@gen @c_file +{ + @expand(UI_StackTable a) + `internal $(a.type) ui_top_$(a.name_lower)(void) { UI_StackTopImpl(ui_state, $(a.name), $(a.name_lower)) }`; + @expand(UI_StackTable a) + `internal $(a.type) ui_bottom_$(a.name_lower)(void) { UI_StackBottomImpl(ui_state, $(a.name), $(a.name_lower)) }`; + @expand(UI_StackTable a) + `internal $(a.type) ui_push_$(a.name_lower)($(a.type) v) { UI_StackPushImpl(ui_state, $(a.name), $(a.name_lower), $(a.type), v) }`; + @expand(UI_StackTable a) + `internal $(a.type) ui_pop_$(a.name_lower)(void) { UI_StackPopImpl(ui_state, $(a.name), $(a.name_lower)) }`; + @expand(UI_StackTable a) + `internal $(a.type) ui_set_next_$(a.name_lower)($(a.type) v) { UI_StackSetNextImpl(ui_state, $(a.name), $(a.name_lower), $(a.type), v) }`; +} diff --git a/src/ui/ui_basic_widgets.c b/src/ui/ui_basic_widgets.c index 6c73ec30..2d051fe7 100644 --- a/src/ui/ui_basic_widgets.c +++ b/src/ui/ui_basic_widgets.c @@ -1,1522 +1,1522 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -//////////////////////////////// -//~ rjf: Basic Widgets - -internal void -ui_divider(UI_Size size) -{ - UI_Box *parent = ui_top_parent(); - ui_set_next_pref_size(parent->child_layout_axis, size); - ui_set_next_child_layout_axis(parent->child_layout_axis); - UI_Box *box = ui_build_box_from_key(0, ui_key_zero()); - UI_Parent(box) UI_PrefSize(parent->child_layout_axis, ui_pct(1, 0)) - { - ui_build_box_from_key(UI_BoxFlag_DrawSideBottom, ui_key_zero()); - ui_build_box_from_key(0, ui_key_zero()); - } -} - -internal UI_Signal -ui_label(String8 string) -{ - UI_Box *box = ui_build_box_from_string(UI_BoxFlag_DrawText, str8_zero()); - ui_box_equip_display_string(box, string); - UI_Signal interact = ui_signal_from_box(box); - return interact; -} - -internal UI_Signal -ui_labelf(char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Signal result = ui_label(string); - scratch_end(scratch); - return result; -} - -internal void -ui_label_multiline(F32 max, String8 string) -{ - Temp scratch = scratch_begin(0, 0); - ui_set_next_child_layout_axis(Axis2_Y); - ui_set_next_pref_height(ui_children_sum(1)); - UI_Box *box = ui_build_box_from_key(0, ui_key_zero()); - String8List lines = f_wrapped_string_lines_from_font_size_string_max(scratch.arena, ui_top_font(), ui_top_font_size(), 0, ui_top_tab_size(), string, max); - for(String8Node *n = lines.first; n != 0; n = n->next) - { - ui_label(n->string); - } - scratch_end(scratch); -} - -internal void -ui_label_multilinef(F32 max, char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - ui_label_multiline(max, string); - scratch_end(scratch); -} - -internal UI_Signal -ui_button(String8 string) -{ - ui_set_next_hover_cursor(OS_Cursor_HandPoint); - UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable| - UI_BoxFlag_DrawBackground| - UI_BoxFlag_DrawBorder| - UI_BoxFlag_DrawText| - UI_BoxFlag_DrawHotEffects| - UI_BoxFlag_DrawActiveEffects, - string); - UI_Signal interact = ui_signal_from_box(box); - return interact; -} - -internal UI_Signal -ui_buttonf(char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Signal result = ui_button(string); - scratch_end(scratch); - return result; -} - -internal UI_Signal -ui_hover_label(String8 string) -{ - UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable|UI_BoxFlag_DrawText, string); - UI_Signal interact = ui_signal_from_box(box); - if(ui_hovering(interact)) - { - box->flags |= UI_BoxFlag_DrawBorder; - } - return interact; -} - -internal UI_Signal -ui_hover_labelf(char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Signal sig = ui_hover_label(string); - scratch_end(scratch); - return sig; -} - -typedef struct UI_LineEditDrawData UI_LineEditDrawData; -struct UI_LineEditDrawData -{ - String8 edited_string; - TxtPt cursor; - TxtPt mark; -}; - -internal UI_BOX_CUSTOM_DRAW(ui_line_edit_draw) -{ - UI_LineEditDrawData *draw_data = (UI_LineEditDrawData *)user_data; - F_Tag font = box->font; - F32 font_size = box->font_size; - F32 tab_size = box->tab_size; - Vec4F32 cursor_color = box->palette->colors[UI_ColorCode_Cursor]; - cursor_color.w *= box->parent->parent->focus_active_t; - Vec4F32 select_color = box->palette->colors[UI_ColorCode_Selection]; - select_color.w *= (box->parent->parent->focus_active_t*0.2f + 0.8f); - Vec2F32 text_position = ui_box_text_position(box); - String8 edited_string = draw_data->edited_string; - TxtPt cursor = draw_data->cursor; - TxtPt mark = draw_data->mark; - F32 cursor_pixel_off = f_dim_from_tag_size_string(font, font_size, 0, tab_size, str8_prefix(edited_string, cursor.column-1)).x; - F32 mark_pixel_off = f_dim_from_tag_size_string(font, font_size, 0, tab_size, str8_prefix(edited_string, mark.column-1)).x; - F32 cursor_thickness = ClampBot(4.f, font_size/6.f); - Rng2F32 cursor_rect = - { - text_position.x + cursor_pixel_off - cursor_thickness*0.50f, - box->rect.y0+4.f, - text_position.x + cursor_pixel_off + cursor_thickness*0.50f, - box->rect.y1-4.f, - }; - Rng2F32 mark_rect = - { - text_position.x + mark_pixel_off - cursor_thickness*0.50f, - box->rect.y0+2.f, - text_position.x + mark_pixel_off + cursor_thickness*0.50f, - box->rect.y1-2.f, - }; - Rng2F32 select_rect = union_2f32(cursor_rect, mark_rect); - d_rect(select_rect, select_color, font_size/2.f, 0, 1.f); - d_rect(cursor_rect, cursor_color, 0.f, 0, 1.f); -} - -internal UI_Signal -ui_line_edit(TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, String8 pre_edit_value, String8 string) -{ - //- rjf: make key - UI_Key key = ui_key_from_string(ui_active_seed_key(), string); - - //- rjf: calculate focus - B32 is_auto_focus_hot = ui_is_key_auto_focus_hot(key); - B32 is_auto_focus_active = ui_is_key_auto_focus_active(key); - ui_push_focus_hot(is_auto_focus_hot ? UI_FocusKind_On : UI_FocusKind_Null); - ui_push_focus_active(is_auto_focus_active ? UI_FocusKind_On : UI_FocusKind_Null); - B32 is_focus_hot = ui_is_focus_hot(); - B32 is_focus_active = ui_is_focus_active(); - B32 is_focus_hot_disabled = (!is_focus_hot && ui_top_focus_hot() == UI_FocusKind_On); - B32 is_focus_active_disabled = (!is_focus_active && ui_top_focus_active() == UI_FocusKind_On); - - //- rjf: build top-level box - ui_set_next_hover_cursor(is_focus_active ? OS_Cursor_IBar : OS_Cursor_HandPoint); - UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawBackground| - UI_BoxFlag_DrawBorder| - UI_BoxFlag_MouseClickable| - UI_BoxFlag_ClickToFocus| - ((is_auto_focus_hot || is_auto_focus_active)*UI_BoxFlag_KeyboardClickable)| - UI_BoxFlag_DrawHotEffects| - (is_focus_active || is_focus_active_disabled)*(UI_BoxFlag_Clip|UI_BoxFlag_AllowOverflowX|UI_BoxFlag_ViewClamp), - key); - - //- rjf: take navigation actions for editing - B32 changes_made = 0; - if(is_focus_active) - { - Temp scratch = scratch_begin(0, 0); - UI_EventList *events = ui_events(); - for(UI_EventNode *n = events->first, *next = 0; n != 0; n = next) - { - String8 edit_string = str8(edit_buffer, edit_string_size_out[0]); - next = n->next; - - // rjf: do not consume anything that doesn't fit a single-line's operations - if((n->v.kind != UI_EventKind_Edit && n->v.kind != UI_EventKind_Navigate && n->v.kind != UI_EventKind_Text) || n->v.delta_2s32.y != 0) - { - continue; - } - - // rjf: map this action to an op - UI_TxtOp op = ui_single_line_txt_op_from_event(scratch.arena, &n->v, edit_string, *cursor, *mark); - - // rjf: perform replace range - if(!txt_pt_match(op.range.min, op.range.max) || op.replace.size != 0) - { - String8 new_string = ui_push_string_replace_range(scratch.arena, edit_string, r1s64(op.range.min.column, op.range.max.column), op.replace); - new_string.size = Min(edit_buffer_size, new_string.size); - MemoryCopy(edit_buffer, new_string.str, new_string.size); - edit_string_size_out[0] = new_string.size; - } - - // rjf: perform copy - if(op.flags & UI_TxtOpFlag_Copy) - { - os_set_clipboard_text(op.copy); - } - - // rjf: commit op's changed cursor & mark to caller-provided state - *cursor = op.cursor; - *mark = op.mark; - - // rjf: consume event - { - ui_eat_event(events, n); - changes_made = 1; - } - } - scratch_end(scratch); - } - - //- rjf: build contents - TxtPt mouse_pt = {0}; - F32 cursor_off = 0; - UI_Parent(box) - { - String8 edit_string = str8(edit_buffer, edit_string_size_out[0]); - if(!is_focus_active && !is_focus_active_disabled) - { - String8 display_string = ui_display_part_from_key_string(string); - if(pre_edit_value.size != 0) - { - display_string = pre_edit_value; - } - ui_label(display_string); - } - else - { - F32 total_text_width = f_dim_from_tag_size_string(ui_top_font(), ui_top_font_size(), 0, ui_top_tab_size(), edit_string).x; - ui_set_next_pref_width(ui_px(total_text_width+ui_top_font_size()*5, 1.f)); - UI_Box *editstr_box = ui_build_box_from_stringf(UI_BoxFlag_DrawText|UI_BoxFlag_DisableTextTrunc, "###editstr"); - UI_LineEditDrawData *draw_data = push_array(ui_build_arena(), UI_LineEditDrawData, 1); - draw_data->edited_string = push_str8_copy(ui_build_arena(), edit_string); - draw_data->cursor = *cursor; - draw_data->mark = *mark; - ui_box_equip_display_string(editstr_box, edit_string); - ui_box_equip_custom_draw(editstr_box, ui_line_edit_draw, draw_data); - mouse_pt = txt_pt(1, 1+ui_box_char_pos_from_xy(editstr_box, ui_mouse())); - cursor_off = f_dim_from_tag_size_string(ui_top_font(), ui_top_font_size(), 0, ui_top_tab_size(), str8_prefix(edit_string, cursor->column-1)).x; - } - } - - //- rjf: interact - UI_Signal sig = ui_signal_from_box(box); - if(!is_focus_active && sig.f&(UI_SignalFlag_DoubleClicked|UI_SignalFlag_KeyboardPressed)) - { - String8 edit_string = pre_edit_value; - edit_string.size = Min(edit_buffer_size, pre_edit_value.size); - MemoryCopy(edit_buffer, edit_string.str, edit_string.size); - edit_string_size_out[0] = edit_string.size; - ui_set_auto_focus_active_key(key); - ui_kill_action(); - *cursor = txt_pt(1, edit_string.size+1); - *mark = txt_pt(1, 1); - } - if(is_focus_active && sig.f&UI_SignalFlag_KeyboardPressed) - { - ui_set_auto_focus_active_key(ui_key_zero()); - sig.f |= UI_SignalFlag_Commit; - } - if(is_focus_active && ui_dragging(sig)) - { - if(ui_pressed(sig)) - { - *mark = mouse_pt; - } - *cursor = mouse_pt; - } - - //- rjf: focus cursor - { - Rng1F32 cursor_range_px = r1f32(cursor_off-ui_top_font_size()*2.f, cursor_off+ui_top_font_size()*2.f); - Rng1F32 visible_range_px = r1f32(box->view_off_target.x, box->view_off_target.x + dim_2f32(box->rect).x); - cursor_range_px.min = ClampBot(0, cursor_range_px.min); - cursor_range_px.max = ClampBot(0, cursor_range_px.max); - F32 min_delta = cursor_range_px.min-visible_range_px.min; - F32 max_delta = cursor_range_px.max-visible_range_px.max; - min_delta = Min(min_delta, 0); - max_delta = Max(max_delta, 0); - box->view_off_target.x += min_delta; - box->view_off_target.x += max_delta; - } - - //- rjf: pop focus - ui_pop_focus_hot(); - ui_pop_focus_active(); - - return sig; -} - -internal UI_Signal -ui_line_editf(TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, String8 pre_edit_value, char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Signal result = ui_line_edit(cursor, mark, edit_buffer, edit_buffer_size, edit_string_size_out, pre_edit_value, string); - scratch_end(scratch); - return result; -} - -//////////////////////////////// -//~ rjf: Images - -typedef struct UI_ImageDrawData UI_ImageDrawData; -struct UI_ImageDrawData -{ - R_Handle texture; - R_Tex2DSampleKind sample_kind; - Rng2F32 region; - Vec4F32 tint; - F32 blur; -}; - -internal UI_BOX_CUSTOM_DRAW(ui_image_draw) -{ - UI_ImageDrawData *draw_data = (UI_ImageDrawData *)user_data; - if(r_handle_match(draw_data->texture, r_handle_zero())) - { - R_Rect2DInst *inst = d_rect(box->rect, v4f32(0, 0, 0, 0), 0, 0, 1.f); - MemoryCopyArray(inst->corner_radii, box->corner_radii); - } - else D_Tex2DSampleKindScope(draw_data->sample_kind) - { - R_Rect2DInst *inst = d_img(box->rect, draw_data->region, draw_data->texture, draw_data->tint, 0, 0, 0); - MemoryCopyArray(inst->corner_radii, box->corner_radii); - } - if(draw_data->blur > 0.01f) - { - Rng2F32 clip = box->rect; - for(UI_Box *b = box->parent; !ui_box_is_nil(b); b = b->parent) - { - if(b->flags & UI_BoxFlag_Clip) - { - clip = intersect_2f32(b->rect, clip); - } - } - R_PassParams_Blur *blur = d_blur(intersect_2f32(clip, box->rect), draw_data->blur, 0); - MemoryCopyArray(blur->corner_radii, box->corner_radii); - } -} - -internal UI_Signal -ui_image(R_Handle texture, R_Tex2DSampleKind sample_kind, Rng2F32 region, Vec4F32 tint, F32 blur, String8 string) -{ - UI_Box *box = ui_build_box_from_string(0, string); - UI_ImageDrawData *draw_data = push_array(ui_build_arena(), UI_ImageDrawData, 1); - draw_data->texture = texture; - draw_data->sample_kind = sample_kind; - draw_data->region = region; - draw_data->tint = tint; - draw_data->blur = blur; - ui_box_equip_custom_draw(box, ui_image_draw, draw_data); - UI_Signal sig = ui_signal_from_box(box); - return sig; -} - -internal UI_Signal -ui_imagef(R_Handle texture, R_Tex2DSampleKind sample_kind, Rng2F32 region, Vec4F32 tint, F32 blur, char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Signal result = ui_image(texture, sample_kind, region, tint, blur, string); - scratch_end(scratch); - return result; -} - -//////////////////////////////// -//~ rjf: Special Buttons - -internal UI_Signal -ui_expander(B32 is_expanded, String8 string) -{ - ui_set_next_hover_cursor(OS_Cursor_HandPoint); - ui_set_next_text_alignment(UI_TextAlign_Center); - ui_set_next_font(ui_icon_font()); - UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable|UI_BoxFlag_DrawText, string); - ui_box_equip_display_string(box, is_expanded ? str8_lit("v") : str8_lit(">")); - UI_Signal sig = ui_signal_from_box(box); - return sig; -} - -internal UI_Signal -ui_expanderf(B32 is_expanded, char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Signal sig = ui_expander(is_expanded, string); - scratch_end(scratch); - return sig; -} - -internal UI_Signal -ui_sort_header(B32 sorting, B32 ascending, String8 string) -{ - ui_set_next_child_layout_axis(Axis2_X); - ui_set_next_hover_cursor(OS_Cursor_HandPoint); - UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable|UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawActiveEffects, string); - ui_push_parent(box); - - // rjf: make icon - if(sorting) - { - ui_set_next_pref_width(ui_em(1.8f, 1.f)); - ui_set_next_text_alignment(UI_TextAlign_Center); - ui_set_next_font(ui_icon_font()); - UI_Box *icon = ui_build_box_from_string(UI_BoxFlag_DrawText, str8_lit("")); - ui_box_equip_display_string(icon, ascending ? str8_lit("^") : str8_lit("v")); - } - - // rjf: make text - { - ui_label(string); - } - - ui_pop_parent(); - UI_Signal interact = ui_signal_from_box(box); - return interact; -} - -internal UI_Signal -ui_sort_headerf(B32 sorting, B32 ascending, char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Signal sig = ui_sort_header(sorting, ascending, string); - scratch_end(scratch); - return sig; -} - -//////////////////////////////// -//~ rjf: Color Pickers - -//- rjf: tooltips - -internal void -ui_do_color_tooltip_hsv(Vec3F32 hsv) -{ - Vec3F32 rgb = rgb_from_hsv(hsv); - UI_Tooltip UI_Padding(ui_em(2.f, 1.f)) - { - UI_PrefWidth(ui_em(22.f, 1.f)) UI_PrefHeight(ui_em(6.f, 1.f)) UI_Row UI_Padding(ui_pct(1, 0)) - { - UI_Palette(ui_build_palette(ui_top_palette(), .background = v4f32(rgb.x, rgb.y, rgb.z, 1.f))) - UI_CornerRadius(4.f) - UI_PrefWidth(ui_em(6.f, 1.f)) UI_PrefHeight(ui_em(6.f, 1.f)) - ui_build_box_from_string(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, str8_lit("")); - } - ui_spacer(ui_em(0.3f, 1.f)); - UI_PrefWidth(ui_em(22.f, 1.f)) UI_TextAlignment(UI_TextAlign_Center) - { - ui_labelf("Hex: #%02x%02x%02x", (U8)(rgb.x*255.f), (U8)(rgb.y*255.f), (U8)(rgb.z*255.f)); - } - ui_spacer(ui_em(0.3f, 1.f)); - UI_PrefWidth(ui_em(22.f, 1.f)) UI_PrefHeight(ui_children_sum(1)) UI_Row - { - UI_WidthFill UI_Column UI_PrefHeight(ui_em(1.8f, 1.f)) - { - ui_labelf("Red: %.2f", rgb.x); - ui_labelf("Green: %.2f", rgb.y); - ui_labelf("Blue: %.2f", rgb.z); - } - UI_WidthFill UI_Column UI_PrefHeight(ui_em(1.8f, 1.f)) - { - ui_labelf("Hue: %.2f", hsv.x); - ui_labelf("Sat: %.2f", hsv.y); - ui_labelf("Val: %.2f", hsv.z); - } - } - } -} - -internal void -ui_do_color_tooltip_hsva(Vec4F32 hsva) -{ - Vec3F32 hsv = v3f32(hsva.x, hsva.y, hsva.z); - Vec3F32 rgb = rgb_from_hsv(hsv); - Vec4F32 rgba = v4f32(rgb.x, rgb.y, rgb.z, hsva.w); - UI_Tooltip UI_Padding(ui_em(2.f, 1.f)) - { - UI_PrefWidth(ui_em(22.f, 1.f)) UI_PrefHeight(ui_em(6.f, 1.f)) UI_Row UI_Padding(ui_pct(1, 0)) - { - UI_Palette(ui_build_palette(ui_top_palette(), .background = rgba)) - UI_CornerRadius(4.f) - UI_PrefWidth(ui_em(6.f, 1.f)) UI_PrefHeight(ui_em(6.f, 1.f)) - ui_build_box_from_string(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, str8_lit("")); - } - ui_spacer(ui_em(0.3f, 1.f)); - UI_PrefWidth(ui_em(22.f, 1.f)) UI_TextAlignment(UI_TextAlign_Center) - { - ui_labelf("Hex: #%02x%02x%02x%02x", (U8)(rgba.x*255.f), (U8)(rgba.y*255.f), (U8)(rgba.z*255.f), (U8)(rgba.w*255.f)); - } - ui_spacer(ui_em(0.3f, 1.f)); - UI_PrefWidth(ui_em(22.f, 1.f)) UI_PrefHeight(ui_children_sum(1)) UI_Row - { - UI_WidthFill UI_Column UI_PrefHeight(ui_em(1.8f, 1.f)) - { - ui_labelf("Red: %.2f", rgba.x); - ui_labelf("Green: %.2f", rgba.y); - ui_labelf("Blue: %.2f", rgba.z); - ui_labelf("Alpha: %.2f", rgba.w); - } - UI_WidthFill UI_Column UI_PrefHeight(ui_em(1.8f, 1.f)) - { - ui_labelf("Hue: %.2f", hsva.x); - ui_labelf("Sat: %.2f", hsva.y); - ui_labelf("Val: %.2f", hsva.z); - ui_labelf("Alpha: %.2f", hsva.w); - } - } - } -} - -//- rjf: saturation/value picker - -typedef struct UI_SatValDrawData UI_SatValDrawData; -struct UI_SatValDrawData -{ - F32 hue; - F32 sat; - F32 val; -}; - -internal UI_BOX_CUSTOM_DRAW(ui_sat_val_picker_draw) -{ - UI_SatValDrawData *data = (UI_SatValDrawData *)user_data; - - // rjf: hue => rgb - Vec3F32 hue_rgb = rgb_from_hsv(v3f32(data->hue, 1, 1)); - - // rjf: white -> rgb background - { - R_Rect2DInst *inst = d_rect(pad_2f32(box->rect, -1.f), v4f32(hue_rgb.x, hue_rgb.y, hue_rgb.z, 1), 4.f, 0, 1.f); - inst->colors[Corner_00] = inst->colors[Corner_01] = v4f32(1, 1, 1, 1); - } - - // rjf: black gradient overlay - { - R_Rect2DInst *inst = d_rect(pad_2f32(box->rect, -1.f), v4f32(0, 0, 0, 0), 4.f, 0, 1.f); - inst->colors[Corner_01] = v4f32(0, 0, 0, 1); - inst->colors[Corner_11] = v4f32(0, 0, 0, 1); - } - - // rjf: indicator - { - Vec2F32 box_rect_dim = dim_2f32(box->rect); - Vec2F32 center = v2f32(box->rect.x0 + data->sat*box_rect_dim.x, box->rect.y0 + (1-data->val)*box_rect_dim.y); - F32 half_size = box->font_size * (0.5f + box->active_t*0.2f); - Rng2F32 rect = r2f32p(center.x - half_size, - center.y - half_size, - center.x + half_size, - center.y + half_size); - d_rect(rect, v4f32(1, 1, 1, 1), half_size/2, 2.f, 1.f); - } -} - -internal UI_Signal -ui_sat_val_picker(F32 hue, F32 *out_sat, F32 *out_val, String8 string) -{ - // rjf: build & interact - ui_set_next_hover_cursor(OS_Cursor_HandPoint); - UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable, string); - UI_SatValDrawData *user = push_array(ui_build_arena(), UI_SatValDrawData, 1); - ui_box_equip_custom_draw(box, ui_sat_val_picker_draw, user); - UI_Signal sig = ui_signal_from_box(box); - - // rjf: click+draw behavior - if(ui_dragging(sig)) - { - Vec2F32 dim = dim_2f32(box->rect); - *out_sat = (ui_mouse().x - box->rect.x0) / dim.x; - *out_val = 1 - (ui_mouse().y - box->rect.y0) / dim.y; - *out_sat = Clamp(0, *out_sat, 1); - *out_val = Clamp(0, *out_val, 1); - ui_do_color_tooltip_hsv(v3f32(hue, *out_sat, *out_val)); - if(ui_pressed(sig)) - { - Vec2F32 data = v2f32(*out_sat, *out_val); - ui_store_drag_struct(&data); - } - if(ui_slot_press(UI_EventActionSlot_Cancel)) - { - Vec2F32 data = *ui_get_drag_struct(Vec2F32); - *out_sat = data.x; - *out_val = data.y; - ui_kill_action(); - } - } - - // rjf: fill draw data - { - user->hue = hue; - user->sat = *out_sat; - user->val = *out_val; - } - - return sig; -} - -internal UI_Signal -ui_sat_val_pickerf(F32 hue, F32 *out_sat, F32 *out_val, char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Signal sig = ui_sat_val_picker(hue, out_sat, out_val, string); - scratch_end(scratch); - return sig; -} - -//- rjf: hue picker - -typedef struct UI_HueDrawData UI_HueDrawData; -struct UI_HueDrawData -{ - F32 hue; - F32 sat; - F32 val; -}; - -internal UI_BOX_CUSTOM_DRAW(ui_hue_picker_draw) -{ - UI_HueDrawData *data = (UI_HueDrawData *)user_data; - Vec2F32 dim = dim_2f32(box->rect); - F32 segment_dim = floor_f32(dim.y/6.f); - Rng2F32 hue_cycle_rect = box->rect; - Vec2F32 hue_cycle_center = center_2f32(hue_cycle_rect); - hue_cycle_rect.x0 += (hue_cycle_center.x - hue_cycle_rect.x0) * 0.3f; - hue_cycle_rect.x1 += (hue_cycle_center.x - hue_cycle_rect.x1) * 0.3f; - Rng2F32 rect = r2f32p(hue_cycle_rect.x0, - hue_cycle_rect.y0, - hue_cycle_rect.x1, - hue_cycle_rect.y0 + segment_dim); - for(int seg = 0; seg < 6; seg += 1) - { - F32 hue0 = (F32)(seg)/6; - F32 hue1 = (F32)(seg+1)/6; - Vec3F32 rgb0 = rgb_from_hsv(v3f32(hue0, 1, 1)); - Vec3F32 rgb1 = rgb_from_hsv(v3f32(hue1, 1, 1)); - Vec4F32 rgba0 = v4f32(rgb0.x, rgb0.y, rgb0.z, 1); - Vec4F32 rgba1 = v4f32(rgb1.x, rgb1.y, rgb1.z, 1); - R_Rect2DInst *inst = d_rect(rect, v4f32(0, 0, 0, 0), 0, 0, 0.f); - inst->colors[Corner_00] = rgba0; - inst->colors[Corner_01] = rgba1; - inst->colors[Corner_10] = rgba0; - inst->colors[Corner_11] = rgba1; - rect.y0 += segment_dim; - rect.y1 += segment_dim; - } - - // rjf: indicator - { - Vec2F32 box_rect_dim = dim_2f32(box->rect); - Vec2F32 center = v2f32((box->rect.x0+box->rect.x1)/2, box->rect.y0 + data->hue*box_rect_dim.y); - F32 half_size = box->font_size * (0.5f + box->active_t*0.2f); - Rng2F32 rect = r2f32p(center.x - half_size, - center.y - 2.f, - center.x + half_size, - center.y + 2.f); - d_rect(rect, v4f32(1, 1, 1, 1), half_size/2, 2.f, 1.f); - } -} - -internal UI_Signal -ui_hue_picker(F32 *out_hue, F32 sat, F32 val, String8 string) -{ - // rjf: build & interact - ui_set_next_hover_cursor(OS_Cursor_HandPoint); - UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable, string); - UI_HueDrawData *user = push_array(ui_build_arena(), UI_HueDrawData, 1); - ui_box_equip_custom_draw(box, ui_hue_picker_draw, user); - UI_Signal sig = ui_signal_from_box(box); - - // rjf: click+draw behavior - if(ui_dragging(sig)) - { - Vec2F32 dim = dim_2f32(box->rect); - *out_hue = (ui_mouse().y - box->rect.y0) / dim.y; - *out_hue = Clamp(0, *out_hue, 1); - ui_do_color_tooltip_hsv(v3f32(*out_hue, sat, val)); - if(ui_pressed(sig)) - { - ui_store_drag_struct(out_hue); - } - if(ui_slot_press(UI_EventActionSlot_Cancel)) - { - *out_hue = *ui_get_drag_struct(F32); - ui_kill_action(); - } - } - - // rjf: fill draw data - { - user->hue = *out_hue; - user->sat = sat; - user->val = val; - } - - return sig; -} - -internal UI_Signal -ui_hue_pickerf(F32 *out_hue, F32 sat, F32 val, char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Signal sig = ui_hue_picker(out_hue, sat, val, string); - scratch_end(scratch); - return sig; -} - -//- rjf: alpha picker - -typedef struct UI_AlphaDrawData UI_AlphaDrawData; -struct UI_AlphaDrawData -{ - F32 alpha; -}; - -internal UI_BOX_CUSTOM_DRAW(ui_alpha_picker_draw) -{ - UI_AlphaDrawData *data = (UI_AlphaDrawData *)user_data; - Vec2F32 dim = dim_2f32(box->rect); - - // rjf: build gradient - { - Rng2F32 rect = box->rect; - Vec2F32 center = center_2f32(rect); - rect.x0 += (center.x - rect.x0) * 0.3f; - rect.x1 += (center.x - rect.x1) * 0.3f; - R_Rect2DInst *inst = d_rect(rect, v4f32(0, 0, 0, 0), 0, 0, 0); - inst->colors[Corner_00] = inst->colors[Corner_10] = v4f32(1, 1, 1, 1); - } - - // rjf: indicator - { - Vec2F32 box_rect_dim = dim_2f32(box->rect); - Vec2F32 center = v2f32((box->rect.x0+box->rect.x1)/2, box->rect.y0 + (1-data->alpha)*box_rect_dim.y); - F32 half_size = box->font_size * (0.5f + box->active_t*0.2f); - Rng2F32 rect = r2f32p(center.x - half_size, - center.y - 2.f, - center.x + half_size, - center.y + 2.f); - d_rect(rect, v4f32(1, 1, 1, 1), half_size/2, 2.f, 1.f); - } -} - -internal UI_Signal -ui_alpha_picker(F32 *out_alpha, String8 string) -{ - // rjf: build & interact - ui_set_next_hover_cursor(OS_Cursor_HandPoint); - UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable, string); - UI_AlphaDrawData *user = push_array(ui_build_arena(), UI_AlphaDrawData, 1); - ui_box_equip_custom_draw(box, ui_alpha_picker_draw, user); - UI_Signal sig = ui_signal_from_box(box); - - // rjf: click+draw behavior - if(ui_dragging(sig)) - { - Vec2F32 dim = dim_2f32(box->rect); - F32 drag_pct = (ui_mouse().y - box->rect.y0) / dim.y; - drag_pct = Clamp(0, drag_pct, 1); - *out_alpha = 1-drag_pct; - if(ui_pressed(sig)) - { - ui_store_drag_struct(out_alpha); - } - if(ui_slot_press(UI_EventActionSlot_Cancel)) - { - *out_alpha = *ui_get_drag_struct(F32); - ui_kill_action(); - } - } - - // rjf: fill draw data - { - user->alpha = *out_alpha; - } - - return sig; -} - -internal UI_Signal -ui_alpha_pickerf(F32 *out_alpha, char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Signal sig = ui_alpha_picker(out_alpha, string); - scratch_end(scratch); - return sig; -} - -//////////////////////////////// -//~ rjf: Simple Layout Widgets - -internal UI_Box *ui_row_begin(void) { return ui_named_row_begin(str8_lit("")); } -internal UI_Signal ui_row_end(void) { return ui_named_row_end(); } -internal UI_Box *ui_column_begin(void) { return ui_named_column_begin(str8_lit("")); } -internal UI_Signal ui_column_end(void) { return ui_named_column_end(); } - -internal UI_Box * -ui_named_row_begin(String8 string) -{ - ui_set_next_child_layout_axis(Axis2_X); - UI_Box *box = ui_build_box_from_string(0, string); - ui_push_parent(box); - return box; -} - -internal UI_Signal -ui_named_row_end(void) -{ - UI_Box *box = ui_pop_parent(); - UI_Signal sig = ui_signal_from_box(box); - return sig; -} - -internal UI_Box * -ui_named_column_begin(String8 string) -{ - ui_set_next_child_layout_axis(Axis2_Y); - UI_Box *box = ui_build_box_from_string(0, string); - ui_push_parent(box); - return box; -} - -internal UI_Signal -ui_named_column_end(void) -{ - UI_Box *box = ui_pop_parent(); - UI_Signal sig = ui_signal_from_box(box); - return sig; -} - -//////////////////////////////// -//~ rjf: Floating Panes - -internal UI_Box * -ui_pane_begin(Rng2F32 rect, String8 string) -{ - ui_push_rect(rect); - ui_set_next_child_layout_axis(Axis2_Y); - UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable|UI_BoxFlag_Clip|UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, string); - ui_pop_rect(); - ui_push_parent(box); - ui_push_pref_width(ui_pct(1, 0)); - return box; -} - -internal UI_Box * -ui_pane_beginf(Rng2F32 rect, char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Box *box = ui_pane_begin(rect, string); - scratch_end(scratch); - return box; -} - -internal UI_Signal -ui_pane_end(void) -{ - ui_pop_pref_width(); - UI_Box *box = ui_pop_parent(); - UI_Signal sig = ui_signal_from_box(box); - return sig; -} - -//////////////////////////////// -//~ rjf: Tables - -thread_static U64 ui_ts_col_pct_count = 0; -thread_static F32 *ui_ts_col_pcts_stable = 0; -thread_static U64 ui_ts_vector_idx = 0; -thread_static U64 ui_ts_cell_idx = 0; - -internal void -ui_table_begin(U64 column_pct_count, F32 **column_pcts, String8 string) -{ - //- rjf: store off persistent, user-provided column info - ui_ts_col_pct_count = column_pct_count; - - //- rjf: build main table parent - ui_set_next_pref_height(ui_children_sum(1)); - ui_set_next_child_layout_axis(Axis2_Y); - UI_Box *table = ui_build_box_from_string(0, string); - ui_push_parent(table); - - //- rjf: build column boundaries - F32 x_off = (ui_ts_col_pct_count > 0 ? *column_pcts[0] : 0) * dim_2f32(table->rect).x; - for(U64 column_idx = 1; column_idx < ui_ts_col_pct_count; column_idx += 1) - { - // rjf: build base rectangle - Rng2F32 rect = {0}; - { - rect.x0 = x_off-3.f; - rect.y0 = 0; - rect.x1 = x_off+3.f; - rect.y1 = dim_2f32(table->rect).y; - x_off += *column_pcts[column_idx] * dim_2f32(table->rect).x; - } - - // rjf: make column boundary widget - UI_Rect(rect) - { - ui_set_next_hover_cursor(OS_Cursor_LeftRight); - UI_Box *box = ui_build_box_from_stringf(UI_BoxFlag_Clickable, "###%S_boundary_%I64u", table->string, column_idx); - - F32 *left_pct_ptr = column_idx < ui_ts_col_pct_count ? column_pcts[column_idx-1] : 0; - F32 *right_pct_ptr = column_idx < ui_ts_col_pct_count ? column_pcts[column_idx] : 0; - - // rjf: boundary dragging - UI_Signal interact = ui_signal_from_box(box); - if(ui_dragging(interact)) - { - if(ui_pressed(interact)) - { - Vec2F32 v = v2f32(*left_pct_ptr, *right_pct_ptr); - ui_store_drag_struct(&v); - } - - // rjf: calculate how much space we're dividing amongst the columns that - // the user can resize - F32 adjustable_table_dim = 0; - if(table->child_layout_axis == Axis2_Y) - { - adjustable_table_dim = dim_2f32(table->rect).x; - } - else - { - U64 child_idx = 0; - for(UI_Box *v = table->first; !ui_box_is_nil(v); v = v->next, child_idx += 1) - { - U64 column_idx = (child_idx+1); - if(column_idx < ui_ts_col_pct_count) - { - adjustable_table_dim += dim_2f32(v->rect).x; - } - else - { - break; - } - } - } - - // rjf: calculate diff - F32 min_size = 30.f; - F32 left_pct__before = ui_get_drag_struct(Vec2F32)->x; - F32 left_pixels__before = left_pct__before * adjustable_table_dim; - F32 left_pixels__after = left_pixels__before + ui_drag_delta().x; - - // rjf: clamp left side - if(left_pixels__after < min_size) - { - left_pixels__after = min_size; - } - - // rjf: calculate right side - F32 left_pct__after = left_pixels__after / adjustable_table_dim; - F32 pct_delta = left_pct__after - left_pct__before; - F32 right_pct__before = ui_get_drag_struct(Vec2F32)->y; - F32 right_pct__after = right_pct__before - pct_delta; - F32 right_pixels__after = right_pct__after * adjustable_table_dim; - - // rjf: clamp right side & back-solve - if(right_pixels__after < min_size) - { - right_pixels__after = min_size; - right_pct__after = right_pixels__after/adjustable_table_dim; - pct_delta = -(right_pct__after-right_pct__before); - left_pct__after = left_pct__before+pct_delta; - } - - // rjf: commit new percentages - *left_pct_ptr = left_pct__after; - *right_pct_ptr = right_pct__after; - } - } - } - - //- rjf: form stable pcts - ui_ts_col_pcts_stable = push_array(ui_build_arena(), F32, ui_ts_col_pct_count); - for(U64 idx = 0; idx < column_pct_count; idx += 1) - { - ui_ts_col_pcts_stable[idx] = *column_pcts[idx]; - } - - ui_ts_vector_idx = 0; -} - -internal void -ui_table_beginf(U64 column_pct_count, F32 **column_pcts, char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - ui_table_begin(column_pct_count, column_pcts, string); - scratch_end(scratch); -} - -internal void -ui_table_end(void) -{ - ui_pop_parent(); -} - -internal UI_Box * -ui_named_table_vector_begin(String8 string) -{ - ui_set_next_pref_width(ui_pct(1, 0)); - ui_set_next_child_layout_axis(Axis2_X); - UI_Box *vector = ui_build_box_from_string(UI_BoxFlag_DrawSideBottom, string); - ui_ts_vector_idx += 1; - ui_ts_cell_idx = 0; - ui_push_parent(vector); - return vector; -} - -internal UI_Box * -ui_named_table_vector_beginf(char *fmt, ...) -{ - Temp scratch = scratch_begin(0, 0); - va_list args; - va_start(args, fmt); - String8 string = push_str8fv(scratch.arena, fmt, args); - va_end(args); - UI_Box *vector = ui_named_table_vector_begin(string); - scratch_end(scratch); - return vector; -} - -internal UI_Box * -ui_table_vector_begin(void) -{ - UI_Box *table = ui_top_parent(); - UI_Box *vector = ui_named_table_vector_beginf("###tbl_vec_%p_%I64u", table, ui_ts_vector_idx); - return vector; -} - -internal UI_Signal -ui_table_vector_end(void) -{ - UI_Box *box = ui_pop_parent(); - return ui_signal_from_box(box); -} - -internal UI_Box * -ui_table_cell_begin(void) -{ - U64 column_idx = ui_ts_cell_idx; - F32 width_pct = column_idx < ui_ts_col_pct_count ? ui_ts_col_pcts_stable[column_idx] : 1.f; - return ui_table_cell_sized_begin(ui_pct(width_pct, 0)); -} - -internal UI_Signal -ui_table_cell_end(void) -{ - UI_Box *cell = ui_pop_parent(); - return ui_signal_from_box(cell); -} - -internal UI_Box * -ui_table_cell_sized_begin(UI_Size size) -{ - UI_Box *vector = ui_top_parent(); - U64 column_idx = ui_ts_cell_idx; - ui_ts_cell_idx += 1; - ui_set_next_pref_width(size); - ui_set_next_child_layout_axis(Axis2_X); - UI_Box *cell = ui_build_box_from_stringf((column_idx > 0 ? UI_BoxFlag_DrawSideLeft : 0), "###tbl_cell_%p_%I64u", vector, ui_ts_cell_idx); - ui_push_parent(cell); - return cell; -} - -//////////////////////////////// -//~ rjf: Scroll Regions - -internal void -ui_scroll_list_row_block_chunk_list_push(Arena *arena, UI_ScrollListRowBlockChunkList *list, U64 cap, UI_ScrollListRowBlock *block) -{ - UI_ScrollListRowBlockChunkNode *n = list->last; - if(n == 0 || n->count >= n->cap) - { - n = push_array(arena, UI_ScrollListRowBlockChunkNode, 1); - n->cap = cap; - n->v = push_array_no_zero(arena, UI_ScrollListRowBlock, n->cap); - SLLQueuePush(list->first, list->last, n); - list->chunk_count += 1; - } - MemoryCopyStruct(&n->v[n->count], block); - n->count += 1; - list->total_count += 1; -} - -internal UI_ScrollListRowBlockArray -ui_scroll_list_row_block_array_from_chunk_list(Arena *arena, UI_ScrollListRowBlockChunkList *list) -{ - UI_ScrollListRowBlockArray array = {0}; - array.count = list->total_count; - array.v = push_array_no_zero(arena, UI_ScrollListRowBlock, array.count); - U64 idx = 0; - for(UI_ScrollListRowBlockChunkNode *n = list->first; n != 0; n = n->next) - { - MemoryCopy(array.v+idx, n->v, sizeof(n->v[0])*n->count); - idx += n->count; - } - return array; -} - -internal U64 -ui_scroll_list_row_from_item(UI_ScrollListRowBlockArray *blocks, U64 item) -{ - U64 result = 0; - { - U64 row_idx = 0; - U64 item_idx = 0; - for(U64 block_idx = 0; block_idx < blocks->count; block_idx += 1) - { - UI_ScrollListRowBlock *block = &blocks->v[block_idx]; - U64 next_row_idx = row_idx + block->row_count; - U64 next_item_idx= item_idx+ block->item_count; - if(item_idx <= item && item < next_item_idx) - { - U64 item_off_rows = (item-item_idx) * (block->row_count/block->item_count); - result = row_idx + item_off_rows; - break; - } - row_idx = next_row_idx; - item_idx = next_item_idx; - } - } - return result; -} - -internal U64 -ui_scroll_list_item_from_row(UI_ScrollListRowBlockArray *blocks, U64 row) -{ - U64 result = 0; - { - U64 row_idx = 0; - U64 item_idx = 0; - for(U64 block_idx = 0; block_idx < blocks->count; block_idx += 1) - { - UI_ScrollListRowBlock *block = &blocks->v[block_idx]; - U64 next_row_idx = row_idx + block->row_count; - U64 next_item_idx= item_idx+ block->item_count; - if(row_idx <= row && row < next_row_idx) - { - result = item_idx; - break; - } - row_idx = next_row_idx; - item_idx = next_item_idx; - } - } - return result; -} - -internal UI_ScrollPt -ui_scroll_bar(Axis2 axis, UI_Size off_axis_size, UI_ScrollPt pt, Rng1S64 idx_range, S64 view_num_indices) -{ - ui_push_palette(ui_state->widget_palette_info.scrollbar_palette); - - //- rjf: unpack - S64 idx_range_dim = Max(dim_1s64(idx_range), 1); - - //- rjf: produce extra flags for cases in which scrolling is disabled - UI_BoxFlags disabled_flags = 0; - if(idx_range.min == idx_range.max) - { - disabled_flags |= UI_BoxFlag_Disabled; - } - - //- rjf: build main container - ui_set_next_pref_size(axis2_flip(axis), off_axis_size); - ui_set_next_child_layout_axis(axis); - UI_Box *container_box = ui_build_box_from_key(UI_BoxFlag_DrawBorder, ui_key_zero()); - - //- rjf: build scroll-min button - UI_Signal min_scroll_sig = {0}; - UI_Parent(container_box) - UI_PrefSize(axis, off_axis_size) - UI_Flags(UI_BoxFlag_DrawBorder|disabled_flags) - UI_TextAlignment(UI_TextAlign_Center) - UI_Font(ui_icon_font()) - { - String8 arrow_string = ui_icon_string_from_kind(axis == Axis2_X ? UI_IconKind_LeftArrow : UI_IconKind_UpArrow); - min_scroll_sig = ui_buttonf("%S##_min_scroll_%i", arrow_string, axis); - } - - //- rjf: main scroller area - UI_Signal space_before_sig = {0}; - UI_Signal space_after_sig = {0}; - UI_Signal scroller_sig = {0}; - UI_Box *scroll_area_box = &ui_g_nil_box; - UI_Box *scroller_box = &ui_g_nil_box; - UI_Parent(container_box) - { - ui_set_next_pref_size(axis, ui_pct(1, 0)); - ui_set_next_child_layout_axis(axis); - scroll_area_box = ui_build_box_from_stringf(0, "##_scroll_area_%i", axis); - UI_Parent(scroll_area_box) - { - // rjf: space before - if(idx_range.max != idx_range.min) - { - ui_set_next_pref_size(axis, ui_pct((F32)((F64)(pt.idx-idx_range.min)/(F64)idx_range_dim), 0)); - ui_set_next_hover_cursor(OS_Cursor_HandPoint); - UI_Box *space_before_box = ui_build_box_from_stringf(UI_BoxFlag_Clickable, "##scroll_area_before"); - space_before_sig = ui_signal_from_box(space_before_box); - } - - // rjf: scroller - UI_Flags(disabled_flags) UI_PrefSize(axis, ui_pct(Clamp(0.01f, (F32)((F64)Max(view_num_indices, 1)/(F64)idx_range_dim), 1.f), 0.f)) - { - scroller_sig = ui_buttonf("##_scroller_%i", axis); - scroller_box = scroller_sig.box; - } - - // rjf: space after - if(idx_range.max != idx_range.min) - { - ui_set_next_pref_size(axis, ui_pct(1.f - (F32)((F64)(pt.idx-idx_range.min)/(F64)idx_range_dim), 0)); - ui_set_next_hover_cursor(OS_Cursor_HandPoint); - UI_Box *space_after_box = ui_build_box_from_stringf(UI_BoxFlag_Clickable, "##scroll_area_after"); - space_after_sig = ui_signal_from_box(space_after_box); - } - } - } - - //- rjf: build scroll-max button - UI_Signal max_scroll_sig = {0}; - UI_Parent(container_box) - UI_PrefSize(axis, off_axis_size) - UI_Flags(UI_BoxFlag_DrawBorder|disabled_flags) - UI_TextAlignment(UI_TextAlign_Center) - UI_Font(ui_icon_font()) - { - String8 arrow_string = ui_icon_string_from_kind(axis == Axis2_X ? UI_IconKind_RightArrow : UI_IconKind_DownArrow); - max_scroll_sig = ui_buttonf("%S##_max_scroll_%i", arrow_string, axis); - } - - //- rjf: pt * signals -> new pt - UI_ScrollPt new_pt = pt; - { - typedef struct UI_ScrollBarDragData UI_ScrollBarDragData; - struct UI_ScrollBarDragData - { - UI_ScrollPt start_pt; - F32 scroll_space_px; - }; - if(ui_dragging(scroller_sig)) - { - if(ui_pressed(scroller_sig)) - { - UI_ScrollBarDragData drag_data = {pt, (floor_f32(dim_2f32(scroll_area_box->rect).v[axis])-floor_f32(dim_2f32(scroller_box->rect).v[axis]))}; - ui_store_drag_struct(&drag_data); - } - UI_ScrollBarDragData *drag_data = ui_get_drag_struct(UI_ScrollBarDragData); - UI_ScrollPt original_pt = drag_data->start_pt; - F32 drag_delta = ui_drag_delta().v[axis]; - F32 drag_pct = drag_delta / drag_data->scroll_space_px; - S64 new_idx = original_pt.idx + drag_pct*idx_range_dim; - new_idx = Clamp(idx_range.min, new_idx, idx_range.max); - ui_scroll_pt_target_idx(&new_pt, new_idx); - new_pt.off = 0; - } - if(ui_dragging(min_scroll_sig) || ui_dragging(space_before_sig)) - { - S64 new_idx = new_pt.idx-1; - new_idx = Clamp(idx_range.min, new_idx, idx_range.max); - ui_scroll_pt_target_idx(&new_pt, new_idx); - } - if(ui_dragging(max_scroll_sig) || ui_dragging(space_after_sig)) - { - S64 new_idx = new_pt.idx+1; - new_idx = Clamp(idx_range.min, new_idx, idx_range.max); - ui_scroll_pt_target_idx(&new_pt, new_idx); - } - } - - ui_pop_palette(); - return new_pt; -} - -thread_static UI_ScrollPt *ui_scroll_list_scroll_pt_ptr = 0; -thread_static F32 ui_scroll_list_scroll_bar_dim_px = 0; -thread_static Vec2F32 ui_scroll_list_dim_px = {0}; -thread_static Rng1S64 ui_scroll_list_scroll_idx_rng = {0}; - -internal void -ui_scroll_list_begin(UI_ScrollListParams *params, UI_ScrollPt *scroll_pt, Vec2S64 *cursor_out, Vec2S64 *mark_out, Rng1S64 *visible_row_range_out, UI_ScrollListSignal *signal_out) -{ - //- rjf: unpack arguments - Rng1S64 scroll_row_idx_range = r1s64(params->item_range.min, ClampBot(params->item_range.min, params->item_range.max-1)); - S64 num_possible_visible_rows = (S64)(params->dim_px.y/params->row_height_px); - - //- rjf: do keyboard navigation - B32 moved = 0; - if(params->flags & UI_ScrollListFlag_Nav && cursor_out != 0 && ui_is_focus_active()) - { - UI_EventList *events = ui_events(); - Vec2S64 cursor = *cursor_out; - Vec2S64 mark = mark_out ? *mark_out : cursor; - for(UI_EventNode *n = events->first, *next = 0; n != 0; n = next) - { - next = n->next; - UI_Event *evt = &n->v; - if((evt->delta_2s32.x == 0 && evt->delta_2s32.y == 0) || - evt->flags & UI_EventFlag_Delete) - { - continue; - } - ui_eat_event(events, n); - moved = 1; - switch(evt->delta_unit) - { - default:{moved = 0;}break; - case UI_EventDeltaUnit_Char: - { - for(Axis2 axis = (Axis2)0; axis < Axis2_COUNT; axis = (Axis2)(axis+1)) - { - cursor.v[axis] += evt->delta_2s32.v[axis]; - if(cursor.v[axis] < params->cursor_range.min.v[axis]) - { - cursor.v[axis] = params->cursor_range.max.v[axis]; - } - if(cursor.v[axis] > params->cursor_range.max.v[axis]) - { - cursor.v[axis] = params->cursor_range.min.v[axis]; - } - cursor.v[axis] = clamp_1s64(r1s64(params->cursor_range.min.v[axis], params->cursor_range.max.v[axis]), cursor.v[axis]); - } - }break; - case UI_EventDeltaUnit_Word: - case UI_EventDeltaUnit_Line: - case UI_EventDeltaUnit_Page: - { - cursor.x = (evt->delta_2s32.x>0 ? params->cursor_range.max.x : evt->delta_2s32.x<0 ? params->cursor_range.min.x + !!params->cursor_min_is_empty_selection[Axis2_X] : cursor.x); - cursor.y += ((evt->delta_2s32.y>0 ? +(num_possible_visible_rows-3) : evt->delta_2s32.y<0 ? -(num_possible_visible_rows-3) : 0)); - cursor.y = clamp_1s64(r1s64(params->cursor_range.min.y + !!params->cursor_min_is_empty_selection[Axis2_Y], params->cursor_range.max.y), cursor.y); - }break; - case UI_EventDeltaUnit_Whole: - { - for(Axis2 axis = (Axis2)0; axis < Axis2_COUNT; axis = (Axis2)(axis+1)) - { - cursor.v[axis] = (evt->delta_2s32.v[axis]>0 ? params->cursor_range.max.v[axis] : evt->delta_2s32.v[axis]<0 ? params->cursor_range.min.v[axis] + !!params->cursor_min_is_empty_selection[axis] : cursor.v[axis]); - } - }break; - } - if(!(evt->flags & UI_EventFlag_KeepMark)) - { - mark = cursor; - } - } - if(moved) - { - *cursor_out = cursor; - if(mark_out) - { - *mark_out = mark; - } - } - } - - //- rjf: moved -> snap - if(params->flags & UI_ScrollListFlag_Snap && moved) - { - S64 cursor_item_idx = cursor_out->y-1; - if(params->item_range.min <= cursor_item_idx && cursor_item_idx <= params->item_range.max) - { - //- rjf: compute visible row range - Rng1S64 visible_row_range = r1s64(scroll_pt->idx + 0 - !!(scroll_pt->off < 0), - scroll_pt->idx + 0 + num_possible_visible_rows + 1); - - //- rjf: compute cursor row range from cursor item - Rng1S64 cursor_visibility_row_range = {0}; - if(params->row_blocks.count == 0) - { - cursor_visibility_row_range = r1s64(cursor_item_idx-1, cursor_item_idx+3); - } - else - { - cursor_visibility_row_range.min = (S64)ui_scroll_list_row_from_item(¶ms->row_blocks, (U64)cursor_item_idx); - cursor_visibility_row_range.max = cursor_visibility_row_range.min + 4; - } - - //- rjf: compute deltas & apply - S64 min_delta = Min(0, cursor_visibility_row_range.min-visible_row_range.min); - S64 max_delta = Max(0, cursor_visibility_row_range.max-visible_row_range.max); - S64 new_idx = scroll_pt->idx+min_delta+max_delta; - new_idx = clamp_1s64(scroll_row_idx_range, new_idx); - ui_scroll_pt_target_idx(scroll_pt, new_idx); - } - } - - //- rjf: output signal - if(signal_out != 0) - { - signal_out->cursor_moved = moved; - } - - //- rjf: determine ranges & limits - Rng1S64 visible_row_range = r1s64(scroll_pt->idx + (S64)(scroll_pt->off) + 0 - !!(scroll_pt->off < 0), - scroll_pt->idx + (S64)(scroll_pt->off) + 0 + num_possible_visible_rows + 1); - visible_row_range.min = clamp_1s64(scroll_row_idx_range, visible_row_range.min); - visible_row_range.max = clamp_1s64(scroll_row_idx_range, visible_row_range.max); - *visible_row_range_out = visible_row_range; - - //- rjf: store thread-locals - ui_scroll_list_scroll_bar_dim_px = ui_top_font_size()*1.5f; - ui_scroll_list_scroll_pt_ptr = scroll_pt; - ui_scroll_list_dim_px = params->dim_px; - ui_scroll_list_scroll_idx_rng = scroll_row_idx_range; - - //- rjf: build top-level container - UI_Box *container_box = &ui_g_nil_box; - UI_FixedWidth(params->dim_px.x) UI_FixedHeight(params->dim_px.y) UI_ChildLayoutAxis(Axis2_X) - { - container_box = ui_build_box_from_key(0, ui_key_zero()); - } - - //- rjf: build scrollable container - UI_Box *scrollable_container_box = &ui_g_nil_box; - UI_Parent(container_box) UI_ChildLayoutAxis(Axis2_Y) UI_FixedWidth(params->dim_px.x-ui_scroll_list_scroll_bar_dim_px) UI_FixedHeight(params->dim_px.y) - { - scrollable_container_box = ui_build_box_from_stringf(UI_BoxFlag_Clip|UI_BoxFlag_AllowOverflowY|UI_BoxFlag_Scroll, "###sp"); - scrollable_container_box->view_off.y = scrollable_container_box->view_off_target.y = params->row_height_px*mod_f32(scroll_pt->off, 1.f) + params->row_height_px*(scroll_pt->off < 0) - params->row_height_px*(scroll_pt->off == -1.f && scroll_pt->idx == 1); - } - - //- rjf: build vertical scroll bar - UI_Parent(container_box) UI_Focus(UI_FocusKind_Null) - { - ui_set_next_fixed_width(ui_scroll_list_scroll_bar_dim_px); - ui_set_next_fixed_height(ui_scroll_list_dim_px.y); - *ui_scroll_list_scroll_pt_ptr = ui_scroll_bar(Axis2_Y, - ui_px(ui_scroll_list_scroll_bar_dim_px, 1.f), - *ui_scroll_list_scroll_pt_ptr, - scroll_row_idx_range, - num_possible_visible_rows); - } - - //- rjf: begin scrollable region - ui_push_parent(container_box); - ui_push_parent(scrollable_container_box); - ui_push_pref_height(ui_px(params->row_height_px, 1.f)); -} - -internal void -ui_scroll_list_end(void) -{ - ui_pop_pref_height(); - UI_Box *scrollable_container_box = ui_pop_parent(); - UI_Box *container_box = ui_pop_parent(); - - //- rjf: scroll - { - UI_Signal sig = ui_signal_from_box(scrollable_container_box); - if(sig.scroll.y != 0) - { - S64 new_idx = ui_scroll_list_scroll_pt_ptr->idx + sig.scroll.y; - new_idx = clamp_1s64(ui_scroll_list_scroll_idx_rng, new_idx); - ui_scroll_pt_target_idx(ui_scroll_list_scroll_pt_ptr, new_idx); - } - ui_scroll_pt_clamp_idx(ui_scroll_list_scroll_pt_ptr, ui_scroll_list_scroll_idx_rng); - } -} +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Basic Widgets + +internal void +ui_divider(UI_Size size) +{ + UI_Box *parent = ui_top_parent(); + ui_set_next_pref_size(parent->child_layout_axis, size); + ui_set_next_child_layout_axis(parent->child_layout_axis); + UI_Box *box = ui_build_box_from_key(0, ui_key_zero()); + UI_Parent(box) UI_PrefSize(parent->child_layout_axis, ui_pct(1, 0)) + { + ui_build_box_from_key(UI_BoxFlag_DrawSideBottom, ui_key_zero()); + ui_build_box_from_key(0, ui_key_zero()); + } +} + +internal UI_Signal +ui_label(String8 string) +{ + UI_Box *box = ui_build_box_from_string(UI_BoxFlag_DrawText, str8_zero()); + ui_box_equip_display_string(box, string); + UI_Signal interact = ui_signal_from_box(box); + return interact; +} + +internal UI_Signal +ui_labelf(char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Signal result = ui_label(string); + scratch_end(scratch); + return result; +} + +internal void +ui_label_multiline(F32 max, String8 string) +{ + Temp scratch = scratch_begin(0, 0); + ui_set_next_child_layout_axis(Axis2_Y); + ui_set_next_pref_height(ui_children_sum(1)); + UI_Box *box = ui_build_box_from_key(0, ui_key_zero()); + String8List lines = f_wrapped_string_lines_from_font_size_string_max(scratch.arena, ui_top_font(), ui_top_font_size(), 0, ui_top_tab_size(), string, max); + for(String8Node *n = lines.first; n != 0; n = n->next) + { + ui_label(n->string); + } + scratch_end(scratch); +} + +internal void +ui_label_multilinef(F32 max, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + ui_label_multiline(max, string); + scratch_end(scratch); +} + +internal UI_Signal +ui_button(String8 string) +{ + ui_set_next_hover_cursor(OS_Cursor_HandPoint); + UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable| + UI_BoxFlag_DrawBackground| + UI_BoxFlag_DrawBorder| + UI_BoxFlag_DrawText| + UI_BoxFlag_DrawHotEffects| + UI_BoxFlag_DrawActiveEffects, + string); + UI_Signal interact = ui_signal_from_box(box); + return interact; +} + +internal UI_Signal +ui_buttonf(char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Signal result = ui_button(string); + scratch_end(scratch); + return result; +} + +internal UI_Signal +ui_hover_label(String8 string) +{ + UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable|UI_BoxFlag_DrawText, string); + UI_Signal interact = ui_signal_from_box(box); + if(ui_hovering(interact)) + { + box->flags |= UI_BoxFlag_DrawBorder; + } + return interact; +} + +internal UI_Signal +ui_hover_labelf(char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Signal sig = ui_hover_label(string); + scratch_end(scratch); + return sig; +} + +typedef struct UI_LineEditDrawData UI_LineEditDrawData; +struct UI_LineEditDrawData +{ + String8 edited_string; + TxtPt cursor; + TxtPt mark; +}; + +internal UI_BOX_CUSTOM_DRAW(ui_line_edit_draw) +{ + UI_LineEditDrawData *draw_data = (UI_LineEditDrawData *)user_data; + F_Tag font = box->font; + F32 font_size = box->font_size; + F32 tab_size = box->tab_size; + Vec4F32 cursor_color = box->palette->colors[UI_ColorCode_Cursor]; + cursor_color.w *= box->parent->parent->focus_active_t; + Vec4F32 select_color = box->palette->colors[UI_ColorCode_Selection]; + select_color.w *= (box->parent->parent->focus_active_t*0.2f + 0.8f); + Vec2F32 text_position = ui_box_text_position(box); + String8 edited_string = draw_data->edited_string; + TxtPt cursor = draw_data->cursor; + TxtPt mark = draw_data->mark; + F32 cursor_pixel_off = f_dim_from_tag_size_string(font, font_size, 0, tab_size, str8_prefix(edited_string, cursor.column-1)).x; + F32 mark_pixel_off = f_dim_from_tag_size_string(font, font_size, 0, tab_size, str8_prefix(edited_string, mark.column-1)).x; + F32 cursor_thickness = ClampBot(4.f, font_size/6.f); + Rng2F32 cursor_rect = + { + text_position.x + cursor_pixel_off - cursor_thickness*0.50f, + box->rect.y0+4.f, + text_position.x + cursor_pixel_off + cursor_thickness*0.50f, + box->rect.y1-4.f, + }; + Rng2F32 mark_rect = + { + text_position.x + mark_pixel_off - cursor_thickness*0.50f, + box->rect.y0+2.f, + text_position.x + mark_pixel_off + cursor_thickness*0.50f, + box->rect.y1-2.f, + }; + Rng2F32 select_rect = union_2f32(cursor_rect, mark_rect); + d_rect(select_rect, select_color, font_size/2.f, 0, 1.f); + d_rect(cursor_rect, cursor_color, 0.f, 0, 1.f); +} + +internal UI_Signal +ui_line_edit(TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, String8 pre_edit_value, String8 string) +{ + //- rjf: make key + UI_Key key = ui_key_from_string(ui_active_seed_key(), string); + + //- rjf: calculate focus + B32 is_auto_focus_hot = ui_is_key_auto_focus_hot(key); + B32 is_auto_focus_active = ui_is_key_auto_focus_active(key); + ui_push_focus_hot(is_auto_focus_hot ? UI_FocusKind_On : UI_FocusKind_Null); + ui_push_focus_active(is_auto_focus_active ? UI_FocusKind_On : UI_FocusKind_Null); + B32 is_focus_hot = ui_is_focus_hot(); + B32 is_focus_active = ui_is_focus_active(); + B32 is_focus_hot_disabled = (!is_focus_hot && ui_top_focus_hot() == UI_FocusKind_On); + B32 is_focus_active_disabled = (!is_focus_active && ui_top_focus_active() == UI_FocusKind_On); + + //- rjf: build top-level box + ui_set_next_hover_cursor(is_focus_active ? OS_Cursor_IBar : OS_Cursor_HandPoint); + UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawBackground| + UI_BoxFlag_DrawBorder| + UI_BoxFlag_MouseClickable| + UI_BoxFlag_ClickToFocus| + ((is_auto_focus_hot || is_auto_focus_active)*UI_BoxFlag_KeyboardClickable)| + UI_BoxFlag_DrawHotEffects| + (is_focus_active || is_focus_active_disabled)*(UI_BoxFlag_Clip|UI_BoxFlag_AllowOverflowX|UI_BoxFlag_ViewClamp), + key); + + //- rjf: take navigation actions for editing + B32 changes_made = 0; + if(is_focus_active) + { + Temp scratch = scratch_begin(0, 0); + UI_EventList *events = ui_events(); + for(UI_EventNode *n = events->first, *next = 0; n != 0; n = next) + { + String8 edit_string = str8(edit_buffer, edit_string_size_out[0]); + next = n->next; + + // rjf: do not consume anything that doesn't fit a single-line's operations + if((n->v.kind != UI_EventKind_Edit && n->v.kind != UI_EventKind_Navigate && n->v.kind != UI_EventKind_Text) || n->v.delta_2s32.y != 0) + { + continue; + } + + // rjf: map this action to an op + UI_TxtOp op = ui_single_line_txt_op_from_event(scratch.arena, &n->v, edit_string, *cursor, *mark); + + // rjf: perform replace range + if(!txt_pt_match(op.range.min, op.range.max) || op.replace.size != 0) + { + String8 new_string = ui_push_string_replace_range(scratch.arena, edit_string, r1s64(op.range.min.column, op.range.max.column), op.replace); + new_string.size = Min(edit_buffer_size, new_string.size); + MemoryCopy(edit_buffer, new_string.str, new_string.size); + edit_string_size_out[0] = new_string.size; + } + + // rjf: perform copy + if(op.flags & UI_TxtOpFlag_Copy) + { + os_set_clipboard_text(op.copy); + } + + // rjf: commit op's changed cursor & mark to caller-provided state + *cursor = op.cursor; + *mark = op.mark; + + // rjf: consume event + { + ui_eat_event(events, n); + changes_made = 1; + } + } + scratch_end(scratch); + } + + //- rjf: build contents + TxtPt mouse_pt = {0}; + F32 cursor_off = 0; + UI_Parent(box) + { + String8 edit_string = str8(edit_buffer, edit_string_size_out[0]); + if(!is_focus_active && !is_focus_active_disabled) + { + String8 display_string = ui_display_part_from_key_string(string); + if(pre_edit_value.size != 0) + { + display_string = pre_edit_value; + } + ui_label(display_string); + } + else + { + F32 total_text_width = f_dim_from_tag_size_string(ui_top_font(), ui_top_font_size(), 0, ui_top_tab_size(), edit_string).x; + ui_set_next_pref_width(ui_px(total_text_width+ui_top_font_size()*5, 1.f)); + UI_Box *editstr_box = ui_build_box_from_stringf(UI_BoxFlag_DrawText|UI_BoxFlag_DisableTextTrunc, "###editstr"); + UI_LineEditDrawData *draw_data = push_array(ui_build_arena(), UI_LineEditDrawData, 1); + draw_data->edited_string = push_str8_copy(ui_build_arena(), edit_string); + draw_data->cursor = *cursor; + draw_data->mark = *mark; + ui_box_equip_display_string(editstr_box, edit_string); + ui_box_equip_custom_draw(editstr_box, ui_line_edit_draw, draw_data); + mouse_pt = txt_pt(1, 1+ui_box_char_pos_from_xy(editstr_box, ui_mouse())); + cursor_off = f_dim_from_tag_size_string(ui_top_font(), ui_top_font_size(), 0, ui_top_tab_size(), str8_prefix(edit_string, cursor->column-1)).x; + } + } + + //- rjf: interact + UI_Signal sig = ui_signal_from_box(box); + if(!is_focus_active && sig.f&(UI_SignalFlag_DoubleClicked|UI_SignalFlag_KeyboardPressed)) + { + String8 edit_string = pre_edit_value; + edit_string.size = Min(edit_buffer_size, pre_edit_value.size); + MemoryCopy(edit_buffer, edit_string.str, edit_string.size); + edit_string_size_out[0] = edit_string.size; + ui_set_auto_focus_active_key(key); + ui_kill_action(); + *cursor = txt_pt(1, edit_string.size+1); + *mark = txt_pt(1, 1); + } + if(is_focus_active && sig.f&UI_SignalFlag_KeyboardPressed) + { + ui_set_auto_focus_active_key(ui_key_zero()); + sig.f |= UI_SignalFlag_Commit; + } + if(is_focus_active && ui_dragging(sig)) + { + if(ui_pressed(sig)) + { + *mark = mouse_pt; + } + *cursor = mouse_pt; + } + + //- rjf: focus cursor + { + Rng1F32 cursor_range_px = r1f32(cursor_off-ui_top_font_size()*2.f, cursor_off+ui_top_font_size()*2.f); + Rng1F32 visible_range_px = r1f32(box->view_off_target.x, box->view_off_target.x + dim_2f32(box->rect).x); + cursor_range_px.min = ClampBot(0, cursor_range_px.min); + cursor_range_px.max = ClampBot(0, cursor_range_px.max); + F32 min_delta = cursor_range_px.min-visible_range_px.min; + F32 max_delta = cursor_range_px.max-visible_range_px.max; + min_delta = Min(min_delta, 0); + max_delta = Max(max_delta, 0); + box->view_off_target.x += min_delta; + box->view_off_target.x += max_delta; + } + + //- rjf: pop focus + ui_pop_focus_hot(); + ui_pop_focus_active(); + + return sig; +} + +internal UI_Signal +ui_line_editf(TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, String8 pre_edit_value, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Signal result = ui_line_edit(cursor, mark, edit_buffer, edit_buffer_size, edit_string_size_out, pre_edit_value, string); + scratch_end(scratch); + return result; +} + +//////////////////////////////// +//~ rjf: Images + +typedef struct UI_ImageDrawData UI_ImageDrawData; +struct UI_ImageDrawData +{ + R_Handle texture; + R_Tex2DSampleKind sample_kind; + Rng2F32 region; + Vec4F32 tint; + F32 blur; +}; + +internal UI_BOX_CUSTOM_DRAW(ui_image_draw) +{ + UI_ImageDrawData *draw_data = (UI_ImageDrawData *)user_data; + if(r_handle_match(draw_data->texture, r_handle_zero())) + { + R_Rect2DInst *inst = d_rect(box->rect, v4f32(0, 0, 0, 0), 0, 0, 1.f); + MemoryCopyArray(inst->corner_radii, box->corner_radii); + } + else D_Tex2DSampleKindScope(draw_data->sample_kind) + { + R_Rect2DInst *inst = d_img(box->rect, draw_data->region, draw_data->texture, draw_data->tint, 0, 0, 0); + MemoryCopyArray(inst->corner_radii, box->corner_radii); + } + if(draw_data->blur > 0.01f) + { + Rng2F32 clip = box->rect; + for(UI_Box *b = box->parent; !ui_box_is_nil(b); b = b->parent) + { + if(b->flags & UI_BoxFlag_Clip) + { + clip = intersect_2f32(b->rect, clip); + } + } + R_PassParams_Blur *blur = d_blur(intersect_2f32(clip, box->rect), draw_data->blur, 0); + MemoryCopyArray(blur->corner_radii, box->corner_radii); + } +} + +internal UI_Signal +ui_image(R_Handle texture, R_Tex2DSampleKind sample_kind, Rng2F32 region, Vec4F32 tint, F32 blur, String8 string) +{ + UI_Box *box = ui_build_box_from_string(0, string); + UI_ImageDrawData *draw_data = push_array(ui_build_arena(), UI_ImageDrawData, 1); + draw_data->texture = texture; + draw_data->sample_kind = sample_kind; + draw_data->region = region; + draw_data->tint = tint; + draw_data->blur = blur; + ui_box_equip_custom_draw(box, ui_image_draw, draw_data); + UI_Signal sig = ui_signal_from_box(box); + return sig; +} + +internal UI_Signal +ui_imagef(R_Handle texture, R_Tex2DSampleKind sample_kind, Rng2F32 region, Vec4F32 tint, F32 blur, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Signal result = ui_image(texture, sample_kind, region, tint, blur, string); + scratch_end(scratch); + return result; +} + +//////////////////////////////// +//~ rjf: Special Buttons + +internal UI_Signal +ui_expander(B32 is_expanded, String8 string) +{ + ui_set_next_hover_cursor(OS_Cursor_HandPoint); + ui_set_next_text_alignment(UI_TextAlign_Center); + ui_set_next_font(ui_icon_font()); + UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable|UI_BoxFlag_DrawText, string); + ui_box_equip_display_string(box, is_expanded ? str8_lit("v") : str8_lit(">")); + UI_Signal sig = ui_signal_from_box(box); + return sig; +} + +internal UI_Signal +ui_expanderf(B32 is_expanded, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Signal sig = ui_expander(is_expanded, string); + scratch_end(scratch); + return sig; +} + +internal UI_Signal +ui_sort_header(B32 sorting, B32 ascending, String8 string) +{ + ui_set_next_child_layout_axis(Axis2_X); + ui_set_next_hover_cursor(OS_Cursor_HandPoint); + UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable|UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawActiveEffects, string); + ui_push_parent(box); + + // rjf: make icon + if(sorting) + { + ui_set_next_pref_width(ui_em(1.8f, 1.f)); + ui_set_next_text_alignment(UI_TextAlign_Center); + ui_set_next_font(ui_icon_font()); + UI_Box *icon = ui_build_box_from_string(UI_BoxFlag_DrawText, str8_lit("")); + ui_box_equip_display_string(icon, ascending ? str8_lit("^") : str8_lit("v")); + } + + // rjf: make text + { + ui_label(string); + } + + ui_pop_parent(); + UI_Signal interact = ui_signal_from_box(box); + return interact; +} + +internal UI_Signal +ui_sort_headerf(B32 sorting, B32 ascending, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Signal sig = ui_sort_header(sorting, ascending, string); + scratch_end(scratch); + return sig; +} + +//////////////////////////////// +//~ rjf: Color Pickers + +//- rjf: tooltips + +internal void +ui_do_color_tooltip_hsv(Vec3F32 hsv) +{ + Vec3F32 rgb = rgb_from_hsv(hsv); + UI_Tooltip UI_Padding(ui_em(2.f, 1.f)) + { + UI_PrefWidth(ui_em(22.f, 1.f)) UI_PrefHeight(ui_em(6.f, 1.f)) UI_Row UI_Padding(ui_pct(1, 0)) + { + UI_Palette(ui_build_palette(ui_top_palette(), .background = v4f32(rgb.x, rgb.y, rgb.z, 1.f))) + UI_CornerRadius(4.f) + UI_PrefWidth(ui_em(6.f, 1.f)) UI_PrefHeight(ui_em(6.f, 1.f)) + ui_build_box_from_string(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, str8_lit("")); + } + ui_spacer(ui_em(0.3f, 1.f)); + UI_PrefWidth(ui_em(22.f, 1.f)) UI_TextAlignment(UI_TextAlign_Center) + { + ui_labelf("Hex: #%02x%02x%02x", (U8)(rgb.x*255.f), (U8)(rgb.y*255.f), (U8)(rgb.z*255.f)); + } + ui_spacer(ui_em(0.3f, 1.f)); + UI_PrefWidth(ui_em(22.f, 1.f)) UI_PrefHeight(ui_children_sum(1)) UI_Row + { + UI_WidthFill UI_Column UI_PrefHeight(ui_em(1.8f, 1.f)) + { + ui_labelf("Red: %.2f", rgb.x); + ui_labelf("Green: %.2f", rgb.y); + ui_labelf("Blue: %.2f", rgb.z); + } + UI_WidthFill UI_Column UI_PrefHeight(ui_em(1.8f, 1.f)) + { + ui_labelf("Hue: %.2f", hsv.x); + ui_labelf("Sat: %.2f", hsv.y); + ui_labelf("Val: %.2f", hsv.z); + } + } + } +} + +internal void +ui_do_color_tooltip_hsva(Vec4F32 hsva) +{ + Vec3F32 hsv = v3f32(hsva.x, hsva.y, hsva.z); + Vec3F32 rgb = rgb_from_hsv(hsv); + Vec4F32 rgba = v4f32(rgb.x, rgb.y, rgb.z, hsva.w); + UI_Tooltip UI_Padding(ui_em(2.f, 1.f)) + { + UI_PrefWidth(ui_em(22.f, 1.f)) UI_PrefHeight(ui_em(6.f, 1.f)) UI_Row UI_Padding(ui_pct(1, 0)) + { + UI_Palette(ui_build_palette(ui_top_palette(), .background = rgba)) + UI_CornerRadius(4.f) + UI_PrefWidth(ui_em(6.f, 1.f)) UI_PrefHeight(ui_em(6.f, 1.f)) + ui_build_box_from_string(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, str8_lit("")); + } + ui_spacer(ui_em(0.3f, 1.f)); + UI_PrefWidth(ui_em(22.f, 1.f)) UI_TextAlignment(UI_TextAlign_Center) + { + ui_labelf("Hex: #%02x%02x%02x%02x", (U8)(rgba.x*255.f), (U8)(rgba.y*255.f), (U8)(rgba.z*255.f), (U8)(rgba.w*255.f)); + } + ui_spacer(ui_em(0.3f, 1.f)); + UI_PrefWidth(ui_em(22.f, 1.f)) UI_PrefHeight(ui_children_sum(1)) UI_Row + { + UI_WidthFill UI_Column UI_PrefHeight(ui_em(1.8f, 1.f)) + { + ui_labelf("Red: %.2f", rgba.x); + ui_labelf("Green: %.2f", rgba.y); + ui_labelf("Blue: %.2f", rgba.z); + ui_labelf("Alpha: %.2f", rgba.w); + } + UI_WidthFill UI_Column UI_PrefHeight(ui_em(1.8f, 1.f)) + { + ui_labelf("Hue: %.2f", hsva.x); + ui_labelf("Sat: %.2f", hsva.y); + ui_labelf("Val: %.2f", hsva.z); + ui_labelf("Alpha: %.2f", hsva.w); + } + } + } +} + +//- rjf: saturation/value picker + +typedef struct UI_SatValDrawData UI_SatValDrawData; +struct UI_SatValDrawData +{ + F32 hue; + F32 sat; + F32 val; +}; + +internal UI_BOX_CUSTOM_DRAW(ui_sat_val_picker_draw) +{ + UI_SatValDrawData *data = (UI_SatValDrawData *)user_data; + + // rjf: hue => rgb + Vec3F32 hue_rgb = rgb_from_hsv(v3f32(data->hue, 1, 1)); + + // rjf: white -> rgb background + { + R_Rect2DInst *inst = d_rect(pad_2f32(box->rect, -1.f), v4f32(hue_rgb.x, hue_rgb.y, hue_rgb.z, 1), 4.f, 0, 1.f); + inst->colors[Corner_00] = inst->colors[Corner_01] = v4f32(1, 1, 1, 1); + } + + // rjf: black gradient overlay + { + R_Rect2DInst *inst = d_rect(pad_2f32(box->rect, -1.f), v4f32(0, 0, 0, 0), 4.f, 0, 1.f); + inst->colors[Corner_01] = v4f32(0, 0, 0, 1); + inst->colors[Corner_11] = v4f32(0, 0, 0, 1); + } + + // rjf: indicator + { + Vec2F32 box_rect_dim = dim_2f32(box->rect); + Vec2F32 center = v2f32(box->rect.x0 + data->sat*box_rect_dim.x, box->rect.y0 + (1-data->val)*box_rect_dim.y); + F32 half_size = box->font_size * (0.5f + box->active_t*0.2f); + Rng2F32 rect = r2f32p(center.x - half_size, + center.y - half_size, + center.x + half_size, + center.y + half_size); + d_rect(rect, v4f32(1, 1, 1, 1), half_size/2, 2.f, 1.f); + } +} + +internal UI_Signal +ui_sat_val_picker(F32 hue, F32 *out_sat, F32 *out_val, String8 string) +{ + // rjf: build & interact + ui_set_next_hover_cursor(OS_Cursor_HandPoint); + UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable, string); + UI_SatValDrawData *user = push_array(ui_build_arena(), UI_SatValDrawData, 1); + ui_box_equip_custom_draw(box, ui_sat_val_picker_draw, user); + UI_Signal sig = ui_signal_from_box(box); + + // rjf: click+draw behavior + if(ui_dragging(sig)) + { + Vec2F32 dim = dim_2f32(box->rect); + *out_sat = (ui_mouse().x - box->rect.x0) / dim.x; + *out_val = 1 - (ui_mouse().y - box->rect.y0) / dim.y; + *out_sat = Clamp(0, *out_sat, 1); + *out_val = Clamp(0, *out_val, 1); + ui_do_color_tooltip_hsv(v3f32(hue, *out_sat, *out_val)); + if(ui_pressed(sig)) + { + Vec2F32 data = v2f32(*out_sat, *out_val); + ui_store_drag_struct(&data); + } + if(ui_slot_press(UI_EventActionSlot_Cancel)) + { + Vec2F32 data = *ui_get_drag_struct(Vec2F32); + *out_sat = data.x; + *out_val = data.y; + ui_kill_action(); + } + } + + // rjf: fill draw data + { + user->hue = hue; + user->sat = *out_sat; + user->val = *out_val; + } + + return sig; +} + +internal UI_Signal +ui_sat_val_pickerf(F32 hue, F32 *out_sat, F32 *out_val, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Signal sig = ui_sat_val_picker(hue, out_sat, out_val, string); + scratch_end(scratch); + return sig; +} + +//- rjf: hue picker + +typedef struct UI_HueDrawData UI_HueDrawData; +struct UI_HueDrawData +{ + F32 hue; + F32 sat; + F32 val; +}; + +internal UI_BOX_CUSTOM_DRAW(ui_hue_picker_draw) +{ + UI_HueDrawData *data = (UI_HueDrawData *)user_data; + Vec2F32 dim = dim_2f32(box->rect); + F32 segment_dim = floor_f32(dim.y/6.f); + Rng2F32 hue_cycle_rect = box->rect; + Vec2F32 hue_cycle_center = center_2f32(hue_cycle_rect); + hue_cycle_rect.x0 += (hue_cycle_center.x - hue_cycle_rect.x0) * 0.3f; + hue_cycle_rect.x1 += (hue_cycle_center.x - hue_cycle_rect.x1) * 0.3f; + Rng2F32 rect = r2f32p(hue_cycle_rect.x0, + hue_cycle_rect.y0, + hue_cycle_rect.x1, + hue_cycle_rect.y0 + segment_dim); + for(int seg = 0; seg < 6; seg += 1) + { + F32 hue0 = (F32)(seg)/6; + F32 hue1 = (F32)(seg+1)/6; + Vec3F32 rgb0 = rgb_from_hsv(v3f32(hue0, 1, 1)); + Vec3F32 rgb1 = rgb_from_hsv(v3f32(hue1, 1, 1)); + Vec4F32 rgba0 = v4f32(rgb0.x, rgb0.y, rgb0.z, 1); + Vec4F32 rgba1 = v4f32(rgb1.x, rgb1.y, rgb1.z, 1); + R_Rect2DInst *inst = d_rect(rect, v4f32(0, 0, 0, 0), 0, 0, 0.f); + inst->colors[Corner_00] = rgba0; + inst->colors[Corner_01] = rgba1; + inst->colors[Corner_10] = rgba0; + inst->colors[Corner_11] = rgba1; + rect.y0 += segment_dim; + rect.y1 += segment_dim; + } + + // rjf: indicator + { + Vec2F32 box_rect_dim = dim_2f32(box->rect); + Vec2F32 center = v2f32((box->rect.x0+box->rect.x1)/2, box->rect.y0 + data->hue*box_rect_dim.y); + F32 half_size = box->font_size * (0.5f + box->active_t*0.2f); + Rng2F32 rect = r2f32p(center.x - half_size, + center.y - 2.f, + center.x + half_size, + center.y + 2.f); + d_rect(rect, v4f32(1, 1, 1, 1), half_size/2, 2.f, 1.f); + } +} + +internal UI_Signal +ui_hue_picker(F32 *out_hue, F32 sat, F32 val, String8 string) +{ + // rjf: build & interact + ui_set_next_hover_cursor(OS_Cursor_HandPoint); + UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable, string); + UI_HueDrawData *user = push_array(ui_build_arena(), UI_HueDrawData, 1); + ui_box_equip_custom_draw(box, ui_hue_picker_draw, user); + UI_Signal sig = ui_signal_from_box(box); + + // rjf: click+draw behavior + if(ui_dragging(sig)) + { + Vec2F32 dim = dim_2f32(box->rect); + *out_hue = (ui_mouse().y - box->rect.y0) / dim.y; + *out_hue = Clamp(0, *out_hue, 1); + ui_do_color_tooltip_hsv(v3f32(*out_hue, sat, val)); + if(ui_pressed(sig)) + { + ui_store_drag_struct(out_hue); + } + if(ui_slot_press(UI_EventActionSlot_Cancel)) + { + *out_hue = *ui_get_drag_struct(F32); + ui_kill_action(); + } + } + + // rjf: fill draw data + { + user->hue = *out_hue; + user->sat = sat; + user->val = val; + } + + return sig; +} + +internal UI_Signal +ui_hue_pickerf(F32 *out_hue, F32 sat, F32 val, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Signal sig = ui_hue_picker(out_hue, sat, val, string); + scratch_end(scratch); + return sig; +} + +//- rjf: alpha picker + +typedef struct UI_AlphaDrawData UI_AlphaDrawData; +struct UI_AlphaDrawData +{ + F32 alpha; +}; + +internal UI_BOX_CUSTOM_DRAW(ui_alpha_picker_draw) +{ + UI_AlphaDrawData *data = (UI_AlphaDrawData *)user_data; + Vec2F32 dim = dim_2f32(box->rect); + + // rjf: build gradient + { + Rng2F32 rect = box->rect; + Vec2F32 center = center_2f32(rect); + rect.x0 += (center.x - rect.x0) * 0.3f; + rect.x1 += (center.x - rect.x1) * 0.3f; + R_Rect2DInst *inst = d_rect(rect, v4f32(0, 0, 0, 0), 0, 0, 0); + inst->colors[Corner_00] = inst->colors[Corner_10] = v4f32(1, 1, 1, 1); + } + + // rjf: indicator + { + Vec2F32 box_rect_dim = dim_2f32(box->rect); + Vec2F32 center = v2f32((box->rect.x0+box->rect.x1)/2, box->rect.y0 + (1-data->alpha)*box_rect_dim.y); + F32 half_size = box->font_size * (0.5f + box->active_t*0.2f); + Rng2F32 rect = r2f32p(center.x - half_size, + center.y - 2.f, + center.x + half_size, + center.y + 2.f); + d_rect(rect, v4f32(1, 1, 1, 1), half_size/2, 2.f, 1.f); + } +} + +internal UI_Signal +ui_alpha_picker(F32 *out_alpha, String8 string) +{ + // rjf: build & interact + ui_set_next_hover_cursor(OS_Cursor_HandPoint); + UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable, string); + UI_AlphaDrawData *user = push_array(ui_build_arena(), UI_AlphaDrawData, 1); + ui_box_equip_custom_draw(box, ui_alpha_picker_draw, user); + UI_Signal sig = ui_signal_from_box(box); + + // rjf: click+draw behavior + if(ui_dragging(sig)) + { + Vec2F32 dim = dim_2f32(box->rect); + F32 drag_pct = (ui_mouse().y - box->rect.y0) / dim.y; + drag_pct = Clamp(0, drag_pct, 1); + *out_alpha = 1-drag_pct; + if(ui_pressed(sig)) + { + ui_store_drag_struct(out_alpha); + } + if(ui_slot_press(UI_EventActionSlot_Cancel)) + { + *out_alpha = *ui_get_drag_struct(F32); + ui_kill_action(); + } + } + + // rjf: fill draw data + { + user->alpha = *out_alpha; + } + + return sig; +} + +internal UI_Signal +ui_alpha_pickerf(F32 *out_alpha, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Signal sig = ui_alpha_picker(out_alpha, string); + scratch_end(scratch); + return sig; +} + +//////////////////////////////// +//~ rjf: Simple Layout Widgets + +internal UI_Box *ui_row_begin(void) { return ui_named_row_begin(str8_lit("")); } +internal UI_Signal ui_row_end(void) { return ui_named_row_end(); } +internal UI_Box *ui_column_begin(void) { return ui_named_column_begin(str8_lit("")); } +internal UI_Signal ui_column_end(void) { return ui_named_column_end(); } + +internal UI_Box * +ui_named_row_begin(String8 string) +{ + ui_set_next_child_layout_axis(Axis2_X); + UI_Box *box = ui_build_box_from_string(0, string); + ui_push_parent(box); + return box; +} + +internal UI_Signal +ui_named_row_end(void) +{ + UI_Box *box = ui_pop_parent(); + UI_Signal sig = ui_signal_from_box(box); + return sig; +} + +internal UI_Box * +ui_named_column_begin(String8 string) +{ + ui_set_next_child_layout_axis(Axis2_Y); + UI_Box *box = ui_build_box_from_string(0, string); + ui_push_parent(box); + return box; +} + +internal UI_Signal +ui_named_column_end(void) +{ + UI_Box *box = ui_pop_parent(); + UI_Signal sig = ui_signal_from_box(box); + return sig; +} + +//////////////////////////////// +//~ rjf: Floating Panes + +internal UI_Box * +ui_pane_begin(Rng2F32 rect, String8 string) +{ + ui_push_rect(rect); + ui_set_next_child_layout_axis(Axis2_Y); + UI_Box *box = ui_build_box_from_string(UI_BoxFlag_Clickable|UI_BoxFlag_Clip|UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, string); + ui_pop_rect(); + ui_push_parent(box); + ui_push_pref_width(ui_pct(1, 0)); + return box; +} + +internal UI_Box * +ui_pane_beginf(Rng2F32 rect, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Box *box = ui_pane_begin(rect, string); + scratch_end(scratch); + return box; +} + +internal UI_Signal +ui_pane_end(void) +{ + ui_pop_pref_width(); + UI_Box *box = ui_pop_parent(); + UI_Signal sig = ui_signal_from_box(box); + return sig; +} + +//////////////////////////////// +//~ rjf: Tables + +thread_static U64 ui_ts_col_pct_count = 0; +thread_static F32 *ui_ts_col_pcts_stable = 0; +thread_static U64 ui_ts_vector_idx = 0; +thread_static U64 ui_ts_cell_idx = 0; + +internal void +ui_table_begin(U64 column_pct_count, F32 **column_pcts, String8 string) +{ + //- rjf: store off persistent, user-provided column info + ui_ts_col_pct_count = column_pct_count; + + //- rjf: build main table parent + ui_set_next_pref_height(ui_children_sum(1)); + ui_set_next_child_layout_axis(Axis2_Y); + UI_Box *table = ui_build_box_from_string(0, string); + ui_push_parent(table); + + //- rjf: build column boundaries + F32 x_off = (ui_ts_col_pct_count > 0 ? *column_pcts[0] : 0) * dim_2f32(table->rect).x; + for(U64 column_idx = 1; column_idx < ui_ts_col_pct_count; column_idx += 1) + { + // rjf: build base rectangle + Rng2F32 rect = {0}; + { + rect.x0 = x_off-3.f; + rect.y0 = 0; + rect.x1 = x_off+3.f; + rect.y1 = dim_2f32(table->rect).y; + x_off += *column_pcts[column_idx] * dim_2f32(table->rect).x; + } + + // rjf: make column boundary widget + UI_Rect(rect) + { + ui_set_next_hover_cursor(OS_Cursor_LeftRight); + UI_Box *box = ui_build_box_from_stringf(UI_BoxFlag_Clickable, "###%S_boundary_%I64u", table->string, column_idx); + + F32 *left_pct_ptr = column_idx < ui_ts_col_pct_count ? column_pcts[column_idx-1] : 0; + F32 *right_pct_ptr = column_idx < ui_ts_col_pct_count ? column_pcts[column_idx] : 0; + + // rjf: boundary dragging + UI_Signal interact = ui_signal_from_box(box); + if(ui_dragging(interact)) + { + if(ui_pressed(interact)) + { + Vec2F32 v = v2f32(*left_pct_ptr, *right_pct_ptr); + ui_store_drag_struct(&v); + } + + // rjf: calculate how much space we're dividing amongst the columns that + // the user can resize + F32 adjustable_table_dim = 0; + if(table->child_layout_axis == Axis2_Y) + { + adjustable_table_dim = dim_2f32(table->rect).x; + } + else + { + U64 child_idx = 0; + for(UI_Box *v = table->first; !ui_box_is_nil(v); v = v->next, child_idx += 1) + { + U64 column_idx = (child_idx+1); + if(column_idx < ui_ts_col_pct_count) + { + adjustable_table_dim += dim_2f32(v->rect).x; + } + else + { + break; + } + } + } + + // rjf: calculate diff + F32 min_size = 30.f; + F32 left_pct__before = ui_get_drag_struct(Vec2F32)->x; + F32 left_pixels__before = left_pct__before * adjustable_table_dim; + F32 left_pixels__after = left_pixels__before + ui_drag_delta().x; + + // rjf: clamp left side + if(left_pixels__after < min_size) + { + left_pixels__after = min_size; + } + + // rjf: calculate right side + F32 left_pct__after = left_pixels__after / adjustable_table_dim; + F32 pct_delta = left_pct__after - left_pct__before; + F32 right_pct__before = ui_get_drag_struct(Vec2F32)->y; + F32 right_pct__after = right_pct__before - pct_delta; + F32 right_pixels__after = right_pct__after * adjustable_table_dim; + + // rjf: clamp right side & back-solve + if(right_pixels__after < min_size) + { + right_pixels__after = min_size; + right_pct__after = right_pixels__after/adjustable_table_dim; + pct_delta = -(right_pct__after-right_pct__before); + left_pct__after = left_pct__before+pct_delta; + } + + // rjf: commit new percentages + *left_pct_ptr = left_pct__after; + *right_pct_ptr = right_pct__after; + } + } + } + + //- rjf: form stable pcts + ui_ts_col_pcts_stable = push_array(ui_build_arena(), F32, ui_ts_col_pct_count); + for(U64 idx = 0; idx < column_pct_count; idx += 1) + { + ui_ts_col_pcts_stable[idx] = *column_pcts[idx]; + } + + ui_ts_vector_idx = 0; +} + +internal void +ui_table_beginf(U64 column_pct_count, F32 **column_pcts, char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + ui_table_begin(column_pct_count, column_pcts, string); + scratch_end(scratch); +} + +internal void +ui_table_end(void) +{ + ui_pop_parent(); +} + +internal UI_Box * +ui_named_table_vector_begin(String8 string) +{ + ui_set_next_pref_width(ui_pct(1, 0)); + ui_set_next_child_layout_axis(Axis2_X); + UI_Box *vector = ui_build_box_from_string(UI_BoxFlag_DrawSideBottom, string); + ui_ts_vector_idx += 1; + ui_ts_cell_idx = 0; + ui_push_parent(vector); + return vector; +} + +internal UI_Box * +ui_named_table_vector_beginf(char *fmt, ...) +{ + Temp scratch = scratch_begin(0, 0); + va_list args; + va_start(args, fmt); + String8 string = push_str8fv(scratch.arena, fmt, args); + va_end(args); + UI_Box *vector = ui_named_table_vector_begin(string); + scratch_end(scratch); + return vector; +} + +internal UI_Box * +ui_table_vector_begin(void) +{ + UI_Box *table = ui_top_parent(); + UI_Box *vector = ui_named_table_vector_beginf("###tbl_vec_%p_%I64u", table, ui_ts_vector_idx); + return vector; +} + +internal UI_Signal +ui_table_vector_end(void) +{ + UI_Box *box = ui_pop_parent(); + return ui_signal_from_box(box); +} + +internal UI_Box * +ui_table_cell_begin(void) +{ + U64 column_idx = ui_ts_cell_idx; + F32 width_pct = column_idx < ui_ts_col_pct_count ? ui_ts_col_pcts_stable[column_idx] : 1.f; + return ui_table_cell_sized_begin(ui_pct(width_pct, 0)); +} + +internal UI_Signal +ui_table_cell_end(void) +{ + UI_Box *cell = ui_pop_parent(); + return ui_signal_from_box(cell); +} + +internal UI_Box * +ui_table_cell_sized_begin(UI_Size size) +{ + UI_Box *vector = ui_top_parent(); + U64 column_idx = ui_ts_cell_idx; + ui_ts_cell_idx += 1; + ui_set_next_pref_width(size); + ui_set_next_child_layout_axis(Axis2_X); + UI_Box *cell = ui_build_box_from_stringf((column_idx > 0 ? UI_BoxFlag_DrawSideLeft : 0), "###tbl_cell_%p_%I64u", vector, ui_ts_cell_idx); + ui_push_parent(cell); + return cell; +} + +//////////////////////////////// +//~ rjf: Scroll Regions + +internal void +ui_scroll_list_row_block_chunk_list_push(Arena *arena, UI_ScrollListRowBlockChunkList *list, U64 cap, UI_ScrollListRowBlock *block) +{ + UI_ScrollListRowBlockChunkNode *n = list->last; + if(n == 0 || n->count >= n->cap) + { + n = push_array(arena, UI_ScrollListRowBlockChunkNode, 1); + n->cap = cap; + n->v = push_array_no_zero(arena, UI_ScrollListRowBlock, n->cap); + SLLQueuePush(list->first, list->last, n); + list->chunk_count += 1; + } + MemoryCopyStruct(&n->v[n->count], block); + n->count += 1; + list->total_count += 1; +} + +internal UI_ScrollListRowBlockArray +ui_scroll_list_row_block_array_from_chunk_list(Arena *arena, UI_ScrollListRowBlockChunkList *list) +{ + UI_ScrollListRowBlockArray array = {0}; + array.count = list->total_count; + array.v = push_array_no_zero(arena, UI_ScrollListRowBlock, array.count); + U64 idx = 0; + for(UI_ScrollListRowBlockChunkNode *n = list->first; n != 0; n = n->next) + { + MemoryCopy(array.v+idx, n->v, sizeof(n->v[0])*n->count); + idx += n->count; + } + return array; +} + +internal U64 +ui_scroll_list_row_from_item(UI_ScrollListRowBlockArray *blocks, U64 item) +{ + U64 result = 0; + { + U64 row_idx = 0; + U64 item_idx = 0; + for(U64 block_idx = 0; block_idx < blocks->count; block_idx += 1) + { + UI_ScrollListRowBlock *block = &blocks->v[block_idx]; + U64 next_row_idx = row_idx + block->row_count; + U64 next_item_idx= item_idx+ block->item_count; + if(item_idx <= item && item < next_item_idx) + { + U64 item_off_rows = (item-item_idx) * (block->row_count/block->item_count); + result = row_idx + item_off_rows; + break; + } + row_idx = next_row_idx; + item_idx = next_item_idx; + } + } + return result; +} + +internal U64 +ui_scroll_list_item_from_row(UI_ScrollListRowBlockArray *blocks, U64 row) +{ + U64 result = 0; + { + U64 row_idx = 0; + U64 item_idx = 0; + for(U64 block_idx = 0; block_idx < blocks->count; block_idx += 1) + { + UI_ScrollListRowBlock *block = &blocks->v[block_idx]; + U64 next_row_idx = row_idx + block->row_count; + U64 next_item_idx= item_idx+ block->item_count; + if(row_idx <= row && row < next_row_idx) + { + result = item_idx; + break; + } + row_idx = next_row_idx; + item_idx = next_item_idx; + } + } + return result; +} + +internal UI_ScrollPt +ui_scroll_bar(Axis2 axis, UI_Size off_axis_size, UI_ScrollPt pt, Rng1S64 idx_range, S64 view_num_indices) +{ + ui_push_palette(ui_state->widget_palette_info.scrollbar_palette); + + //- rjf: unpack + S64 idx_range_dim = Max(dim_1s64(idx_range), 1); + + //- rjf: produce extra flags for cases in which scrolling is disabled + UI_BoxFlags disabled_flags = 0; + if(idx_range.min == idx_range.max) + { + disabled_flags |= UI_BoxFlag_Disabled; + } + + //- rjf: build main container + ui_set_next_pref_size(axis2_flip(axis), off_axis_size); + ui_set_next_child_layout_axis(axis); + UI_Box *container_box = ui_build_box_from_key(UI_BoxFlag_DrawBorder, ui_key_zero()); + + //- rjf: build scroll-min button + UI_Signal min_scroll_sig = {0}; + UI_Parent(container_box) + UI_PrefSize(axis, off_axis_size) + UI_Flags(UI_BoxFlag_DrawBorder|disabled_flags) + UI_TextAlignment(UI_TextAlign_Center) + UI_Font(ui_icon_font()) + { + String8 arrow_string = ui_icon_string_from_kind(axis == Axis2_X ? UI_IconKind_LeftArrow : UI_IconKind_UpArrow); + min_scroll_sig = ui_buttonf("%S##_min_scroll_%i", arrow_string, axis); + } + + //- rjf: main scroller area + UI_Signal space_before_sig = {0}; + UI_Signal space_after_sig = {0}; + UI_Signal scroller_sig = {0}; + UI_Box *scroll_area_box = &ui_g_nil_box; + UI_Box *scroller_box = &ui_g_nil_box; + UI_Parent(container_box) + { + ui_set_next_pref_size(axis, ui_pct(1, 0)); + ui_set_next_child_layout_axis(axis); + scroll_area_box = ui_build_box_from_stringf(0, "##_scroll_area_%i", axis); + UI_Parent(scroll_area_box) + { + // rjf: space before + if(idx_range.max != idx_range.min) + { + ui_set_next_pref_size(axis, ui_pct((F32)((F64)(pt.idx-idx_range.min)/(F64)idx_range_dim), 0)); + ui_set_next_hover_cursor(OS_Cursor_HandPoint); + UI_Box *space_before_box = ui_build_box_from_stringf(UI_BoxFlag_Clickable, "##scroll_area_before"); + space_before_sig = ui_signal_from_box(space_before_box); + } + + // rjf: scroller + UI_Flags(disabled_flags) UI_PrefSize(axis, ui_pct(Clamp(0.01f, (F32)((F64)Max(view_num_indices, 1)/(F64)idx_range_dim), 1.f), 0.f)) + { + scroller_sig = ui_buttonf("##_scroller_%i", axis); + scroller_box = scroller_sig.box; + } + + // rjf: space after + if(idx_range.max != idx_range.min) + { + ui_set_next_pref_size(axis, ui_pct(1.f - (F32)((F64)(pt.idx-idx_range.min)/(F64)idx_range_dim), 0)); + ui_set_next_hover_cursor(OS_Cursor_HandPoint); + UI_Box *space_after_box = ui_build_box_from_stringf(UI_BoxFlag_Clickable, "##scroll_area_after"); + space_after_sig = ui_signal_from_box(space_after_box); + } + } + } + + //- rjf: build scroll-max button + UI_Signal max_scroll_sig = {0}; + UI_Parent(container_box) + UI_PrefSize(axis, off_axis_size) + UI_Flags(UI_BoxFlag_DrawBorder|disabled_flags) + UI_TextAlignment(UI_TextAlign_Center) + UI_Font(ui_icon_font()) + { + String8 arrow_string = ui_icon_string_from_kind(axis == Axis2_X ? UI_IconKind_RightArrow : UI_IconKind_DownArrow); + max_scroll_sig = ui_buttonf("%S##_max_scroll_%i", arrow_string, axis); + } + + //- rjf: pt * signals -> new pt + UI_ScrollPt new_pt = pt; + { + typedef struct UI_ScrollBarDragData UI_ScrollBarDragData; + struct UI_ScrollBarDragData + { + UI_ScrollPt start_pt; + F32 scroll_space_px; + }; + if(ui_dragging(scroller_sig)) + { + if(ui_pressed(scroller_sig)) + { + UI_ScrollBarDragData drag_data = {pt, (floor_f32(dim_2f32(scroll_area_box->rect).v[axis])-floor_f32(dim_2f32(scroller_box->rect).v[axis]))}; + ui_store_drag_struct(&drag_data); + } + UI_ScrollBarDragData *drag_data = ui_get_drag_struct(UI_ScrollBarDragData); + UI_ScrollPt original_pt = drag_data->start_pt; + F32 drag_delta = ui_drag_delta().v[axis]; + F32 drag_pct = drag_delta / drag_data->scroll_space_px; + S64 new_idx = original_pt.idx + drag_pct*idx_range_dim; + new_idx = Clamp(idx_range.min, new_idx, idx_range.max); + ui_scroll_pt_target_idx(&new_pt, new_idx); + new_pt.off = 0; + } + if(ui_dragging(min_scroll_sig) || ui_dragging(space_before_sig)) + { + S64 new_idx = new_pt.idx-1; + new_idx = Clamp(idx_range.min, new_idx, idx_range.max); + ui_scroll_pt_target_idx(&new_pt, new_idx); + } + if(ui_dragging(max_scroll_sig) || ui_dragging(space_after_sig)) + { + S64 new_idx = new_pt.idx+1; + new_idx = Clamp(idx_range.min, new_idx, idx_range.max); + ui_scroll_pt_target_idx(&new_pt, new_idx); + } + } + + ui_pop_palette(); + return new_pt; +} + +thread_static UI_ScrollPt *ui_scroll_list_scroll_pt_ptr = 0; +thread_static F32 ui_scroll_list_scroll_bar_dim_px = 0; +thread_static Vec2F32 ui_scroll_list_dim_px = {0}; +thread_static Rng1S64 ui_scroll_list_scroll_idx_rng = {0}; + +internal void +ui_scroll_list_begin(UI_ScrollListParams *params, UI_ScrollPt *scroll_pt, Vec2S64 *cursor_out, Vec2S64 *mark_out, Rng1S64 *visible_row_range_out, UI_ScrollListSignal *signal_out) +{ + //- rjf: unpack arguments + Rng1S64 scroll_row_idx_range = r1s64(params->item_range.min, ClampBot(params->item_range.min, params->item_range.max-1)); + S64 num_possible_visible_rows = (S64)(params->dim_px.y/params->row_height_px); + + //- rjf: do keyboard navigation + B32 moved = 0; + if(params->flags & UI_ScrollListFlag_Nav && cursor_out != 0 && ui_is_focus_active()) + { + UI_EventList *events = ui_events(); + Vec2S64 cursor = *cursor_out; + Vec2S64 mark = mark_out ? *mark_out : cursor; + for(UI_EventNode *n = events->first, *next = 0; n != 0; n = next) + { + next = n->next; + UI_Event *evt = &n->v; + if((evt->delta_2s32.x == 0 && evt->delta_2s32.y == 0) || + evt->flags & UI_EventFlag_Delete) + { + continue; + } + ui_eat_event(events, n); + moved = 1; + switch(evt->delta_unit) + { + default:{moved = 0;}break; + case UI_EventDeltaUnit_Char: + { + for(Axis2 axis = (Axis2)0; axis < Axis2_COUNT; axis = (Axis2)(axis+1)) + { + cursor.v[axis] += evt->delta_2s32.v[axis]; + if(cursor.v[axis] < params->cursor_range.min.v[axis]) + { + cursor.v[axis] = params->cursor_range.max.v[axis]; + } + if(cursor.v[axis] > params->cursor_range.max.v[axis]) + { + cursor.v[axis] = params->cursor_range.min.v[axis]; + } + cursor.v[axis] = clamp_1s64(r1s64(params->cursor_range.min.v[axis], params->cursor_range.max.v[axis]), cursor.v[axis]); + } + }break; + case UI_EventDeltaUnit_Word: + case UI_EventDeltaUnit_Line: + case UI_EventDeltaUnit_Page: + { + cursor.x = (evt->delta_2s32.x>0 ? params->cursor_range.max.x : evt->delta_2s32.x<0 ? params->cursor_range.min.x + !!params->cursor_min_is_empty_selection[Axis2_X] : cursor.x); + cursor.y += ((evt->delta_2s32.y>0 ? +(num_possible_visible_rows-3) : evt->delta_2s32.y<0 ? -(num_possible_visible_rows-3) : 0)); + cursor.y = clamp_1s64(r1s64(params->cursor_range.min.y + !!params->cursor_min_is_empty_selection[Axis2_Y], params->cursor_range.max.y), cursor.y); + }break; + case UI_EventDeltaUnit_Whole: + { + for(Axis2 axis = (Axis2)0; axis < Axis2_COUNT; axis = (Axis2)(axis+1)) + { + cursor.v[axis] = (evt->delta_2s32.v[axis]>0 ? params->cursor_range.max.v[axis] : evt->delta_2s32.v[axis]<0 ? params->cursor_range.min.v[axis] + !!params->cursor_min_is_empty_selection[axis] : cursor.v[axis]); + } + }break; + } + if(!(evt->flags & UI_EventFlag_KeepMark)) + { + mark = cursor; + } + } + if(moved) + { + *cursor_out = cursor; + if(mark_out) + { + *mark_out = mark; + } + } + } + + //- rjf: moved -> snap + if(params->flags & UI_ScrollListFlag_Snap && moved) + { + S64 cursor_item_idx = cursor_out->y-1; + if(params->item_range.min <= cursor_item_idx && cursor_item_idx <= params->item_range.max) + { + //- rjf: compute visible row range + Rng1S64 visible_row_range = r1s64(scroll_pt->idx + 0 - !!(scroll_pt->off < 0), + scroll_pt->idx + 0 + num_possible_visible_rows + 1); + + //- rjf: compute cursor row range from cursor item + Rng1S64 cursor_visibility_row_range = {0}; + if(params->row_blocks.count == 0) + { + cursor_visibility_row_range = r1s64(cursor_item_idx-1, cursor_item_idx+3); + } + else + { + cursor_visibility_row_range.min = (S64)ui_scroll_list_row_from_item(¶ms->row_blocks, (U64)cursor_item_idx); + cursor_visibility_row_range.max = cursor_visibility_row_range.min + 4; + } + + //- rjf: compute deltas & apply + S64 min_delta = Min(0, cursor_visibility_row_range.min-visible_row_range.min); + S64 max_delta = Max(0, cursor_visibility_row_range.max-visible_row_range.max); + S64 new_idx = scroll_pt->idx+min_delta+max_delta; + new_idx = clamp_1s64(scroll_row_idx_range, new_idx); + ui_scroll_pt_target_idx(scroll_pt, new_idx); + } + } + + //- rjf: output signal + if(signal_out != 0) + { + signal_out->cursor_moved = moved; + } + + //- rjf: determine ranges & limits + Rng1S64 visible_row_range = r1s64(scroll_pt->idx + (S64)(scroll_pt->off) + 0 - !!(scroll_pt->off < 0), + scroll_pt->idx + (S64)(scroll_pt->off) + 0 + num_possible_visible_rows + 1); + visible_row_range.min = clamp_1s64(scroll_row_idx_range, visible_row_range.min); + visible_row_range.max = clamp_1s64(scroll_row_idx_range, visible_row_range.max); + *visible_row_range_out = visible_row_range; + + //- rjf: store thread-locals + ui_scroll_list_scroll_bar_dim_px = ui_top_font_size()*1.5f; + ui_scroll_list_scroll_pt_ptr = scroll_pt; + ui_scroll_list_dim_px = params->dim_px; + ui_scroll_list_scroll_idx_rng = scroll_row_idx_range; + + //- rjf: build top-level container + UI_Box *container_box = &ui_g_nil_box; + UI_FixedWidth(params->dim_px.x) UI_FixedHeight(params->dim_px.y) UI_ChildLayoutAxis(Axis2_X) + { + container_box = ui_build_box_from_key(0, ui_key_zero()); + } + + //- rjf: build scrollable container + UI_Box *scrollable_container_box = &ui_g_nil_box; + UI_Parent(container_box) UI_ChildLayoutAxis(Axis2_Y) UI_FixedWidth(params->dim_px.x-ui_scroll_list_scroll_bar_dim_px) UI_FixedHeight(params->dim_px.y) + { + scrollable_container_box = ui_build_box_from_stringf(UI_BoxFlag_Clip|UI_BoxFlag_AllowOverflowY|UI_BoxFlag_Scroll, "###sp"); + scrollable_container_box->view_off.y = scrollable_container_box->view_off_target.y = params->row_height_px*mod_f32(scroll_pt->off, 1.f) + params->row_height_px*(scroll_pt->off < 0) - params->row_height_px*(scroll_pt->off == -1.f && scroll_pt->idx == 1); + } + + //- rjf: build vertical scroll bar + UI_Parent(container_box) UI_Focus(UI_FocusKind_Null) + { + ui_set_next_fixed_width(ui_scroll_list_scroll_bar_dim_px); + ui_set_next_fixed_height(ui_scroll_list_dim_px.y); + *ui_scroll_list_scroll_pt_ptr = ui_scroll_bar(Axis2_Y, + ui_px(ui_scroll_list_scroll_bar_dim_px, 1.f), + *ui_scroll_list_scroll_pt_ptr, + scroll_row_idx_range, + num_possible_visible_rows); + } + + //- rjf: begin scrollable region + ui_push_parent(container_box); + ui_push_parent(scrollable_container_box); + ui_push_pref_height(ui_px(params->row_height_px, 1.f)); +} + +internal void +ui_scroll_list_end(void) +{ + ui_pop_pref_height(); + UI_Box *scrollable_container_box = ui_pop_parent(); + UI_Box *container_box = ui_pop_parent(); + + //- rjf: scroll + { + UI_Signal sig = ui_signal_from_box(scrollable_container_box); + if(sig.scroll.y != 0) + { + S64 new_idx = ui_scroll_list_scroll_pt_ptr->idx + sig.scroll.y; + new_idx = clamp_1s64(ui_scroll_list_scroll_idx_rng, new_idx); + ui_scroll_pt_target_idx(ui_scroll_list_scroll_pt_ptr, new_idx); + } + ui_scroll_pt_clamp_idx(ui_scroll_list_scroll_pt_ptr, ui_scroll_list_scroll_idx_rng); + } +} diff --git a/src/ui/ui_basic_widgets.h b/src/ui/ui_basic_widgets.h index a3a3464d..31c592a3 100644 --- a/src/ui/ui_basic_widgets.h +++ b/src/ui/ui_basic_widgets.h @@ -1,183 +1,183 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef UI_BASIC_WIDGETS_H -#define UI_BASIC_WIDGETS_H - -//////////////////////////////// -//~ rjf: Scroll List Types - -typedef U32 UI_ScrollListFlags; -enum -{ - UI_ScrollListFlag_Nav = (1<<0), - UI_ScrollListFlag_Snap = (1<<1), - UI_ScrollListFlag_All = 0xffffffff, -}; - -typedef struct UI_ScrollListRowBlock UI_ScrollListRowBlock; -struct UI_ScrollListRowBlock -{ - U64 row_count; - U64 item_count; -}; - -typedef struct UI_ScrollListRowBlockChunkNode UI_ScrollListRowBlockChunkNode; -struct UI_ScrollListRowBlockChunkNode -{ - UI_ScrollListRowBlockChunkNode *next; - UI_ScrollListRowBlock *v; - U64 count; - U64 cap; -}; - -typedef struct UI_ScrollListRowBlockChunkList UI_ScrollListRowBlockChunkList; -struct UI_ScrollListRowBlockChunkList -{ - UI_ScrollListRowBlockChunkNode *first; - UI_ScrollListRowBlockChunkNode *last; - U64 chunk_count; - U64 total_count; -}; - -typedef struct UI_ScrollListRowBlockArray UI_ScrollListRowBlockArray; -struct UI_ScrollListRowBlockArray -{ - UI_ScrollListRowBlock *v; - U64 count; -}; - -typedef struct UI_ScrollListParams UI_ScrollListParams; -struct UI_ScrollListParams -{ - UI_ScrollListFlags flags; - Vec2F32 dim_px; - F32 row_height_px; - UI_ScrollListRowBlockArray row_blocks; - Rng2S64 cursor_range; - Rng1S64 item_range; - B32 cursor_min_is_empty_selection[Axis2_COUNT]; -}; - -typedef struct UI_ScrollListSignal UI_ScrollListSignal; -struct UI_ScrollListSignal -{ - B32 cursor_moved; -}; - -//////////////////////////////// -//~ rjf: Basic Widgets - -internal void ui_divider(UI_Size size); -internal UI_Signal ui_label(String8 string); -internal UI_Signal ui_labelf(char *fmt, ...); -internal void ui_label_multiline(F32 max, String8 string); -internal void ui_label_multilinef(F32 max, char *fmt, ...); -internal UI_Signal ui_button(String8 string); -internal UI_Signal ui_buttonf(char *fmt, ...); -internal UI_Signal ui_hover_label(String8 string); -internal UI_Signal ui_hover_labelf(char *fmt, ...); -internal UI_Signal ui_line_edit(TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, String8 pre_edit_value, String8 string); -internal UI_Signal ui_line_editf(TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, String8 pre_edit_value, char *fmt, ...); - -//////////////////////////////// -//~ rjf: Images - -internal UI_Signal ui_image(R_Handle texture, R_Tex2DSampleKind sample_kind, Rng2F32 region, Vec4F32 tint, F32 blur, String8 string); -internal UI_Signal ui_imagef(R_Handle texture, R_Tex2DSampleKind sample_kind, Rng2F32 region, Vec4F32 tint, F32 blur, char *fmt, ...); - -//////////////////////////////// -//~ rjf: Special Buttons - -internal UI_Signal ui_expander(B32 is_expanded, String8 string); -internal UI_Signal ui_expanderf(B32 is_expanded, char *fmt, ...); -internal UI_Signal ui_sort_header(B32 sorting, B32 ascending, String8 string); -internal UI_Signal ui_sort_headerf(B32 sorting, B32 ascending, char *fmt, ...); - -//////////////////////////////// -//~ rjf: Color Pickers - -//- rjf: tooltips -internal void ui_do_color_tooltip_hsv(Vec3F32 hsv); -internal void ui_do_color_tooltip_hsva(Vec4F32 hsva); - -//- rjf: saturation/value picker -internal UI_Signal ui_sat_val_picker(F32 hue, F32 *out_sat, F32 *out_val, String8 string); -internal UI_Signal ui_sat_val_pickerf(F32 hue, F32 *out_sat, F32 *out_val, char *fmt, ...); - -//- rjf: hue picker -internal UI_Signal ui_hue_picker(F32 *out_hue, F32 sat, F32 val, String8 string); -internal UI_Signal ui_hue_pickerf(F32 *out_hue, F32 sat, F32 val, char *fmt, ...); - -//- rjf: alpha picker -internal UI_Signal ui_alpha_picker(F32 *out_alpha, String8 string); -internal UI_Signal ui_alpha_pickerf(F32 *out_alpha, char *fmt, ...); - -//////////////////////////////// -//~ rjf: Simple Layout Widgets - -internal UI_Box *ui_row_begin(void); -internal UI_Signal ui_row_end(void); -internal UI_Box *ui_column_begin(void); -internal UI_Signal ui_column_end(void); -internal UI_Box *ui_named_row_begin(String8 string); -internal UI_Signal ui_named_row_end(void); -internal UI_Box *ui_named_column_begin(String8 string); -internal UI_Signal ui_named_column_end(void); - -//////////////////////////////// -//~ rjf: Floating Panes - -internal UI_Box *ui_pane_begin(Rng2F32 rect, String8 string); -internal UI_Box *ui_pane_beginf(Rng2F32 rect, char *fmt, ...); -internal UI_Signal ui_pane_end(void); - -//////////////////////////////// -//~ rjf: Tables - -internal void ui_table_begin(U64 column_pct_count, F32 **column_pcts, String8 string); -internal void ui_table_beginf(U64 column_pct_count, F32 **column_pcts, char *fmt, ...); -internal void ui_table_end(void); -internal UI_Box * ui_named_table_vector_begin(String8 string); -internal UI_Box * ui_named_table_vector_beginf(char *fmt, ...); -internal UI_Box * ui_table_vector_begin(void); -internal UI_Signal ui_table_vector_end(void); -internal UI_Box * ui_table_cell_begin(void); -internal UI_Signal ui_table_cell_end(void); -internal UI_Box * ui_table_cell_sized_begin(UI_Size size); - -//////////////////////////////// -//~ rjf: Scroll Regions - -internal void ui_scroll_list_row_block_chunk_list_push(Arena *arena, UI_ScrollListRowBlockChunkList *list, U64 cap, UI_ScrollListRowBlock *block); -internal UI_ScrollListRowBlockArray ui_scroll_list_row_block_array_from_chunk_list(Arena *arena, UI_ScrollListRowBlockChunkList *list); -internal U64 ui_scroll_list_row_from_item(UI_ScrollListRowBlockArray *blocks, U64 item); -internal U64 ui_scroll_list_item_from_row(UI_ScrollListRowBlockArray *blocks, U64 row); - -internal UI_ScrollPt ui_scroll_bar(Axis2 axis, UI_Size off_axis_size, UI_ScrollPt pt, Rng1S64 idx_range, S64 view_num_indices); -internal void ui_scroll_list_begin(UI_ScrollListParams *params, UI_ScrollPt *scroll_pt_out, Vec2S64 *cursor_out, Vec2S64 *mark_out, Rng1S64 *visible_row_range_out, UI_ScrollListSignal *signal_out); -internal void ui_scroll_list_end(void); - -//////////////////////////////// -//~ rjf: Macro Loop Wrappers - -#define UI_Row DeferLoop(ui_row_begin(), ui_row_end()) -#define UI_Column DeferLoop(ui_column_begin(), ui_column_end()) -#define UI_NamedRow(s) DeferLoop(ui_named_row_begin(s), ui_named_row_end()) -#define UI_NamedColumn(s) DeferLoop(ui_named_column_begin(s), ui_named_column_end()) -#define UI_Pane(r, s) DeferLoop(ui_pane_begin(r, s), ui_pane_end()) -#define UI_PaneF(r, ...) DeferLoop(ui_pane_beginf(r, __VA_ARGS__), ui_pane_end()) -#define UI_Padding(size) DeferLoop(ui_spacer(size), ui_spacer(size)) -#define UI_Center UI_Padding(ui_pct(1, 0)) - -#define UI_Table(col_pct_count, col_pcts, s) DeferLoop(ui_table_begin(col_pct_count, col_pcts, s), ui_table_end()) -#define UI_TableF(col_pct_count, col_pcts, ...) DeferLoop(ui_table_beginf(col_pct_count, col_pcts, __VA_ARGS__), ui_table_end()) -#define UI_NamedTableVector(s) DeferLoop(ui_named_table_vector_begin(s), ui_table_vector_end()) -#define UI_NamedTableVectorF(...) DeferLoop(ui_named_table_vector_beginf(__VA_ARGS__), ui_table_vector_end()) -#define UI_TableVector DeferLoop(ui_table_vector_begin(), ui_table_vector_end()) -#define UI_TableCell DeferLoop(ui_table_cell_begin(), ui_table_cell_end()) -#define UI_TableCellSized(size) DeferLoop(ui_table_cell_sized_begin(size), ui_table_cell_end()) - -#define UI_ScrollList(params, scroll_pt_out, cursor_out, mark_out, visible_row_range_out, signal_out) DeferLoop(ui_scroll_list_begin((params), (scroll_pt_out), (cursor_out), (mark_out), (visible_row_range_out), (signal_out)), ui_scroll_list_end()) - -#endif // UI_BASIC_WIDGETS_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef UI_BASIC_WIDGETS_H +#define UI_BASIC_WIDGETS_H + +//////////////////////////////// +//~ rjf: Scroll List Types + +typedef U32 UI_ScrollListFlags; +enum +{ + UI_ScrollListFlag_Nav = (1<<0), + UI_ScrollListFlag_Snap = (1<<1), + UI_ScrollListFlag_All = 0xffffffff, +}; + +typedef struct UI_ScrollListRowBlock UI_ScrollListRowBlock; +struct UI_ScrollListRowBlock +{ + U64 row_count; + U64 item_count; +}; + +typedef struct UI_ScrollListRowBlockChunkNode UI_ScrollListRowBlockChunkNode; +struct UI_ScrollListRowBlockChunkNode +{ + UI_ScrollListRowBlockChunkNode *next; + UI_ScrollListRowBlock *v; + U64 count; + U64 cap; +}; + +typedef struct UI_ScrollListRowBlockChunkList UI_ScrollListRowBlockChunkList; +struct UI_ScrollListRowBlockChunkList +{ + UI_ScrollListRowBlockChunkNode *first; + UI_ScrollListRowBlockChunkNode *last; + U64 chunk_count; + U64 total_count; +}; + +typedef struct UI_ScrollListRowBlockArray UI_ScrollListRowBlockArray; +struct UI_ScrollListRowBlockArray +{ + UI_ScrollListRowBlock *v; + U64 count; +}; + +typedef struct UI_ScrollListParams UI_ScrollListParams; +struct UI_ScrollListParams +{ + UI_ScrollListFlags flags; + Vec2F32 dim_px; + F32 row_height_px; + UI_ScrollListRowBlockArray row_blocks; + Rng2S64 cursor_range; + Rng1S64 item_range; + B32 cursor_min_is_empty_selection[Axis2_COUNT]; +}; + +typedef struct UI_ScrollListSignal UI_ScrollListSignal; +struct UI_ScrollListSignal +{ + B32 cursor_moved; +}; + +//////////////////////////////// +//~ rjf: Basic Widgets + +internal void ui_divider(UI_Size size); +internal UI_Signal ui_label(String8 string); +internal UI_Signal ui_labelf(char *fmt, ...); +internal void ui_label_multiline(F32 max, String8 string); +internal void ui_label_multilinef(F32 max, char *fmt, ...); +internal UI_Signal ui_button(String8 string); +internal UI_Signal ui_buttonf(char *fmt, ...); +internal UI_Signal ui_hover_label(String8 string); +internal UI_Signal ui_hover_labelf(char *fmt, ...); +internal UI_Signal ui_line_edit(TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, String8 pre_edit_value, String8 string); +internal UI_Signal ui_line_editf(TxtPt *cursor, TxtPt *mark, U8 *edit_buffer, U64 edit_buffer_size, U64 *edit_string_size_out, String8 pre_edit_value, char *fmt, ...); + +//////////////////////////////// +//~ rjf: Images + +internal UI_Signal ui_image(R_Handle texture, R_Tex2DSampleKind sample_kind, Rng2F32 region, Vec4F32 tint, F32 blur, String8 string); +internal UI_Signal ui_imagef(R_Handle texture, R_Tex2DSampleKind sample_kind, Rng2F32 region, Vec4F32 tint, F32 blur, char *fmt, ...); + +//////////////////////////////// +//~ rjf: Special Buttons + +internal UI_Signal ui_expander(B32 is_expanded, String8 string); +internal UI_Signal ui_expanderf(B32 is_expanded, char *fmt, ...); +internal UI_Signal ui_sort_header(B32 sorting, B32 ascending, String8 string); +internal UI_Signal ui_sort_headerf(B32 sorting, B32 ascending, char *fmt, ...); + +//////////////////////////////// +//~ rjf: Color Pickers + +//- rjf: tooltips +internal void ui_do_color_tooltip_hsv(Vec3F32 hsv); +internal void ui_do_color_tooltip_hsva(Vec4F32 hsva); + +//- rjf: saturation/value picker +internal UI_Signal ui_sat_val_picker(F32 hue, F32 *out_sat, F32 *out_val, String8 string); +internal UI_Signal ui_sat_val_pickerf(F32 hue, F32 *out_sat, F32 *out_val, char *fmt, ...); + +//- rjf: hue picker +internal UI_Signal ui_hue_picker(F32 *out_hue, F32 sat, F32 val, String8 string); +internal UI_Signal ui_hue_pickerf(F32 *out_hue, F32 sat, F32 val, char *fmt, ...); + +//- rjf: alpha picker +internal UI_Signal ui_alpha_picker(F32 *out_alpha, String8 string); +internal UI_Signal ui_alpha_pickerf(F32 *out_alpha, char *fmt, ...); + +//////////////////////////////// +//~ rjf: Simple Layout Widgets + +internal UI_Box *ui_row_begin(void); +internal UI_Signal ui_row_end(void); +internal UI_Box *ui_column_begin(void); +internal UI_Signal ui_column_end(void); +internal UI_Box *ui_named_row_begin(String8 string); +internal UI_Signal ui_named_row_end(void); +internal UI_Box *ui_named_column_begin(String8 string); +internal UI_Signal ui_named_column_end(void); + +//////////////////////////////// +//~ rjf: Floating Panes + +internal UI_Box *ui_pane_begin(Rng2F32 rect, String8 string); +internal UI_Box *ui_pane_beginf(Rng2F32 rect, char *fmt, ...); +internal UI_Signal ui_pane_end(void); + +//////////////////////////////// +//~ rjf: Tables + +internal void ui_table_begin(U64 column_pct_count, F32 **column_pcts, String8 string); +internal void ui_table_beginf(U64 column_pct_count, F32 **column_pcts, char *fmt, ...); +internal void ui_table_end(void); +internal UI_Box * ui_named_table_vector_begin(String8 string); +internal UI_Box * ui_named_table_vector_beginf(char *fmt, ...); +internal UI_Box * ui_table_vector_begin(void); +internal UI_Signal ui_table_vector_end(void); +internal UI_Box * ui_table_cell_begin(void); +internal UI_Signal ui_table_cell_end(void); +internal UI_Box * ui_table_cell_sized_begin(UI_Size size); + +//////////////////////////////// +//~ rjf: Scroll Regions + +internal void ui_scroll_list_row_block_chunk_list_push(Arena *arena, UI_ScrollListRowBlockChunkList *list, U64 cap, UI_ScrollListRowBlock *block); +internal UI_ScrollListRowBlockArray ui_scroll_list_row_block_array_from_chunk_list(Arena *arena, UI_ScrollListRowBlockChunkList *list); +internal U64 ui_scroll_list_row_from_item(UI_ScrollListRowBlockArray *blocks, U64 item); +internal U64 ui_scroll_list_item_from_row(UI_ScrollListRowBlockArray *blocks, U64 row); + +internal UI_ScrollPt ui_scroll_bar(Axis2 axis, UI_Size off_axis_size, UI_ScrollPt pt, Rng1S64 idx_range, S64 view_num_indices); +internal void ui_scroll_list_begin(UI_ScrollListParams *params, UI_ScrollPt *scroll_pt_out, Vec2S64 *cursor_out, Vec2S64 *mark_out, Rng1S64 *visible_row_range_out, UI_ScrollListSignal *signal_out); +internal void ui_scroll_list_end(void); + +//////////////////////////////// +//~ rjf: Macro Loop Wrappers + +#define UI_Row DeferLoop(ui_row_begin(), ui_row_end()) +#define UI_Column DeferLoop(ui_column_begin(), ui_column_end()) +#define UI_NamedRow(s) DeferLoop(ui_named_row_begin(s), ui_named_row_end()) +#define UI_NamedColumn(s) DeferLoop(ui_named_column_begin(s), ui_named_column_end()) +#define UI_Pane(r, s) DeferLoop(ui_pane_begin(r, s), ui_pane_end()) +#define UI_PaneF(r, ...) DeferLoop(ui_pane_beginf(r, __VA_ARGS__), ui_pane_end()) +#define UI_Padding(size) DeferLoop(ui_spacer(size), ui_spacer(size)) +#define UI_Center UI_Padding(ui_pct(1, 0)) + +#define UI_Table(col_pct_count, col_pcts, s) DeferLoop(ui_table_begin(col_pct_count, col_pcts, s), ui_table_end()) +#define UI_TableF(col_pct_count, col_pcts, ...) DeferLoop(ui_table_beginf(col_pct_count, col_pcts, __VA_ARGS__), ui_table_end()) +#define UI_NamedTableVector(s) DeferLoop(ui_named_table_vector_begin(s), ui_table_vector_end()) +#define UI_NamedTableVectorF(...) DeferLoop(ui_named_table_vector_beginf(__VA_ARGS__), ui_table_vector_end()) +#define UI_TableVector DeferLoop(ui_table_vector_begin(), ui_table_vector_end()) +#define UI_TableCell DeferLoop(ui_table_cell_begin(), ui_table_cell_end()) +#define UI_TableCellSized(size) DeferLoop(ui_table_cell_sized_begin(size), ui_table_cell_end()) + +#define UI_ScrollList(params, scroll_pt_out, cursor_out, mark_out, visible_row_range_out, signal_out) DeferLoop(ui_scroll_list_begin((params), (scroll_pt_out), (cursor_out), (mark_out), (visible_row_range_out), (signal_out)), ui_scroll_list_end()) + +#endif // UI_BASIC_WIDGETS_H diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index 959b3d36..0257ff34 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -1,1035 +1,1035 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef UI_H -#define UI_H - -//////////////////////////////// -//~ rjf: Icon Info - -typedef enum UI_IconKind -{ - UI_IconKind_Null, - UI_IconKind_RightArrow, - UI_IconKind_DownArrow, - UI_IconKind_LeftArrow, - UI_IconKind_UpArrow, - UI_IconKind_RightCaret, - UI_IconKind_DownCaret, - UI_IconKind_LeftCaret, - UI_IconKind_UpCaret, - UI_IconKind_CheckHollow, - UI_IconKind_CheckFilled, - UI_IconKind_COUNT -} -UI_IconKind; - -typedef struct UI_IconInfo UI_IconInfo; -struct UI_IconInfo -{ - F_Tag icon_font; - String8 icon_kind_text_map[UI_IconKind_COUNT]; -}; - -//////////////////////////////// -//~ rjf: Mouse Button Kinds - -typedef enum UI_MouseButtonKind -{ - UI_MouseButtonKind_Left, - UI_MouseButtonKind_Middle, - UI_MouseButtonKind_Right, - UI_MouseButtonKind_COUNT -} -UI_MouseButtonKind; - -//////////////////////////////// -//~ rjf: Focus Types - -typedef enum UI_FocusKind -{ - UI_FocusKind_Null, - UI_FocusKind_Off, - UI_FocusKind_On, - UI_FocusKind_Root, - UI_FocusKind_COUNT -} -UI_FocusKind; - -//////////////////////////////// -//~ rjf: Events - -// TODO(rjf): clean all this up - -typedef enum UI_EventKind -{ - UI_EventKind_Null, - UI_EventKind_Press, - UI_EventKind_Release, - UI_EventKind_Text, - UI_EventKind_Navigate, - UI_EventKind_Edit, - UI_EventKind_MouseMove, - UI_EventKind_Scroll, - UI_EventKind_AutocompleteHint, - UI_EventKind_COUNT -} -UI_EventKind; - -typedef enum UI_EventActionSlot -{ - UI_EventActionSlot_Null, - UI_EventActionSlot_Accept, - UI_EventActionSlot_Cancel, - UI_EventActionSlot_Edit, - UI_EventActionSlot_COUNT -} -UI_EventActionSlot; - -typedef U32 UI_EventFlags; -enum -{ - UI_EventFlag_KeepMark = (1<<0), - UI_EventFlag_Delete = (1<<1), - UI_EventFlag_Copy = (1<<2), - UI_EventFlag_Paste = (1<<3), - UI_EventFlag_ZeroDeltaOnSelect = (1<<4), - UI_EventFlag_PickSelectSide = (1<<5), - UI_EventFlag_CapAtLine = (1<<6), - UI_EventFlag_ExplicitDirectional = (1<<7), - UI_EventFlag_Reorder = (1<<8), -}; - -typedef enum UI_EventDeltaUnit -{ - UI_EventDeltaUnit_Null, - UI_EventDeltaUnit_Char, - UI_EventDeltaUnit_Word, - UI_EventDeltaUnit_Line, - UI_EventDeltaUnit_Page, - UI_EventDeltaUnit_Whole, - UI_EventDeltaUnit_COUNT -} -UI_EventDeltaUnit; - -typedef struct UI_Event UI_Event; -struct UI_Event -{ - UI_EventKind kind; - UI_EventActionSlot slot; - UI_EventFlags flags; - UI_EventDeltaUnit delta_unit; - OS_Key key; - OS_EventFlags modifiers; - String8 string; - Vec2F32 pos; - Vec2F32 delta_2f32; - Vec2S32 delta_2s32; - U64 timestamp_us; -}; - -typedef struct UI_EventNode UI_EventNode; -struct UI_EventNode -{ - UI_EventNode *next; - UI_EventNode *prev; - UI_Event v; -}; - -typedef struct UI_EventList UI_EventList; -struct UI_EventList -{ - UI_EventNode *first; - UI_EventNode *last; - U64 count; -}; - -//////////////////////////////// -//~ rjf: Textual Operations - -typedef U32 UI_TxtOpFlags; -enum -{ - UI_TxtOpFlag_Invalid = (1<<0), - UI_TxtOpFlag_Copy = (1<<1), -}; - -typedef struct UI_TxtOp UI_TxtOp; -struct UI_TxtOp -{ - UI_TxtOpFlags flags; - String8 replace; - String8 copy; - TxtRng range; - TxtPt cursor; - TxtPt mark; -}; - -//////////////////////////////// -//~ rjf: Keys - -typedef struct UI_Key UI_Key; -struct UI_Key -{ - U64 u64[1]; -}; - -//////////////////////////////// -//~ rjf: Sizes - -typedef enum UI_SizeKind -{ - UI_SizeKind_Null, - UI_SizeKind_Pixels, // size is computed via a preferred pixel value - UI_SizeKind_TextContent, // size is computed via the dimensions of box's rendered string - UI_SizeKind_ParentPct, // size is computed via a well-determined parent or grandparent size - UI_SizeKind_ChildrenSum, // size is computed via summing well-determined sizes of children -} -UI_SizeKind; - -typedef struct UI_Size UI_Size; -struct UI_Size -{ - UI_SizeKind kind; - F32 value; - F32 strictness; -}; - -//////////////////////////////// -//~ rjf: Palettes - -typedef enum UI_ColorCode -{ - UI_ColorCode_Null, - UI_ColorCode_Background, - UI_ColorCode_Text, - UI_ColorCode_TextWeak, - UI_ColorCode_Border, - UI_ColorCode_Overlay, - UI_ColorCode_Cursor, - UI_ColorCode_Selection, - UI_ColorCode_COUNT -} -UI_ColorCode; - -typedef struct UI_Palette UI_Palette; -struct UI_Palette -{ - union - { - Vec4F32 colors[UI_ColorCode_COUNT]; - struct - { - Vec4F32 null; - Vec4F32 background; - Vec4F32 text; - Vec4F32 text_weak; - Vec4F32 border; - Vec4F32 overlay; - Vec4F32 cursor; - Vec4F32 selection; - }; - }; -}; - -typedef struct UI_WidgetPaletteInfo UI_WidgetPaletteInfo; -struct UI_WidgetPaletteInfo -{ - UI_Palette *tooltip_palette; - UI_Palette *ctx_menu_palette; - UI_Palette *scrollbar_palette; -}; - -//////////////////////////////// -//~ rjf: Animation Info - -typedef U32 UI_AnimationInfoFlags; -enum -{ - UI_AnimationInfoFlag_HotAnimations = (1<<0), - UI_AnimationInfoFlag_ActiveAnimations = (1<<1), - UI_AnimationInfoFlag_FocusAnimations = (1<<2), - UI_AnimationInfoFlag_TooltipAnimations = (1<<3), - UI_AnimationInfoFlag_ContextMenuAnimations = (1<<4), - UI_AnimationInfoFlag_ScrollingAnimations = (1<<5), - UI_AnimationInfoFlag_All = 0xffffffff, -}; - -typedef struct UI_AnimationInfo UI_AnimationInfo; -struct UI_AnimationInfo -{ - UI_AnimationInfoFlags flags; -}; - -//////////////////////////////// -//~ rjf: Scroll Positions - -typedef struct UI_ScrollPt UI_ScrollPt; -struct UI_ScrollPt -{ - S64 idx; - F32 off; -}; - -typedef union UI_ScrollPt2 UI_ScrollPt2; -union UI_ScrollPt2 -{ - UI_ScrollPt v[2]; - struct - { - UI_ScrollPt x; - UI_ScrollPt y; - }; -}; - -//////////////////////////////// -//~ rjf: Box Types - -typedef enum UI_TextAlign -{ - UI_TextAlign_Left, - UI_TextAlign_Center, - UI_TextAlign_Right, - UI_TextAlign_COUNT -} -UI_TextAlign; - -struct UI_Box; -#define UI_BOX_CUSTOM_DRAW(name) void name(struct UI_Box *box, void *user_data) -typedef UI_BOX_CUSTOM_DRAW(UI_BoxCustomDrawFunctionType); - -typedef U64 UI_BoxFlags; -//{ -//- rjf: interaction -# define UI_BoxFlag_MouseClickable (UI_BoxFlags)(1ull<<0) -# define UI_BoxFlag_KeyboardClickable (UI_BoxFlags)(1ull<<1) -# define UI_BoxFlag_DropSite (UI_BoxFlags)(1ull<<2) -# define UI_BoxFlag_ClickToFocus (UI_BoxFlags)(1ull<<3) -# define UI_BoxFlag_Scroll (UI_BoxFlags)(1ull<<4) -# define UI_BoxFlag_ViewScrollX (UI_BoxFlags)(1ull<<5) -# define UI_BoxFlag_ViewScrollY (UI_BoxFlags)(1ull<<6) -# define UI_BoxFlag_ViewClampX (UI_BoxFlags)(1ull<<7) -# define UI_BoxFlag_ViewClampY (UI_BoxFlags)(1ull<<8) -# define UI_BoxFlag_FocusHot (UI_BoxFlags)(1ull<<9) -# define UI_BoxFlag_FocusActive (UI_BoxFlags)(1ull<<10) -# define UI_BoxFlag_FocusHotDisabled (UI_BoxFlags)(1ull<<11) -# define UI_BoxFlag_FocusActiveDisabled (UI_BoxFlags)(1ull<<12) -# define UI_BoxFlag_DefaultFocusNavX (UI_BoxFlags)(1ull<<13) -# define UI_BoxFlag_DefaultFocusNavY (UI_BoxFlags)(1ull<<14) -# define UI_BoxFlag_DefaultFocusEdit (UI_BoxFlags)(1ull<<15) -# define UI_BoxFlag_FocusNavSkip (UI_BoxFlags)(1ull<<16) -# define UI_BoxFlag_DisableTruncatedHover (UI_BoxFlags)(1ull<<17) -# define UI_BoxFlag_Disabled (UI_BoxFlags)(1ull<<18) - -//- rjf: layout -# define UI_BoxFlag_FloatingX (UI_BoxFlags)(1ull<<19) -# define UI_BoxFlag_FloatingY (UI_BoxFlags)(1ull<<20) -# define UI_BoxFlag_FixedWidth (UI_BoxFlags)(1ull<<21) -# define UI_BoxFlag_FixedHeight (UI_BoxFlags)(1ull<<22) -# define UI_BoxFlag_AllowOverflowX (UI_BoxFlags)(1ull<<23) -# define UI_BoxFlag_AllowOverflowY (UI_BoxFlags)(1ull<<24) -# define UI_BoxFlag_SkipViewOffX (UI_BoxFlags)(1ull<<25) -# define UI_BoxFlag_SkipViewOffY (UI_BoxFlags)(1ull<<26) - -//- rjf: appearance / animation -# define UI_BoxFlag_DrawDropShadow (UI_BoxFlags)(1ull<<27) -# define UI_BoxFlag_DrawBackgroundBlur (UI_BoxFlags)(1ull<<28) -# define UI_BoxFlag_DrawBackground (UI_BoxFlags)(1ull<<29) -# define UI_BoxFlag_DrawBorder (UI_BoxFlags)(1ull<<30) -# define UI_BoxFlag_DrawSideTop (UI_BoxFlags)(1ull<<31) -# define UI_BoxFlag_DrawSideBottom (UI_BoxFlags)(1ull<<32) -# define UI_BoxFlag_DrawSideLeft (UI_BoxFlags)(1ull<<33) -# define UI_BoxFlag_DrawSideRight (UI_BoxFlags)(1ull<<34) -# define UI_BoxFlag_DrawText (UI_BoxFlags)(1ull<<35) -# define UI_BoxFlag_DrawTextFastpathCodepoint (UI_BoxFlags)(1ull<<36) -# define UI_BoxFlag_DrawTextWeak (UI_BoxFlags)(1ull<<37) -# define UI_BoxFlag_DrawHotEffects (UI_BoxFlags)(1ull<<38) -# define UI_BoxFlag_DrawActiveEffects (UI_BoxFlags)(1ull<<39) -# define UI_BoxFlag_DrawOverlay (UI_BoxFlags)(1ull<<40) -# define UI_BoxFlag_DrawBucket (UI_BoxFlags)(1ull<<41) -# define UI_BoxFlag_Clip (UI_BoxFlags)(1ull<<42) -# define UI_BoxFlag_AnimatePosX (UI_BoxFlags)(1ull<<43) -# define UI_BoxFlag_AnimatePosY (UI_BoxFlags)(1ull<<44) -# define UI_BoxFlag_DisableTextTrunc (UI_BoxFlags)(1ull<<45) -# define UI_BoxFlag_DisableIDString (UI_BoxFlags)(1ull<<46) -# define UI_BoxFlag_DisableFocusBorder (UI_BoxFlags)(1ull<<47) -# define UI_BoxFlag_DisableFocusOverlay (UI_BoxFlags)(1ull<<48) -# define UI_BoxFlag_HasDisplayString (UI_BoxFlags)(1ull<<49) -# define UI_BoxFlag_HasFuzzyMatchRanges (UI_BoxFlags)(1ull<<50) -# define UI_BoxFlag_RoundChildrenByParent (UI_BoxFlags)(1ull<<51) - -//- rjf: bundles -# define UI_BoxFlag_Clickable (UI_BoxFlag_MouseClickable|UI_BoxFlag_KeyboardClickable) -# define UI_BoxFlag_DefaultFocusNav (UI_BoxFlag_DefaultFocusNavX|UI_BoxFlag_DefaultFocusNavY|UI_BoxFlag_DefaultFocusEdit) -# define UI_BoxFlag_Floating (UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY) -# define UI_BoxFlag_FixedSize (UI_BoxFlag_FixedWidth|UI_BoxFlag_FixedHeight) -# define UI_BoxFlag_AllowOverflow (UI_BoxFlag_AllowOverflowX|UI_BoxFlag_AllowOverflowY) -# define UI_BoxFlag_AnimatePos (UI_BoxFlag_AnimatePosX|UI_BoxFlag_AnimatePosY) -# define UI_BoxFlag_ViewScroll (UI_BoxFlag_ViewScrollX|UI_BoxFlag_ViewScrollY) -# define UI_BoxFlag_ViewClamp (UI_BoxFlag_ViewClampX|UI_BoxFlag_ViewClampY) -# define UI_BoxFlag_DisableFocusEffects (UI_BoxFlag_DisableFocusBorder|UI_BoxFlag_DisableFocusOverlay) -//} - -typedef struct UI_Box UI_Box; -struct UI_Box -{ - //- rjf: persistent links - UI_Box *hash_next; - UI_Box *hash_prev; - - //- rjf: per-build links/data - UI_Box *first; - UI_Box *last; - UI_Box *next; - UI_Box *prev; - UI_Box *parent; - U64 child_count; - - //- rjf: per-build equipment - UI_Key key; - UI_BoxFlags flags; - String8 string; - UI_TextAlign text_align; - Vec2F32 fixed_position; - Vec2F32 fixed_size; - UI_Size pref_size[Axis2_COUNT]; - Axis2 child_layout_axis; - OS_Cursor hover_cursor; - U32 fastpath_codepoint; - UI_Key group_key; - D_Bucket *draw_bucket; - UI_BoxCustomDrawFunctionType *custom_draw; - void *custom_draw_user_data; - UI_Palette *palette; - F_Tag font; - F32 font_size; - F32 tab_size; - F_RasterFlags text_raster_flags; - F32 corner_radii[Corner_COUNT]; - F32 blur_size; - F32 transparency; - F32 squish; - F32 text_padding; - - //- rjf: per-build artifacts - D_FancyRunList display_string_runs; - Rng2F32 rect; - Vec2F32 fixed_position_animated; - Vec2F32 position_delta; - FuzzyMatchRangeList fuzzy_match_ranges; - - //- rjf: persistent data - U64 first_touched_build_index; - U64 last_touched_build_index; - U64 first_disabled_build_index; - F32 hot_t; - F32 active_t; - F32 disabled_t; - F32 focus_hot_t; - F32 focus_active_t; - F32 focus_active_disabled_t; - Vec2F32 view_off; - Vec2F32 view_off_target; - Vec2F32 view_bounds; - UI_Key default_nav_focus_hot_key; - UI_Key default_nav_focus_active_key; - UI_Key default_nav_focus_next_hot_key; - UI_Key default_nav_focus_next_active_key; -}; - -typedef struct UI_BoxRec UI_BoxRec; -struct UI_BoxRec -{ - UI_Box *next; - S32 push_count; - S32 pop_count; -}; - -typedef struct UI_BoxNode UI_BoxNode; -struct UI_BoxNode -{ - UI_BoxNode *next; - UI_Box *box; -}; - -typedef struct UI_BoxList UI_BoxList; -struct UI_BoxList -{ - UI_BoxNode *first; - UI_BoxNode *last; - U64 count; -}; - -typedef U32 UI_SignalFlags; -enum -{ - // rjf: mouse press -> box was pressed while hovering - UI_SignalFlag_LeftPressed = (1<<0), - UI_SignalFlag_MiddlePressed = (1<<1), - UI_SignalFlag_RightPressed = (1<<2), - - // rjf: dragging -> box was previously pressed, user is still holding button - UI_SignalFlag_LeftDragging = (1<<3), - UI_SignalFlag_MiddleDragging = (1<<4), - UI_SignalFlag_RightDragging = (1<<5), - - // rjf: double-dragging -> box was previously double-clicked, user is still holding button - UI_SignalFlag_LeftDoubleDragging = (1<<6), - UI_SignalFlag_MiddleDoubleDragging= (1<<7), - UI_SignalFlag_RightDoubleDragging = (1<<8), - - // rjf: triple-dragging -> box was previously triple-clicked, user is still holding button - UI_SignalFlag_LeftTripleDragging = (1<<9), - UI_SignalFlag_MiddleTripleDragging= (1<<10), - UI_SignalFlag_RightTripleDragging = (1<<11), - - // rjf: released -> box was previously pressed & user released, in or out of bounds - UI_SignalFlag_LeftReleased = (1<<12), - UI_SignalFlag_MiddleReleased = (1<<13), - UI_SignalFlag_RightReleased = (1<<14), - - // rjf: clicked -> box was previously pressed & user released, in bounds - UI_SignalFlag_LeftClicked = (1<<15), - UI_SignalFlag_MiddleClicked = (1<<16), - UI_SignalFlag_RightClicked = (1<<17), - - // rjf: double clicked -> box was previously clicked, pressed again - UI_SignalFlag_LeftDoubleClicked = (1<<18), - UI_SignalFlag_MiddleDoubleClicked = (1<<19), - UI_SignalFlag_RightDoubleClicked = (1<<20), - - // rjf: triple clicked -> box was previously clicked twice, pressed again - UI_SignalFlag_LeftTripleClicked = (1<<21), - UI_SignalFlag_MiddleTripleClicked = (1<<22), - UI_SignalFlag_RightTripleClicked = (1<<23), - - // rjf: keyboard pressed -> box had focus, user activated via their keyboard - UI_SignalFlag_KeyboardPressed = (1<<24), - - // rjf: passive mouse info - UI_SignalFlag_Hovering = (1<<25), // hovering specifically this box - UI_SignalFlag_MouseOver = (1<<26), // mouse is over, but may be occluded - - // rjf: committing state changes via user interaction - UI_SignalFlag_Commit = (1<<27), - - // rjf: high-level combos - UI_SignalFlag_Pressed = UI_SignalFlag_LeftPressed|UI_SignalFlag_KeyboardPressed, - UI_SignalFlag_Released = UI_SignalFlag_LeftReleased, - UI_SignalFlag_Clicked = UI_SignalFlag_LeftClicked|UI_SignalFlag_KeyboardPressed, - UI_SignalFlag_DoubleClicked = UI_SignalFlag_LeftDoubleClicked, - UI_SignalFlag_TripleClicked = UI_SignalFlag_LeftTripleClicked, - UI_SignalFlag_Dragging = UI_SignalFlag_LeftDragging, -}; - -typedef struct UI_Signal UI_Signal; -struct UI_Signal -{ - UI_Box *box; - OS_EventFlags event_flags; - Vec2S16 scroll; - UI_SignalFlags f; -}; - -#define ui_pressed(s) !!((s).f&UI_SignalFlag_Pressed) -#define ui_clicked(s) !!((s).f&UI_SignalFlag_Clicked) -#define ui_released(s) !!((s).f&UI_SignalFlag_Released) -#define ui_double_clicked(s) !!((s).f&UI_SignalFlag_DoubleClicked) -#define ui_triple_clicked(s) !!((s).f&UI_SignalFlag_TripleClicked) -#define ui_middle_clicked(s) !!((s).f&UI_SignalFlag_MiddleClicked) -#define ui_right_clicked(s) !!((s).f&UI_SignalFlag_RightClicked) -#define ui_dragging(s) !!((s).f&UI_SignalFlag_Dragging) -#define ui_hovering(s) !!((s).f&UI_SignalFlag_Hovering) -#define ui_mouse_over(s) !!((s).f&UI_SignalFlag_MouseOver) -#define ui_committed(s) !!((s).f&UI_SignalFlag_Commit) - -typedef struct UI_Nav UI_Nav; -struct UI_Nav -{ - B32 moved; - Vec2S64 new_p; -}; - -//////////////////////////////// -//~ rjf: Generated Code - -#include "generated/ui.meta.h" - -//////////////////////////////// -//~ rjf: State Types - -typedef struct UI_BoxHashSlot UI_BoxHashSlot; -struct UI_BoxHashSlot -{ - UI_Box *hash_first; - UI_Box *hash_last; -}; - -typedef struct UI_State UI_State; -struct UI_State -{ - //- rjf: main arena - Arena *arena; - - //- rjf: build arenas - Arena *build_arenas[2]; - U64 build_index; - - //- rjf: box cache - UI_Box *first_free_box; - U64 box_table_size; - UI_BoxHashSlot *box_table; - - //- rjf: build state machine state - B32 is_in_open_ctx_menu; - - //- rjf: build phase output - UI_Box *root; - UI_Box *tooltip_root; - UI_Box *ctx_menu_root; - UI_Key default_nav_root_key; - U64 build_box_count; - U64 last_build_box_count; - B32 ctx_menu_touched_this_frame; - B32 is_animating; - - //- rjf: build parameters - UI_IconInfo icon_info; - UI_WidgetPaletteInfo widget_palette_info; - UI_AnimationInfo animation_info; - OS_Handle window; - UI_EventList *events; - Vec2F32 mouse; - F32 animation_dt; - - //- rjf: user interaction state - UI_Key hot_box_key; - UI_Key active_box_key[UI_MouseButtonKind_COUNT]; - UI_Key drop_hot_box_key; - UI_Key clipboard_copy_key; - U64 press_timestamp_history_us[UI_MouseButtonKind_COUNT][3]; - UI_Key press_key_history[UI_MouseButtonKind_COUNT][3]; - Vec2F32 press_pos_history[UI_MouseButtonKind_COUNT][3]; - Vec2F32 drag_start_mouse; - Arena *drag_state_arena; - String8 drag_state_data; - Arena *string_hover_arena; - String8 string_hover_string; - D_FancyRunList string_hover_fancy_runs; - U64 string_hover_begin_us; - U64 string_hover_build_index; - U64 last_time_mousemoved_us; - - //- rjf: tooltip state - F32 tooltip_open_t; - B32 tooltip_open; - - //- rjf: context menu state - UI_Key ctx_menu_anchor_key; - UI_Key next_ctx_menu_anchor_key; - Vec2F32 ctx_menu_anchor_box_last_pos; - Vec2F32 ctx_menu_anchor_off; - B32 ctx_menu_open; - B32 next_ctx_menu_open; - F32 ctx_menu_open_t; - UI_Key ctx_menu_key; - B32 ctx_menu_changed; - - //- rjf: build phase stacks - UI_DeclStackNils; - UI_DeclStacks; -}; - -//////////////////////////////// -//~ rjf: Basic Type Functions - -internal U64 ui_hash_from_string(U64 seed, String8 string); -internal String8 ui_hash_part_from_key_string(String8 string); -internal String8 ui_display_part_from_key_string(String8 string); -internal UI_Key ui_key_zero(void); -internal UI_Key ui_key_make(U64 v); -internal UI_Key ui_key_from_string(UI_Key seed_key, String8 string); -internal UI_Key ui_key_from_stringf(UI_Key seed_key, char *fmt, ...); -internal B32 ui_key_match(UI_Key a, UI_Key b); - -//////////////////////////////// -//~ rjf: Event Type Functions - -internal UI_EventNode *ui_event_list_push(Arena *arena, UI_EventList *list, UI_Event *v); -internal void ui_eat_event(UI_EventList *list, UI_EventNode *node); - -//////////////////////////////// -//~ rjf: Text Operation Functions - -internal B32 ui_char_is_scan_boundary(U8 c); -internal S64 ui_scanned_column_from_column(String8 string, S64 start_column, Side side); -internal UI_TxtOp ui_single_line_txt_op_from_event(Arena *arena, UI_Event *event, String8 string, TxtPt cursor, TxtPt mark); -internal String8 ui_push_string_replace_range(Arena *arena, String8 string, Rng1S64 range, String8 replace); - -//////////////////////////////// -//~ rjf: Size Type Functions - -internal UI_Size ui_size(UI_SizeKind kind, F32 value, F32 strictness); -#define ui_px(value, strictness) ui_size(UI_SizeKind_Pixels, value, strictness) -#define ui_em(value, strictness) ui_size(UI_SizeKind_Pixels, (value) * ui_top_font_size(), strictness) -#define ui_text_dim(padding, strictness) ui_size(UI_SizeKind_TextContent, padding, strictness) -#define ui_pct(value, strictness) ui_size(UI_SizeKind_ParentPct, value, strictness) -#define ui_children_sum(strictness) ui_size(UI_SizeKind_ChildrenSum, 0.f, strictness) - -//////////////////////////////// -//~ rjf: Color Scheme Type Functions - -read_only global UI_Palette ui_g_nil_palette = {0}; - -//////////////////////////////// -//~ rjf: Scroll Point Type Functions - -internal UI_ScrollPt ui_scroll_pt(S64 idx, F32 off); -internal void ui_scroll_pt_target_idx(UI_ScrollPt *v, S64 idx); -internal void ui_scroll_pt_clamp_idx(UI_ScrollPt *v, Rng1S64 range); - -//////////////////////////////// -//~ rjf: Box Type Functions - -read_only global UI_Box ui_g_nil_box = -{ - &ui_g_nil_box, - &ui_g_nil_box, - &ui_g_nil_box, - &ui_g_nil_box, - &ui_g_nil_box, - &ui_g_nil_box, - &ui_g_nil_box, -}; -internal B32 ui_box_is_nil(UI_Box *box); -internal UI_BoxRec ui_box_rec_df(UI_Box *box, UI_Box *root, U64 sib_member_off, U64 child_member_off); -#define ui_box_rec_df_pre(box, root) ui_box_rec_df(box, root, OffsetOf(UI_Box, next), OffsetOf(UI_Box, first)) -#define ui_box_rec_df_post(box, root) ui_box_rec_df(box, root, OffsetOf(UI_Box, prev), OffsetOf(UI_Box, last)) -internal void ui_box_list_push(Arena *arena, UI_BoxList *list, UI_Box *box); - -//////////////////////////////// -//~ rjf: State Allocating / Selection - -internal UI_State *ui_state_alloc(void); -internal void ui_state_release(UI_State *state); -internal UI_Box * ui_root_from_state(UI_State *state); -internal B32 ui_animating_from_state(UI_State *state); -internal void ui_select_state(UI_State *state); -internal UI_State *ui_get_selected_state(void); - -//////////////////////////////// -//~ rjf: Implicit State Accessors/Mutators - -//- rjf: per-frame info -internal Arena * ui_build_arena(void); -internal OS_Handle ui_window(void); -internal UI_EventList * ui_events(void); -internal Vec2F32 ui_mouse(void); -internal F_Tag ui_icon_font(void); -internal String8 ui_icon_string_from_kind(UI_IconKind icon_kind); -internal F32 ui_dt(void); - -//- rjf: event consumption helpers -internal B32 ui_key_press(OS_EventFlags mods, OS_Key key); -internal B32 ui_key_release(OS_EventFlags mods, OS_Key key); -internal B32 ui_text(U32 character); -internal B32 ui_slot_press(UI_EventActionSlot slot); - -//- rjf: drag data -internal Vec2F32 ui_drag_start_mouse(void); -internal Vec2F32 ui_drag_delta(void); -internal void ui_store_drag_data(String8 string); -internal String8 ui_get_drag_data(U64 min_required_size); -#define ui_store_drag_struct(ptr) ui_store_drag_data(str8_struct(ptr)) -#define ui_get_drag_struct(type) ((type *)ui_get_drag_data(sizeof(type)).str) - -//- rjf: hovered string info -internal B32 ui_string_hover_active(void); -internal D_FancyRunList ui_string_hover_runs(Arena *arena); - -//- rjf: interaction keys -internal UI_Key ui_hot_key(void); -internal UI_Key ui_active_key(UI_MouseButtonKind button_kind); -internal UI_Key ui_drop_hot_key(void); - -//- rjf: controls over interaction -internal void ui_kill_action(void); - -//- rjf: box cache lookup -internal UI_Box * ui_box_from_key(UI_Key key); - -//////////////////////////////// -//~ rjf: Top-Level Building API - -internal void ui_begin_build(OS_Handle window, UI_EventList *events, UI_IconInfo *icon_info, UI_WidgetPaletteInfo *widget_palette_info, UI_AnimationInfo *animation_info, F32 real_dt, F32 animation_dt); -internal void ui_end_build(void); -internal void ui_calc_sizes_standalone__in_place_rec(UI_Box *root, Axis2 axis); -internal void ui_calc_sizes_upwards_dependent__in_place_rec(UI_Box *root, Axis2 axis); -internal void ui_calc_sizes_downwards_dependent__in_place_rec(UI_Box *root, Axis2 axis); -internal void ui_layout_enforce_constraints__in_place_rec(UI_Box *root, Axis2 axis); -internal void ui_layout_position__in_place_rec(UI_Box *root, Axis2 axis); -internal void ui_layout_root(UI_Box *root, Axis2 axis); - -//////////////////////////////// -//~ rjf: Box Tree Building API - -//- rjf: spacers -internal UI_Signal ui_spacer(UI_Size size); - -//- rjf: tooltips -internal void ui_tooltip_begin_base(void); -internal void ui_tooltip_end_base(void); -internal void ui_tooltip_begin(void); -internal void ui_tooltip_end(void); - -//- rjf: context menus -internal void ui_ctx_menu_open(UI_Key key, UI_Key anchor_box_key, Vec2F32 anchor_off); -internal void ui_ctx_menu_close(void); -internal B32 ui_begin_ctx_menu(UI_Key key); -internal void ui_end_ctx_menu(void); -internal B32 ui_ctx_menu_is_open(UI_Key key); -internal B32 ui_any_ctx_menu_is_open(void); - -//- rjf: focus tree coloring -internal B32 ui_is_focus_hot(void); -internal B32 ui_is_focus_active(void); - -//- rjf: implicit auto-managed tree-based focus state -internal B32 ui_is_key_auto_focus_active(UI_Key key); -internal B32 ui_is_key_auto_focus_hot(UI_Key key); -internal void ui_set_auto_focus_active_key(UI_Key key); -internal void ui_set_auto_focus_hot_key(UI_Key key); - -//- rjf: palette forming -internal UI_Palette * ui_build_palette_(UI_Palette *base, UI_Palette *overrides); -#define ui_build_palette(base, ...) ui_build_palette_((base), &(UI_Palette){.text = v4f32(0, 0, 0, 0), __VA_ARGS__}) - -//- rjf: box node construction -internal UI_Box * ui_build_box_from_key(UI_BoxFlags flags, UI_Key key); -internal UI_Key ui_active_seed_key(void); -internal UI_Box * ui_build_box_from_string(UI_BoxFlags flags, String8 string); -internal UI_Box * ui_build_box_from_stringf(UI_BoxFlags flags, char *fmt, ...); - -//- rjf: box node equipment -internal inline void ui_box_equip_display_string(UI_Box *box, String8 string); -internal inline void ui_box_equip_display_fancy_strings(UI_Box *box, D_FancyStringList *strings); -internal inline void ui_box_equip_display_string_fancy_runs(UI_Box *box, String8 string, D_FancyRunList *runs); -internal inline void ui_box_equip_fuzzy_match_ranges(UI_Box *box, FuzzyMatchRangeList *matches); -internal inline void ui_box_equip_draw_bucket(UI_Box *box, D_Bucket *bucket); -internal inline void ui_box_equip_custom_draw(UI_Box *box, UI_BoxCustomDrawFunctionType *custom_draw, void *user_data); - -//- rjf: box accessors / queries -internal String8 ui_box_display_string(UI_Box *box); -internal Vec2F32 ui_box_text_position(UI_Box *box); -internal U64 ui_box_char_pos_from_xy(UI_Box *box, Vec2F32 xy); - -//////////////////////////////// -//~ rjf: User Interaction - -internal UI_Signal ui_signal_from_box(UI_Box *box); - -//////////////////////////////// -//~ rjf: Stacks - -//- rjf: base -internal UI_Box * ui_top_parent(void); -internal Axis2 ui_top_child_layout_axis(void); -internal F32 ui_top_fixed_x(void); -internal F32 ui_top_fixed_y(void); -internal F32 ui_top_fixed_width(void); -internal F32 ui_top_fixed_height(void); -internal UI_Size ui_top_pref_width(void); -internal UI_Size ui_top_pref_height(void); -internal UI_BoxFlags ui_top_flags(void); -internal UI_FocusKind ui_top_focus_hot(void); -internal UI_FocusKind ui_top_focus_active(void); -internal U32 ui_top_fastpath_codepoint(void); -internal UI_Key ui_top_group_key(void); -internal F32 ui_top_transparency(void); -internal UI_Palette* ui_top_palette(void); -internal F32 ui_top_squish(void); -internal OS_Cursor ui_top_hover_cursor(void); -internal F_Tag ui_top_font(void); -internal F32 ui_top_font_size(void); -internal F_RasterFlags ui_top_text_raster_flags(void); -internal F32 ui_top_tab_size(void); -internal F32 ui_top_corner_radius_00(void); -internal F32 ui_top_corner_radius_01(void); -internal F32 ui_top_corner_radius_10(void); -internal F32 ui_top_corner_radius_11(void); -internal F32 ui_top_blur_size(void); -internal F32 ui_top_text_padding(void); -internal UI_TextAlign ui_top_text_alignment(void); -internal UI_Box * ui_bottom_parent(void); -internal Axis2 ui_bottom_child_layout_axis(void); -internal F32 ui_bottom_fixed_x(void); -internal F32 ui_bottom_fixed_y(void); -internal F32 ui_bottom_fixed_width(void); -internal F32 ui_bottom_fixed_height(void); -internal UI_Size ui_bottom_pref_width(void); -internal UI_Size ui_bottom_pref_height(void); -internal UI_BoxFlags ui_bottom_flags(void); -internal UI_FocusKind ui_bottom_focus_hot(void); -internal UI_FocusKind ui_bottom_focus_active(void); -internal U32 ui_bottom_fastpath_codepoint(void); -internal UI_Key ui_bottom_group_key(void); -internal F32 ui_bottom_transparency(void); -internal UI_Palette* ui_bottom_palette(void); -internal F32 ui_bottom_squish(void); -internal OS_Cursor ui_bottom_hover_cursor(void); -internal F_Tag ui_bottom_font(void); -internal F32 ui_bottom_font_size(void); -internal F_RasterFlags ui_bottom_text_raster_flags(void); -internal F32 ui_bottom_tab_size(void); -internal F32 ui_bottom_corner_radius_00(void); -internal F32 ui_bottom_corner_radius_01(void); -internal F32 ui_bottom_corner_radius_10(void); -internal F32 ui_bottom_corner_radius_11(void); -internal F32 ui_bottom_blur_size(void); -internal F32 ui_bottom_text_padding(void); -internal UI_TextAlign ui_bottom_text_alignment(void); -internal UI_Box * ui_push_parent(UI_Box * v); -internal Axis2 ui_push_child_layout_axis(Axis2 v); -internal F32 ui_push_fixed_x(F32 v); -internal F32 ui_push_fixed_y(F32 v); -internal F32 ui_push_fixed_width(F32 v); -internal F32 ui_push_fixed_height(F32 v); -internal UI_Size ui_push_pref_width(UI_Size v); -internal UI_Size ui_push_pref_height(UI_Size v); -internal UI_BoxFlags ui_push_flags(UI_BoxFlags v); -internal UI_FocusKind ui_push_focus_hot(UI_FocusKind v); -internal UI_FocusKind ui_push_focus_active(UI_FocusKind v); -internal U32 ui_push_fastpath_codepoint(U32 v); -internal UI_Key ui_push_group_key(UI_Key v); -internal F32 ui_push_transparency(F32 v); -internal UI_Palette* ui_push_palette(UI_Palette* v); -internal F32 ui_push_squish(F32 v); -internal OS_Cursor ui_push_hover_cursor(OS_Cursor v); -internal F_Tag ui_push_font(F_Tag v); -internal F32 ui_push_font_size(F32 v); -internal F_RasterFlags ui_push_text_raster_flags(F_RasterFlags v); -internal F32 ui_push_tab_size(F32 v); -internal F32 ui_push_corner_radius_00(F32 v); -internal F32 ui_push_corner_radius_01(F32 v); -internal F32 ui_push_corner_radius_10(F32 v); -internal F32 ui_push_corner_radius_11(F32 v); -internal F32 ui_push_blur_size(F32 v); -internal F32 ui_push_text_padding(F32 v); -internal UI_TextAlign ui_push_text_alignment(UI_TextAlign v); -internal UI_Box * ui_pop_parent(void); -internal Axis2 ui_pop_child_layout_axis(void); -internal F32 ui_pop_fixed_x(void); -internal F32 ui_pop_fixed_y(void); -internal F32 ui_pop_fixed_width(void); -internal F32 ui_pop_fixed_height(void); -internal UI_Size ui_pop_pref_width(void); -internal UI_Size ui_pop_pref_height(void); -internal UI_BoxFlags ui_pop_flags(void); -internal UI_FocusKind ui_pop_focus_hot(void); -internal UI_FocusKind ui_pop_focus_active(void); -internal U32 ui_pop_fastpath_codepoint(void); -internal UI_Key ui_pop_group_key(void); -internal F32 ui_pop_transparency(void); -internal UI_Palette* ui_pop_palette(void); -internal F32 ui_pop_squish(void); -internal OS_Cursor ui_pop_hover_cursor(void); -internal F_Tag ui_pop_font(void); -internal F32 ui_pop_font_size(void); -internal F_RasterFlags ui_pop_text_raster_flags(void); -internal F32 ui_pop_tab_size(void); -internal F32 ui_pop_corner_radius_00(void); -internal F32 ui_pop_corner_radius_01(void); -internal F32 ui_pop_corner_radius_10(void); -internal F32 ui_pop_corner_radius_11(void); -internal F32 ui_pop_blur_size(void); -internal F32 ui_pop_text_padding(void); -internal UI_TextAlign ui_pop_text_alignment(void); -internal UI_Box * ui_set_next_parent(UI_Box * v); -internal Axis2 ui_set_next_child_layout_axis(Axis2 v); -internal F32 ui_set_next_fixed_x(F32 v); -internal F32 ui_set_next_fixed_y(F32 v); -internal F32 ui_set_next_fixed_width(F32 v); -internal F32 ui_set_next_fixed_height(F32 v); -internal UI_Size ui_set_next_pref_width(UI_Size v); -internal UI_Size ui_set_next_pref_height(UI_Size v); -internal UI_BoxFlags ui_set_next_flags(UI_BoxFlags v); -internal UI_FocusKind ui_set_next_focus_hot(UI_FocusKind v); -internal UI_FocusKind ui_set_next_focus_active(UI_FocusKind v); -internal U32 ui_set_next_fastpath_codepoint(U32 v); -internal UI_Key ui_set_next_group_key(UI_Key v); -internal F32 ui_set_next_transparency(F32 v); -internal UI_Palette* ui_set_next_palette(UI_Palette* v); -internal F32 ui_set_next_squish(F32 v); -internal OS_Cursor ui_set_next_hover_cursor(OS_Cursor v); -internal F_Tag ui_set_next_font(F_Tag v); -internal F32 ui_set_next_font_size(F32 v); -internal F_RasterFlags ui_set_next_text_raster_flags(F_RasterFlags v); -internal F32 ui_set_next_tab_size(F32 v); -internal F32 ui_set_next_corner_radius_00(F32 v); -internal F32 ui_set_next_corner_radius_01(F32 v); -internal F32 ui_set_next_corner_radius_10(F32 v); -internal F32 ui_set_next_corner_radius_11(F32 v); -internal F32 ui_set_next_blur_size(F32 v); -internal F32 ui_set_next_text_padding(F32 v); -internal UI_TextAlign ui_set_next_text_alignment(UI_TextAlign v); - -//- rjf: helpers -internal Rng2F32 ui_push_rect(Rng2F32 rect); -internal Rng2F32 ui_pop_rect(void); -internal void ui_set_next_rect(Rng2F32 rect); -internal UI_Size ui_push_pref_size(Axis2 axis, UI_Size v); -internal UI_Size ui_pop_pref_size(Axis2 axis); -internal UI_Size ui_set_next_pref_size(Axis2 axis, UI_Size v); -internal void ui_push_corner_radius(F32 v); -internal void ui_pop_corner_radius(void); - -//////////////////////////////// -//~ rjf: Macro Loop Wrappers - -//- rjf: stacks (base) -#define UI_Parent(v) DeferLoop(ui_push_parent(v), ui_pop_parent()) -#define UI_ChildLayoutAxis(v) DeferLoop(ui_push_child_layout_axis(v), ui_pop_child_layout_axis()) -#define UI_FixedX(v) DeferLoop(ui_push_fixed_x(v), ui_pop_fixed_x()) -#define UI_FixedY(v) DeferLoop(ui_push_fixed_y(v), ui_pop_fixed_y()) -#define UI_FixedWidth(v) DeferLoop(ui_push_fixed_width(v), ui_pop_fixed_width()) -#define UI_FixedHeight(v) DeferLoop(ui_push_fixed_height(v), ui_pop_fixed_height()) -#define UI_PrefWidth(v) DeferLoop(ui_push_pref_width(v), ui_pop_pref_width()) -#define UI_PrefHeight(v) DeferLoop(ui_push_pref_height(v), ui_pop_pref_height()) -#define UI_Flags(v) DeferLoop(ui_push_flags(v), ui_pop_flags()) -#define UI_FocusHot(v) DeferLoop(ui_push_focus_hot(v), ui_pop_focus_hot()) -#define UI_FocusActive(v) DeferLoop(ui_push_focus_active(v), ui_pop_focus_active()) -#define UI_FastpathCodepoint(v) DeferLoop(ui_push_fastpath_codepoint(v), ui_pop_fastpath_codepoint()) -#define UI_GroupKey(v) DeferLoop(ui_push_group_key(v), ui_pop_group_key()) -#define UI_Transparency(v) DeferLoop(ui_push_transparency(v), ui_pop_transparency()) -#define UI_Palette(v) DeferLoop(ui_push_palette(v), ui_pop_palette()) -#define UI_Squish(v) DeferLoop(ui_push_squish(v), ui_pop_squish()) -#define UI_HoverCursor(v) DeferLoop(ui_push_hover_cursor(v), ui_pop_hover_cursor()) -#define UI_Font(v) DeferLoop(ui_push_font(v), ui_pop_font()) -#define UI_FontSize(v) DeferLoop(ui_push_font_size(v), ui_pop_font_size()) -#define UI_TextRasterFlags(v) DeferLoop(ui_push_text_raster_flags(v), ui_pop_text_raster_flags()) -#define UI_TabSize(v) DeferLoop(ui_push_tab_size(v), ui_pop_tab_size()) -#define UI_CornerRadius00(v) DeferLoop(ui_push_corner_radius_00(v), ui_pop_corner_radius_00()) -#define UI_CornerRadius01(v) DeferLoop(ui_push_corner_radius_01(v), ui_pop_corner_radius_01()) -#define UI_CornerRadius10(v) DeferLoop(ui_push_corner_radius_10(v), ui_pop_corner_radius_10()) -#define UI_CornerRadius11(v) DeferLoop(ui_push_corner_radius_11(v), ui_pop_corner_radius_11()) -#define UI_BlurSize(v) DeferLoop(ui_push_blur_size(v), ui_pop_blur_size()) -#define UI_TextPadding(v) DeferLoop(ui_push_text_padding(v), ui_pop_text_padding()) -#define UI_TextAlignment(v) DeferLoop(ui_push_text_alignment(v), ui_pop_text_alignment()) - -//- rjf: stacks (compositions) -#define UI_WidthFill UI_PrefWidth(ui_pct(1.f, 0.f)) -#define UI_HeightFill UI_PrefHeight(ui_pct(1.f, 0.f)) -#define UI_Rect(r) DeferLoop(ui_push_rect(r), ui_pop_rect()) -#define UI_PrefSize(axis, v) DeferLoop(ui_push_pref_size((axis), (v)), ui_pop_pref_size(axis)) -#define UI_CornerRadius(v) DeferLoop(ui_push_corner_radius(v), ui_pop_corner_radius()) -#define UI_Focus(kind) DeferLoop((ui_push_focus_hot(kind), ui_push_focus_active(kind)), (ui_pop_focus_hot(), ui_pop_focus_active())) -#define UI_FlagsAdd(v) DeferLoop(ui_push_flags(ui_top_flags()|(v)), ui_pop_flags()) - -//- rjf: tooltip -#define UI_TooltipBase DeferLoop(ui_tooltip_begin_base(), ui_tooltip_end_base()) -#define UI_Tooltip DeferLoop(ui_tooltip_begin(), ui_tooltip_end()) - -//- rjf: context menu -#define UI_CtxMenu(key) DeferLoopChecked(ui_begin_ctx_menu(key), ui_end_ctx_menu()) - -#endif // UI_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef UI_H +#define UI_H + +//////////////////////////////// +//~ rjf: Icon Info + +typedef enum UI_IconKind +{ + UI_IconKind_Null, + UI_IconKind_RightArrow, + UI_IconKind_DownArrow, + UI_IconKind_LeftArrow, + UI_IconKind_UpArrow, + UI_IconKind_RightCaret, + UI_IconKind_DownCaret, + UI_IconKind_LeftCaret, + UI_IconKind_UpCaret, + UI_IconKind_CheckHollow, + UI_IconKind_CheckFilled, + UI_IconKind_COUNT +} +UI_IconKind; + +typedef struct UI_IconInfo UI_IconInfo; +struct UI_IconInfo +{ + F_Tag icon_font; + String8 icon_kind_text_map[UI_IconKind_COUNT]; +}; + +//////////////////////////////// +//~ rjf: Mouse Button Kinds + +typedef enum UI_MouseButtonKind +{ + UI_MouseButtonKind_Left, + UI_MouseButtonKind_Middle, + UI_MouseButtonKind_Right, + UI_MouseButtonKind_COUNT +} +UI_MouseButtonKind; + +//////////////////////////////// +//~ rjf: Focus Types + +typedef enum UI_FocusKind +{ + UI_FocusKind_Null, + UI_FocusKind_Off, + UI_FocusKind_On, + UI_FocusKind_Root, + UI_FocusKind_COUNT +} +UI_FocusKind; + +//////////////////////////////// +//~ rjf: Events + +// TODO(rjf): clean all this up + +typedef enum UI_EventKind +{ + UI_EventKind_Null, + UI_EventKind_Press, + UI_EventKind_Release, + UI_EventKind_Text, + UI_EventKind_Navigate, + UI_EventKind_Edit, + UI_EventKind_MouseMove, + UI_EventKind_Scroll, + UI_EventKind_AutocompleteHint, + UI_EventKind_COUNT +} +UI_EventKind; + +typedef enum UI_EventActionSlot +{ + UI_EventActionSlot_Null, + UI_EventActionSlot_Accept, + UI_EventActionSlot_Cancel, + UI_EventActionSlot_Edit, + UI_EventActionSlot_COUNT +} +UI_EventActionSlot; + +typedef U32 UI_EventFlags; +enum +{ + UI_EventFlag_KeepMark = (1<<0), + UI_EventFlag_Delete = (1<<1), + UI_EventFlag_Copy = (1<<2), + UI_EventFlag_Paste = (1<<3), + UI_EventFlag_ZeroDeltaOnSelect = (1<<4), + UI_EventFlag_PickSelectSide = (1<<5), + UI_EventFlag_CapAtLine = (1<<6), + UI_EventFlag_ExplicitDirectional = (1<<7), + UI_EventFlag_Reorder = (1<<8), +}; + +typedef enum UI_EventDeltaUnit +{ + UI_EventDeltaUnit_Null, + UI_EventDeltaUnit_Char, + UI_EventDeltaUnit_Word, + UI_EventDeltaUnit_Line, + UI_EventDeltaUnit_Page, + UI_EventDeltaUnit_Whole, + UI_EventDeltaUnit_COUNT +} +UI_EventDeltaUnit; + +typedef struct UI_Event UI_Event; +struct UI_Event +{ + UI_EventKind kind; + UI_EventActionSlot slot; + UI_EventFlags flags; + UI_EventDeltaUnit delta_unit; + OS_Key key; + OS_EventFlags modifiers; + String8 string; + Vec2F32 pos; + Vec2F32 delta_2f32; + Vec2S32 delta_2s32; + U64 timestamp_us; +}; + +typedef struct UI_EventNode UI_EventNode; +struct UI_EventNode +{ + UI_EventNode *next; + UI_EventNode *prev; + UI_Event v; +}; + +typedef struct UI_EventList UI_EventList; +struct UI_EventList +{ + UI_EventNode *first; + UI_EventNode *last; + U64 count; +}; + +//////////////////////////////// +//~ rjf: Textual Operations + +typedef U32 UI_TxtOpFlags; +enum +{ + UI_TxtOpFlag_Invalid = (1<<0), + UI_TxtOpFlag_Copy = (1<<1), +}; + +typedef struct UI_TxtOp UI_TxtOp; +struct UI_TxtOp +{ + UI_TxtOpFlags flags; + String8 replace; + String8 copy; + TxtRng range; + TxtPt cursor; + TxtPt mark; +}; + +//////////////////////////////// +//~ rjf: Keys + +typedef struct UI_Key UI_Key; +struct UI_Key +{ + U64 u64[1]; +}; + +//////////////////////////////// +//~ rjf: Sizes + +typedef enum UI_SizeKind +{ + UI_SizeKind_Null, + UI_SizeKind_Pixels, // size is computed via a preferred pixel value + UI_SizeKind_TextContent, // size is computed via the dimensions of box's rendered string + UI_SizeKind_ParentPct, // size is computed via a well-determined parent or grandparent size + UI_SizeKind_ChildrenSum, // size is computed via summing well-determined sizes of children +} +UI_SizeKind; + +typedef struct UI_Size UI_Size; +struct UI_Size +{ + UI_SizeKind kind; + F32 value; + F32 strictness; +}; + +//////////////////////////////// +//~ rjf: Palettes + +typedef enum UI_ColorCode +{ + UI_ColorCode_Null, + UI_ColorCode_Background, + UI_ColorCode_Text, + UI_ColorCode_TextWeak, + UI_ColorCode_Border, + UI_ColorCode_Overlay, + UI_ColorCode_Cursor, + UI_ColorCode_Selection, + UI_ColorCode_COUNT +} +UI_ColorCode; + +typedef struct UI_Palette UI_Palette; +struct UI_Palette +{ + union + { + Vec4F32 colors[UI_ColorCode_COUNT]; + struct + { + Vec4F32 null; + Vec4F32 background; + Vec4F32 text; + Vec4F32 text_weak; + Vec4F32 border; + Vec4F32 overlay; + Vec4F32 cursor; + Vec4F32 selection; + }; + }; +}; + +typedef struct UI_WidgetPaletteInfo UI_WidgetPaletteInfo; +struct UI_WidgetPaletteInfo +{ + UI_Palette *tooltip_palette; + UI_Palette *ctx_menu_palette; + UI_Palette *scrollbar_palette; +}; + +//////////////////////////////// +//~ rjf: Animation Info + +typedef U32 UI_AnimationInfoFlags; +enum +{ + UI_AnimationInfoFlag_HotAnimations = (1<<0), + UI_AnimationInfoFlag_ActiveAnimations = (1<<1), + UI_AnimationInfoFlag_FocusAnimations = (1<<2), + UI_AnimationInfoFlag_TooltipAnimations = (1<<3), + UI_AnimationInfoFlag_ContextMenuAnimations = (1<<4), + UI_AnimationInfoFlag_ScrollingAnimations = (1<<5), + UI_AnimationInfoFlag_All = 0xffffffff, +}; + +typedef struct UI_AnimationInfo UI_AnimationInfo; +struct UI_AnimationInfo +{ + UI_AnimationInfoFlags flags; +}; + +//////////////////////////////// +//~ rjf: Scroll Positions + +typedef struct UI_ScrollPt UI_ScrollPt; +struct UI_ScrollPt +{ + S64 idx; + F32 off; +}; + +typedef union UI_ScrollPt2 UI_ScrollPt2; +union UI_ScrollPt2 +{ + UI_ScrollPt v[2]; + struct + { + UI_ScrollPt x; + UI_ScrollPt y; + }; +}; + +//////////////////////////////// +//~ rjf: Box Types + +typedef enum UI_TextAlign +{ + UI_TextAlign_Left, + UI_TextAlign_Center, + UI_TextAlign_Right, + UI_TextAlign_COUNT +} +UI_TextAlign; + +struct UI_Box; +#define UI_BOX_CUSTOM_DRAW(name) void name(struct UI_Box *box, void *user_data) +typedef UI_BOX_CUSTOM_DRAW(UI_BoxCustomDrawFunctionType); + +typedef U64 UI_BoxFlags; +//{ +//- rjf: interaction +# define UI_BoxFlag_MouseClickable (UI_BoxFlags)(1ull<<0) +# define UI_BoxFlag_KeyboardClickable (UI_BoxFlags)(1ull<<1) +# define UI_BoxFlag_DropSite (UI_BoxFlags)(1ull<<2) +# define UI_BoxFlag_ClickToFocus (UI_BoxFlags)(1ull<<3) +# define UI_BoxFlag_Scroll (UI_BoxFlags)(1ull<<4) +# define UI_BoxFlag_ViewScrollX (UI_BoxFlags)(1ull<<5) +# define UI_BoxFlag_ViewScrollY (UI_BoxFlags)(1ull<<6) +# define UI_BoxFlag_ViewClampX (UI_BoxFlags)(1ull<<7) +# define UI_BoxFlag_ViewClampY (UI_BoxFlags)(1ull<<8) +# define UI_BoxFlag_FocusHot (UI_BoxFlags)(1ull<<9) +# define UI_BoxFlag_FocusActive (UI_BoxFlags)(1ull<<10) +# define UI_BoxFlag_FocusHotDisabled (UI_BoxFlags)(1ull<<11) +# define UI_BoxFlag_FocusActiveDisabled (UI_BoxFlags)(1ull<<12) +# define UI_BoxFlag_DefaultFocusNavX (UI_BoxFlags)(1ull<<13) +# define UI_BoxFlag_DefaultFocusNavY (UI_BoxFlags)(1ull<<14) +# define UI_BoxFlag_DefaultFocusEdit (UI_BoxFlags)(1ull<<15) +# define UI_BoxFlag_FocusNavSkip (UI_BoxFlags)(1ull<<16) +# define UI_BoxFlag_DisableTruncatedHover (UI_BoxFlags)(1ull<<17) +# define UI_BoxFlag_Disabled (UI_BoxFlags)(1ull<<18) + +//- rjf: layout +# define UI_BoxFlag_FloatingX (UI_BoxFlags)(1ull<<19) +# define UI_BoxFlag_FloatingY (UI_BoxFlags)(1ull<<20) +# define UI_BoxFlag_FixedWidth (UI_BoxFlags)(1ull<<21) +# define UI_BoxFlag_FixedHeight (UI_BoxFlags)(1ull<<22) +# define UI_BoxFlag_AllowOverflowX (UI_BoxFlags)(1ull<<23) +# define UI_BoxFlag_AllowOverflowY (UI_BoxFlags)(1ull<<24) +# define UI_BoxFlag_SkipViewOffX (UI_BoxFlags)(1ull<<25) +# define UI_BoxFlag_SkipViewOffY (UI_BoxFlags)(1ull<<26) + +//- rjf: appearance / animation +# define UI_BoxFlag_DrawDropShadow (UI_BoxFlags)(1ull<<27) +# define UI_BoxFlag_DrawBackgroundBlur (UI_BoxFlags)(1ull<<28) +# define UI_BoxFlag_DrawBackground (UI_BoxFlags)(1ull<<29) +# define UI_BoxFlag_DrawBorder (UI_BoxFlags)(1ull<<30) +# define UI_BoxFlag_DrawSideTop (UI_BoxFlags)(1ull<<31) +# define UI_BoxFlag_DrawSideBottom (UI_BoxFlags)(1ull<<32) +# define UI_BoxFlag_DrawSideLeft (UI_BoxFlags)(1ull<<33) +# define UI_BoxFlag_DrawSideRight (UI_BoxFlags)(1ull<<34) +# define UI_BoxFlag_DrawText (UI_BoxFlags)(1ull<<35) +# define UI_BoxFlag_DrawTextFastpathCodepoint (UI_BoxFlags)(1ull<<36) +# define UI_BoxFlag_DrawTextWeak (UI_BoxFlags)(1ull<<37) +# define UI_BoxFlag_DrawHotEffects (UI_BoxFlags)(1ull<<38) +# define UI_BoxFlag_DrawActiveEffects (UI_BoxFlags)(1ull<<39) +# define UI_BoxFlag_DrawOverlay (UI_BoxFlags)(1ull<<40) +# define UI_BoxFlag_DrawBucket (UI_BoxFlags)(1ull<<41) +# define UI_BoxFlag_Clip (UI_BoxFlags)(1ull<<42) +# define UI_BoxFlag_AnimatePosX (UI_BoxFlags)(1ull<<43) +# define UI_BoxFlag_AnimatePosY (UI_BoxFlags)(1ull<<44) +# define UI_BoxFlag_DisableTextTrunc (UI_BoxFlags)(1ull<<45) +# define UI_BoxFlag_DisableIDString (UI_BoxFlags)(1ull<<46) +# define UI_BoxFlag_DisableFocusBorder (UI_BoxFlags)(1ull<<47) +# define UI_BoxFlag_DisableFocusOverlay (UI_BoxFlags)(1ull<<48) +# define UI_BoxFlag_HasDisplayString (UI_BoxFlags)(1ull<<49) +# define UI_BoxFlag_HasFuzzyMatchRanges (UI_BoxFlags)(1ull<<50) +# define UI_BoxFlag_RoundChildrenByParent (UI_BoxFlags)(1ull<<51) + +//- rjf: bundles +# define UI_BoxFlag_Clickable (UI_BoxFlag_MouseClickable|UI_BoxFlag_KeyboardClickable) +# define UI_BoxFlag_DefaultFocusNav (UI_BoxFlag_DefaultFocusNavX|UI_BoxFlag_DefaultFocusNavY|UI_BoxFlag_DefaultFocusEdit) +# define UI_BoxFlag_Floating (UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY) +# define UI_BoxFlag_FixedSize (UI_BoxFlag_FixedWidth|UI_BoxFlag_FixedHeight) +# define UI_BoxFlag_AllowOverflow (UI_BoxFlag_AllowOverflowX|UI_BoxFlag_AllowOverflowY) +# define UI_BoxFlag_AnimatePos (UI_BoxFlag_AnimatePosX|UI_BoxFlag_AnimatePosY) +# define UI_BoxFlag_ViewScroll (UI_BoxFlag_ViewScrollX|UI_BoxFlag_ViewScrollY) +# define UI_BoxFlag_ViewClamp (UI_BoxFlag_ViewClampX|UI_BoxFlag_ViewClampY) +# define UI_BoxFlag_DisableFocusEffects (UI_BoxFlag_DisableFocusBorder|UI_BoxFlag_DisableFocusOverlay) +//} + +typedef struct UI_Box UI_Box; +struct UI_Box +{ + //- rjf: persistent links + UI_Box *hash_next; + UI_Box *hash_prev; + + //- rjf: per-build links/data + UI_Box *first; + UI_Box *last; + UI_Box *next; + UI_Box *prev; + UI_Box *parent; + U64 child_count; + + //- rjf: per-build equipment + UI_Key key; + UI_BoxFlags flags; + String8 string; + UI_TextAlign text_align; + Vec2F32 fixed_position; + Vec2F32 fixed_size; + UI_Size pref_size[Axis2_COUNT]; + Axis2 child_layout_axis; + OS_Cursor hover_cursor; + U32 fastpath_codepoint; + UI_Key group_key; + D_Bucket *draw_bucket; + UI_BoxCustomDrawFunctionType *custom_draw; + void *custom_draw_user_data; + UI_Palette *palette; + F_Tag font; + F32 font_size; + F32 tab_size; + F_RasterFlags text_raster_flags; + F32 corner_radii[Corner_COUNT]; + F32 blur_size; + F32 transparency; + F32 squish; + F32 text_padding; + + //- rjf: per-build artifacts + D_FancyRunList display_string_runs; + Rng2F32 rect; + Vec2F32 fixed_position_animated; + Vec2F32 position_delta; + FuzzyMatchRangeList fuzzy_match_ranges; + + //- rjf: persistent data + U64 first_touched_build_index; + U64 last_touched_build_index; + U64 first_disabled_build_index; + F32 hot_t; + F32 active_t; + F32 disabled_t; + F32 focus_hot_t; + F32 focus_active_t; + F32 focus_active_disabled_t; + Vec2F32 view_off; + Vec2F32 view_off_target; + Vec2F32 view_bounds; + UI_Key default_nav_focus_hot_key; + UI_Key default_nav_focus_active_key; + UI_Key default_nav_focus_next_hot_key; + UI_Key default_nav_focus_next_active_key; +}; + +typedef struct UI_BoxRec UI_BoxRec; +struct UI_BoxRec +{ + UI_Box *next; + S32 push_count; + S32 pop_count; +}; + +typedef struct UI_BoxNode UI_BoxNode; +struct UI_BoxNode +{ + UI_BoxNode *next; + UI_Box *box; +}; + +typedef struct UI_BoxList UI_BoxList; +struct UI_BoxList +{ + UI_BoxNode *first; + UI_BoxNode *last; + U64 count; +}; + +typedef U32 UI_SignalFlags; +enum +{ + // rjf: mouse press -> box was pressed while hovering + UI_SignalFlag_LeftPressed = (1<<0), + UI_SignalFlag_MiddlePressed = (1<<1), + UI_SignalFlag_RightPressed = (1<<2), + + // rjf: dragging -> box was previously pressed, user is still holding button + UI_SignalFlag_LeftDragging = (1<<3), + UI_SignalFlag_MiddleDragging = (1<<4), + UI_SignalFlag_RightDragging = (1<<5), + + // rjf: double-dragging -> box was previously double-clicked, user is still holding button + UI_SignalFlag_LeftDoubleDragging = (1<<6), + UI_SignalFlag_MiddleDoubleDragging= (1<<7), + UI_SignalFlag_RightDoubleDragging = (1<<8), + + // rjf: triple-dragging -> box was previously triple-clicked, user is still holding button + UI_SignalFlag_LeftTripleDragging = (1<<9), + UI_SignalFlag_MiddleTripleDragging= (1<<10), + UI_SignalFlag_RightTripleDragging = (1<<11), + + // rjf: released -> box was previously pressed & user released, in or out of bounds + UI_SignalFlag_LeftReleased = (1<<12), + UI_SignalFlag_MiddleReleased = (1<<13), + UI_SignalFlag_RightReleased = (1<<14), + + // rjf: clicked -> box was previously pressed & user released, in bounds + UI_SignalFlag_LeftClicked = (1<<15), + UI_SignalFlag_MiddleClicked = (1<<16), + UI_SignalFlag_RightClicked = (1<<17), + + // rjf: double clicked -> box was previously clicked, pressed again + UI_SignalFlag_LeftDoubleClicked = (1<<18), + UI_SignalFlag_MiddleDoubleClicked = (1<<19), + UI_SignalFlag_RightDoubleClicked = (1<<20), + + // rjf: triple clicked -> box was previously clicked twice, pressed again + UI_SignalFlag_LeftTripleClicked = (1<<21), + UI_SignalFlag_MiddleTripleClicked = (1<<22), + UI_SignalFlag_RightTripleClicked = (1<<23), + + // rjf: keyboard pressed -> box had focus, user activated via their keyboard + UI_SignalFlag_KeyboardPressed = (1<<24), + + // rjf: passive mouse info + UI_SignalFlag_Hovering = (1<<25), // hovering specifically this box + UI_SignalFlag_MouseOver = (1<<26), // mouse is over, but may be occluded + + // rjf: committing state changes via user interaction + UI_SignalFlag_Commit = (1<<27), + + // rjf: high-level combos + UI_SignalFlag_Pressed = UI_SignalFlag_LeftPressed|UI_SignalFlag_KeyboardPressed, + UI_SignalFlag_Released = UI_SignalFlag_LeftReleased, + UI_SignalFlag_Clicked = UI_SignalFlag_LeftClicked|UI_SignalFlag_KeyboardPressed, + UI_SignalFlag_DoubleClicked = UI_SignalFlag_LeftDoubleClicked, + UI_SignalFlag_TripleClicked = UI_SignalFlag_LeftTripleClicked, + UI_SignalFlag_Dragging = UI_SignalFlag_LeftDragging, +}; + +typedef struct UI_Signal UI_Signal; +struct UI_Signal +{ + UI_Box *box; + OS_EventFlags event_flags; + Vec2S16 scroll; + UI_SignalFlags f; +}; + +#define ui_pressed(s) !!((s).f&UI_SignalFlag_Pressed) +#define ui_clicked(s) !!((s).f&UI_SignalFlag_Clicked) +#define ui_released(s) !!((s).f&UI_SignalFlag_Released) +#define ui_double_clicked(s) !!((s).f&UI_SignalFlag_DoubleClicked) +#define ui_triple_clicked(s) !!((s).f&UI_SignalFlag_TripleClicked) +#define ui_middle_clicked(s) !!((s).f&UI_SignalFlag_MiddleClicked) +#define ui_right_clicked(s) !!((s).f&UI_SignalFlag_RightClicked) +#define ui_dragging(s) !!((s).f&UI_SignalFlag_Dragging) +#define ui_hovering(s) !!((s).f&UI_SignalFlag_Hovering) +#define ui_mouse_over(s) !!((s).f&UI_SignalFlag_MouseOver) +#define ui_committed(s) !!((s).f&UI_SignalFlag_Commit) + +typedef struct UI_Nav UI_Nav; +struct UI_Nav +{ + B32 moved; + Vec2S64 new_p; +}; + +//////////////////////////////// +//~ rjf: Generated Code + +#include "generated/ui.meta.h" + +//////////////////////////////// +//~ rjf: State Types + +typedef struct UI_BoxHashSlot UI_BoxHashSlot; +struct UI_BoxHashSlot +{ + UI_Box *hash_first; + UI_Box *hash_last; +}; + +typedef struct UI_State UI_State; +struct UI_State +{ + //- rjf: main arena + Arena *arena; + + //- rjf: build arenas + Arena *build_arenas[2]; + U64 build_index; + + //- rjf: box cache + UI_Box *first_free_box; + U64 box_table_size; + UI_BoxHashSlot *box_table; + + //- rjf: build state machine state + B32 is_in_open_ctx_menu; + + //- rjf: build phase output + UI_Box *root; + UI_Box *tooltip_root; + UI_Box *ctx_menu_root; + UI_Key default_nav_root_key; + U64 build_box_count; + U64 last_build_box_count; + B32 ctx_menu_touched_this_frame; + B32 is_animating; + + //- rjf: build parameters + UI_IconInfo icon_info; + UI_WidgetPaletteInfo widget_palette_info; + UI_AnimationInfo animation_info; + OS_Handle window; + UI_EventList *events; + Vec2F32 mouse; + F32 animation_dt; + + //- rjf: user interaction state + UI_Key hot_box_key; + UI_Key active_box_key[UI_MouseButtonKind_COUNT]; + UI_Key drop_hot_box_key; + UI_Key clipboard_copy_key; + U64 press_timestamp_history_us[UI_MouseButtonKind_COUNT][3]; + UI_Key press_key_history[UI_MouseButtonKind_COUNT][3]; + Vec2F32 press_pos_history[UI_MouseButtonKind_COUNT][3]; + Vec2F32 drag_start_mouse; + Arena *drag_state_arena; + String8 drag_state_data; + Arena *string_hover_arena; + String8 string_hover_string; + D_FancyRunList string_hover_fancy_runs; + U64 string_hover_begin_us; + U64 string_hover_build_index; + U64 last_time_mousemoved_us; + + //- rjf: tooltip state + F32 tooltip_open_t; + B32 tooltip_open; + + //- rjf: context menu state + UI_Key ctx_menu_anchor_key; + UI_Key next_ctx_menu_anchor_key; + Vec2F32 ctx_menu_anchor_box_last_pos; + Vec2F32 ctx_menu_anchor_off; + B32 ctx_menu_open; + B32 next_ctx_menu_open; + F32 ctx_menu_open_t; + UI_Key ctx_menu_key; + B32 ctx_menu_changed; + + //- rjf: build phase stacks + UI_DeclStackNils; + UI_DeclStacks; +}; + +//////////////////////////////// +//~ rjf: Basic Type Functions + +internal U64 ui_hash_from_string(U64 seed, String8 string); +internal String8 ui_hash_part_from_key_string(String8 string); +internal String8 ui_display_part_from_key_string(String8 string); +internal UI_Key ui_key_zero(void); +internal UI_Key ui_key_make(U64 v); +internal UI_Key ui_key_from_string(UI_Key seed_key, String8 string); +internal UI_Key ui_key_from_stringf(UI_Key seed_key, char *fmt, ...); +internal B32 ui_key_match(UI_Key a, UI_Key b); + +//////////////////////////////// +//~ rjf: Event Type Functions + +internal UI_EventNode *ui_event_list_push(Arena *arena, UI_EventList *list, UI_Event *v); +internal void ui_eat_event(UI_EventList *list, UI_EventNode *node); + +//////////////////////////////// +//~ rjf: Text Operation Functions + +internal B32 ui_char_is_scan_boundary(U8 c); +internal S64 ui_scanned_column_from_column(String8 string, S64 start_column, Side side); +internal UI_TxtOp ui_single_line_txt_op_from_event(Arena *arena, UI_Event *event, String8 string, TxtPt cursor, TxtPt mark); +internal String8 ui_push_string_replace_range(Arena *arena, String8 string, Rng1S64 range, String8 replace); + +//////////////////////////////// +//~ rjf: Size Type Functions + +internal UI_Size ui_size(UI_SizeKind kind, F32 value, F32 strictness); +#define ui_px(value, strictness) ui_size(UI_SizeKind_Pixels, value, strictness) +#define ui_em(value, strictness) ui_size(UI_SizeKind_Pixels, (value) * ui_top_font_size(), strictness) +#define ui_text_dim(padding, strictness) ui_size(UI_SizeKind_TextContent, padding, strictness) +#define ui_pct(value, strictness) ui_size(UI_SizeKind_ParentPct, value, strictness) +#define ui_children_sum(strictness) ui_size(UI_SizeKind_ChildrenSum, 0.f, strictness) + +//////////////////////////////// +//~ rjf: Color Scheme Type Functions + +read_only global UI_Palette ui_g_nil_palette = {0}; + +//////////////////////////////// +//~ rjf: Scroll Point Type Functions + +internal UI_ScrollPt ui_scroll_pt(S64 idx, F32 off); +internal void ui_scroll_pt_target_idx(UI_ScrollPt *v, S64 idx); +internal void ui_scroll_pt_clamp_idx(UI_ScrollPt *v, Rng1S64 range); + +//////////////////////////////// +//~ rjf: Box Type Functions + +read_only global UI_Box ui_g_nil_box = +{ + &ui_g_nil_box, + &ui_g_nil_box, + &ui_g_nil_box, + &ui_g_nil_box, + &ui_g_nil_box, + &ui_g_nil_box, + &ui_g_nil_box, +}; +internal B32 ui_box_is_nil(UI_Box *box); +internal UI_BoxRec ui_box_rec_df(UI_Box *box, UI_Box *root, U64 sib_member_off, U64 child_member_off); +#define ui_box_rec_df_pre(box, root) ui_box_rec_df(box, root, OffsetOf(UI_Box, next), OffsetOf(UI_Box, first)) +#define ui_box_rec_df_post(box, root) ui_box_rec_df(box, root, OffsetOf(UI_Box, prev), OffsetOf(UI_Box, last)) +internal void ui_box_list_push(Arena *arena, UI_BoxList *list, UI_Box *box); + +//////////////////////////////// +//~ rjf: State Allocating / Selection + +internal UI_State *ui_state_alloc(void); +internal void ui_state_release(UI_State *state); +internal UI_Box * ui_root_from_state(UI_State *state); +internal B32 ui_animating_from_state(UI_State *state); +internal void ui_select_state(UI_State *state); +internal UI_State *ui_get_selected_state(void); + +//////////////////////////////// +//~ rjf: Implicit State Accessors/Mutators + +//- rjf: per-frame info +internal Arena * ui_build_arena(void); +internal OS_Handle ui_window(void); +internal UI_EventList * ui_events(void); +internal Vec2F32 ui_mouse(void); +internal F_Tag ui_icon_font(void); +internal String8 ui_icon_string_from_kind(UI_IconKind icon_kind); +internal F32 ui_dt(void); + +//- rjf: event consumption helpers +internal B32 ui_key_press(OS_EventFlags mods, OS_Key key); +internal B32 ui_key_release(OS_EventFlags mods, OS_Key key); +internal B32 ui_text(U32 character); +internal B32 ui_slot_press(UI_EventActionSlot slot); + +//- rjf: drag data +internal Vec2F32 ui_drag_start_mouse(void); +internal Vec2F32 ui_drag_delta(void); +internal void ui_store_drag_data(String8 string); +internal String8 ui_get_drag_data(U64 min_required_size); +#define ui_store_drag_struct(ptr) ui_store_drag_data(str8_struct(ptr)) +#define ui_get_drag_struct(type) ((type *)ui_get_drag_data(sizeof(type)).str) + +//- rjf: hovered string info +internal B32 ui_string_hover_active(void); +internal D_FancyRunList ui_string_hover_runs(Arena *arena); + +//- rjf: interaction keys +internal UI_Key ui_hot_key(void); +internal UI_Key ui_active_key(UI_MouseButtonKind button_kind); +internal UI_Key ui_drop_hot_key(void); + +//- rjf: controls over interaction +internal void ui_kill_action(void); + +//- rjf: box cache lookup +internal UI_Box * ui_box_from_key(UI_Key key); + +//////////////////////////////// +//~ rjf: Top-Level Building API + +internal void ui_begin_build(OS_Handle window, UI_EventList *events, UI_IconInfo *icon_info, UI_WidgetPaletteInfo *widget_palette_info, UI_AnimationInfo *animation_info, F32 real_dt, F32 animation_dt); +internal void ui_end_build(void); +internal void ui_calc_sizes_standalone__in_place_rec(UI_Box *root, Axis2 axis); +internal void ui_calc_sizes_upwards_dependent__in_place_rec(UI_Box *root, Axis2 axis); +internal void ui_calc_sizes_downwards_dependent__in_place_rec(UI_Box *root, Axis2 axis); +internal void ui_layout_enforce_constraints__in_place_rec(UI_Box *root, Axis2 axis); +internal void ui_layout_position__in_place_rec(UI_Box *root, Axis2 axis); +internal void ui_layout_root(UI_Box *root, Axis2 axis); + +//////////////////////////////// +//~ rjf: Box Tree Building API + +//- rjf: spacers +internal UI_Signal ui_spacer(UI_Size size); + +//- rjf: tooltips +internal void ui_tooltip_begin_base(void); +internal void ui_tooltip_end_base(void); +internal void ui_tooltip_begin(void); +internal void ui_tooltip_end(void); + +//- rjf: context menus +internal void ui_ctx_menu_open(UI_Key key, UI_Key anchor_box_key, Vec2F32 anchor_off); +internal void ui_ctx_menu_close(void); +internal B32 ui_begin_ctx_menu(UI_Key key); +internal void ui_end_ctx_menu(void); +internal B32 ui_ctx_menu_is_open(UI_Key key); +internal B32 ui_any_ctx_menu_is_open(void); + +//- rjf: focus tree coloring +internal B32 ui_is_focus_hot(void); +internal B32 ui_is_focus_active(void); + +//- rjf: implicit auto-managed tree-based focus state +internal B32 ui_is_key_auto_focus_active(UI_Key key); +internal B32 ui_is_key_auto_focus_hot(UI_Key key); +internal void ui_set_auto_focus_active_key(UI_Key key); +internal void ui_set_auto_focus_hot_key(UI_Key key); + +//- rjf: palette forming +internal UI_Palette * ui_build_palette_(UI_Palette *base, UI_Palette *overrides); +#define ui_build_palette(base, ...) ui_build_palette_((base), &(UI_Palette){.text = v4f32(0, 0, 0, 0), __VA_ARGS__}) + +//- rjf: box node construction +internal UI_Box * ui_build_box_from_key(UI_BoxFlags flags, UI_Key key); +internal UI_Key ui_active_seed_key(void); +internal UI_Box * ui_build_box_from_string(UI_BoxFlags flags, String8 string); +internal UI_Box * ui_build_box_from_stringf(UI_BoxFlags flags, char *fmt, ...); + +//- rjf: box node equipment +internal inline void ui_box_equip_display_string(UI_Box *box, String8 string); +internal inline void ui_box_equip_display_fancy_strings(UI_Box *box, D_FancyStringList *strings); +internal inline void ui_box_equip_display_string_fancy_runs(UI_Box *box, String8 string, D_FancyRunList *runs); +internal inline void ui_box_equip_fuzzy_match_ranges(UI_Box *box, FuzzyMatchRangeList *matches); +internal inline void ui_box_equip_draw_bucket(UI_Box *box, D_Bucket *bucket); +internal inline void ui_box_equip_custom_draw(UI_Box *box, UI_BoxCustomDrawFunctionType *custom_draw, void *user_data); + +//- rjf: box accessors / queries +internal String8 ui_box_display_string(UI_Box *box); +internal Vec2F32 ui_box_text_position(UI_Box *box); +internal U64 ui_box_char_pos_from_xy(UI_Box *box, Vec2F32 xy); + +//////////////////////////////// +//~ rjf: User Interaction + +internal UI_Signal ui_signal_from_box(UI_Box *box); + +//////////////////////////////// +//~ rjf: Stacks + +//- rjf: base +internal UI_Box * ui_top_parent(void); +internal Axis2 ui_top_child_layout_axis(void); +internal F32 ui_top_fixed_x(void); +internal F32 ui_top_fixed_y(void); +internal F32 ui_top_fixed_width(void); +internal F32 ui_top_fixed_height(void); +internal UI_Size ui_top_pref_width(void); +internal UI_Size ui_top_pref_height(void); +internal UI_BoxFlags ui_top_flags(void); +internal UI_FocusKind ui_top_focus_hot(void); +internal UI_FocusKind ui_top_focus_active(void); +internal U32 ui_top_fastpath_codepoint(void); +internal UI_Key ui_top_group_key(void); +internal F32 ui_top_transparency(void); +internal UI_Palette* ui_top_palette(void); +internal F32 ui_top_squish(void); +internal OS_Cursor ui_top_hover_cursor(void); +internal F_Tag ui_top_font(void); +internal F32 ui_top_font_size(void); +internal F_RasterFlags ui_top_text_raster_flags(void); +internal F32 ui_top_tab_size(void); +internal F32 ui_top_corner_radius_00(void); +internal F32 ui_top_corner_radius_01(void); +internal F32 ui_top_corner_radius_10(void); +internal F32 ui_top_corner_radius_11(void); +internal F32 ui_top_blur_size(void); +internal F32 ui_top_text_padding(void); +internal UI_TextAlign ui_top_text_alignment(void); +internal UI_Box * ui_bottom_parent(void); +internal Axis2 ui_bottom_child_layout_axis(void); +internal F32 ui_bottom_fixed_x(void); +internal F32 ui_bottom_fixed_y(void); +internal F32 ui_bottom_fixed_width(void); +internal F32 ui_bottom_fixed_height(void); +internal UI_Size ui_bottom_pref_width(void); +internal UI_Size ui_bottom_pref_height(void); +internal UI_BoxFlags ui_bottom_flags(void); +internal UI_FocusKind ui_bottom_focus_hot(void); +internal UI_FocusKind ui_bottom_focus_active(void); +internal U32 ui_bottom_fastpath_codepoint(void); +internal UI_Key ui_bottom_group_key(void); +internal F32 ui_bottom_transparency(void); +internal UI_Palette* ui_bottom_palette(void); +internal F32 ui_bottom_squish(void); +internal OS_Cursor ui_bottom_hover_cursor(void); +internal F_Tag ui_bottom_font(void); +internal F32 ui_bottom_font_size(void); +internal F_RasterFlags ui_bottom_text_raster_flags(void); +internal F32 ui_bottom_tab_size(void); +internal F32 ui_bottom_corner_radius_00(void); +internal F32 ui_bottom_corner_radius_01(void); +internal F32 ui_bottom_corner_radius_10(void); +internal F32 ui_bottom_corner_radius_11(void); +internal F32 ui_bottom_blur_size(void); +internal F32 ui_bottom_text_padding(void); +internal UI_TextAlign ui_bottom_text_alignment(void); +internal UI_Box * ui_push_parent(UI_Box * v); +internal Axis2 ui_push_child_layout_axis(Axis2 v); +internal F32 ui_push_fixed_x(F32 v); +internal F32 ui_push_fixed_y(F32 v); +internal F32 ui_push_fixed_width(F32 v); +internal F32 ui_push_fixed_height(F32 v); +internal UI_Size ui_push_pref_width(UI_Size v); +internal UI_Size ui_push_pref_height(UI_Size v); +internal UI_BoxFlags ui_push_flags(UI_BoxFlags v); +internal UI_FocusKind ui_push_focus_hot(UI_FocusKind v); +internal UI_FocusKind ui_push_focus_active(UI_FocusKind v); +internal U32 ui_push_fastpath_codepoint(U32 v); +internal UI_Key ui_push_group_key(UI_Key v); +internal F32 ui_push_transparency(F32 v); +internal UI_Palette* ui_push_palette(UI_Palette* v); +internal F32 ui_push_squish(F32 v); +internal OS_Cursor ui_push_hover_cursor(OS_Cursor v); +internal F_Tag ui_push_font(F_Tag v); +internal F32 ui_push_font_size(F32 v); +internal F_RasterFlags ui_push_text_raster_flags(F_RasterFlags v); +internal F32 ui_push_tab_size(F32 v); +internal F32 ui_push_corner_radius_00(F32 v); +internal F32 ui_push_corner_radius_01(F32 v); +internal F32 ui_push_corner_radius_10(F32 v); +internal F32 ui_push_corner_radius_11(F32 v); +internal F32 ui_push_blur_size(F32 v); +internal F32 ui_push_text_padding(F32 v); +internal UI_TextAlign ui_push_text_alignment(UI_TextAlign v); +internal UI_Box * ui_pop_parent(void); +internal Axis2 ui_pop_child_layout_axis(void); +internal F32 ui_pop_fixed_x(void); +internal F32 ui_pop_fixed_y(void); +internal F32 ui_pop_fixed_width(void); +internal F32 ui_pop_fixed_height(void); +internal UI_Size ui_pop_pref_width(void); +internal UI_Size ui_pop_pref_height(void); +internal UI_BoxFlags ui_pop_flags(void); +internal UI_FocusKind ui_pop_focus_hot(void); +internal UI_FocusKind ui_pop_focus_active(void); +internal U32 ui_pop_fastpath_codepoint(void); +internal UI_Key ui_pop_group_key(void); +internal F32 ui_pop_transparency(void); +internal UI_Palette* ui_pop_palette(void); +internal F32 ui_pop_squish(void); +internal OS_Cursor ui_pop_hover_cursor(void); +internal F_Tag ui_pop_font(void); +internal F32 ui_pop_font_size(void); +internal F_RasterFlags ui_pop_text_raster_flags(void); +internal F32 ui_pop_tab_size(void); +internal F32 ui_pop_corner_radius_00(void); +internal F32 ui_pop_corner_radius_01(void); +internal F32 ui_pop_corner_radius_10(void); +internal F32 ui_pop_corner_radius_11(void); +internal F32 ui_pop_blur_size(void); +internal F32 ui_pop_text_padding(void); +internal UI_TextAlign ui_pop_text_alignment(void); +internal UI_Box * ui_set_next_parent(UI_Box * v); +internal Axis2 ui_set_next_child_layout_axis(Axis2 v); +internal F32 ui_set_next_fixed_x(F32 v); +internal F32 ui_set_next_fixed_y(F32 v); +internal F32 ui_set_next_fixed_width(F32 v); +internal F32 ui_set_next_fixed_height(F32 v); +internal UI_Size ui_set_next_pref_width(UI_Size v); +internal UI_Size ui_set_next_pref_height(UI_Size v); +internal UI_BoxFlags ui_set_next_flags(UI_BoxFlags v); +internal UI_FocusKind ui_set_next_focus_hot(UI_FocusKind v); +internal UI_FocusKind ui_set_next_focus_active(UI_FocusKind v); +internal U32 ui_set_next_fastpath_codepoint(U32 v); +internal UI_Key ui_set_next_group_key(UI_Key v); +internal F32 ui_set_next_transparency(F32 v); +internal UI_Palette* ui_set_next_palette(UI_Palette* v); +internal F32 ui_set_next_squish(F32 v); +internal OS_Cursor ui_set_next_hover_cursor(OS_Cursor v); +internal F_Tag ui_set_next_font(F_Tag v); +internal F32 ui_set_next_font_size(F32 v); +internal F_RasterFlags ui_set_next_text_raster_flags(F_RasterFlags v); +internal F32 ui_set_next_tab_size(F32 v); +internal F32 ui_set_next_corner_radius_00(F32 v); +internal F32 ui_set_next_corner_radius_01(F32 v); +internal F32 ui_set_next_corner_radius_10(F32 v); +internal F32 ui_set_next_corner_radius_11(F32 v); +internal F32 ui_set_next_blur_size(F32 v); +internal F32 ui_set_next_text_padding(F32 v); +internal UI_TextAlign ui_set_next_text_alignment(UI_TextAlign v); + +//- rjf: helpers +internal Rng2F32 ui_push_rect(Rng2F32 rect); +internal Rng2F32 ui_pop_rect(void); +internal void ui_set_next_rect(Rng2F32 rect); +internal UI_Size ui_push_pref_size(Axis2 axis, UI_Size v); +internal UI_Size ui_pop_pref_size(Axis2 axis); +internal UI_Size ui_set_next_pref_size(Axis2 axis, UI_Size v); +internal void ui_push_corner_radius(F32 v); +internal void ui_pop_corner_radius(void); + +//////////////////////////////// +//~ rjf: Macro Loop Wrappers + +//- rjf: stacks (base) +#define UI_Parent(v) DeferLoop(ui_push_parent(v), ui_pop_parent()) +#define UI_ChildLayoutAxis(v) DeferLoop(ui_push_child_layout_axis(v), ui_pop_child_layout_axis()) +#define UI_FixedX(v) DeferLoop(ui_push_fixed_x(v), ui_pop_fixed_x()) +#define UI_FixedY(v) DeferLoop(ui_push_fixed_y(v), ui_pop_fixed_y()) +#define UI_FixedWidth(v) DeferLoop(ui_push_fixed_width(v), ui_pop_fixed_width()) +#define UI_FixedHeight(v) DeferLoop(ui_push_fixed_height(v), ui_pop_fixed_height()) +#define UI_PrefWidth(v) DeferLoop(ui_push_pref_width(v), ui_pop_pref_width()) +#define UI_PrefHeight(v) DeferLoop(ui_push_pref_height(v), ui_pop_pref_height()) +#define UI_Flags(v) DeferLoop(ui_push_flags(v), ui_pop_flags()) +#define UI_FocusHot(v) DeferLoop(ui_push_focus_hot(v), ui_pop_focus_hot()) +#define UI_FocusActive(v) DeferLoop(ui_push_focus_active(v), ui_pop_focus_active()) +#define UI_FastpathCodepoint(v) DeferLoop(ui_push_fastpath_codepoint(v), ui_pop_fastpath_codepoint()) +#define UI_GroupKey(v) DeferLoop(ui_push_group_key(v), ui_pop_group_key()) +#define UI_Transparency(v) DeferLoop(ui_push_transparency(v), ui_pop_transparency()) +#define UI_Palette(v) DeferLoop(ui_push_palette(v), ui_pop_palette()) +#define UI_Squish(v) DeferLoop(ui_push_squish(v), ui_pop_squish()) +#define UI_HoverCursor(v) DeferLoop(ui_push_hover_cursor(v), ui_pop_hover_cursor()) +#define UI_Font(v) DeferLoop(ui_push_font(v), ui_pop_font()) +#define UI_FontSize(v) DeferLoop(ui_push_font_size(v), ui_pop_font_size()) +#define UI_TextRasterFlags(v) DeferLoop(ui_push_text_raster_flags(v), ui_pop_text_raster_flags()) +#define UI_TabSize(v) DeferLoop(ui_push_tab_size(v), ui_pop_tab_size()) +#define UI_CornerRadius00(v) DeferLoop(ui_push_corner_radius_00(v), ui_pop_corner_radius_00()) +#define UI_CornerRadius01(v) DeferLoop(ui_push_corner_radius_01(v), ui_pop_corner_radius_01()) +#define UI_CornerRadius10(v) DeferLoop(ui_push_corner_radius_10(v), ui_pop_corner_radius_10()) +#define UI_CornerRadius11(v) DeferLoop(ui_push_corner_radius_11(v), ui_pop_corner_radius_11()) +#define UI_BlurSize(v) DeferLoop(ui_push_blur_size(v), ui_pop_blur_size()) +#define UI_TextPadding(v) DeferLoop(ui_push_text_padding(v), ui_pop_text_padding()) +#define UI_TextAlignment(v) DeferLoop(ui_push_text_alignment(v), ui_pop_text_alignment()) + +//- rjf: stacks (compositions) +#define UI_WidthFill UI_PrefWidth(ui_pct(1.f, 0.f)) +#define UI_HeightFill UI_PrefHeight(ui_pct(1.f, 0.f)) +#define UI_Rect(r) DeferLoop(ui_push_rect(r), ui_pop_rect()) +#define UI_PrefSize(axis, v) DeferLoop(ui_push_pref_size((axis), (v)), ui_pop_pref_size(axis)) +#define UI_CornerRadius(v) DeferLoop(ui_push_corner_radius(v), ui_pop_corner_radius()) +#define UI_Focus(kind) DeferLoop((ui_push_focus_hot(kind), ui_push_focus_active(kind)), (ui_pop_focus_hot(), ui_pop_focus_active())) +#define UI_FlagsAdd(v) DeferLoop(ui_push_flags(ui_top_flags()|(v)), ui_pop_flags()) + +//- rjf: tooltip +#define UI_TooltipBase DeferLoop(ui_tooltip_begin_base(), ui_tooltip_end_base()) +#define UI_Tooltip DeferLoop(ui_tooltip_begin(), ui_tooltip_end()) + +//- rjf: context menu +#define UI_CtxMenu(key) DeferLoopChecked(ui_begin_ctx_menu(key), ui_end_ctx_menu()) + +#endif // UI_H diff --git a/src/ui/ui_inc.c b/src/ui/ui_inc.c index dcedafd5..50390e9b 100644 --- a/src/ui/ui_inc.c +++ b/src/ui/ui_inc.c @@ -1,8 +1,8 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#undef RADDBG_LAYER_COLOR -#define RADDBG_LAYER_COLOR 0.70f, 0.30f, 0.15f - -#include "ui_core.c" -#include "ui_basic_widgets.c" +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#undef RADDBG_LAYER_COLOR +#define RADDBG_LAYER_COLOR 0.70f, 0.30f, 0.15f + +#include "ui_core.c" +#include "ui_basic_widgets.c" diff --git a/src/ui/ui_inc.h b/src/ui/ui_inc.h index d071b15c..2b763f3b 100644 --- a/src/ui/ui_inc.h +++ b/src/ui/ui_inc.h @@ -1,10 +1,10 @@ -// Copyright (c) 2024 Epic Games Tools -// Licensed under the MIT license (https://opensource.org/license/mit/) - -#ifndef UI_INC_H -#define UI_INC_H - -#include "ui_core.h" -#include "ui_basic_widgets.h" - -#endif // UI_INC_H +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef UI_INC_H +#define UI_INC_H + +#include "ui_core.h" +#include "ui_basic_widgets.h" + +#endif // UI_INC_H